wake lock: Implement new proposed API.

This is another big update to the implementation, which now follows the
API proposed in https://github.com/w3c/wake-lock/issues/226.

From a Blink/Chromium internals perspective, things have not changed much,
and we still perform the same permission checks and connect to the wake lock
service via WakeLockStateRecord. The biggest change is that WakeLockController
was merged into WakeLock itself.

From an end-user perspective, though, the API now looks like a mix of the
old, navigator-based API and the one we implemented a few months ago:

* The main idea is that WakeLock.request() no longer returns a promise that
  does not resolve while the lock is held, as that is not very intuitive.
  Instead, it returns a new WakeLockSentinel object.
* WakeLockSentinel is a new interface that can release() a lock and receive
  "release" events.
* We do go back to a Navigator-based API, but we also support
  WorkerNavigator.
* There is no WakeLockRequest interface or state tracking in the WakeLock
  interface.
* WakeLock.requestPermission() was removed.
* Consequently, we now have a WakeLockEvent interface.

The changes to the spec still need to land, but there is some rough
consensus on what the API should look like, and we do not want to miss the
M79 branch cut.

Bug: 257511, 1010162
Change-Id: If1baed914ccd9eb7103a1bb71a40529976777ec2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1849683
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Bo <boliu@chromium.org>
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Raphael Kubo da Costa <raphael.kubo.da.costa@intel.com>
Cr-Commit-Position: refs/heads/master@{#705632}
diff --git a/chrome/browser/wake_lock/wake_lock_browsertest.cc b/chrome/browser/wake_lock/wake_lock_browsertest.cc
index 051c1f33..17f230f 100644
--- a/chrome/browser/wake_lock/wake_lock_browsertest.cc
+++ b/chrome/browser/wake_lock/wake_lock_browsertest.cc
@@ -119,7 +119,8 @@
   PermissionRequestObserver observer(
       browser()->tab_strip_model()->GetActiveWebContents());
   const std::string kWorkerScript =
-      "WakeLock.request('screen').catch(err => self.postMessage(err.name))";
+      "navigator.wakeLock.request('screen').catch(err => "
+      "    self.postMessage(err.name))";
   NavigateToAndRespondWithScript(
       "/workers/create_dedicated_worker.html?worker_url=/js-response",
       kWorkerScript);
@@ -136,7 +137,8 @@
   PermissionRequestObserver observer(
       browser()->tab_strip_model()->GetActiveWebContents());
   const std::string kWorkerScript =
-      "WakeLock.request('system').catch(err => self.postMessage(err.name))";
+      "navigator.wakeLock.request('system').catch(err => "
+      "    self.postMessage(err.name))";
   NavigateToAndRespondWithScript(
       "/workers/create_dedicated_worker.html?worker_url=/js-response",
       kWorkerScript);
@@ -156,7 +158,8 @@
       browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_EQ("granted", content::EvalJs(
                            browser()->tab_strip_model()->GetActiveWebContents(),
-                           "WakeLock.requestPermission('screen')"));
+                           "navigator.wakeLock.request('screen').then(lock => {"
+                           "    lock.release(); return 'granted'; });"));
   EXPECT_EQ(observer.request_shown(), false);
 }
 
@@ -171,7 +174,8 @@
   EXPECT_EQ(
       "granted",
       content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                      "WakeLock.requestPermission('screen')",
+                      "navigator.wakeLock.request('screen').then(lock => {"
+                      "    lock.release(); return 'granted'; });",
                       content::EvalJsOptions::EXECUTE_SCRIPT_NO_USER_GESTURE));
   EXPECT_EQ(observer.request_shown(), false);
 }
@@ -183,9 +187,11 @@
 
   PermissionRequestObserver observer(
       browser()->tab_strip_model()->GetActiveWebContents());
-  EXPECT_EQ("denied", content::EvalJs(
-                          browser()->tab_strip_model()->GetActiveWebContents(),
-                          "WakeLock.requestPermission('system')"));
+  EXPECT_EQ(
+      "NotAllowedError",
+      content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+                      "navigator.wakeLock.request('system').catch(err => {"
+                      "    return err.name; });"));
   EXPECT_EQ(observer.request_shown(), false);
 }
 
@@ -198,9 +204,10 @@
   PermissionRequestObserver observer(
       browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_EQ(
-      "denied",
+      "NotAllowedError",
       content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                      "WakeLock.requestPermission('system')",
+                      "navigator.wakeLock.request('system').catch(err => {"
+                      "    return err.name; });",
                       content::EvalJsOptions::EXECUTE_SCRIPT_NO_USER_GESTURE));
   EXPECT_EQ(observer.request_shown(), false);
 }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/WakeLockTest.java b/content/public/android/javatests/src/org/chromium/content/browser/WakeLockTest.java
index 36eb758..57c1fdb 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/WakeLockTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/WakeLockTest.java
@@ -44,7 +44,7 @@
     }
 
     private void getWakeLock(String type) throws TimeoutException {
-        final String code = "WakeLock.request('" + type + "');";
+        final String code = "navigator.wakeLock.request('" + type + "');";
         JavaScriptUtils.executeJavaScriptAndWaitForResult(mActivityTestRule.getWebContents(), code);
     }
 
diff --git a/third_party/blink/renderer/bindings/modules/BUILD.gn b/third_party/blink/renderer/bindings/modules/BUILD.gn
index 0f17d1f..3e85261 100644
--- a/third_party/blink/renderer/bindings/modules/BUILD.gn
+++ b/third_party/blink/renderer/bindings/modules/BUILD.gn
@@ -69,6 +69,7 @@
     "//third_party/blink/renderer/modules/speech/speech_synthesis_event.idl",
     "//third_party/blink/renderer/modules/storage/storage_event.idl",
     "//third_party/blink/renderer/modules/vr/vr_display_event.idl",
+    "//third_party/blink/renderer/modules/wake_lock/wake_lock_event.idl",
     "//third_party/blink/renderer/modules/webaudio/audio_processing_event.idl",
     "//third_party/blink/renderer/modules/webaudio/offline_audio_completion_event.idl",
     "//third_party/blink/renderer/modules/webgl/webgl_context_event.idl",
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index 4ac3f41..9d613885 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -229,6 +229,7 @@
     "reading",
     "readystatechange",
     "rejectionhandled",
+    "release",
     "removesourcebuffer",
     "removestream",
     "removetrack",
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 732eb32..baa98be 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -401,8 +401,9 @@
     "service_worker/service_worker_timeout_timer_test.cc",
     "service_worker/thread_safe_script_container_test.cc",
     "service_worker/web_embedded_worker_impl_test.cc",
-    "wake_lock/wake_lock_controller_test.cc",
+    "wake_lock/wake_lock_sentinel_test.cc",
     "wake_lock/wake_lock_state_record_test.cc",
+    "wake_lock/wake_lock_test.cc",
     "wake_lock/wake_lock_test_utils.cc",
     "wake_lock/wake_lock_test_utils.h",
     "webaudio/audio_basic_processor_handler_test.cc",
diff --git a/third_party/blink/renderer/modules/event_target_modules_names.json5 b/third_party/blink/renderer/modules/event_target_modules_names.json5
index 3bb3fe8..3445dc8 100644
--- a/third_party/blink/renderer/modules/event_target_modules_names.json5
+++ b/third_party/blink/renderer/modules/event_target_modules_names.json5
@@ -77,5 +77,6 @@
       ImplementedAs: "DOMWebSocket",
     },
     "USB",
+    "WakeLockSentinel",
   ],
 }
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 2fb3d77c..39fbbaf 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -354,6 +354,8 @@
           "vr/vr_pose.idl",
           "vr/vr_stage_parameters.idl",
           "wake_lock/wake_lock.idl",
+          "wake_lock/wake_lock_event.idl",
+          "wake_lock/wake_lock_sentinel.idl",
           "webaudio/analyser_node.idl",
           "webaudio/audio_buffer.idl",
           "webaudio/audio_buffer_source_node.idl",
@@ -801,7 +803,7 @@
           "storage/storage_event_init.idl",
           "vr/vr_display_event_init.idl",
           "vr/vr_layer_init.idl",
-          "wake_lock/wake_lock_request_options.idl",
+          "wake_lock/wake_lock_event_init.idl",
           "webaudio/analyser_options.idl",
           "webaudio/audio_buffer_options.idl",
           "webaudio/audio_buffer_source_options.idl",
@@ -1011,6 +1013,8 @@
           "storage/window_storage.idl",
           "vibration/navigator_vibration.idl",
           "vr/navigator_vr.idl",
+          "wake_lock/navigator_wake_lock.idl",
+          "wake_lock/worker_navigator_wake_lock.idl",
           "webdatabase/window_web_database.idl",
           "webgl/webgl2_rendering_context_base.idl",
           "webgl/webgl_rendering_context_base.idl",
