blob: 1378ca96d53b0e5e2830e9831e048a83f5991e02 [file] [log] [blame]
// Copyright 2017 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/clipboard/clipboard_promise.h"
#include "base/single_thread_task_runner.h"
#include "third_party/blink/public/mojom/page/page_visibility_state.mojom-blink.h"
#include "third_party/blink/public/platform/modules/permissions/permission.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/clipboard/clipboard_mime_types.h"
#include "third_party/blink/renderer/core/clipboard/data_object.h"
#include "third_party/blink/renderer/core/clipboard/data_transfer.h"
#include "third_party/blink/renderer/core/clipboard/data_transfer_access_policy.h"
#include "third_party/blink/renderer/core/clipboard/data_transfer_item.h"
#include "third_party/blink/renderer/core/clipboard/data_transfer_item_list.h"
#include "third_party/blink/renderer/core/clipboard/system_clipboard.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/frame/local_frame.h"
#include "third_party/blink/renderer/modules/permissions/permission_utils.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
// And now, a brief note about clipboard permissions.
//
// There are 2 clipboard permissions defined in the spec:
// * clipboard-read
// * clipboard-write
// See https://w3c.github.io/clipboard-apis/#clipboard-permissions
//
// In Chromium we automatically grant clipboard-write access and clipboard-read
// access is gated behind a permission prompt. Both clipboard read and write
// require the tab to be focused (and Chromium must be the foreground app) for
// the operation to be allowed.
namespace blink {
using mojom::blink::PermissionStatus;
using mojom::blink::PermissionService;
using mojom::PageVisibilityState;
ScriptPromise ClipboardPromise::CreateForRead(ScriptState* script_state) {
ClipboardPromise* clipboard_promise = new ClipboardPromise(script_state);
clipboard_promise->GetTaskRunner()->PostTask(
FROM_HERE, WTF::Bind(&ClipboardPromise::HandleRead,
WrapPersistent(clipboard_promise)));
return clipboard_promise->script_promise_resolver_->Promise();
}
ScriptPromise ClipboardPromise::CreateForReadText(ScriptState* script_state) {
ClipboardPromise* clipboard_promise = new ClipboardPromise(script_state);
clipboard_promise->GetTaskRunner()->PostTask(
FROM_HERE, WTF::Bind(&ClipboardPromise::HandleReadText,
WrapPersistent(clipboard_promise)));
return clipboard_promise->script_promise_resolver_->Promise();
}
ScriptPromise ClipboardPromise::CreateForWrite(ScriptState* script_state,
DataTransfer* data) {
ClipboardPromise* clipboard_promise = new ClipboardPromise(script_state);
clipboard_promise->GetTaskRunner()->PostTask(
FROM_HERE,
WTF::Bind(&ClipboardPromise::HandleWrite,
WrapPersistent(clipboard_promise), WrapPersistent(data)));
return clipboard_promise->script_promise_resolver_->Promise();
}
ScriptPromise ClipboardPromise::CreateForWriteText(ScriptState* script_state,
const String& data) {
ClipboardPromise* clipboard_promise = new ClipboardPromise(script_state);
clipboard_promise->GetTaskRunner()->PostTask(
FROM_HERE, WTF::Bind(&ClipboardPromise::HandleWriteText,
WrapPersistent(clipboard_promise), data));
return clipboard_promise->script_promise_resolver_->Promise();
}
ClipboardPromise::ClipboardPromise(ScriptState* script_state)
: ContextLifecycleObserver(blink::ExecutionContext::From(script_state)),
script_state_(script_state),
script_promise_resolver_(ScriptPromiseResolver::Create(script_state)),
buffer_(mojom::ClipboardBuffer::kStandard) {}
scoped_refptr<base::SingleThreadTaskRunner> ClipboardPromise::GetTaskRunner() {
// TODO(garykac): Replace MiscPlatformAPI with TaskType specific to clipboard.
return GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI);
}
PermissionService* ClipboardPromise::GetPermissionService() {
if (!permission_service_) {
ConnectToPermissionService(ExecutionContext::From(script_state_),
mojo::MakeRequest(&permission_service_));
}
return permission_service_.get();
}
bool ClipboardPromise::IsFocusedDocument(ExecutionContext* context) {
Document* doc = To<Document>(context);
return doc && doc->hasFocus();
}
void ClipboardPromise::RequestReadPermission(
PermissionService::RequestPermissionCallback callback) {
DCHECK(script_promise_resolver_);
ExecutionContext* context = ExecutionContext::From(script_state_);
DCHECK(context->IsSecureContext()); // [SecureContext] in IDL
// Document must be focused.
if (!IsFocusedDocument(context) || !GetPermissionService()) {
script_promise_resolver_->Reject();
return;
}
// Query for permission if necessary.
// See crbug.com/795929 for moving this check into the Browser process.
permission_service_->RequestPermission(
CreateClipboardPermissionDescriptor(
mojom::blink::PermissionName::CLIPBOARD_READ, false),
false, std::move(callback));
}
void ClipboardPromise::CheckWritePermission(
PermissionService::HasPermissionCallback callback) {
DCHECK(script_promise_resolver_);
ExecutionContext* context = ExecutionContext::From(script_state_);
DCHECK(context->IsSecureContext()); // [SecureContext] in IDL
// Document must be focused.
if (!IsFocusedDocument(context) || !GetPermissionService()) {
script_promise_resolver_->Reject();
return;
}
// Check current permission (but do not query the user).
// See crbug.com/795929 for moving this check into the Browser process.
permission_service_->HasPermission(
CreateClipboardPermissionDescriptor(
mojom::blink::PermissionName::CLIPBOARD_WRITE, false),
std::move(callback));
}
void ClipboardPromise::HandleRead() {
RequestReadPermission(WTF::Bind(&ClipboardPromise::HandleReadWithPermission,
WrapPersistent(this)));
}
// TODO(garykac): This currently only handles plain text.
void ClipboardPromise::HandleReadWithPermission(PermissionStatus status) {
if (status != PermissionStatus::GRANTED) {
script_promise_resolver_->Reject();
return;
}
String plain_text = SystemClipboard::GetInstance().ReadPlainText(buffer_);
const DataTransfer::DataTransferType type =
DataTransfer::DataTransferType::kCopyAndPaste;
const DataTransferAccessPolicy access = DataTransferAccessPolicy::kReadable;
DataObject* data = DataObject::CreateFromString(plain_text);
DataTransfer* dt = DataTransfer::Create(type, access, data);
script_promise_resolver_->Resolve(dt);
}
void ClipboardPromise::HandleReadText() {
RequestReadPermission(WTF::Bind(
&ClipboardPromise::HandleReadTextWithPermission, WrapPersistent(this)));
}
void ClipboardPromise::HandleReadTextWithPermission(PermissionStatus status) {
if (status != PermissionStatus::GRANTED) {
script_promise_resolver_->Reject();
return;
}
String text = SystemClipboard::GetInstance().ReadPlainText(buffer_);
script_promise_resolver_->Resolve(text);
}
// TODO(garykac): This currently only handles plain text.
void ClipboardPromise::HandleWrite(DataTransfer* data) {
// Scan DataTransfer and extract data types that we support.
uint32_t num_items = data->items()->length();
for (uint32_t i = 0; i < num_items; i++) {
DataTransferItem* item = data->items()->item(i);
DataObjectItem* objectItem = item->GetDataObjectItem();
if (objectItem->Kind() == DataObjectItem::kStringKind &&
objectItem->GetType() == kMimeTypeTextPlain) {
write_data_ = objectItem->GetAsString();
break;
}
}
CheckWritePermission(WTF::Bind(&ClipboardPromise::HandleWriteWithPermission,
WrapPersistent(this)));
}
void ClipboardPromise::HandleWriteWithPermission(PermissionStatus status) {
if (status != PermissionStatus::GRANTED) {
script_promise_resolver_->Reject();
return;
}
SystemClipboard::GetInstance().WritePlainText(write_data_);
script_promise_resolver_->Resolve();
}
void ClipboardPromise::HandleWriteText(const String& data) {
write_data_ = data;
CheckWritePermission(WTF::Bind(
&ClipboardPromise::HandleWriteTextWithPermission, WrapPersistent(this)));
}
void ClipboardPromise::HandleWriteTextWithPermission(PermissionStatus status) {
if (status != PermissionStatus::GRANTED) {
script_promise_resolver_->Reject();
return;
}
DCHECK(script_promise_resolver_);
SystemClipboard::GetInstance().WritePlainText(write_data_);
script_promise_resolver_->Resolve();
}
void ClipboardPromise::Trace(blink::Visitor* visitor) {
visitor->Trace(script_state_);
visitor->Trace(script_promise_resolver_);
ContextLifecycleObserver::Trace(visitor);
}
} // namespace blink