blob: e73c88394eb81925241467b93a5e1da79fb795b7 [file] [log] [blame]
// Copyright 2018 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 "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/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/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"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
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();
// 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;
}
// static
ScriptPromise WakeLock::request(ScriptState* script_state,
const String& type,
WakeLockRequestOptions* options) {
// https://w3c.github.io/wake-lock/#request-static-method
auto* context = ExecutionContext::From(script_state);
DCHECK(context->IsDocument() || context->IsDedicatedWorkerGlobalScope());
// 2.1. If document is not allowed to use the policy-controlled feature named
// "wake-lock", reject promise with a "NotAllowedError" DOMException and
// return promise.
// [N.B. Per https://github.com/w3c/webappsec-feature-policy/issues/207 there
// is no official support for workers in the Feature Policy spec, but we can
// perform FP checks in workers in Blink]
// 2.2. If the user agent denies the wake lock of this type for document,
// reject promise with a "NotAllowedError" DOMException and return
// promise.
if (!context->GetSecurityContext().IsFeatureEnabled(
mojom::FeaturePolicyFeature::kWakeLock,
ReportOptions::kReportOnFailure)) {
return ScriptPromise::RejectWithDOMException(
script_state,
MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError,
"Access to WakeLock features is disallowed by feature policy"));
}
if (context->IsDedicatedWorkerGlobalScope()) {
// 3. If the current global object is the DedicatedWorkerGlobalScope object:
// 3.1. If the current global object's owner set is empty, reject promise
// with a "NotAllowedError" DOMException and return promise.
// 3.2. If type is "screen", reject promise with a "NotAllowedError"
// DOMException, and return promise.
if (type == "screen") {
return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError,
"Screen locks cannot be requested from workers"));
}
} else if (context->IsDocument()) {
// 2. Let document be the responsible document of the current settings
// object.
auto* document = To<Document>(context);
// 4. Otherwise, if the current global object is the Window object:
// 4.1. If the document's browsing context is null, reject promise with a
// "NotAllowedError" DOMException and return promise.
if (!document) {
return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError,
"The document has no associated browsing context"));
}
// 4.2. If document is not fully active, reject promise with a
// "NotAllowedError" DOMException, and return promise.
if (!document->IsActive()) {
return ScriptPromise::RejectWithDOMException(
script_state,
MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotAllowedError,
"The document is not active"));
}
// 4.3. If type is "screen" and the Document of the top-level browsing
// context is hidden, reject promise with a "NotAllowedError"
// DOMException, and return promise.
if (type == "screen" &&
!(document->GetPage() && document->GetPage()->IsPageVisible())) {
return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError,
"The requesting page is not visible"));
}
}
// 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:
UseCounter::Count(context, WebFeature::kWakeLockAcquireScreenLock);
break;
case WakeLockType::kSystem:
UseCounter::Count(context, WebFeature::kWakeLockAcquireSystemLock);
break;
default:
NOTREACHED();
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());
// 7. Return promise.
return promise;
}
} // namespace blink