diff --git a/third_party/blink/renderer/modules/wake_lock/BUILD.gn b/third_party/blink/renderer/modules/wake_lock/BUILD.gn
index 5ac56b2..d9feea5 100644
--- a/third_party/blink/renderer/modules/wake_lock/BUILD.gn
+++ b/third_party/blink/renderer/modules/wake_lock/BUILD.gn
@@ -6,14 +6,20 @@
 
 blink_modules_sources("wake_lock") {
   sources = [
+    "navigator_wake_lock.cc",
+    "navigator_wake_lock.h",
     "wake_lock.cc",
     "wake_lock.h",
-    "wake_lock_controller.cc",
-    "wake_lock_controller.h",
+    "wake_lock_event.cc",
+    "wake_lock_event.h",
+    "wake_lock_sentinel.cc",
+    "wake_lock_sentinel.h",
     "wake_lock_state_record.cc",
     "wake_lock_state_record.h",
     "wake_lock_type.cc",
     "wake_lock_type.h",
+    "worker_navigator_wake_lock.cc",
+    "worker_navigator_wake_lock.h",
   ]
   deps = [
     "//mojo/public/cpp/bindings",
diff --git a/third_party/blink/renderer/modules/wake_lock/DEPS b/third_party/blink/renderer/modules/wake_lock/DEPS
index 12b0536..271ccdb 100644
--- a/third_party/blink/renderer/modules/wake_lock/DEPS
+++ b/third_party/blink/renderer/modules/wake_lock/DEPS
@@ -6,7 +6,7 @@
 ]
 
 specific_include_rules = {
-  "wake_lock_test_utils\.cc": [
+  "wake_lock_test_utils\.cc|.+_test\.cc": [
     "+base/run_loop.h",
   ],
 }
diff --git a/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.cc
new file mode 100644
index 0000000..e6f900b
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.cc
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.h"
+
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/navigator.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock.h"
+
+namespace blink {
+
+NavigatorWakeLock::NavigatorWakeLock(Navigator& navigator)
+    : Supplement<Navigator>(navigator) {}
+
+WakeLock* NavigatorWakeLock::GetWakeLock() {
+  if (!wake_lock_) {
+    auto* frame = GetSupplementable()->GetFrame();
+    if (frame) {
+      DCHECK(frame->GetDocument());
+      wake_lock_ = MakeGarbageCollected<WakeLock>(*frame->GetDocument());
+    }
+  }
+  return wake_lock_;
+}
+
+// static
+const char NavigatorWakeLock::kSupplementName[] = "NavigatorWakeLock";
+
+// static
+NavigatorWakeLock& NavigatorWakeLock::From(Navigator& navigator) {
+  NavigatorWakeLock* supplement =
+      Supplement<Navigator>::From<NavigatorWakeLock>(navigator);
+  if (!supplement) {
+    supplement = MakeGarbageCollected<NavigatorWakeLock>(navigator);
+    ProvideTo(navigator, supplement);
+  }
+  return *supplement;
+}
+
+// static
+WakeLock* NavigatorWakeLock::wakeLock(Navigator& navigator) {
+  return NavigatorWakeLock::From(navigator).GetWakeLock();
+}
+
+void NavigatorWakeLock::Trace(blink::Visitor* visitor) {
+  visitor->Trace(wake_lock_);
+  Supplement<Navigator>::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.h b/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.h
new file mode 100644
index 0000000..bdfd505c
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.h
@@ -0,0 +1,39 @@
+// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_NAVIGATOR_WAKE_LOCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_NAVIGATOR_WAKE_LOCK_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+
+namespace blink {
+
+class Navigator;
+class WakeLock;
+
+class NavigatorWakeLock final : public GarbageCollected<NavigatorWakeLock>,
+                                public Supplement<Navigator> {
+  USING_GARBAGE_COLLECTED_MIXIN(NavigatorWakeLock);
+
+ public:
+  static const char kSupplementName[];
+
+  static NavigatorWakeLock& From(Navigator&);
+
+  static WakeLock* wakeLock(Navigator&);
+
+  explicit NavigatorWakeLock(Navigator&);
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  WakeLock* GetWakeLock();
+
+  Member<WakeLock> wake_lock_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_NAVIGATOR_WAKE_LOCK_H_
diff --git a/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.idl b/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.idl
new file mode 100644
index 0000000..36fc458d
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/navigator_wake_lock.idl
@@ -0,0 +1,11 @@
+// Copyright 2019 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.
+
+[
+  ImplementedAs=NavigatorWakeLock,
+  RuntimeEnabled=WakeLock,
+  SecureContext
+] partial interface Navigator {
+  [SameObject] readonly attribute WakeLock wakeLock;
+};
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
index e73c883..4d6da59 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
@@ -6,13 +6,13 @@
 
 #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/core/dom/abort_signal.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h"
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_request_options.h"
+#include "third_party/blink/renderer/modules/permissions/permission_utils.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_type.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -20,28 +20,28 @@
 
 namespace blink {
 
-// static
-ScriptPromise WakeLock::requestPermission(ScriptState* script_state,
-                                          const String& type) {
-  // https://w3c.github.io/wake-lock/#requestpermission-static-method
-  // 1. Let promise be a new promise.
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise promise = resolver->Promise();
+using mojom::blink::PermissionService;
+using mojom::blink::PermissionStatus;
 
-  // 2. Return promise and run the following steps in parallel:
-  // 2.1. Let state be the result of running and waiting for the obtain
-  //      permission steps with type.
-  // 2.2. Resolve promise with state.
-  auto* document = To<Document>(ExecutionContext::From(script_state));
-  WakeLockController::From(document).RequestPermission(ToWakeLockType(type),
-                                                       resolver);
-  return promise;
-}
+WakeLock::WakeLock(Document& document)
+    : ContextLifecycleObserver(&document),
+      PageVisibilityObserver(document.GetPage()),
+      state_records_{
+          MakeGarbageCollected<WakeLockStateRecord>(&document,
+                                                    WakeLockType::kScreen),
+          MakeGarbageCollected<WakeLockStateRecord>(&document,
+                                                    WakeLockType::kSystem)} {}
 
-// static
-ScriptPromise WakeLock::request(ScriptState* script_state,
-                                const String& type,
-                                WakeLockRequestOptions* options) {
+WakeLock::WakeLock(DedicatedWorkerGlobalScope& worker_scope)
+    : ContextLifecycleObserver(&worker_scope),
+      PageVisibilityObserver(nullptr),
+      state_records_{
+          MakeGarbageCollected<WakeLockStateRecord>(&worker_scope,
+                                                    WakeLockType::kScreen),
+          MakeGarbageCollected<WakeLockStateRecord>(&worker_scope,
+                                                    WakeLockType::kSystem)} {}
+
+ScriptPromise WakeLock::request(ScriptState* script_state, const String& type) {
   // https://w3c.github.io/wake-lock/#request-static-method
   auto* context = ExecutionContext::From(script_state);
   DCHECK(context->IsDocument() || context->IsDedicatedWorkerGlobalScope());
@@ -112,21 +112,10 @@
     }
   }
 
-  // 5. If options' signal member is present, then run the following steps:
-  // 5.1. Let signal be options's signal member.
-  // 5.2. If signal’s aborted flag is set, then reject promise with an
-  //      "AbortError" DOMException and return promise.
-  if (options->hasSignal() && options->signal()->aborted()) {
-    return ScriptPromise::RejectWithDOMException(
-        script_state,
-        MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
-  }
-
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
   WakeLockType wake_lock_type = ToWakeLockType(type);
-  WakeLockController& controller = WakeLockController::From(context);
 
   switch (wake_lock_type) {
     case WakeLockType::kScreen:
@@ -140,20 +129,140 @@
       break;
   }
 
-  // 5.3. Otherwise, add to signal:
-  // 5.3.1. Run release a wake lock with promise and type.
-  if (options->hasSignal()) {
-    options->signal()->AddAlgorithm(WTF::Bind(
-        &WakeLockController::ReleaseWakeLock, WrapWeakPersistent(&controller),
-        wake_lock_type, WrapPersistent(resolver)));
-  }
-
   // 6. Run the following steps in parallel, but abort when options' signal
   // member is present and its aborted flag is set:
-  controller.RequestWakeLock(wake_lock_type, resolver, options->signal());
+  // 6.1. Let state be the result of awaiting obtain permission steps with
+  //      type:
+  DoRequest(wake_lock_type, resolver);
 
   // 7. Return promise.
   return promise;
 }
 
+void WakeLock::DoRequest(WakeLockType type, ScriptPromiseResolver* resolver) {
+  ObtainPermission(
+      type, WTF::Bind(&WakeLock::DidReceivePermissionResponse,
+                      WrapPersistent(this), type, WrapPersistent(resolver)));
+}
+
+void WakeLock::DidReceivePermissionResponse(WakeLockType type,
+                                            ScriptPromiseResolver* resolver,
+                                            PermissionStatus status) {
+  // https://w3c.github.io/wake-lock/#request-static-method
+  DCHECK(status == PermissionStatus::GRANTED ||
+         status == PermissionStatus::DENIED);
+  DCHECK(resolver);
+  // 6.1.1. If state is "denied", then reject promise with a "NotAllowedError"
+  //        DOMException, and abort these steps.
+  if (status != PermissionStatus::GRANTED) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kNotAllowedError,
+        "Wake Lock permission request denied"));
+    return;
+  }
+  // https://github.com/w3c/wake-lock/issues/222: the page can become hidden
+  // between request() and WakeLockStateRecord::AcquireWakeLock(), in which case
+  // we need to abort early.
+  if (type == WakeLockType::kScreen &&
+      !(GetPage() && GetPage()->IsPageVisible())) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kNotAllowedError,
+        "The requesting page is not visible"));
+    return;
+  }
+  // 6.2. Let success be the result of awaiting acquire a wake lock with promise
+  // and type:
+  // 6.2.1. If success is false then reject promise with a "NotAllowedError"
+  //        DOMException, and abort these steps.
+  WakeLockStateRecord* state_record = state_records_[static_cast<size_t>(type)];
+  DCHECK(state_record);
+  state_record->AcquireWakeLock(resolver);
+}
+
+void WakeLock::ContextDestroyed(ExecutionContext*) {
+  // https://w3c.github.io/wake-lock/#handling-document-loss-of-full-activity
+  // 1. Let document be the responsible document of the current settings object.
+  // 2. Let screenRecord be the platform wake lock's state record associated
+  // with document and wake lock type "screen".
+  // 3. For each lockPromise in screenRecord.[[WakeLockStateRecord]]:
+  // 3.1. Run release a wake lock with lockPromise and "screen".
+  // 4. Let systemRecord be the platform wake lock's state record associated
+  // with document and wake lock type "system".
+  // 5. For each lockPromise in systemRecord.[[WakeLockStateRecord]]:
+  // 5.1. Run release a wake lock with lockPromise and "system".
+  for (WakeLockStateRecord* state_record : state_records_) {
+    if (state_record)
+      state_record->ClearWakeLocks();
+  }
+}
+
+void WakeLock::PageVisibilityChanged() {
+  // https://w3c.github.io/wake-lock/#handling-document-loss-of-visibility
+  // 1. Let document be the Document of the top-level browsing context.
+  // 2. If document's visibility state is "visible", abort these steps.
+  if (GetPage() && GetPage()->IsPageVisible())
+    return;
+  // 3. Let screenRecord be the platform wake lock's state record associated
+  // with wake lock type "screen".
+  // 4. For each lockPromise in screenRecord.[[WakeLockStateRecord]]:
+  // 4.1. Run release a wake lock with lockPromise and "screen".
+  WakeLockStateRecord* state_record =
+      state_records_[static_cast<size_t>(WakeLockType::kScreen)];
+  if (state_record)
+    state_record->ClearWakeLocks();
+}
+
+void WakeLock::ObtainPermission(
+    WakeLockType type,
+    base::OnceCallback<void(PermissionStatus)> callback) {
+  // https://w3c.github.io/wake-lock/#dfn-obtain-permission
+  // Note we actually implement a simplified version of the "obtain permission"
+  // algorithm that essentially just calls the "request permission to use"
+  // algorithm from the Permissions spec (i.e. we bypass all the steps covering
+  // calling the "query a permission" algorithm and handling its result).
+  // * Right now, we can do that because there is no way for Chromium's
+  //   permission system to get to the "prompt" state given how
+  //   WakeLockPermissionContext is currently implemented.
+  // * Even if WakeLockPermissionContext changes in the future, this Blink
+  //   implementation is unlikely to change because
+  //   WakeLockPermissionContext::RequestPermission() will take its
+  //   |user_gesture| argument into account to actually implement a slightly
+  //   altered version of "request permission to use", the behavior of which
+  //   will match the definition of "obtain permission" in the Wake Lock spec.
+  DCHECK(type == WakeLockType::kScreen || type == WakeLockType::kSystem);
+  static_assert(
+      static_cast<mojom::blink::WakeLockType>(WakeLockType::kScreen) ==
+          mojom::blink::WakeLockType::kScreen,
+      "WakeLockType and mojom::blink::WakeLockType must have identical values");
+  static_assert(
+      static_cast<mojom::blink::WakeLockType>(WakeLockType::kSystem) ==
+          mojom::blink::WakeLockType::kSystem,
+      "WakeLockType and mojom::blink::WakeLockType must have identical values");
+
+  auto* local_frame = GetExecutionContext()->IsDocument()
+                          ? To<Document>(GetExecutionContext())->GetFrame()
+                          : nullptr;
+  GetPermissionService()->RequestPermission(
+      CreateWakeLockPermissionDescriptor(
+          static_cast<mojom::blink::WakeLockType>(type)),
+      LocalFrame::HasTransientUserActivation(local_frame), std::move(callback));
+}
+
+PermissionService* WakeLock::GetPermissionService() {
+  if (!permission_service_) {
+    ConnectToPermissionService(
+        GetExecutionContext(),
+        permission_service_.BindNewPipeAndPassReceiver());
+  }
+  return permission_service_.get();
+}
+
+void WakeLock::Trace(Visitor* visitor) {
+  for (WakeLockStateRecord* state_record : state_records_)
+    visitor->Trace(state_record);
+  PageVisibilityObserver::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+  ScriptWrappable::Trace(visitor);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.h b/third_party/blink/renderer/modules/wake_lock/wake_lock.h
index 4042b2b..a22e5e2 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.h
@@ -5,8 +5,18 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_H_
 
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/core/page/page_visibility_observer.h"
+#include "third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_type.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace WTF {
 
@@ -16,17 +26,59 @@
 
 namespace blink {
 
+class ExecutionContext;
 class ScriptState;
-class WakeLockRequestOptions;
+class WakeLockStateRecord;
 
-class WakeLock final : public ScriptWrappable {
+class MODULES_EXPORT WakeLock final : public ScriptWrappable,
+                                      public ContextLifecycleObserver,
+                                      public PageVisibilityObserver {
+  USING_GARBAGE_COLLECTED_MIXIN(WakeLock);
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static ScriptPromise requestPermission(ScriptState*, const WTF::String& type);
-  static ScriptPromise request(ScriptState*,
-                               const WTF::String& type,
-                               WakeLockRequestOptions*);
+  explicit WakeLock(Document&);
+  explicit WakeLock(DedicatedWorkerGlobalScope&);
+
+  ScriptPromise request(ScriptState*, const WTF::String& type);
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  // While this could be part of request() itself, having it as a separate
+  // function makes testing (which uses a custom ScriptPromiseResolver) a lot
+  // easier.
+  void DoRequest(WakeLockType, ScriptPromiseResolver*);
+
+  void DidReceivePermissionResponse(WakeLockType,
+                                    ScriptPromiseResolver*,
+                                    mojom::blink::PermissionStatus);
+
+  // ContextLifecycleObserver implementation
+  void ContextDestroyed(ExecutionContext*) override;
+
+  // PageVisibilityObserver implementation
+  void PageVisibilityChanged() override;
+
+  // Permission handling
+  void ObtainPermission(
+      WakeLockType,
+      base::OnceCallback<void(mojom::blink::PermissionStatus)> callback);
+  mojom::blink::PermissionService* GetPermissionService();
+
+  mojo::Remote<mojom::blink::PermissionService> permission_service_;
+
+  // https://w3c.github.io/wake-lock/#concepts-and-state-record
+  // Each platform wake lock (one per wake lock type) has an associated state
+  // record per responsible document [...] internal slots.
+  Member<WakeLockStateRecord> state_records_[kWakeLockTypeCount];
+
+  FRIEND_TEST_ALL_PREFIXES(WakeLockTest, RequestWakeLockGranted);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockTest, RequestWakeLockDenied);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockTest, LossOfDocumentActivity);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockTest, PageVisibilityHidden);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockTest,
+                           PageVisibilityHiddenBeforeLockAcquisition);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.idl b/third_party/blink/renderer/modules/wake_lock/wake_lock.idl
index 6fd074a..806981c 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.idl
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.idl
@@ -13,6 +13,5 @@
   Exposed=(DedicatedWorker,Window),
   RuntimeEnabled=WakeLock
 ] interface WakeLock {
-  [CallWith=ScriptState, Exposed=Window] static Promise<PermissionState> requestPermission(WakeLockType type);
-  [CallWith=ScriptState] static Promise<void> request(WakeLockType type, optional WakeLockRequestOptions options);
+  [CallWith=ScriptState] Promise<WakeLockSentinel> request(WakeLockType type);
 };
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.cc
deleted file mode 100644
index d760cfe..0000000
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h"
-
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/core/dom/abort_signal.h"
-#include "third_party/blink/renderer/core/dom/dom_exception.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/modules/permissions/permission_utils.h"
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-
-namespace blink {
-
-using mojom::blink::PermissionService;
-using mojom::blink::PermissionStatus;
-
-WakeLockController::WakeLockController(Document& document)
-    : Supplement<ExecutionContext>(document),
-      ContextLifecycleObserver(&document),
-      PageVisibilityObserver(document.GetPage()),
-      state_records_{
-          MakeGarbageCollected<WakeLockStateRecord>(&document,
-                                                    WakeLockType::kScreen),
-          MakeGarbageCollected<WakeLockStateRecord>(&document,
-                                                    WakeLockType::kSystem)} {}
-
-WakeLockController::WakeLockController(DedicatedWorkerGlobalScope& worker_scope)
-    : Supplement<ExecutionContext>(worker_scope),
-      ContextLifecycleObserver(&worker_scope),
-      PageVisibilityObserver(nullptr),
-      state_records_{
-          MakeGarbageCollected<WakeLockStateRecord>(&worker_scope,
-                                                    WakeLockType::kScreen),
-          MakeGarbageCollected<WakeLockStateRecord>(&worker_scope,
-                                                    WakeLockType::kSystem)} {}
-
-const char WakeLockController::kSupplementName[] = "WakeLockController";
-
-// static
-WakeLockController& WakeLockController::From(
-    ExecutionContext* execution_context) {
-  DCHECK(execution_context->IsDocument() ||
-         execution_context->IsDedicatedWorkerGlobalScope());
-  auto* controller =
-      Supplement<ExecutionContext>::From<WakeLockController>(execution_context);
-  if (!controller) {
-    if (execution_context->IsDocument()) {
-      controller = MakeGarbageCollected<WakeLockController>(
-          *To<Document>(execution_context));
-    } else {
-      controller = MakeGarbageCollected<WakeLockController>(
-          *To<DedicatedWorkerGlobalScope>(execution_context));
-    }
-    Supplement<ExecutionContext>::ProvideTo(*execution_context, controller);
-  }
-  return *controller;
-}
-
-void WakeLockController::Trace(Visitor* visitor) {
-  for (WakeLockStateRecord* state_record : state_records_)
-    visitor->Trace(state_record);
-  PageVisibilityObserver::Trace(visitor);
-  ContextLifecycleObserver::Trace(visitor);
-  Supplement<ExecutionContext>::Trace(visitor);
-}
-
-void WakeLockController::RequestWakeLock(WakeLockType type,
-                                         ScriptPromiseResolver* resolver,
-                                         AbortSignal* signal) {
-  // https://w3c.github.io/wake-lock/#request-static-method
-  // 6. [...] abort when options' signal member is present and its aborted flag
-  // is set:
-  DCHECK(resolver);
-  // We were called via WakeLock::request(), which should have handled the
-  // signal->aborted() case.
-  DCHECK(!signal || !signal->aborted());
-  // 6.1. Let state be the result of awaiting obtain permission steps with
-  // type:
-  ObtainPermission(
-      type, WTF::Bind(&WakeLockController::DidReceivePermissionResponse,
-                      WrapPersistent(this), type, WrapPersistent(resolver),
-                      WrapPersistent(signal)));
-}
-
-void WakeLockController::DidReceivePermissionResponse(
-    WakeLockType type,
-    ScriptPromiseResolver* resolver,
-    AbortSignal* signal,
-    PermissionStatus status) {
-  // https://w3c.github.io/wake-lock/#request-static-method
-  DCHECK(status == PermissionStatus::GRANTED ||
-         status == PermissionStatus::DENIED);
-  DCHECK(resolver);
-  if (signal && signal->aborted())
-    return;
-  // 6.1.1. If state is "denied", then reject promise with a "NotAllowedError"
-  //        DOMException, and abort these steps.
-  if (status != PermissionStatus::GRANTED) {
-    resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kNotAllowedError,
-        "Wake Lock permission request denied"));
-    return;
-  }
-  // https://github.com/w3c/wake-lock/issues/222: the page can become hidden
-  // between RequestWakeLock() and AcquireWakeLock(), in which case we need to
-  // abort early.
-  if (type == WakeLockType::kScreen &&
-      !(GetPage() && GetPage()->IsPageVisible())) {
-    resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kNotAllowedError,
-        "The requesting page is not visible"));
-    return;
-  }
-  // 6.2. Let success be the result of awaiting acquire a wake lock with promise
-  // and type:
-  // 6.2.1. If success is false then reject promise with a "NotAllowedError"
-  //        DOMException, and abort these steps.
-  AcquireWakeLock(type, resolver);
-}
-
-void WakeLockController::ReleaseWakeLock(WakeLockType type,
-                                         ScriptPromiseResolver* resolver) {
-  DCHECK_LE(type, WakeLockType::kMaxValue);
-  WakeLockStateRecord* state_record = state_records_[static_cast<size_t>(type)];
-  DCHECK(state_record);
-  state_record->ReleaseWakeLock(resolver);
-}
-
-void WakeLockController::RequestPermission(WakeLockType type,
-                                           ScriptPromiseResolver* resolver) {
-  // https://w3c.github.io/wake-lock/#requestpermission-static-method
-  // 2.1. Let state be the result of running and waiting for the obtain
-  //      permission steps with type.
-  // 2.2. Resolve promise with state.
-  auto permission_callback = WTF::Bind(
-      [](ScriptPromiseResolver* resolver, PermissionStatus status) {
-        DCHECK(status == PermissionStatus::GRANTED ||
-               status == PermissionStatus::DENIED);
-        DCHECK(resolver);
-        resolver->Resolve(PermissionStatusToString(status));
-      },
-      WrapPersistent(resolver));
-  ObtainPermission(type, std::move(permission_callback));
-}
-
-void WakeLockController::ContextDestroyed(ExecutionContext*) {
-  // https://w3c.github.io/wake-lock/#handling-document-loss-of-full-activity
-  // 1. Let document be the responsible document of the current settings object.
-  // 2. Let screenRecord be the platform wake lock's state record associated
-  // with document and wake lock type "screen".
-  // 3. For each lockPromise in screenRecord.[[WakeLockStateRecord]]:
-  // 3.1. Run release a wake lock with lockPromise and "screen".
-  // 4. Let systemRecord be the platform wake lock's state record associated
-  // with document and wake lock type "system".
-  // 5. For each lockPromise in systemRecord.[[WakeLockStateRecord]]:
-  // 5.1. Run release a wake lock with lockPromise and "system".
-  for (WakeLockStateRecord* state_record : state_records_) {
-    if (state_record)
-      state_record->ClearWakeLocks();
-  }
-}
-
-void WakeLockController::PageVisibilityChanged() {
-  // https://w3c.github.io/wake-lock/#handling-document-loss-of-visibility
-  // 1. Let document be the Document of the top-level browsing context.
-  // 2. If document's visibility state is "visible", abort these steps.
-  if (GetPage() && GetPage()->IsPageVisible())
-    return;
-  // 3. Let screenRecord be the platform wake lock's state record associated
-  // with wake lock type "screen".
-  // 4. For each lockPromise in screenRecord.[[WakeLockStateRecord]]:
-  // 4.1. Run release a wake lock with lockPromise and "screen".
-  WakeLockStateRecord* state_record =
-      state_records_[static_cast<size_t>(WakeLockType::kScreen)];
-  if (state_record)
-    state_record->ClearWakeLocks();
-}
-
-void WakeLockController::AcquireWakeLock(WakeLockType type,
-                                         ScriptPromiseResolver* resolver) {
-  DCHECK_LE(type, WakeLockType::kMaxValue);
-  WakeLockStateRecord* state_record = state_records_[static_cast<size_t>(type)];
-  DCHECK(state_record);
-  state_record->AcquireWakeLock(resolver);
-}
-
-void WakeLockController::ObtainPermission(
-    WakeLockType type,
-    base::OnceCallback<void(PermissionStatus)> callback) {
-  // https:w3c.github.io/wake-lock/#dfn-obtain-permission
-  // Note we actually implement a simplified version of the "obtain permission"
-  // algorithm that essentially just calls the "request permission to use"
-  // algorithm from the Permissions spec (i.e. we bypass all the steps covering
-  // calling the "query a permission" algorithm and handling its result).
-  // * Right now, we can do that because there is no way for Chromium's
-  //   permission system to get to the "prompt" state given how
-  //   WakeLockPermissionContext is currently implemented.
-  // * Even if WakeLockPermissionContext changes in the future, this Blink
-  //   implementation is unlikely to change because
-  //   WakeLockPermissionContext::RequestPermission() will take its
-  //   |user_gesture| argument into account to actually implement a slightly
-  //   altered version of "request permission to use", the behavior of which
-  //   will match the definition of "obtain permission" in the Wake Lock spec.
-  DCHECK(type == WakeLockType::kScreen || type == WakeLockType::kSystem);
-  static_assert(
-      static_cast<mojom::blink::WakeLockType>(WakeLockType::kScreen) ==
-          mojom::blink::WakeLockType::kScreen,
-      "WakeLockType and mojom::blink::WakeLockType must have identical values");
-  static_assert(
-      static_cast<mojom::blink::WakeLockType>(WakeLockType::kSystem) ==
-          mojom::blink::WakeLockType::kSystem,
-      "WakeLockType and mojom::blink::WakeLockType must have identical values");
-
-  auto* local_frame = GetExecutionContext()->IsDocument()
-                          ? To<Document>(GetExecutionContext())->GetFrame()
-                          : nullptr;
-
-  GetPermissionService()->RequestPermission(
-      CreateWakeLockPermissionDescriptor(
-          static_cast<mojom::blink::WakeLockType>(type)),
-      LocalFrame::HasTransientUserActivation(local_frame), std::move(callback));
-}
-
-PermissionService* WakeLockController::GetPermissionService() {
-  if (!permission_service_) {
-    ConnectToPermissionService(
-        GetExecutionContext(),
-        permission_service_.BindNewPipeAndPassReceiver());
-  }
-  return permission_service_.get();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h
deleted file mode 100644
index 2cc46bb..0000000
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_CONTROLLER_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_CONTROLLER_H_
-
-#include "base/callback.h"
-#include "base/gtest_prod_util.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/core/page/page_visibility_observer.h"
-#include "third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_type.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-
-namespace blink {
-
-class AbortSignal;
-class ExecutionContext;
-class ScriptPromiseResolver;
-class WakeLockStateRecord;
-
-// WakeLockController is used to track per-Document wake lock state and react to
-// Document changes appropriately.
-class MODULES_EXPORT WakeLockController final
-    : public GarbageCollected<WakeLockController>,
-      public Supplement<ExecutionContext>,
-      public ContextLifecycleObserver,
-      public PageVisibilityObserver {
-  USING_GARBAGE_COLLECTED_MIXIN(WakeLockController);
-
- public:
-  static const char kSupplementName[];
-
-  explicit WakeLockController(Document&);
-  explicit WakeLockController(DedicatedWorkerGlobalScope&);
-
-  static WakeLockController& From(ExecutionContext*);
-
-  void Trace(blink::Visitor*) override;
-
-  void RequestWakeLock(WakeLockType type,
-                       ScriptPromiseResolver* resolver,
-                       AbortSignal* signal);
-
-  void ReleaseWakeLock(WakeLockType type, ScriptPromiseResolver*);
-
-  void RequestPermission(WakeLockType type, ScriptPromiseResolver*);
-
- private:
-  // ContextLifecycleObserver implementation
-  void ContextDestroyed(ExecutionContext*) override;
-
-  // PageVisibilityObserver implementation
-  void PageVisibilityChanged() override;
-
-  void AcquireWakeLock(WakeLockType type, ScriptPromiseResolver*);
-
-  void DidReceivePermissionResponse(WakeLockType type,
-                                    ScriptPromiseResolver*,
-                                    AbortSignal*,
-                                    mojom::blink::PermissionStatus);
-
-  // Permission handling
-  void ObtainPermission(
-      WakeLockType type,
-      base::OnceCallback<void(mojom::blink::PermissionStatus)> callback);
-  mojom::blink::PermissionService* GetPermissionService();
-
-  mojo::Remote<mojom::blink::PermissionService> permission_service_;
-
-  // https://w3c.github.io/wake-lock/#concepts-and-state-record
-  // Each platform wake lock (one per wake lock type) has an associated state
-  // record per responsible document [...] internal slots.
-  Member<WakeLockStateRecord> state_records_[kWakeLockTypeCount];
-
-  FRIEND_TEST_ALL_PREFIXES(WakeLockControllerTest, AcquireScreenWakeLock);
-  FRIEND_TEST_ALL_PREFIXES(WakeLockControllerTest, AcquireSystemWakeLock);
-  FRIEND_TEST_ALL_PREFIXES(WakeLockControllerTest, AcquireMultipleLocks);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_CONTROLLER_H_
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller_test.cc
deleted file mode 100644
index 1d14f44fe..0000000
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller_test.cc
+++ /dev/null
@@ -1,488 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/core/dom/abort_signal.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/dom_exception.h"
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
-#include "v8/include/v8.h"
-
-namespace blink {
-
-TEST(WakeLockControllerTest, RequestWakeLockGranted) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
-
-  auto* screen_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise screen_promise = screen_resolver->Promise();
-
-  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver, nullptr);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  MockPermissionService& permission_service = context.GetPermissionService();
-
-  permission_service.WaitForPermissionRequest(WakeLockType::kScreen);
-  screen_lock.WaitForRequest();
-
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(screen_promise));
-  EXPECT_TRUE(screen_lock.is_acquired());
-}
-
-TEST(WakeLockControllerTest, RequestWakeLockDenied) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kSystem, mojom::blink::PermissionStatus::DENIED);
-
-  auto* system_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise system_promise = system_resolver->Promise();
-
-  controller.RequestWakeLock(WakeLockType::kSystem, system_resolver, nullptr);
-
-  MockWakeLock& system_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
-  MockPermissionService& permission_service = context.GetPermissionService();
-
-  permission_service.WaitForPermissionRequest(WakeLockType::kSystem);
-  context.WaitForPromiseRejection(system_promise);
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(system_promise));
-  EXPECT_FALSE(system_lock.is_acquired());
-
-  // System locks are not allowed by default, so the promise should have been
-  // rejected with a NotAllowedError DOMException.
-  DOMException* dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(system_promise);
-  ASSERT_NE(dom_exception, nullptr);
-  EXPECT_EQ("NotAllowedError", dom_exception->name());
-}
-
-// Abort early in DidReceivePermissionResponse().
-TEST(WakeLockControllerTest, RequestWakeLockAbortEarly) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  auto* screen_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise screen_promise = screen_resolver->Promise();
-
-  auto* abort_signal = MakeGarbageCollected<AbortSignal>(context.GetDocument());
-  abort_signal->AddAlgorithm(WTF::Bind(
-      &WakeLockController::ReleaseWakeLock, WrapWeakPersistent(&controller),
-      WakeLockType::kScreen, WrapPersistent(screen_resolver)));
-
-  controller.RequestWakeLock(
-      WakeLockType::kScreen,
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState()),
-      abort_signal);
-
-  MockPermissionService& permission_service = context.GetPermissionService();
-
-  permission_service.WaitForPermissionRequest(WakeLockType::kScreen);
-  abort_signal->SignalAbort();
-
-  context.WaitForPromiseRejection(screen_promise);
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(screen_promise));
-  EXPECT_FALSE(screen_lock.is_acquired());
-
-  DOMException* dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(screen_promise);
-  ASSERT_NE(dom_exception, nullptr);
-  EXPECT_EQ("AbortError", dom_exception->name());
-}
-
-TEST(WakeLockControllerTest, AcquireScreenWakeLock) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  controller.AcquireWakeLock(
-      WakeLockType::kScreen,
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState()));
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  screen_lock.WaitForRequest();
-
-  EXPECT_TRUE(screen_lock.is_acquired());
-}
-
-TEST(WakeLockControllerTest, AcquireSystemWakeLock) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  controller.AcquireWakeLock(
-      WakeLockType::kSystem,
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState()));
-  MockWakeLock& system_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
-  system_lock.WaitForRequest();
-
-  EXPECT_TRUE(system_lock.is_acquired());
-}
-
-TEST(WakeLockControllerTest, AcquireMultipleLocks) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  controller.AcquireWakeLock(
-      WakeLockType::kScreen,
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState()));
-  controller.AcquireWakeLock(
-      WakeLockType::kSystem,
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState()));
-  controller.AcquireWakeLock(
-      WakeLockType::kSystem,
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState()));
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  screen_lock.WaitForRequest();
-  MockWakeLock& system_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
-  system_lock.WaitForRequest();
-
-  EXPECT_TRUE(screen_lock.is_acquired());
-  EXPECT_TRUE(system_lock.is_acquired());
-}
-
-TEST(WakeLockControllerTest, ReleaseUnaquiredWakeLockRejectsPromise) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-
-  auto* resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise promise = resolver->Promise();
-
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(promise));
-  controller.ReleaseWakeLock(WakeLockType::kScreen, resolver);
-  context.WaitForPromiseRejection(promise);
-
-  // The promise is always rejected, even if it is not in [[ActiveLocks]].
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise));
-  EXPECT_FALSE(screen_lock.is_acquired());
-}
-
-TEST(WakeLockControllerTest, ReleaseWakeLock) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-
-  auto* resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise promise = resolver->Promise();
-
-  controller.RequestWakeLock(WakeLockType::kScreen, resolver,
-                             /*signal=*/nullptr);
-  screen_lock.WaitForRequest();
-  controller.ReleaseWakeLock(WakeLockType::kScreen, resolver);
-  context.WaitForPromiseRejection(promise);
-  screen_lock.WaitForCancelation();
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise));
-  EXPECT_FALSE(screen_lock.is_acquired());
-}
-
-// This is actually part of WakeLock::request(), but it is easier to just test
-// it here.
-TEST(WakeLockControllerTest, AbortSignal) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  auto* screen_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise screen_promise = screen_resolver->Promise();
-
-  auto* abort_signal = MakeGarbageCollected<AbortSignal>(context.GetDocument());
-  abort_signal->AddAlgorithm(WTF::Bind(
-      &WakeLockController::ReleaseWakeLock, WrapWeakPersistent(&controller),
-      WakeLockType::kScreen, WrapPersistent(screen_resolver)));
-
-  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver,
-                             /*signal=*/nullptr);
-  screen_lock.WaitForRequest();
-
-  abort_signal->SignalAbort();
-  context.WaitForPromiseRejection(screen_promise);
-  screen_lock.WaitForCancelation();
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(screen_promise));
-  EXPECT_FALSE(screen_lock.is_acquired());
-}
-
-// https://w3c.github.io/wake-lock/#handling-document-loss-of-full-activity
-TEST(WakeLockControllerTest, LossOfDocumentActivity) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  MockWakeLock& system_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
-
-  // First, acquire a handful of locks of different types.
-  auto* screen_resolver1 =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  screen_resolver1->Promise();
-  auto* screen_resolver2 =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  screen_resolver2->Promise();
-  auto* system_resolver1 =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  system_resolver1->Promise();
-  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver1,
-                             /*signal=*/nullptr);
-  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver2,
-                             /*signal=*/nullptr);
-  screen_lock.WaitForRequest();
-  controller.RequestWakeLock(WakeLockType::kSystem, system_resolver1,
-                             /*signal=*/nullptr);
-  system_lock.WaitForRequest();
-
-  // Now shut down our Document and make sure all [[ActiveLocks]] slots have
-  // been cleared. We cannot check that the promises have been rejected because
-  // ScriptPromiseResolver::Reject() will bail out if we no longer have a valid
-  // execution context.
-  context.GetDocument()->Shutdown();
-  screen_lock.WaitForCancelation();
-  system_lock.WaitForCancelation();
-
-  EXPECT_FALSE(screen_lock.is_acquired());
-  EXPECT_FALSE(system_lock.is_acquired());
-}
-
-// https://w3c.github.io/wake-lock/#handling-document-loss-of-visibility
-TEST(WakeLockControllerTest, PageVisibilityHidden) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  auto* screen_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise screen_promise = screen_resolver->Promise();
-
-  MockWakeLock& system_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
-  auto* system_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise system_promise = system_resolver->Promise();
-
-  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver,
-                             /*signal=*/nullptr);
-  controller.RequestWakeLock(WakeLockType::kSystem, system_resolver,
-                             /*signal=*/nullptr);
-
-  screen_lock.WaitForRequest();
-  system_lock.WaitForRequest();
-
-  context.GetDocument()->GetPage()->SetIsHidden(true, false);
-
-  context.WaitForPromiseRejection(screen_promise);
-  screen_lock.WaitForCancelation();
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(screen_promise));
-  DOMException* dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(screen_promise);
-  ASSERT_NE(dom_exception, nullptr);
-  EXPECT_EQ("AbortError", dom_exception->name());
-  EXPECT_FALSE(screen_lock.is_acquired());
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(system_promise));
-  EXPECT_TRUE(system_lock.is_acquired());
-
-  context.GetDocument()->GetPage()->SetIsHidden(false, false);
-
-  auto* other_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise other_promise = system_resolver->Promise();
-  controller.RequestWakeLock(WakeLockType::kScreen, other_resolver,
-                             /*signal=*/nullptr);
-  screen_lock.WaitForRequest();
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(other_promise));
-  EXPECT_TRUE(screen_lock.is_acquired());
-}
-
-// https://w3c.github.io/wake-lock/#handling-document-loss-of-visibility
-TEST(WakeLockControllerTest, PageVisibilityHiddenBeforeLockAcquisition) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  auto* screen_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise screen_promise = screen_resolver->Promise();
-
-  MockWakeLock& system_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
-  auto* system_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise system_promise = system_resolver->Promise();
-
-  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver,
-                             /*signal=*/nullptr);
-  controller.RequestWakeLock(WakeLockType::kSystem, system_resolver,
-                             /*signal=*/nullptr);
-  context.GetDocument()->GetPage()->SetIsHidden(true, false);
-
-  context.WaitForPromiseRejection(screen_promise);
-  system_lock.WaitForRequest();
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(screen_promise));
-  EXPECT_FALSE(screen_lock.is_acquired());
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(system_promise));
-  EXPECT_TRUE(system_lock.is_acquired());
-}
-
-// Check that hiding a page and signaling abort does not try to delete a
-// screen lock twice.
-TEST(WakeLockControllerTest, PageVisibilityAndAbortSignal) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-  auto* screen_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise screen_promise = screen_resolver->Promise();
-
-  auto* abort_signal = MakeGarbageCollected<AbortSignal>(context.GetDocument());
-  abort_signal->AddAlgorithm(WTF::Bind(
-      &WakeLockController::ReleaseWakeLock, WrapWeakPersistent(&controller),
-      WakeLockType::kScreen, WrapPersistent(screen_resolver)));
-
-  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver,
-                             /*signal=*/nullptr);
-  screen_lock.WaitForRequest();
-
-  context.GetDocument()->GetPage()->SetIsHidden(true, false);
-  context.WaitForPromiseRejection(screen_promise);
-  screen_lock.WaitForCancelation();
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(screen_promise));
-  EXPECT_FALSE(screen_lock.is_acquired());
-
-  abort_signal->SignalAbort();
-  test::RunPendingTasks();
-
-  EXPECT_FALSE(screen_lock.is_acquired());
-}
-
-TEST(WakeLockControllerTest, RequestPermissionGranted) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
-
-  auto* system_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise system_promise = system_resolver->Promise();
-
-  controller.RequestPermission(WakeLockType::kSystem, system_resolver);
-  context.WaitForPromiseFulfillment(system_promise);
-
-  EXPECT_EQ(v8::Promise::kFulfilled,
-            ScriptPromiseUtils::GetPromiseState(system_promise));
-  EXPECT_EQ("granted",
-            ScriptPromiseUtils::GetPromiseResolutionAsString(system_promise));
-}
-
-TEST(WakeLockControllerTest, RequestPermissionDenied) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto& controller = WakeLockController::From(context.GetDocument());
-
-  context.GetPermissionService().SetPermissionResponse(
-      WakeLockType::kSystem, mojom::blink::PermissionStatus::DENIED);
-
-  auto* system_resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise system_promise = system_resolver->Promise();
-
-  controller.RequestPermission(WakeLockType::kSystem, system_resolver);
-  context.WaitForPromiseFulfillment(system_promise);
-
-  EXPECT_EQ(v8::Promise::kFulfilled,
-            ScriptPromiseUtils::GetPromiseState(system_promise));
-  EXPECT_EQ("denied",
-            ScriptPromiseUtils::GetPromiseResolutionAsString(system_promise));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_event.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_event.cc
new file mode 100644
index 0000000..866fbaeb
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_event.cc
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_event.h"
+
+#include "base/logging.h"
+#include "third_party/blink/renderer/modules/event_interface_modules_names.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_event_init.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+// static
+WakeLockEvent* WakeLockEvent::Create(const AtomicString& type,
+                                     const WakeLockEventInit* initializer) {
+  DCHECK(initializer->hasLock());
+  return MakeGarbageCollected<WakeLockEvent>(type, initializer);
+}
+
+WakeLockEvent::WakeLockEvent(const AtomicString& type,
+                             const WakeLockEventInit* initializer)
+    : Event(type, initializer), lock_(initializer->lock()) {
+  DCHECK_NE(nullptr, lock_);
+}
+
+WakeLockEvent::WakeLockEvent(const AtomicString& type, WakeLockSentinel* lock)
+    : Event(type, Bubbles::kNo, Cancelable::kNo), lock_(lock) {}
+
+WakeLockEvent::~WakeLockEvent() = default;
+
+WakeLockSentinel* WakeLockEvent::lock() const {
+  return lock_;
+}
+
+const AtomicString& WakeLockEvent::InterfaceName() const {
+  return event_interface_names::kWakeLockEvent;
+}
+
+void WakeLockEvent::Trace(blink::Visitor* visitor) {
+  visitor->Trace(lock_);
+  Event::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_event.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_event.h
new file mode 100644
index 0000000..228a4b13
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_event.h
@@ -0,0 +1,41 @@
+// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_EVENT_H_
+
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class WakeLockEventInit;
+class WakeLockSentinel;
+
+class WakeLockEvent final : public Event {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static WakeLockEvent* Create(const AtomicString& type,
+                               const WakeLockEventInit* initializer);
+
+  WakeLockEvent(const AtomicString& type, const WakeLockEventInit* initializer);
+  WakeLockEvent(const AtomicString& type, WakeLockSentinel* lock);
+  ~WakeLockEvent() override;
+
+  // Web-exposed APIs.
+  WakeLockSentinel* lock() const;
+
+  // Event overrides.
+  const AtomicString& InterfaceName() const override;
+  void Trace(blink::Visitor* visitor) override;
+
+ private:
+  const Member<WakeLockSentinel> lock_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_EVENT_H_
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_event.idl b/third_party/blink/renderer/modules/wake_lock/wake_lock_event.idl
new file mode 100644
index 0000000..9ad1cfb5
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_event.idl
@@ -0,0 +1,12 @@
+// Copyright 2019 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.
+
+[
+  SecureContext,
+  Exposed=(DedicatedWorker,Window),
+  Constructor(DOMString type, WakeLockEventInit init),
+  RuntimeEnabled=WakeLock
+] interface WakeLockEvent : Event {
+  readonly attribute WakeLockSentinel lock;
+};
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_event_init.idl b/third_party/blink/renderer/modules/wake_lock/wake_lock_event_init.idl
new file mode 100644
index 0000000..40d203e
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_event_init.idl
@@ -0,0 +1,7 @@
+// Copyright 2019 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.
+
+dictionary WakeLockEventInit : EventInit {
+  required WakeLockSentinel lock;
+};
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_request_options.idl b/third_party/blink/renderer/modules/wake_lock/wake_lock_request_options.idl
deleted file mode 100644
index 074fe3c7..0000000
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_request_options.idl
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2019 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.
-
-// https://w3c.github.io/wake-lock/#the-wakelockrequestoptions-dictionary
-dictionary WakeLockRequestOptions {
-  AbortSignal signal;
-};
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.cc
new file mode 100644
index 0000000..9a3ad58ced
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.cc
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h"
+
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/modules/event_target_modules_names.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_event.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_event_init.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+
+namespace blink {
+
+WakeLockSentinel::WakeLockSentinel(ScriptState* script_state,
+                                   WakeLockType type,
+                                   WakeLockStateRecord* manager)
+    : ContextLifecycleObserver(ExecutionContext::From(script_state)),
+      manager_(manager),
+      type_(type) {}
+
+WakeLockSentinel::~WakeLockSentinel() = default;
+
+ScriptPromise WakeLockSentinel::release(ScriptState* script_state) {
+  DoRelease();
+  return ScriptPromise::CastUndefined(script_state);
+}
+
+String WakeLockSentinel::type() const {
+  switch (type_) {
+    case WakeLockType::kScreen:
+      return "screen";
+    case WakeLockType::kSystem:
+      return "system";
+  }
+}
+
+ExecutionContext* WakeLockSentinel::GetExecutionContext() const {
+  return ContextLifecycleObserver::GetExecutionContext();
+}
+
+const AtomicString& WakeLockSentinel::InterfaceName() const {
+  return event_target_names::kWakeLockSentinel;
+}
+
+void WakeLockSentinel::Trace(blink::Visitor* visitor) {
+  visitor->Trace(manager_);
+  EventTargetWithInlineData::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+}
+
+bool WakeLockSentinel::HasPendingActivity() const {
+  return HasEventListeners();
+}
+
+void WakeLockSentinel::ContextDestroyed(ExecutionContext*) {
+  // Release all event listeners so that HasPendingActivity() does not return
+  // true forever once a listener has been added to the object.
+  RemoveAllEventListeners();
+  DCHECK(!HasEventListeners());
+}
+
+void WakeLockSentinel::DoRelease() {
+  if (!manager_)
+    return;
+
+  manager_->UnregisterSentinel(this);
+  manager_.Clear();
+
+  DispatchEvent(
+      *MakeGarbageCollected<WakeLockEvent>(event_type_names::kRelease, this));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h
new file mode 100644
index 0000000..30d954a
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h
@@ -0,0 +1,72 @@
+// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_SENTINEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_SENTINEL_H_
+
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_type.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class ExecutionContext;
+class ScriptState;
+class WakeLockStateRecord;
+
+class MODULES_EXPORT WakeLockSentinel final
+    : public EventTargetWithInlineData,
+      public ActiveScriptWrappable<WakeLockSentinel>,
+      public ContextLifecycleObserver {
+  DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(WakeLockSentinel);
+
+ public:
+  WakeLockSentinel(ScriptState* script_state,
+                   WakeLockType type,
+                   WakeLockStateRecord* manager);
+  ~WakeLockSentinel() override;
+
+  // Web-exposed interfaces
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(release, kRelease)
+  ScriptPromise release(ScriptState*);
+  String type() const;
+
+  // EventTarget overrides.
+  ExecutionContext* GetExecutionContext() const override;
+  const AtomicString& InterfaceName() const override;
+  void Trace(blink::Visitor*) override;
+
+  // ActiveScriptWrappable overrides.
+  bool HasPendingActivity() const override;
+
+  // ContextLifecycleObserver overrides.
+  void ContextDestroyed(ExecutionContext*) override;
+
+ private:
+  friend class WakeLockStateRecord;
+
+  // This function, which only has any effect once, detaches this sentinel from
+  // its |manager_|, and fires a "release" event.
+  // It is implemented separately from release() itself so that |manager_| can
+  // call it without triggering the creation of a new ScriptPromise, as it is
+  // not relevant to |manager_| and this function may be called from a context
+  // where |script_state_|'s context is no longer valid.
+  void DoRelease();
+
+  Member<WakeLockStateRecord> manager_;
+  const WakeLockType type_;
+
+  FRIEND_TEST_ALL_PREFIXES(WakeLockSentinelTest, MultipleReleaseCalls);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_SENTINEL_H_
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.idl b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.idl
new file mode 100644
index 0000000..f747650
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.idl
@@ -0,0 +1,16 @@
+// Copyright 2019 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.
+
+[
+  ActiveScriptWrappable,
+  Exposed=(DedicatedWorker,Window),
+  RuntimeEnabled=WakeLock,
+  SecureContext
+] interface WakeLockSentinel : EventTarget {
+  attribute EventHandler onrelease;
+
+  readonly attribute WakeLockType type;
+
+  [CallWith=ScriptState] Promise<void> release();
+};
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc
new file mode 100644
index 0000000..70cf9dd5
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc
@@ -0,0 +1,106 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h"
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
+#include "third_party/blink/renderer/modules/event_target_modules_names.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+namespace {
+
+class SyncEventListener final : public NativeEventListener {
+ public:
+  SyncEventListener(base::OnceClosure invocation_callback)
+      : invocation_callback_(std::move(invocation_callback)) {}
+  void Invoke(ExecutionContext*, Event*) override {
+    DCHECK(invocation_callback_);
+    std::move(invocation_callback_).Run();
+  }
+
+ private:
+  base::OnceClosure invocation_callback_;
+};
+
+}  // namespace
+
+TEST(WakeLockSentinelTest, SentinelType) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+
+  auto* sentinel = MakeGarbageCollected<WakeLockSentinel>(
+      context.GetScriptState(), WakeLockType::kScreen, /*manager=*/nullptr);
+  EXPECT_EQ("screen", sentinel->type());
+
+  sentinel = MakeGarbageCollected<WakeLockSentinel>(
+      context.GetScriptState(), WakeLockType::kSystem, /*manager=*/nullptr);
+  EXPECT_EQ("system", sentinel->type());
+}
+
+TEST(WakeLockSentinelTest, MultipleReleaseCalls) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+
+  auto* state_record = MakeGarbageCollected<WakeLockStateRecord>(
+      context.GetDocument(), WakeLockType::kScreen);
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise promise = resolver->Promise();
+  state_record->AcquireWakeLock(resolver);
+  context.WaitForPromiseFulfillment(promise);
+  auto* sentinel =
+      ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise);
+  ASSERT_NE(nullptr, sentinel);
+
+  base::RunLoop run_loop;
+  auto* event_listener =
+      MakeGarbageCollected<SyncEventListener>(run_loop.QuitClosure());
+  sentinel->addEventListener(event_type_names::kRelease, event_listener);
+  sentinel->release(context.GetScriptState());
+  run_loop.Run();
+  sentinel->removeEventListener(event_type_names::kRelease, event_listener);
+
+  EXPECT_EQ(nullptr, sentinel->manager_);
+
+  event_listener = MakeGarbageCollected<SyncEventListener>(WTF::Bind([]() {
+    EXPECT_TRUE(false) << "This event handler should not be reached.";
+  }));
+  sentinel->addEventListener(event_type_names::kRelease, event_listener);
+  sentinel->release(context.GetScriptState());
+}
+
+TEST(WakeLockSentinelTest, ContextDestruction) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+
+  auto* sentinel = MakeGarbageCollected<WakeLockSentinel>(
+      context.GetScriptState(), WakeLockType::kScreen,
+      MakeGarbageCollected<WakeLockStateRecord>(context.GetDocument(),
+                                                WakeLockType::kScreen));
+
+  auto* event_listener =
+      MakeGarbageCollected<SyncEventListener>(WTF::Bind([]() {
+        EXPECT_TRUE(false) << "This event handler should not be reached.";
+      }));
+  sentinel->addEventListener(event_type_names::kRelease, event_listener);
+  EXPECT_TRUE(sentinel->HasPendingActivity());
+
+  context.GetDocument()->Shutdown();
+
+  // If the method returns false the object can be GC'ed.
+  EXPECT_FALSE(sentinel->HasPendingActivity());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
index 13fd3241..c5194fb2de 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
@@ -10,14 +10,14 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 
 WakeLockStateRecord::WakeLockStateRecord(ExecutionContext* execution_context,
                                          WakeLockType type)
-    : wake_lock_type_(ToMojomWakeLockType(type)),
-      execution_context_(execution_context) {
+    : wake_lock_type_(type), execution_context_(execution_context) {
   DCHECK_NE(execution_context, nullptr);
 }
 
@@ -41,54 +41,35 @@
     execution_context_->GetBrowserInterfaceBroker().GetInterface(
         wake_lock_service.BindNewPipeAndPassReceiver());
 
-    wake_lock_service->GetWakeLock(
-        wake_lock_type_, device::mojom::blink::WakeLockReason::kOther,
-        "Blink Wake Lock", wake_lock_.BindNewPipeAndPassReceiver());
+    wake_lock_service->GetWakeLock(ToMojomWakeLockType(wake_lock_type_),
+                                   device::mojom::blink::WakeLockReason::kOther,
+                                   "Blink Wake Lock",
+                                   wake_lock_.BindNewPipeAndPassReceiver());
     wake_lock_.set_disconnect_handler(
         WTF::Bind(&WakeLockStateRecord::OnWakeLockConnectionError,
                   WrapWeakPersistent(this)));
     wake_lock_->RequestWakeLock();
   }
-  DCHECK(!active_locks_.Contains(resolver));
-  active_locks_.insert(resolver);
+  auto* sentinel = MakeGarbageCollected<WakeLockSentinel>(
+      resolver->GetScriptState(), wake_lock_type_, this);
+  wake_lock_sentinels_.insert(sentinel);
+  resolver->Resolve(sentinel);
 }
 
