blob: 691726eb23434005adc559534fc0aeede937cc92 [file] [log] [blame] [edit]
// Copyright 2016 The Chromium Authors
// 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/permissions/permission_utils.h"
#include <utility>
#include "build/build_config.h"
#include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_permission_state.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_camera_device_permission_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_clipboard_permission_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_fullscreen_permission_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_midi_permission_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_permission_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_permission_name.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_push_permission_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_top_level_storage_access_permission_descriptor.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/core/workers/worker_thread.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace {
constexpr V8PermissionState::Enum ToPermissionStateEnum(
mojom::blink::PermissionStatus status) {
// This assertion protects against the IDL enum changing without updating the
// corresponding mojom interface, while the lack of a default case in the
// switch statement below ensures the opposite.
static_assert(
V8PermissionState::kEnumSize == 3u,
"the number of fields in the PermissionStatus mojom enum "
"must match the number of fields in the PermissionState blink enum");
switch (status) {
case mojom::blink::PermissionStatus::GRANTED:
return V8PermissionState::Enum::kGranted;
case mojom::blink::PermissionStatus::DENIED:
return V8PermissionState::Enum::kDenied;
case mojom::blink::PermissionStatus::ASK:
return V8PermissionState::Enum::kPrompt;
}
}
} // namespace
// There are two PermissionDescriptor, one in Mojo bindings and one
// in v8 bindings so we'll rename one here.
using MojoPermissionDescriptor = mojom::blink::PermissionDescriptor;
using mojom::blink::PermissionDescriptorPtr;
using mojom::blink::PermissionName;
void ConnectToPermissionService(
ExecutionContext* execution_context,
mojo::PendingReceiver<mojom::blink::PermissionService> receiver) {
execution_context->GetBrowserInterfaceBroker().GetInterface(
std::move(receiver));
}
V8PermissionState ToV8PermissionState(mojom::blink::PermissionStatus status) {
return V8PermissionState(ToPermissionStateEnum(status));
}
String PermissionNameToString(PermissionName name) {
// TODO(crbug.com/1395451): Change these strings to match the JS permission
// strings (dashes instead of underscores).
switch (name) {
case PermissionName::GEOLOCATION:
return "geolocation";
case PermissionName::NOTIFICATIONS:
return "notifications";
case PermissionName::MIDI:
return "midi";
case PermissionName::PROTECTED_MEDIA_IDENTIFIER:
return "protected_media_identifier";
case PermissionName::DURABLE_STORAGE:
return "durable_storage";
case PermissionName::AUDIO_CAPTURE:
return "audio_capture";
case PermissionName::VIDEO_CAPTURE:
return "video_capture";
case PermissionName::BACKGROUND_SYNC:
return "background_sync";
case PermissionName::SENSORS:
return "sensors";
case PermissionName::CLIPBOARD_READ:
return "clipboard_read";
case PermissionName::CLIPBOARD_WRITE:
return "clipboard_write";
case PermissionName::PAYMENT_HANDLER:
return "payment_handler";
case PermissionName::BACKGROUND_FETCH:
return "background_fetch";
case PermissionName::IDLE_DETECTION:
return "idle_detection";
case PermissionName::PERIODIC_BACKGROUND_SYNC:
return "periodic_background_sync";
case PermissionName::SCREEN_WAKE_LOCK:
return "screen_wake_lock";
case PermissionName::SYSTEM_WAKE_LOCK:
return "system_wake_lock";
case PermissionName::NFC:
return "nfc";
case PermissionName::STORAGE_ACCESS:
return "storage-access";
case PermissionName::WINDOW_MANAGEMENT:
return "window-management";
case PermissionName::LOCAL_FONTS:
return "local_fonts";
case PermissionName::DISPLAY_CAPTURE:
return "display_capture";
case PermissionName::TOP_LEVEL_STORAGE_ACCESS:
return "top-level-storage-access";
case PermissionName::CAPTURED_SURFACE_CONTROL:
return "captured-surface-control";
case PermissionName::SPEAKER_SELECTION:
return "speaker-selection";
case PermissionName::KEYBOARD_LOCK:
return "keyboard-lock";
case PermissionName::POINTER_LOCK:
return "pointer-lock";
case PermissionName::FULLSCREEN:
return "fullscreen";
case PermissionName::WEB_APP_INSTALLATION:
return "web-app-installation";
case PermissionName::LOCAL_NETWORK_ACCESS:
return "local-network-access";
case PermissionName::VR:
return "vr";
case PermissionName::AR:
return "ar";
case PermissionName::HAND_TRACKING:
return "hand-tracking";
case PermissionName::WEB_PRINTING:
return "web-printing";
case PermissionName::SMART_CARD:
return "smart-card";
}
}
PermissionDescriptorPtr CreatePermissionDescriptor(PermissionName name) {
auto descriptor = MojoPermissionDescriptor::New();
descriptor->name = name;
return descriptor;
}
PermissionDescriptorPtr CreateMidiPermissionDescriptor(bool sysex) {
auto descriptor = CreatePermissionDescriptor(PermissionName::MIDI);
auto midi_extension = mojom::blink::MidiPermissionDescriptor::New();
midi_extension->sysex = sysex;
descriptor->extension = mojom::blink::PermissionDescriptorExtension::NewMidi(
std::move(midi_extension));
return descriptor;
}
PermissionDescriptorPtr CreateClipboardPermissionDescriptor(
PermissionName name,
bool has_user_gesture,
bool will_be_sanitized) {
auto descriptor = CreatePermissionDescriptor(name);
auto clipboard_extension = mojom::blink::ClipboardPermissionDescriptor::New(
has_user_gesture, will_be_sanitized);
descriptor->extension =
mojom::blink::PermissionDescriptorExtension::NewClipboard(
std::move(clipboard_extension));
return descriptor;
}
PermissionDescriptorPtr CreateVideoCapturePermissionDescriptor(
bool pan_tilt_zoom) {
auto descriptor = CreatePermissionDescriptor(PermissionName::VIDEO_CAPTURE);
auto camera_device_extension =
mojom::blink::CameraDevicePermissionDescriptor::New(pan_tilt_zoom);
descriptor->extension =
mojom::blink::PermissionDescriptorExtension::NewCameraDevice(
std::move(camera_device_extension));
return descriptor;
}
PermissionDescriptorPtr CreateTopLevelStorageAccessPermissionDescriptor(
const KURL& origin_as_kurl) {
auto descriptor =
CreatePermissionDescriptor(PermissionName::TOP_LEVEL_STORAGE_ACCESS);
scoped_refptr<SecurityOrigin> supplied_origin =
SecurityOrigin::Create(origin_as_kurl);
auto top_level_storage_access_extension =
mojom::blink::TopLevelStorageAccessPermissionDescriptor::New();
top_level_storage_access_extension->requestedOrigin = supplied_origin;
descriptor->extension =
mojom::blink::PermissionDescriptorExtension::NewTopLevelStorageAccess(
std::move(top_level_storage_access_extension));
return descriptor;
}
PermissionDescriptorPtr CreateFullscreenPermissionDescriptor(
bool allow_without_user_gesture) {
auto descriptor = CreatePermissionDescriptor(PermissionName::FULLSCREEN);
auto fullscreen_extension = mojom::blink::FullscreenPermissionDescriptor::New(
allow_without_user_gesture);
descriptor->extension =
mojom::blink::PermissionDescriptorExtension::NewFullscreen(
std::move(fullscreen_extension));
return descriptor;
}
PermissionDescriptorPtr ParsePermissionDescriptor(
ScriptState* script_state,
const ScriptValue& raw_descriptor,
ExceptionState& exception_state) {
PermissionDescriptor* permission =
NativeValueTraits<PermissionDescriptor>::NativeValue(
script_state->GetIsolate(), raw_descriptor.V8Value(),
exception_state);
if (exception_state.HadException()) {
return nullptr;
}
const auto& name = permission->name();
if (name == V8PermissionName::Enum::kGeolocation) {
return CreatePermissionDescriptor(PermissionName::GEOLOCATION);
}
if (name == V8PermissionName::Enum::kCamera) {
CameraDevicePermissionDescriptor* camera_device_permission =
NativeValueTraits<CameraDevicePermissionDescriptor>::NativeValue(
script_state->GetIsolate(), raw_descriptor.V8Value(),
exception_state);
if (exception_state.HadException()) {
return nullptr;
}
return CreateVideoCapturePermissionDescriptor(
camera_device_permission->panTiltZoom());
}
if (name == V8PermissionName::Enum::kMicrophone) {
return CreatePermissionDescriptor(PermissionName::AUDIO_CAPTURE);
}
if (name == V8PermissionName::Enum::kNotifications) {
return CreatePermissionDescriptor(PermissionName::NOTIFICATIONS);
}
if (name == V8PermissionName::Enum::kPersistentStorage) {
return CreatePermissionDescriptor(PermissionName::DURABLE_STORAGE);
}
if (name == V8PermissionName::Enum::kPush) {
PushPermissionDescriptor* push_permission =
NativeValueTraits<PushPermissionDescriptor>::NativeValue(
script_state->GetIsolate(), raw_descriptor.V8Value(),
exception_state);
if (exception_state.HadException()) {
return nullptr;
}
// Only "userVisibleOnly" push is supported for now.
if (!push_permission->userVisibleOnly()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"Push Permission without userVisibleOnly:true isn't supported yet.");
return nullptr;
}
return CreatePermissionDescriptor(PermissionName::NOTIFICATIONS);
}
if (name == V8PermissionName::Enum::kMidi) {
MidiPermissionDescriptor* midi_permission =
NativeValueTraits<MidiPermissionDescriptor>::NativeValue(
script_state->GetIsolate(), raw_descriptor.V8Value(),
exception_state);
return CreateMidiPermissionDescriptor(midi_permission->sysex());
}
if (name == V8PermissionName::Enum::kBackgroundSync) {
return CreatePermissionDescriptor(PermissionName::BACKGROUND_SYNC);
}
if (name == V8PermissionName::Enum ::kAmbientLightSensor ||
name == V8PermissionName::Enum::kAccelerometer ||
name == V8PermissionName::Enum::kGyroscope ||
name == V8PermissionName::Enum::kMagnetometer) {
// ALS requires an extra flag.
if (name == V8PermissionName::Enum::kAmbientLightSensor) {
if (!RuntimeEnabledFeatures::SensorExtraClassesEnabled()) {
exception_state.ThrowTypeError(
"GenericSensorExtraClasses flag is not enabled.");
return nullptr;
}
}
return CreatePermissionDescriptor(PermissionName::SENSORS);
}
if (name == V8PermissionName::Enum::kClipboardRead ||
name == V8PermissionName::Enum::kClipboardWrite) {
PermissionName permission_name = PermissionName::CLIPBOARD_READ;
if (name == V8PermissionName::Enum::kClipboardWrite) {
permission_name = PermissionName::CLIPBOARD_WRITE;
}
ClipboardPermissionDescriptor* clipboard_permission =
NativeValueTraits<ClipboardPermissionDescriptor>::NativeValue(
script_state->GetIsolate(), raw_descriptor.V8Value(),
exception_state);
return CreateClipboardPermissionDescriptor(
permission_name,
/*has_user_gesture=*/!clipboard_permission->allowWithoutGesture(),
/*will_be_sanitized=*/
!clipboard_permission->allowWithoutSanitization());
}
if (name == V8PermissionName::Enum::kPaymentHandler) {
return CreatePermissionDescriptor(PermissionName::PAYMENT_HANDLER);
}
if (name == V8PermissionName::Enum::kBackgroundFetch) {
return CreatePermissionDescriptor(PermissionName::BACKGROUND_FETCH);
}
if (name == V8PermissionName::Enum::kIdleDetection) {
return CreatePermissionDescriptor(PermissionName::IDLE_DETECTION);
}
if (name == V8PermissionName::Enum::kPeriodicBackgroundSync) {
return CreatePermissionDescriptor(PermissionName::PERIODIC_BACKGROUND_SYNC);
}
if (name == V8PermissionName::Enum::kScreenWakeLock) {
return CreatePermissionDescriptor(PermissionName::SCREEN_WAKE_LOCK);
}
if (name == V8PermissionName::Enum::kSystemWakeLock) {
if (!RuntimeEnabledFeatures::SystemWakeLockEnabled(
ExecutionContext::From(script_state))) {
exception_state.ThrowTypeError("System Wake Lock is not enabled.");
return nullptr;
}
return CreatePermissionDescriptor(PermissionName::SYSTEM_WAKE_LOCK);
}
if (name == V8PermissionName::Enum::kNfc) {
if (!RuntimeEnabledFeatures::WebNFCEnabled(
ExecutionContext::From(script_state))) {
exception_state.ThrowTypeError("Web NFC is not enabled.");
return nullptr;
}
return CreatePermissionDescriptor(PermissionName::NFC);
}
if (name == V8PermissionName::Enum::kStorageAccess) {
return CreatePermissionDescriptor(PermissionName::STORAGE_ACCESS);
}
if (name == V8PermissionName::Enum::kTopLevelStorageAccess) {
TopLevelStorageAccessPermissionDescriptor*
top_level_storage_access_permission =
NativeValueTraits<TopLevelStorageAccessPermissionDescriptor>::
NativeValue(script_state->GetIsolate(),
raw_descriptor.V8Value(), exception_state);
if (exception_state.HadException()) {
return nullptr;
}
KURL origin_as_kurl{top_level_storage_access_permission->requestedOrigin()};
if (!origin_as_kurl.IsValid()) {
exception_state.ThrowTypeError("The requested origin is invalid.");
return nullptr;
}
return CreateTopLevelStorageAccessPermissionDescriptor(origin_as_kurl);
}
if (name == V8PermissionName::Enum::kWindowManagement) {
return CreatePermissionDescriptor(PermissionName::WINDOW_MANAGEMENT);
}
if (name == V8PermissionName::Enum::kLocalFonts) {
if (!RuntimeEnabledFeatures::FontAccessEnabled(
ExecutionContext::From(script_state))) {
exception_state.ThrowTypeError("Local Fonts Access API is not enabled.");
return nullptr;
}
return CreatePermissionDescriptor(PermissionName::LOCAL_FONTS);
}
if (name == V8PermissionName::Enum::kDisplayCapture) {
return CreatePermissionDescriptor(PermissionName::DISPLAY_CAPTURE);
}
if (name == V8PermissionName::Enum::kCapturedSurfaceControl) {
if (!RuntimeEnabledFeatures::CapturedSurfaceControlEnabled(
ExecutionContext::From(script_state))) {
exception_state.ThrowTypeError(
"The Captured Surface Control API is not enabled.");
return nullptr;
}
return CreatePermissionDescriptor(PermissionName::CAPTURED_SURFACE_CONTROL);
}
if (name == V8PermissionName::Enum::kSpeakerSelection) {
if (!RuntimeEnabledFeatures::SpeakerSelectionEnabled(
ExecutionContext::From(script_state))) {
exception_state.ThrowTypeError(
"The Speaker Selection API is not enabled.");
return nullptr;
}
return CreatePermissionDescriptor(PermissionName::SPEAKER_SELECTION);
}
if (name == V8PermissionName::Enum::kKeyboardLock) {
#if !BUILDFLAG(IS_ANDROID)
return CreatePermissionDescriptor(PermissionName::KEYBOARD_LOCK);
#else
exception_state.ThrowTypeError(
"The Keyboard Lock permission isn't available on Android.");
return nullptr;
#endif
}
if (name == V8PermissionName::Enum::kPointerLock) {
#if !BUILDFLAG(IS_ANDROID)
return CreatePermissionDescriptor(PermissionName::POINTER_LOCK);
#else
exception_state.ThrowTypeError(
"The Pointer Lock permission isn't available on Android.");
return nullptr;
#endif
}
if (name == V8PermissionName::Enum::kFullscreen) {
FullscreenPermissionDescriptor* fullscreen_permission =
NativeValueTraits<FullscreenPermissionDescriptor>::NativeValue(
script_state->GetIsolate(), raw_descriptor.V8Value(),
exception_state);
if (exception_state.HadException()) {
return nullptr;
}
if (!fullscreen_permission->allowWithoutGesture()) {
// There is no permission state for fullscreen with user gesture.
exception_state.ThrowTypeError(
"Fullscreen Permission only supports allowWithoutGesture:true.");
return nullptr;
}
return CreateFullscreenPermissionDescriptor(
fullscreen_permission->allowWithoutGesture());
}
if (name == V8PermissionName::Enum::kWebAppInstallation) {
if (!RuntimeEnabledFeatures::WebAppInstallationEnabled(
ExecutionContext::From(script_state))) {
exception_state.ThrowTypeError("The Web App Install API is not enabled.");
return nullptr;
}
return CreatePermissionDescriptor(PermissionName::WEB_APP_INSTALLATION);
}
if (name == V8PermissionName::Enum::kLocalNetworkAccess) {
return CreatePermissionDescriptor(PermissionName::LOCAL_NETWORK_ACCESS);
}
return nullptr;
}
} // namespace blink