Skip to content

Commit

Permalink
[Clipboard API] Clipboard Web Custom Formats implementation.
Browse files Browse the repository at this point in the history
This patch addresses the changes proposed by the EditingWG[1] and
agreed upon by all browser vendors. We are removing the `unsanitized`
option, and instead, adding custom format support for MIME types that
have "web " prefix in them.
Added few wpt tests to test these changes.
Below is a summary of the changes in this CL:
1. Removed `unsanitized` option from read/write methods.
2. If the custom format doesn't have a "web " prefix, then clipboard
   read/write fails.
3. Transient user activation is applicable to all supported formats -
   text/html, text/plain, image/png and web custom formats.
4. There are two "buckets" of clipboard formats. One for the
   well-known formats and the other for the web custom format. If the
   author doesn't specify the web format explicitly, then they don't
   get access to it. This means, we won't write web custom formats
   for well-known types implicitly if authors have not indicated that
   during the write call via a "web " prefix (e.g. "web text/html").
   Same  applies for reading web custom formats for well-known types-
   if there aren't any formats in the web custom format map, then we
   won't return any web custom formats i.e. text/html won't be
   automatically converted into "web text/html".

Spec: w3c/clipboard-apis#175
Explainer: https://github.com/w3c/editing/blob/gh-pages/docs/clipboard-pickling/explainer.md
i2p: https://groups.google.com/a/chromium.org/g/blink-dev/c/Lo7WBM_v_LY/m/LncCKkXeAwAJ
i2s: https://groups.google.com/a/chromium.org/g/blink-dev/c/k2rgX-4Cigc/m/P0RijrpzBAAJ?utm_medium=email&utm_source=footer&pli=1

1. Github issue: w3c/clipboard-apis#165

Bug: 106449

Change-Id: I86aae6a662089efeede2a01ac87cb698e9646df5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3650952
Commit-Queue: Anupam Snigdha <[email protected]>
Reviewed-by: Alexander Timin <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Austin Sullivan <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1011078}
NOKEYCHECK=True
GitOrigin-RevId: a3b96a459cf17ddc683b510718bfcbe40ed08195
  • Loading branch information