-void WakeLockStateRecord::ReleaseWakeLock(ScriptPromiseResolver* resolver) {
-  // https://w3c.github.io/wake-lock/#release-wake-lock-algorithm
-  // 1. Reject lockPromise with an "AbortError" DOMException.
-  resolver->Reject(MakeGarbageCollected<DOMException>(
-      DOMExceptionCode::kAbortError, "Wake Lock released"));
+void WakeLockStateRecord::UnregisterSentinel(WakeLockSentinel* sentinel) {
+  auto iterator = wake_lock_sentinels_.find(sentinel);
+  DCHECK(iterator != wake_lock_sentinels_.end());
+  wake_lock_sentinels_.erase(iterator);
 
-  // 2. Let document be the responsible document of the current settings object.
-  // 3. Let record be the platform wake lock's state record associated with
-  // document and type.
-  // 4. If record.[[ActiveLocks]] does not contain lockPromise, abort these
-  // steps.
-  auto iterator = active_locks_.find(resolver);
-  if (iterator == active_locks_.end())
-    return;
-
-  // 5. Remove lockPromise from record.[[ActiveLocks]].
-  active_locks_.erase(iterator);
-
-  // 6. If the internal slot [[ActiveLocks]] of all the platform wake lock's
-  // state records are all empty, then run the following steps in parallel:
-  // 6.1. Ask the underlying operation system to release the wake lock of type
-  //      type and let success be true if the operation succeeded, or else
-  //      false.
-  if (active_locks_.IsEmpty() && wake_lock_.is_bound()) {
+  if (wake_lock_sentinels_.IsEmpty() && wake_lock_.is_bound()) {
     wake_lock_->CancelWakeLock();
     wake_lock_.reset();
-
-    // 6.2. If success is true and type is "screen" run the following:
-    // 6.2.1. Reset the platform-specific inactivity timer after which the
-    //        screen is actually turned off.
   }
 }
 
 void WakeLockStateRecord::ClearWakeLocks() {
-  while (!active_locks_.IsEmpty())
-    ReleaseWakeLock(*active_locks_.begin());
+  while (!wake_lock_sentinels_.IsEmpty())
+    (*wake_lock_sentinels_.begin())->DoRelease();
 }
 
 void WakeLockStateRecord::OnWakeLockConnectionError() {
@@ -98,7 +79,7 @@
 
 void WakeLockStateRecord::Trace(blink::Visitor* visitor) {
   visitor->Trace(execution_context_);
-  visitor->Trace(active_locks_);
+  visitor->Trace(wake_lock_sentinels_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h
index 9873b0c..86f624c 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h
@@ -16,6 +16,7 @@
 
 class ExecutionContext;
 class ScriptPromiseResolver;
+class WakeLockSentinel;
 
 // https://w3c.github.io/wake-lock/#concepts-and-state-record
 // Per-document and per-wake lock type internal data.
@@ -25,36 +26,31 @@
   WakeLockStateRecord(ExecutionContext*, WakeLockType);
 
   void AcquireWakeLock(ScriptPromiseResolver*);
-  void ReleaseWakeLock(ScriptPromiseResolver*);
   void ClearWakeLocks();
 
+  void UnregisterSentinel(WakeLockSentinel*);
+
   void Trace(blink::Visitor* visitor);
 
  private:
-  using ActiveLocksType = HeapHashSet<Member<ScriptPromiseResolver>>;
-
-  friend class WakeLockControllerTest;
-
   // Handle connection errors from |wake_lock_|.
   void OnWakeLockConnectionError();
 
-  // A list of Promises representing active wake locks associated with the
-  // responsible document.
-  ActiveLocksType active_locks_;
+  // A set with all WakeLockSentinel instances belonging to this
+  // Navigator/WorkerNavigator.
+  HeapHashSet<Member<WakeLockSentinel>> wake_lock_sentinels_;
 
   // An actual platform WakeLock. If bound, it means there is an active wake
   // lock for a given type.
   mojo::Remote<device::mojom::blink::WakeLock> wake_lock_;
-  device::mojom::blink::WakeLockType wake_lock_type_;
+  WakeLockType wake_lock_type_;
 
   // ExecutionContext from which we will connect to |wake_lock_service_|.
   Member<ExecutionContext> execution_context_;
 
   FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, AcquireWakeLock);
   FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, ReleaseAllWakeLocks);
-  FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, ReleaseNonExistentWakeLock);
   FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, ReleaseOneWakeLock);
-  FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, ReleaseRejectsPromise);
   FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, ClearWakeLocks);
   FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, WakeLockConnectionError);
 };
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc
index 3fea7bc..ed2828c 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc
@@ -33,6 +33,7 @@
   MockWakeLock& screen_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
   EXPECT_FALSE(screen_lock.is_acquired());
+  EXPECT_FALSE(state_record->wake_lock_.is_bound());
 
   auto* resolver1 =
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
@@ -45,12 +46,19 @@
   state_record->AcquireWakeLock(resolver2);
   screen_lock.WaitForRequest();
 
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(promise2));
+  context.WaitForPromiseFulfillment(promise1);
+  context.WaitForPromiseFulfillment(promise2);
+
+  auto* sentinel1 =
+      ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise1);
+  auto* sentinel2 =
+      ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise2);
+
+  EXPECT_TRUE(state_record->wake_lock_sentinels_.Contains(sentinel1));
+  EXPECT_TRUE(state_record->wake_lock_sentinels_.Contains(sentinel2));
+  EXPECT_EQ(2U, state_record->wake_lock_sentinels_.size());
   EXPECT_TRUE(screen_lock.is_acquired());
-  EXPECT_EQ(2U, state_record->active_locks_.size());
+  EXPECT_TRUE(state_record->wake_lock_.is_bound());
 }
 
 TEST(WakeLockStateRecordTest, ReleaseAllWakeLocks) {
@@ -67,61 +75,20 @@
 
   state_record->AcquireWakeLock(resolver);
   screen_lock.WaitForRequest();
+  context.WaitForPromiseFulfillment(promise);
 
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(promise));
-  EXPECT_EQ(1U, state_record->active_locks_.size());
+  EXPECT_EQ(1U, state_record->wake_lock_sentinels_.size());
   EXPECT_TRUE(screen_lock.is_acquired());
 