snianu authored and copybara-github committed Jun 6, 2022
1 parent c3b3173 commit 7f34b5a
Show file tree
Hide file tree
Showing 56 changed files with 353 additions and 220 deletions.
2 changes: 0 additions & 2 deletions blink/renderer/bindings/generated_in_modules.gni
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,6 @@ generated_dictionary_sources_in_modules = [
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_channel_splitter_options.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_client_query_options.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_client_query_options.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_clipboard_item_options.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_clipboard_item_options.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_clipboard_permission_descriptor.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_clipboard_permission_descriptor.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_close_event_init.cc",
Expand Down
1 change: 0 additions & 1 deletion blink/renderer/bindings/idl_in_modules.gni
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ static_idl_files_in_modules = get_path_info(
"//third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl",
"//third_party/blink/renderer/modules/clipboard/clipboard.idl",
"//third_party/blink/renderer/modules/clipboard/clipboard_item.idl",
"//third_party/blink/renderer/modules/clipboard/clipboard_item_options.idl",
"//third_party/blink/renderer/modules/clipboard/navigator_clipboard.idl",
"//third_party/blink/renderer/modules/compression/compression_stream.idl",
"//third_party/blink/renderer/modules/compression/decompression_stream.idl",
Expand Down
22 changes: 15 additions & 7 deletions blink/renderer/core/testing/mock_clipboard_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/renderer/platform/graphics/color_behavior.h"
#include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/skia/include/core/SkBitmap.h"

namespace blink {
Expand Down Expand Up @@ -40,9 +41,7 @@ void MockClipboardHost::GetSequenceNumber(
std::move(callback).Run(sequence_number_);
}

void MockClipboardHost::ReadAvailableTypes(
mojom::ClipboardBuffer clipboard_buffer,
ReadAvailableTypesCallback callback) {
Vector<String> MockClipboardHost::ReadStandardFormatNames() {
Vector<String> types;
if (!plain_text_.IsEmpty())
types.push_back("text/plain");
Expand All @@ -56,7 +55,14 @@ void MockClipboardHost::ReadAvailableTypes(
CHECK(!base::Contains(types, it.key));
types.push_back(it.key);
}
std::move(callback).Run(types);
return types;
}

void MockClipboardHost::ReadAvailableTypes(
mojom::ClipboardBuffer clipboard_buffer,
ReadAvailableTypesCallback callback) {
Vector<String> types = ReadStandardFormatNames();
std::move(callback).Run(std::move(types));
}

void MockClipboardHost::IsFormatAvailable(
Expand Down Expand Up @@ -173,10 +179,10 @@ void MockClipboardHost::CommitWrite() {

void MockClipboardHost::ReadAvailableCustomAndStandardFormats(
ReadAvailableCustomAndStandardFormatsCallback callback) {
Vector<String> format_names;
Vector<String> format_names = ReadStandardFormatNames();
for (const auto& item : unsanitized_custom_data_map_)
format_names.emplace_back(item.key);
std::move(callback).Run(format_names);
std::move(callback).Run(std::move(format_names));
}

void MockClipboardHost::ReadUnsanitizedCustomFormat(
Expand All @@ -199,7 +205,9 @@ void MockClipboardHost::WriteUnsanitizedCustomFormat(
// Simulate the underlying platform copying this data.
Vector<uint8_t> data_copy(base::saturated_cast<wtf_size_t>(data.size()),
*data.data());
unsanitized_custom_data_map_.Set(format, data_copy);
// Append the "web " prefix since it is removed by the clipboard writer during
// write.
unsanitized_custom_data_map_.Set("web " + format, std::move(data_copy));
}

#if BUILDFLAG(IS_MAC)
Expand Down
1 change: 1 addition & 0 deletions blink/renderer/core/testing/mock_clipboard_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class MockClipboardHost : public mojom::blink::ClipboardHost {
#if BUILDFLAG(IS_MAC)
void WriteStringToFindPboard(const String& text) override;
#endif
Vector<String> ReadStandardFormatNames();

mojo::ReceiverSet<mojom::blink::ClipboardHost> receivers_;
ClipboardSequenceNumberToken sequence_number_;
Expand Down
1 change: 1 addition & 0 deletions blink/renderer/modules/clipboard/DEPS
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
include_rules = [
"+mojo/public/cpp/base/big_buffer.h",
"+net/base/mime_util.h",
"+third_party/blink/renderer/core/clipboard",
"-third_party/blink/renderer/modules",
"+third_party/blink/renderer/modules/event_modules.h",
Expand Down
20 changes: 12 additions & 8 deletions blink/renderer/modules/clipboard/clipboard.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

#include <utility>
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_clipboard_item_options.h"
#include "third_party/blink/renderer/core/event_target_names.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/navigator.h"
#include "third_party/blink/renderer/modules/clipboard/clipboard_promise.h"
#include "ui/base/clipboard/clipboard_constants.h"

namespace blink {

Expand All @@ -29,13 +29,7 @@ Clipboard* Clipboard::clipboard(Navigator& navigator) {
Clipboard::Clipboard(Navigator& navigator) : Supplement<Navigator>(navigator) {}

ScriptPromise Clipboard::read(ScriptState* script_state) {
return read(script_state, ClipboardItemOptions::Create());
}

ScriptPromise Clipboard::read(ScriptState* script_state,
ClipboardItemOptions* options) {
return ClipboardPromise::CreateForRead(GetExecutionContext(), script_state,
options);
return ClipboardPromise::CreateForRead(GetExecutionContext(), script_state);
}

ScriptPromise Clipboard::readText(ScriptState* script_state) {
Expand Down Expand Up @@ -63,6 +57,16 @@ ExecutionContext* Clipboard::GetExecutionContext() const {
return GetSupplementable()->DomWindow();
}

// static
String Clipboard::ParseWebCustomFormat(const String& format) {
String web_custom_format;
if (format.StartsWith(ui::kWebClipboardFormatPrefix)) {
web_custom_format = format.Substring(
static_cast<unsigned>(std::strlen(ui::kWebClipboardFormatPrefix)));
}
return web_custom_format;
}

void Clipboard::Trace(Visitor* visitor) const {
EventTargetWithInlineData::Trace(visitor);
Supplement<Navigator>::Trace(visitor);
Expand Down
4 changes: 2 additions & 2 deletions blink/renderer/modules/clipboard/clipboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

namespace blink {

class ClipboardItemOptions;
class Navigator;
class ScriptState;

Expand All @@ -30,7 +29,6 @@ class Clipboard : public EventTargetWithInlineData,
Clipboard& operator=(const Clipboard&) = delete;

ScriptPromise read(ScriptState*);
ScriptPromise read(ScriptState*, ClipboardItemOptions*);
ScriptPromise readText(ScriptState*);

ScriptPromise write(ScriptState*, const HeapVector<Member<ClipboardItem>>&);
Expand All @@ -40,6 +38,8 @@ class Clipboard : public EventTargetWithInlineData,
const AtomicString& InterfaceName() const override;
ExecutionContext* GetExecutionContext() const override;

static String ParseWebCustomFormat(const String& format);

void Trace(Visitor*) const override;
};

Expand Down
5 changes: 0 additions & 5 deletions blink/renderer/modules/clipboard/clipboard.idl
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
CallWith=ScriptState
] Promise<sequence<ClipboardItem>> read();

[MeasureAs=AsyncClipboardAPIRead,
CallWith=ScriptState,
RuntimeEnabled=ClipboardCustomFormats
] Promise<sequence<ClipboardItem>> read(ClipboardItemOptions options);

[MeasureAs=AsyncClipboardAPIReadText,
CallWith=ScriptState
] Promise<DOMString> readText();
Expand Down
46 changes: 33 additions & 13 deletions blink/renderer/modules/clipboard/clipboard_item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,61 @@

#include "third_party/blink/renderer/modules/clipboard/clipboard_item.h"

#include "net/base/mime_util.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_clipboard_item_options.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/modules/clipboard/clipboard.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "ui/base/clipboard/clipboard_constants.h"

namespace blink {

// static
ClipboardItem* ClipboardItem::Create(
const HeapVector<std::pair<String, ScriptPromise>>& items,
const ClipboardItemOptions* options,
ExceptionState& exception_state) {
DCHECK(options);
// Check that incoming dictionary isn't empty. If it is, it's possible that
// Javascript bindings implicitly converted an Object (like a ScriptPromise)
// into {}, an empty dictionary.
if (!items.size()) {
exception_state.ThrowTypeError("Empty dictionary argument");
return nullptr;
}
return MakeGarbageCollected<ClipboardItem>(items, options);
return MakeGarbageCollected<ClipboardItem>(items);
}

ClipboardItem::ClipboardItem(
const HeapVector<std::pair<String, ScriptPromise>>& items,
const ClipboardItemOptions* options)
: items_(items) {
DCHECK(items_.size());
if (options->hasUnsanitized()) {
for (const auto& unsanitized_item : options->unsanitized()) {
custom_format_items_.push_back(unsanitized_item);
const HeapVector<std::pair<String, ScriptPromise>>& items) {
DCHECK(items.size());
for (const auto& item : items) {
String web_custom_format = Clipboard::ParseWebCustomFormat(item.first);
if (!web_custom_format.IsEmpty()) {
// Types with "web " prefix are special, so we do some level of MIME type
// parsing here to get a valid web custom format type.
// We want to ensure that the string after removing the "web " prefix is
// a valid MIME type.
// e.g. "web text/html" is a web custom MIME type & "text/html" is a
// well-known MIME type. Removing the "web " prefix makes it hard to
// differentiate between the two.
std::string web_top_level_mime_type;
std::string web_mime_sub_type;
if (net::ParseMimeTypeWithoutParameter(web_custom_format.Utf8(),
&web_top_level_mime_type,
&web_mime_sub_type)) {
String web_custom_format_string = String::Format(
"%s%s/%s", ui::kWebClipboardFormatPrefix,
web_top_level_mime_type.c_str(), web_mime_sub_type.c_str());
items_.emplace_back(web_custom_format_string, item.second);
custom_format_items_.push_back(web_custom_format_string);
continue;
}
}
// Any arbitrary type can be added to ClipboardItem, but there may not be
// any read/write support for that type.
items_.push_back(item);
}
}

Expand All @@ -54,9 +75,8 @@ ScriptPromise ClipboardItem::getType(ScriptState* script_state,
const String& type,
ExceptionState& exception_state) const {
for (const auto& item : items_) {
if (type == item.first) {
if (type == item.first)
return item.second;
}
}

exception_state.ThrowDOMException(DOMExceptionCode::kNotFoundError,
Expand Down
7 changes: 2 additions & 5 deletions blink/renderer/modules/clipboard/clipboard_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,17 @@
namespace blink {

class ScriptState;
class ClipboardItemOptions;

class ClipboardItem final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();

public:
static ClipboardItem* Create(
const HeapVector<std::pair<String, ScriptPromise>>& items,
const ClipboardItemOptions* options,
ExceptionState& exception_state);

explicit ClipboardItem(
const HeapVector<std::pair<String, ScriptPromise>>& items,
const ClipboardItemOptions* options);
const HeapVector<std::pair<String, ScriptPromise>>& items);
Vector<String> types() const;
ScriptPromise getType(ScriptState* script_state,
const String& type,
Expand All @@ -35,7 +32,7 @@ class ClipboardItem final : public ScriptWrappable {
return items_;
}

// Returns the custom formats passed to direct option.
// Returns the custom formats that have a "web " prefix.
const Vector<String>& CustomFormats() const { return custom_format_items_; }

void Trace(Visitor*) const override;
Expand Down
3 changes: 1 addition & 2 deletions blink/renderer/modules/clipboard/clipboard_item.idl
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
SecureContext,
Exposed=Window
] interface ClipboardItem {
[RaisesException] constructor(record<DOMString, Promise<Blob>> items,
optional ClipboardItemOptions options = {});
[RaisesException] constructor(record<DOMString, Promise<Blob>> items);
readonly attribute FrozenArray<DOMString> types;

[
Expand Down
9 changes: 0 additions & 9 deletions blink/renderer/modules/clipboard/clipboard_item_options.idl

This file was deleted.

Loading

0 comments on commit 7f34b5a

Please sign in to comment.