-  state_record->ReleaseWakeLock(resolver);
-  context.WaitForPromiseRejection(promise);
+  auto* sentinel =
+      ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise);
+
+  state_record->UnregisterSentinel(sentinel);
   screen_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise));
-  DOMException* dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise);
-  ASSERT_NE(nullptr, dom_exception);
-  EXPECT_EQ("AbortError", dom_exception->name());
-
-  EXPECT_EQ(0U, state_record->active_locks_.size());
+  EXPECT_EQ(0U, state_record->wake_lock_sentinels_.size());
   EXPECT_FALSE(screen_lock.is_acquired());
-}
-
-// Test that trying to remove an entry that does not exist is a no-op. This can
-// happen, for example, when a page visibility change releases a screen lock
-// and a call to AbortController.abort() causes an attempt to release the same
-// lock again.
-TEST(WakeLockStateRecordTest, ReleaseNonExistentWakeLock) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto* state_record = MakeStateRecord(context, WakeLockType::kScreen);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-
-  auto* resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise promise = resolver->Promise();
-
-  state_record->AcquireWakeLock(resolver);
-  screen_lock.WaitForRequest();
-  state_record->ReleaseWakeLock(resolver);
-  context.WaitForPromiseRejection(promise);
-  screen_lock.WaitForCancelation();
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise));
-  EXPECT_EQ(0U, state_record->active_locks_.size());
-  EXPECT_FALSE(screen_lock.is_acquired());
-
-  state_record->ReleaseWakeLock(resolver);
-  test::RunPendingTasks();
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise));
-  EXPECT_EQ(0U, state_record->active_locks_.size());
-  EXPECT_FALSE(screen_lock.is_acquired());
+  EXPECT_FALSE(state_record->wake_lock_.is_bound());
 }
 
 TEST(WakeLockStateRecordTest, ReleaseOneWakeLock) {
@@ -143,54 +110,24 @@
   state_record->AcquireWakeLock(resolver2);
   screen_lock.WaitForRequest();
 
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(promise2));
-  EXPECT_EQ(2U, state_record->active_locks_.size());
+  context.WaitForPromiseFulfillment(promise1);
+  context.WaitForPromiseFulfillment(promise2);
+
   EXPECT_TRUE(screen_lock.is_acquired());
+  EXPECT_EQ(2U, state_record->wake_lock_sentinels_.size());
 
-  state_record->ReleaseWakeLock(resolver1);
-  context.WaitForPromiseRejection(promise1);
+  auto* sentinel1 =
+      ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise1);
+  EXPECT_TRUE(state_record->wake_lock_sentinels_.Contains(sentinel1));
 
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(promise2));
-  DOMException* dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise1);
-  ASSERT_NE(nullptr, dom_exception);
-  EXPECT_EQ("AbortError", dom_exception->name());
-
-  EXPECT_EQ(1U, state_record->active_locks_.size());
+  state_record->UnregisterSentinel(sentinel1);
+  EXPECT_FALSE(state_record->wake_lock_sentinels_.Contains(sentinel1));
+  EXPECT_TRUE(state_record->wake_lock_.is_bound());
+  EXPECT_EQ(1U, state_record->wake_lock_sentinels_.size());
   EXPECT_TRUE(screen_lock.is_acquired());
 }
 
-TEST(WakeLockStateRecordTest, ReleaseRejectsPromise) {
-  MockWakeLockService wake_lock_service;
-  WakeLockTestingContext context(&wake_lock_service);
-  auto* state_record = MakeStateRecord(context, WakeLockType::kScreen);
-
-  MockWakeLock& screen_lock =
-      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
-
-  auto* resolver =
-      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
-  ScriptPromise promise = resolver->Promise();
-
-  EXPECT_EQ(v8::Promise::kPending,
-            ScriptPromiseUtils::GetPromiseState(promise));
-
-  state_record->ReleaseWakeLock(resolver);
-  context.WaitForPromiseRejection(promise);
-
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise));
-  EXPECT_EQ(0U, state_record->active_locks_.size());
-  EXPECT_FALSE(screen_lock.is_acquired());
-}
-
-TEST(WakeLockStateRecordTest, ClearEmptyActiveLocksList) {
+TEST(WakeLockStateRecordTest, ClearEmptyWakeLockSentinelList) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
   auto* state_record = MakeStateRecord(context, WakeLockType::kSystem);
@@ -223,26 +160,15 @@
   state_record->AcquireWakeLock(resolver1);
   state_record->AcquireWakeLock(resolver2);
   system_lock.WaitForRequest();
+  context.WaitForPromiseFulfillment(promise1);
+  context.WaitForPromiseFulfillment(promise2);
+
+  EXPECT_EQ(2U, state_record->wake_lock_sentinels_.size());
 
   state_record->ClearWakeLocks();
-  context.WaitForPromiseRejection(promise1);
-  context.WaitForPromiseRejection(promise2);
   system_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise2));
-  DOMException* dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise1);
-  ASSERT_NE(nullptr, dom_exception);
-  EXPECT_EQ("AbortError", dom_exception->name());
-  dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise2);
-  ASSERT_NE(nullptr, dom_exception);
-  EXPECT_EQ("AbortError", dom_exception->name());
-
-  EXPECT_EQ(0U, state_record->active_locks_.size());
+  EXPECT_EQ(0U, state_record->wake_lock_sentinels_.size());
   EXPECT_FALSE(system_lock.is_acquired());
 }
 
@@ -264,25 +190,17 @@
   state_record->AcquireWakeLock(resolver1);
   state_record->AcquireWakeLock(resolver2);
   system_lock.WaitForRequest();
+  context.WaitForPromiseFulfillment(promise1);
+  context.WaitForPromiseFulfillment(promise2);
 
+  EXPECT_EQ(2U, state_record->wake_lock_sentinels_.size());
+
+  // Unbind and wait for the disconnection to reach |wake_lock_|'s
+  // disconnection handler.
   system_lock.Unbind();
-  context.WaitForPromiseRejection(promise1);
-  context.WaitForPromiseRejection(promise2);
+  state_record->wake_lock_.FlushForTesting();
 
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kRejected,
-            ScriptPromiseUtils::GetPromiseState(promise2));
-  DOMException* dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise1);
-  ASSERT_NE(nullptr, dom_exception);
-  EXPECT_EQ("AbortError", dom_exception->name());
-  dom_exception =
-      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise2);
-  ASSERT_NE(nullptr, dom_exception);
-  EXPECT_EQ("AbortError", dom_exception->name());
-
-  EXPECT_EQ(0U, state_record->active_locks_.size());
+  EXPECT_EQ(0U, state_record->wake_lock_sentinels_.size());
   EXPECT_FALSE(state_record->wake_lock_);
   EXPECT_FALSE(system_lock.is_acquired());
 }
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_test.cc
new file mode 100644
index 0000000..049f2e9
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_test.cc
@@ -0,0 +1,212 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+TEST(WakeLockTest, RequestWakeLockGranted) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+
+  auto* screen_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise screen_promise = screen_resolver->Promise();
+
+  auto* wake_lock = MakeGarbageCollected<WakeLock>(*context.GetDocument());
+  wake_lock->DoRequest(WakeLockType::kScreen, screen_resolver);
+
+  MockWakeLock& screen_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
+  MockPermissionService& permission_service = context.GetPermissionService();
+
+  permission_service.WaitForPermissionRequest(WakeLockType::kScreen);
+  screen_lock.WaitForRequest();
+  context.WaitForPromiseFulfillment(screen_promise);
+
+  EXPECT_NE(nullptr, ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(
+                         screen_promise));
+  EXPECT_TRUE(screen_lock.is_acquired());
+}
+
+TEST(WakeLockTest, RequestWakeLockDenied) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::DENIED);
+
+  auto* system_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise system_promise = system_resolver->Promise();
+
+  auto* wake_lock = MakeGarbageCollected<WakeLock>(*context.GetDocument());
+  wake_lock->DoRequest(WakeLockType::kSystem, system_resolver);
+
+  MockWakeLock& system_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
+  MockPermissionService& permission_service = context.GetPermissionService();
+
+  permission_service.WaitForPermissionRequest(WakeLockType::kSystem);
+  context.WaitForPromiseRejection(system_promise);
+
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(system_promise));
+  EXPECT_FALSE(system_lock.is_acquired());
+
+  // System locks are not allowed by default, so the promise should have been
+  // rejected with a NotAllowedError DOMException.
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(system_promise);
+  ASSERT_NE(dom_exception, nullptr);
+  EXPECT_EQ("NotAllowedError", dom_exception->name());
+}
+
+// https://w3c.github.io/wake-lock/#handling-document-loss-of-full-activity
+TEST(WakeLockTest, LossOfDocumentActivity) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+
+  MockWakeLock& screen_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
+  MockWakeLock& system_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
+
+  // First, acquire a handful of locks of different types.
+  auto* screen_resolver1 =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  screen_resolver1->Promise();
+  auto* screen_resolver2 =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  screen_resolver2->Promise();
+  auto* system_resolver1 =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  system_resolver1->Promise();
+
+  auto* wake_lock = MakeGarbageCollected<WakeLock>(*context.GetDocument());
+  wake_lock->DoRequest(WakeLockType::kScreen, screen_resolver1);
+  wake_lock->DoRequest(WakeLockType::kScreen, screen_resolver2);
+  screen_lock.WaitForRequest();
+  wake_lock->DoRequest(WakeLockType::kSystem, system_resolver1);
+  system_lock.WaitForRequest();
+
+  // Now shut down our Document and make sure all [[ActiveLocks]] slots have
+  // been cleared. We cannot check that the promises have been rejected because
+  // ScriptPromiseResolver::Reject() will bail out if we no longer have a valid
+  // execution context.
+  context.GetDocument()->Shutdown();
+  screen_lock.WaitForCancelation();
+  system_lock.WaitForCancelation();
+
+  EXPECT_FALSE(screen_lock.is_acquired());
+  EXPECT_FALSE(system_lock.is_acquired());
+}
+
+// https://w3c.github.io/wake-lock/#handling-document-loss-of-visibility
+TEST(WakeLockTest, PageVisibilityHidden) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
+
+  MockWakeLock& screen_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
+  auto* screen_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise screen_promise = screen_resolver->Promise();
+
+  MockWakeLock& system_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
+  auto* system_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise system_promise = system_resolver->Promise();
+
+  auto* wake_lock = MakeGarbageCollected<WakeLock>(*context.GetDocument());
+  wake_lock->DoRequest(WakeLockType::kScreen, screen_resolver);
+  screen_lock.WaitForRequest();
+  wake_lock->DoRequest(WakeLockType::kSystem, system_resolver);
+  system_lock.WaitForRequest();
+
+  context.WaitForPromiseFulfillment(screen_promise);
+  context.WaitForPromiseFulfillment(system_promise);
+
+  context.GetDocument()->GetPage()->SetIsHidden(true, false);
+
+  screen_lock.WaitForCancelation();
+
+  EXPECT_FALSE(screen_lock.is_acquired());
+  EXPECT_TRUE(system_lock.is_acquired());
+
+  context.GetDocument()->GetPage()->SetIsHidden(false, false);
+
+  auto* other_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise other_promise = other_resolver->Promise();
+  wake_lock->DoRequest(WakeLockType::kScreen, other_resolver);
+  screen_lock.WaitForRequest();
+  context.WaitForPromiseFulfillment(other_promise);
+  EXPECT_TRUE(screen_lock.is_acquired());
+}
+
+// https://w3c.github.io/wake-lock/#handling-document-loss-of-visibility
+TEST(WakeLockTest, PageVisibilityHiddenBeforeLockAcquisition) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
+
+  MockWakeLock& screen_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
+  auto* screen_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise screen_promise = screen_resolver->Promise();
+
+  MockWakeLock& system_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
+  auto* system_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise system_promise = system_resolver->Promise();
+
+  auto* wake_lock = MakeGarbageCollected<WakeLock>(*context.GetDocument());
+  wake_lock->DoRequest(WakeLockType::kScreen, screen_resolver);
+  wake_lock->DoRequest(WakeLockType::kSystem, system_resolver);
+  context.GetDocument()->GetPage()->SetIsHidden(true, false);
+
+  context.WaitForPromiseRejection(screen_promise);
+  system_lock.WaitForRequest();
+  context.WaitForPromiseFulfillment(system_promise);
+
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(screen_promise));
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(screen_promise);
+  ASSERT_NE(dom_exception, nullptr);
+  EXPECT_EQ("NotAllowedError", dom_exception->name());
+
+  EXPECT_FALSE(screen_lock.is_acquired());
+  EXPECT_TRUE(system_lock.is_acquired());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc
index ccb0301a..80416984 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_wake_lock_sentinel.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_type.h"
 #include "third_party/blink/renderer/platform/bindings/v8_binding.h"
@@ -317,25 +318,17 @@
 }
 
 // static
-String ScriptPromiseUtils::GetPromiseResolutionAsString(
-    const ScriptPromise& promise) {
-  auto v8_promise = promise.V8Value().As<v8::Promise>();
-  if (v8_promise->State() == v8::Promise::kPending) {
-    return g_empty_string;
-  }
-  ScriptValue promise_result(promise.GetIsolate(), v8_promise->Result());
-  String value;
-  if (!promise_result.ToString(value)) {
-    return g_empty_string;
-  }
-  return value;
-}
-
-// static
 DOMException* ScriptPromiseUtils::GetPromiseResolutionAsDOMException(
     const ScriptPromise& promise) {
   return V8DOMException::ToImplWithTypeCheck(
       promise.GetIsolate(), promise.V8Value().As<v8::Promise>()->Result());
 }
 
+// static
+WakeLockSentinel* ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(
+    const ScriptPromise& promise) {
+  return V8WakeLockSentinel::ToImplWithTypeCheck(
+      promise.GetIsolate(), promise.V8Value().As<v8::Promise>()->Result());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
index 136613a..3228e9c7 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
@@ -22,7 +22,9 @@
 
 namespace blink {
 
+class DOMException;
 class Document;
+class WakeLockSentinel;
 
 // Mock WakeLock implementation that tracks whether it's bound or acquired, and
 // provides a few helper methods to synchronously wait for RequestWakeLock()
@@ -175,15 +177,16 @@
   static v8::Promise::PromiseState GetPromiseState(
       const ScriptPromise& promise);
 
-  // Shorthand for getting a String out of a ScriptPromise. This assumes the
-  // promise has been resolved with a string. If anything wrong happens during
-  // the conversion, an empty string is returned.
-  static String GetPromiseResolutionAsString(const ScriptPromise&);
-
   // Shorthand for getting a DOMException* out of a ScriptPromise. This assumes
   // the promise has been resolved with a DOMException. If the conversion fails,
   // nullptr is returned.
   static DOMException* GetPromiseResolutionAsDOMException(const ScriptPromise&);
+
+  // Shorthand for getting a WakeLockSentinel* out of a ScriptPromise. This
+  // assumes the promise has been resolved with a WakeLockSentinel. If the
+  // conversion fails, nullptr is returned.
+  static WakeLockSentinel* GetPromiseResolutionAsWakeLockSentinel(
+      const ScriptPromise&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.cc
new file mode 100644
index 0000000..094a3ba
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.cc
@@ -0,0 +1,57 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.h"
+
+#include "third_party/blink/renderer/core/workers/worker_navigator.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock.h"
+
+namespace blink {
+
+WorkerNavigatorWakeLock::WorkerNavigatorWakeLock(WorkerNavigator& navigator)
+    : Supplement<WorkerNavigator>(navigator) {}
+
+// static
+const char WorkerNavigatorWakeLock::kSupplementName[] =
+    "WorkerNavigatorWakeLock";
+
+// static
+WorkerNavigatorWakeLock& WorkerNavigatorWakeLock::From(
+    WorkerNavigator& navigator) {
+  WorkerNavigatorWakeLock* supplement =
+      Supplement<WorkerNavigator>::From<WorkerNavigatorWakeLock>(navigator);
+  if (!supplement) {
+    supplement = MakeGarbageCollected<WorkerNavigatorWakeLock>(navigator);
+    ProvideTo(navigator, supplement);
+  }
+  return *supplement;
+}
+
+// static
+WakeLock* WorkerNavigatorWakeLock::wakeLock(ScriptState* script_state,
+                                            WorkerNavigator& navigator) {
+  return WorkerNavigatorWakeLock::From(navigator).GetWakeLock(script_state);
+}
+
+WakeLock* WorkerNavigatorWakeLock::GetWakeLock(ScriptState* script_state) {
+  if (!wake_lock_) {
+    auto* execution_context = ExecutionContext::From(script_state);
+    DCHECK(execution_context);
+
+    // TODO(https://crbug.com/839117): Remove this check once the Exposed
+    // attribute is fixed to only expose this property in dedicated workers.
+    if (execution_context->IsDedicatedWorkerGlobalScope()) {
+      wake_lock_ = MakeGarbageCollected<WakeLock>(
+          *To<DedicatedWorkerGlobalScope>(execution_context));
+    }
+  }
+  return wake_lock_;
+}
+
+void WorkerNavigatorWakeLock::Trace(blink::Visitor* visitor) {
+  visitor->Trace(wake_lock_);
+  Supplement<WorkerNavigator>::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.h b/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.h
new file mode 100644
index 0000000..bc01e30
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.h
@@ -0,0 +1,41 @@
+// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WORKER_NAVIGATOR_WAKE_LOCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WORKER_NAVIGATOR_WAKE_LOCK_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+
+namespace blink {
+
+class ScriptState;
+class WakeLock;
+class WorkerNavigator;
+
+class WorkerNavigatorWakeLock final
+    : public GarbageCollected<WorkerNavigatorWakeLock>,
+      public Supplement<WorkerNavigator> {
+  USING_GARBAGE_COLLECTED_MIXIN(WorkerNavigatorWakeLock);
+
+ public:
+  static const char kSupplementName[];
+
+  static WorkerNavigatorWakeLock& From(WorkerNavigator&);
+
+  static WakeLock* wakeLock(ScriptState*, WorkerNavigator&);
+
+  explicit WorkerNavigatorWakeLock(WorkerNavigator&);
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  WakeLock* GetWakeLock(ScriptState*);
+
+  Member<WakeLock> wake_lock_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WORKER_NAVIGATOR_WAKE_LOCK_H_
diff --git a/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.idl b/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.idl
new file mode 100644
index 0000000..ddcc2c2
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/worker_navigator_wake_lock.idl
@@ -0,0 +1,11 @@
+// Copyright 2019 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.
+
+[
+  ImplementedAs=WorkerNavigatorWakeLock,
+  RuntimeEnabled=WakeLock,
+  SecureContext
+] partial interface WorkerNavigator {
+  [CallWith=ScriptState, SameObject] readonly attribute WakeLock wakeLock;
+};
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-wakelock.html b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-wakelock.html
index 24fe7a5..fac421c 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-wakelock.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-wakelock.html
@@ -2,24 +2,12 @@
 "use strict";
 
 Promise.resolve().then(async () => {
-  // On success, WakeLock.request() returns a promise that never resolves. To
-  // prevent a timeout, abort it with an AbortController and use the different
-  // DOMExceptions we get to determine if this worked or not.
-  const controller = new AbortController();
-  const wakeLock = WakeLock.request("screen", { signal: controller.signal });
-  wakeLock.catch(e => {
-    if (e.name == "AbortError") {
-      // We stopped due to the call to AbortController.abort(), so we did manage
-      // to get the lock.
-      window.parent.postMessage({ enabled: true }, "*");
-    } else if (e.name == "NotAllowedError") {
-      // This means requesting the lock failed.
-      window.parent.postMessage({ enabled: false }, "*");
-    } else {
-      // We should not really hit this branch.
-      window.parent.postMessage({ enabled: false }, "*");
-    }
-  });
-  controller.abort();
+  try {
+    const wakeLock = await navigator.wakeLock.request("screen");
+    await wakeLock.release();
+    window.parent.postMessage({ enabled: true }, "*");
+  } catch (e) {
+    window.parent.postMessage({ enabled: false }, "*");
+  }
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt
new file mode 100644
index 0000000..af5b66f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS idl_test validation
+PASS WakeLock interface: existence and properties of interface object
+PASS WakeLock interface object length
+PASS WakeLock interface object name
+PASS WakeLock interface: existence and properties of interface prototype object
+PASS WakeLock interface: existence and properties of interface prototype object's "constructor" property
+PASS WakeLock interface: existence and properties of interface prototype object's @@unscopables property
+FAIL WakeLock interface: operation requestPermission(WakeLockType) assert_own_property: interface object missing static operation expected property "requestPermission" missing
+FAIL WakeLock interface: operation request(WakeLockType, WakeLockRequestOptions) assert_own_property: interface object missing static operation expected property "request" missing
+PASS WakeLock must be primary interface of navigator.wakeLock
+PASS Stringification of navigator.wakeLock
+PASS WakeLock interface: navigator.wakeLock must inherit property "requestPermission(WakeLockType)" with the proper type
+FAIL WakeLock interface: calling requestPermission(WakeLockType) on navigator.wakeLock with too few arguments must throw TypeError assert_own_property: interface object must have static operation as own property expected property "requestPermission" missing
+PASS WakeLock interface: navigator.wakeLock must inherit property "request(WakeLockType, WakeLockRequestOptions)" with the proper type
+FAIL WakeLock interface: calling request(WakeLockType, WakeLockRequestOptions) on navigator.wakeLock with too few arguments must throw TypeError assert_own_property: interface object must have static operation as own property expected property "request" missing
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.js b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.js
index 2ad9980..31d20aed 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.js
@@ -10,7 +10,7 @@
   ['dom', 'html', 'permissions'],
   idl_array => {
     idl_array.add_objects({
-      WakeLock: []
+      WakeLock: ['navigator.wakeLock'],
     });
   }
 );
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt
new file mode 100644
index 0000000..6c96a32
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS idl_test validation
+PASS WakeLock interface: existence and properties of interface object
+PASS WakeLock interface object length
+PASS WakeLock interface object name
+PASS WakeLock interface: existence and properties of interface prototype object
+PASS WakeLock interface: existence and properties of interface prototype object's "constructor" property
+PASS WakeLock interface: existence and properties of interface prototype object's @@unscopables property
+PASS WakeLock interface: member requestPermission
+FAIL WakeLock interface: operation request(WakeLockType, WakeLockRequestOptions) assert_own_property: interface object missing static operation expected property "request" missing
+PASS WakeLock must be primary interface of navigator.wakeLock
+PASS Stringification of navigator.wakeLock
+PASS WakeLock interface: navigator.wakeLock must not have property "requestPermission"
+PASS WakeLock interface: navigator.wakeLock must inherit property "request(WakeLockType, WakeLockRequestOptions)" with the proper type
+FAIL WakeLock interface: calling request(WakeLockType, WakeLockRequestOptions) on navigator.wakeLock with too few arguments must throw TypeError assert_own_property: interface object must have static operation as own property expected property "request" missing
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-abortsignal.https.any.js b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-abortsignal.https.any.js
deleted file mode 100644
index 671852f..0000000
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-abortsignal.https.any.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// META: title=WakeLock.request() AbortSignal Test
-
-'use strict';
-
-promise_test(async t => {
-  const invalidSignals = [
-    "string",
-    123,
-    {},
-    true,
-    Symbol(),
-    () => {},
-    self
-  ];
-
-  for (let signal of invalidSignals) {
-    await promise_rejects(t, new TypeError(), WakeLock.request('system', { signal: signal }));
-  }
-}, "'TypeError' is thrown when the signal option is not an AbortSignal");
-
-promise_test(t => {
-  const abortController = new AbortController();
-  const abortSignal = abortController.signal;
-  abortController.abort();
-  assert_true(abortSignal.aborted);
-
-  return promise_rejects(t, "AbortError", WakeLock.request('system', { signal: abortSignal }));
-}, "A WakeLock request with an AbortSignal whose abort flag is set always aborts");
-
-promise_test(async t => {
-  const abortController = new AbortController();
-  const abortSignal = abortController.signal;
-  abortController.abort();
-  assert_true(abortSignal.aborted);
-
-  const lock1 = WakeLock.request('system', { signal: abortSignal });
-  const lock2 = WakeLock.request('system', { signal: abortSignal });
-  const lock3 = WakeLock.request('system', { signal: abortSignal });
-
-  await promise_rejects(t, "AbortError", lock1);
-  await promise_rejects(t, "AbortError", lock2);
-  await promise_rejects(t, "AbortError", lock3);
-}, "The same AbortSignal can be used to cause multiple wake locks to abort");
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-active-document.https.window.js b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-active-document.https.window.js
index f0f1e38..ad6250e 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-active-document.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-active-document.https.window.js
@@ -3,8 +3,8 @@
     iframe.addEventListener(
       "load",
       () => {
-        const { WakeLock } = iframe.contentWindow;
-        resolve(WakeLock);
+        const { wakeLock } = iframe.contentWindow.navigator;
+        resolve(wakeLock);
       },
       { once: true }
     );
@@ -35,7 +35,7 @@
   );
   // We are done, so clean up.
   iframe.remove();
-}, "WakeLock.request() aborts if the document is not active.");
+}, "navigator.wakeLock.request() aborts if the document is not active.");
 
 promise_test(async t => {
   // We nest two iframes and wait for them to load.
@@ -78,4 +78,4 @@
   );
   // We are done, so clean up.
   outerIframe.remove();
-}, "WakeLock.request() aborts if the document is active, but not fully active.");
+}, "navigator.wakeLock.request() aborts if the document is active, but not fully active.");
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-disabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-disabled-by-feature-policy.https.sub.html
index 0d451c9..07eeb36 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-disabled-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-disabled-by-feature-policy.https.sub.html
@@ -12,12 +12,12 @@
     "https://{{domains[www]}}:{{ports[https][0]}}" + same_origin_src;
 
   promise_test(t => {
-    return promise_rejects(t, "NotAllowedError", WakeLock.request("screen"));
+    return promise_rejects(t, "NotAllowedError", navigator.wakeLock.request("screen"));
   }, 'Feature-Policy header {"wake-lock" : []} disallows the top-level document.');
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       same_origin_src,
       expect_feature_unavailable_default
@@ -26,7 +26,7 @@
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       cross_origin_src,
       expect_feature_unavailable_default
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-document-hidden-manual.https.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-document-hidden-manual.https.html
index d7c1248..babd629 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-document-hidden-manual.https.html
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-document-hidden-manual.https.html
@@ -8,30 +8,20 @@
 <script>
 
 promise_test(async t => {
-  const controller = new AbortController();
-  const screenWakeLock = WakeLock.request('screen', { signal: controller.signal });
-  const systemWakeLock = WakeLock.request('system', { signal: controller.signal });
-  const systemWakeLockPromise = new Promise((resolve, reject) => {
-    systemWakeLock.catch(error => {
-      assert_equals("AbortError", error.name, "systemWakeLock must have been aborted");
-      assert_false(document.hidden, "systemWakeLock must have been aborted after the page is visible again");
-      resolve();
-    });
-  });
+  const screenWakeLock = await navigator.wakeLock.request('screen');
+  const screenWakeLockReleased =
+      new EventWatcher(t, screenWakeLock, "release").wait_for("release");
 
   const eventWatcher = new EventWatcher(t, document, "visibilitychange");
   await eventWatcher.wait_for("visibilitychange");
   assert_true(document.hidden, "document is hidden after the visibilitychange event");
-  await promise_rejects(t, "AbortError", screenWakeLock, "existing screen locks are aborted");
-  await promise_rejects(t, "NotAllowedError", WakeLock.request('screen'),
+  await screenWakeLockReleased;
+  await promise_rejects(t, "NotAllowedError", navigator.wakeLock.request('screen'),
       "new screen locks are not allowed when the page is not visible");
 
   await eventWatcher.wait_for("visibilitychange");
   assert_false(document.hidden, "document is no longer hidden after the visibilitychange event");
-  controller.abort();
-
-  return systemWakeLockPromise;
-}, "Test screen locks respect page visibility changes and system locks are unchanged");
+}, "Test screen locks respect page visibility changes");
 
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub-expected.txt
new file mode 100644
index 0000000..5c21953
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Feature-Policy allow="wake-lock" allows same-origin relocation assert_true: navigator.wakeLock.request("screen") expected true got false
+PASS Feature-Policy allow="wake-lock" disallows cross-origin relocation
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
index b27fbef..18a2d7c 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
@@ -12,9 +12,13 @@
   const cross_origin_src =
     base_src + "https://{{domains[www]}}:{{ports[https][0]}}" + relative_path;
 
+  // request() checks for both Feature Policy and permission, so the tests below
+  // can have inconsistent results due to the default permission a wake lock
+  // request might return.
+
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       same_origin_src,
       expect_feature_available_default,
@@ -24,7 +28,7 @@
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       cross_origin_src,
       expect_feature_unavailable_default,
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute.https.sub-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute.https.sub-expected.txt
new file mode 100644
index 0000000..265fa6d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute.https.sub-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Feature policy "wake-lock" can be enabled in same-origin iframe using allow="wake-lock" attribute assert_true: navigator.wakeLock.request("screen") expected true got false
+FAIL Feature policy "wake-lock" can be enabled in cross-origin iframe using allow="wake-lock" attribute assert_true: navigator.wakeLock.request("screen") expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute.https.sub.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute.https.sub.html
index 3897df6..5587a85 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy-attribute.https.sub.html
@@ -13,7 +13,7 @@
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       same_origin_src,
       expect_feature_available_default,
@@ -23,7 +23,7 @@
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       cross_origin_src,
       expect_feature_available_default,
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy.https.sub-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy.https.sub-expected.txt
new file mode 100644
index 0000000..a0108cf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy.https.sub-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL Feature-Policy header {"wake-lock" : ["*"]} allows the top-level document. promise_test: Unhandled rejection with value: object "NotAllowedError: Wake Lock permission request denied"
+FAIL Feature-Policy header {"wake-lock" : ["*"]} allows same-origin iframes. assert_true: navigator.wakeLock.request("screen") expected true got false
+FAIL Feature-Policy header {"wake-lock" : ["*"]} allows cross-origin iframes. assert_true: navigator.wakeLock.request("screen") expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy.https.sub.html
index f4aaa73..8573f7a 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-by-feature-policy.https.sub.html
@@ -11,16 +11,17 @@
   const cross_origin_src =
     "https://{{domains[www]}}:{{ports[https][0]}}" + same_origin_src;
 
+  // request() checks for both Feature Policy and permission, so the tests below
+  // can have inconsistent results due to the default permission a wake lock
+  // request might return.
+
   promise_test(t => {
-    const controller = new AbortController();
-    const lock = WakeLock.request("screen", { signal: controller.signal });
-    controller.abort();
-    return promise_rejects(t, "AbortError", lock);
+    return navigator.wakeLock.request('screen').then(lock => lock.release());
   }, 'Feature-Policy header {"wake-lock" : ["*"]} allows the top-level document.');
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       same_origin_src,
       expect_feature_available_default
@@ -29,7 +30,7 @@
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       cross_origin_src,
       expect_feature_available_default
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-on-self-origin-by-feature-policy.https.sub-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-on-self-origin-by-feature-policy.https.sub-expected.txt
new file mode 100644
index 0000000..969fb83a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-on-self-origin-by-feature-policy.https.sub-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL Feature-Policy header wake-lock "self" allows the top-level document. promise_test: Unhandled rejection with value: object "NotAllowedError: Wake Lock permission request denied"
+FAIL Feature-Policy header wake-lock "self" allows same-origin iframes. assert_true: navigator.wakeLock.request("screen") expected true got false
+PASS Feature-Policy header wake-lock "self" disallows cross-origin iframes.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-on-self-origin-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-on-self-origin-by-feature-policy.https.sub.html
index 19bf59d3..3082170 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-on-self-origin-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-enabled-on-self-origin-by-feature-policy.https.sub.html
@@ -12,16 +12,17 @@
   const cross_origin_src =
     "https://{{domains[www]}}:{{ports[https][0]}}" + same_origin_src;
 
+  // request() checks for both Feature Policy and permission, so the tests below
+  // can have inconsistent results due to the default permission a wake lock
+  // request might return.
+
   promise_test(t => {
-    const controller = new AbortController();
-    const lock = WakeLock.request("screen", { signal: controller.signal });
-    controller.abort();
-    return promise_rejects(t, "AbortError", lock);
+    return navigator.wakeLock.request('screen').then(lock => lock.release());
   }, 'Feature-Policy header wake-lock "self" allows the top-level document.');
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       same_origin_src,
       expect_feature_available_default
@@ -30,7 +31,7 @@
 
   async_test(t => {
     test_feature_availability(
-      'WakeLock.request("screen")',
+      'navigator.wakeLock.request("screen")',
       t,
       cross_origin_src,
       expect_feature_unavailable_default
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker.js b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker.js
index 28e3394..5450fe5b 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker.js
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker.js
@@ -2,7 +2,7 @@
 importScripts("/resources/testharness.js");
 
 promise_test(t => {
-  return promise_rejects(t, "NotAllowedError", WakeLock.request('screen'));
+  return promise_rejects(t, "NotAllowedError", navigator.wakeLock.request('screen'));
 }, "Screen wake lock should not be allowed in dedicated worker");
 
 done();
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.js b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.js
index cc37c76..62f59a7 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.js
@@ -1,7 +1,7 @@
-//META: title=WakeLock.request() with invalid type
+//META: title=navigator.wakeLock.request() with invalid type
 
 promise_test(async t => {
-  await promise_rejects(t, new TypeError(), WakeLock.request());
+  return promise_rejects(t, new TypeError(), navigator.wakeLock.request());
 }, "'TypeError' is thrown when set an empty wake lock type");
 
 promise_test(t => {
@@ -14,6 +14,6 @@
     true
   ];
   return Promise.all(invalidTypes.map(invalidType => {
-    return promise_rejects(t, new TypeError(), WakeLock.request(invalidType));
+    return promise_rejects(t, new TypeError(), navigator.wakeLock.request(invalidType));
   }));
 }, "'TypeError' is thrown when set an invalid wake lock type");
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 93fd88db..c767408d 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -3744,6 +3744,7 @@
     getter storage
     getter usb
     getter userAgent
+    getter wakeLock
     method constructor
 interface WritableStream
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html b/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
index e0fc6b3..73986d7 100644
--- a/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
+++ b/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
@@ -11,28 +11,38 @@
 promise_test(t => {
   window.testRunner.setPageVisibility('hidden');
   assert_true(document.hidden);
-  return promise_rejects(t, "NotAllowedError", WakeLock.request('screen'));
-}, "WakeLock.request('screen') fails when the document is hidden");
+  return promise_rejects(t, "NotAllowedError", navigator.wakeLock.request('screen'));
+}, "navigator.wakeLock.request('screen') fails when the document is hidden");
 
+// This test currently checks for Chromium-specific behavior: WakeLock::request()
+// performs an asynchronous permission request, which is why it is possible to
+// call navigator.wakeLock.request(), change page visibility later and still get
+// a NotAllowedError.
+// A more interoperable test would allow both |screenLock| to reject with
+// NotAllowedError as well as it resolving to a WakeLockSentinel that would fire
+// a "release" event upon page vsibility change.
 promise_test(t => {
   window.testRunner.setPageVisibility('visible');
 
-  const screenLock = WakeLock.request('screen');
+  const screenLock = navigator.wakeLock.request('screen');
   window.testRunner.setPageVisibility('hidden');
   assert_true(document.hidden);
   return promise_rejects(t, "NotAllowedError", screenLock);
-}, "WakeLock.request('screen') aborts when the page is hidden");
+}, "navigator.wakeLock.request('screen') aborts when the page is hidden");
 
 promise_test(async t => {
   window.testRunner.setPageVisibility('visible');
 
-  const controller = new AbortController();
-  const screenLock = WakeLock.request('screen', { signal: controller.signal });
+  const screenLock1 = await navigator.wakeLock.request('screen');
+  const screenLock2 = await navigator.wakeLock.request('screen');
+
+  const wait1 = new EventWatcher(t, screenLock1, 'release').wait_for('release');
+  const wait2 = new EventWatcher(t, screenLock2, 'release').wait_for('release');
+
   window.testRunner.setPageVisibility('hidden');
-  assert_true(document.hidden);
-  await promise_rejects(t, "NotAllowedError", screenLock);
-  controller.abort();
-}, "Aborting a rejected wake lock does not crash");
+
+  return Promise.all([wait1, wait2]);
+}, "Screen wake locks are released when the document the page is hidden");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/wake-lock/wakelock-request-denied.html b/third_party/blink/web_tests/wake-lock/wakelock-request-denied.html
index adecbf0..2819e5ca 100644
--- a/third_party/blink/web_tests/wake-lock/wakelock-request-denied.html
+++ b/third_party/blink/web_tests/wake-lock/wakelock-request-denied.html
@@ -8,7 +8,7 @@
 
 promise_test(t => {
   self.testRunner.setPermission('wake-lock-screen', 'denied', location.origin, location.origin);
-  return promise_rejects(t, "NotAllowedError", WakeLock.request('screen'));
+  return promise_rejects(t, "NotAllowedError", navigator.wakeLock.request('screen'));
 }, 'Denied requests should abort with NotAllowedError');
 </script>
 </body>
diff --git a/third_party/blink/web_tests/wake-lock/wakelock-requestPermission.https.html b/third_party/blink/web_tests/wake-lock/wakelock-requestPermission.https.html
deleted file mode 100644
index be7df136..0000000
--- a/third_party/blink/web_tests/wake-lock/wakelock-requestPermission.https.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script>
-'use strict';
-
-promise_test(async t => {
-  self.testRunner.setPermission('wake-lock-screen', 'granted', location.origin, location.origin);
-  const status = await WakeLock.requestPermission('screen');
-  assert_equals(status, 'granted');
-}, 'WakeLock.requestPermission() returns "granted" for allowed requests');
-
-promise_test(async t => {
-  self.testRunner.setPermission('wake-lock-system', 'denied', location.origin, location.origin);
-  const status = await WakeLock.requestPermission('system');
-  assert_equals(status, 'denied');
-}, 'WakeLock.requestPermission() returns "denied" for blocked requests');
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index e6fff60..dd04540 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1515,9 +1515,20 @@
 [Worker]     getter isActive
 [Worker]     method constructor
 [Worker] interface WakeLock
-[Worker]     static method request
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
+[Worker]     method request
+[Worker] interface WakeLockEvent : Event
+[Worker]     attribute @@toStringTag
+[Worker]     getter lock
+[Worker]     method constructor
+[Worker] interface WakeLockSentinel : EventTarget
+[Worker]     attribute @@toStringTag
+[Worker]     getter onrelease
+[Worker]     getter type
+[Worker]     method constructor
+[Worker]     method release
+[Worker]     setter onrelease
 [Worker] interface WebGL2ComputeRenderingContext
 [Worker]     attribute @@toStringTag
 [Worker]     attribute ACTIVE_ATOMIC_COUNTER_BUFFERS
@@ -3794,6 +3805,7 @@
 [Worker]     getter storage
 [Worker]     getter usb
 [Worker]     getter userAgent
+[Worker]     getter wakeLock
 [Worker]     method constructor
 [Worker] interface WritableStream
 [Worker]     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index fcfa531..5d9a5c1 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5206,6 +5206,7 @@
     getter userAgent
     getter vendor
     getter vendorSub
+    getter wakeLock
     getter webkitPersistentStorage
     getter webkitTemporaryStorage
     getter xr
@@ -8464,10 +8465,20 @@
     setter onresize
     setter onscroll
 interface WakeLock
-    static method request
-    static method requestPermission
     attribute @@toStringTag
     method constructor
+    method request
+interface WakeLockEvent : Event
+    attribute @@toStringTag
+    getter lock
+    method constructor
+interface WakeLockSentinel : EventTarget
+    attribute @@toStringTag
+    getter onrelease
+    getter type
+    method constructor
+    method release
+    setter onrelease
 interface WaveShaperNode : AudioNode
     attribute @@toStringTag
     getter curve
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 1b87eaa..647debe 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -3643,6 +3643,7 @@
 [Worker]     getter storage
 [Worker]     getter usb
 [Worker]     getter userAgent
+[Worker]     getter wakeLock
 [Worker]     method constructor
 [Worker] interface WritableStream
 [Worker]     attribute @@toStringTag