Commit a1079329 authored by Darwin Huang's avatar Darwin Huang Committed by Chromium LUCI CQ

Clipboard: Filter unsupported types when reading.

Ensure that a format is valid before creating a placeholder Blob, so
that clipboard.read() no longer returns nullptr blobs. In particular,
filter out custom/pickled formats not supported by the Clipboard API

Previously, custom/pickled formats not supported by the Clipboard API,
but exposed via blink::SystemClipboard, would create nullptr Blobs when
read via clipboard.read(), and return them in the output ClipboardItems.
This output ClipboardItem could then be written via clipboard.write(),
and cause a crash.

Bug: 1153361
Change-Id: Ib57ccca0e79adb09ee51e64c324c32283c788cbd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2568818
Commit-Queue: Darwin Huang <huangdarwin@chromium.org>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#833501}
parent f45725ca
......@@ -272,8 +272,10 @@ void ClipboardPromise::OnReadAvailableFormatNames(
clipboard_item_data_.ReserveInitialCapacity(format_names.size());
for (const String& format_name : format_names) {
clipboard_item_data_.emplace_back(format_name,
/* Placeholder value. */ nullptr);
if (ClipboardWriter::IsValidType(format_name, is_raw_)) {
clipboard_item_data_.emplace_back(format_name,
/* Placeholder value. */ nullptr);
}
}
ReadNextRepresentation();
}
......
......@@ -12,6 +12,7 @@
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
#include "third_party/blink/renderer/modules/clipboard/clipboard_promise.h"
#include "third_party/blink/renderer/modules/clipboard/clipboard_writer.h"
#include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
......@@ -303,6 +304,7 @@ class ClipboardSvgReader final : public ClipboardReader {
ClipboardReader* ClipboardReader::Create(SystemClipboard* system_clipboard,
const String& mime_type,
ClipboardPromise* promise) {
DCHECK(ClipboardWriter::IsValidType(mime_type, /*is_raw=*/false));
if (mime_type == kMimeTypeImagePng)
return MakeGarbageCollected<ClipboardImageReader>(system_clipboard,
promise);
......@@ -315,7 +317,9 @@ ClipboardReader* ClipboardReader::Create(SystemClipboard* system_clipboard,
if (mime_type == kMimeTypeImageSvg &&
RuntimeEnabledFeatures::ClipboardSvgEnabled())
return MakeGarbageCollected<ClipboardSvgReader>(system_clipboard, promise);
// The MIME type is not supported.
NOTREACHED()
<< "IsValidType() and Create() have inconsistent implementations.";
return nullptr;
}
......
......@@ -40,7 +40,7 @@ class ClipboardPromise;
// ClipboardReader::NextRead().
class ClipboardReader : public GarbageCollected<ClipboardReader> {
public:
// Returns nullptr if there is no implementation for the given mime_type.
// ClipboardWriter::IsValidType() must return true for `mime_type`.
static ClipboardReader* Create(SystemClipboard* system_clipboard,
const String& mime_type,
ClipboardPromise* promise);
......
......@@ -251,6 +251,7 @@ class ClipboardRawDataWriter final : public ClipboardWriter {
ClipboardWriter* ClipboardWriter::Create(SystemClipboard* system_clipboard,
const String& mime_type,
ClipboardPromise* promise) {
DCHECK(ClipboardWriter::IsValidType(mime_type, /*is_raw=*/false));
if (mime_type == kMimeTypeImagePng) {
return MakeGarbageCollected<ClipboardImageWriter>(system_clipboard,
promise);
......@@ -265,7 +266,8 @@ ClipboardWriter* ClipboardWriter::Create(SystemClipboard* system_clipboard,
RuntimeEnabledFeatures::ClipboardSvgEnabled())
return MakeGarbageCollected<ClipboardSvgWriter>(system_clipboard, promise);
NOTREACHED() << "Type " << mime_type << " was not implemented";
NOTREACHED()
<< "IsValidType() and Create() have inconsistent implementations.";
return nullptr;
}
......@@ -275,7 +277,7 @@ ClipboardWriter* ClipboardWriter::Create(
const String& mime_type,
ClipboardPromise* promise) {
DCHECK(base::FeatureList::IsEnabled(features::kRawClipboard));
DCHECK(ClipboardWriter::IsValidType(mime_type, /*is_raw=*/true));
return MakeGarbageCollected<ClipboardRawDataWriter>(raw_system_clipboard,
promise, mime_type);
}
......@@ -309,9 +311,12 @@ bool ClipboardWriter::IsValidType(const String& type, bool is_raw) {
if (is_raw)
return type.length() < mojom::blink::RawClipboardHost::kMaxFormatSize;
if (type == kMimeTypeImageSvg)
return RuntimeEnabledFeatures::ClipboardSvgEnabled();
// TODO(https://crbug.com/1029857): Add support for other types.
return type == kMimeTypeImagePng || type == kMimeTypeTextPlain ||
type == kMimeTypeTextHTML || type == kMimeTypeImageSvg;
type == kMimeTypeTextHTML;
}
void ClipboardWriter::WriteToSystem(Blob* blob) {
......
......@@ -55,10 +55,12 @@ class ClipboardWriter : public GarbageCollected<ClipboardWriter>,
public FileReaderLoaderClient {
public:
// For writing sanitized MIME types.
// IsValidType() must return true on types passed into `mime_type`.
static ClipboardWriter* Create(SystemClipboard* system_clipboard,
const String& mime_type,
ClipboardPromise* promise);
// For writing unsanitized types.
// IsValidType() must return true on types passed into `mime_type`.
static ClipboardWriter* Create(RawSystemClipboard* raw_system_clipboard,
const String& mime_type,
ClipboardPromise* promise);
......@@ -66,8 +68,12 @@ class ClipboardWriter : public GarbageCollected<ClipboardWriter>,
// Returns whether ClipboardWriter has implemented support for this type.
//
// If this API returns false for a `mime_type`, Create() must not be called
// with that `mime_type`.
// IsValidType() is expected to be called before Create(). If it returns false
// for a `mime_type`, Create() must not be called with that `mime_type`.
//
// IsValidType() is used for both ClipboardWriter and ClipboardReader, as read
// and write currently support the same types. If this changes in the future,
// please create separate IsValidType functions.
static bool IsValidType(const String& mime_type, bool is_raw);
// Begins the sequence of writing the Blob to the system clipbaord.
void WriteToSystem(Blob* blob);
......
<!doctype html>
<meta charset="utf-8">
<title>Async Clipboard read unsupported types removal test.</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/testdriver.js"></script>
<script src="../../resources/testdriver-vendor.js"></script>
<body>
<script>
'use strict';
promise_test(async t => {
// Write supported and unsupported types to clipboard.
document.addEventListener('copy', event => {
event.clipboardData.setData('text/plain', 'Supported text.');
event.clipboardData.setData('unsupported-type', 'Unsupported custom text.');
event.preventDefault();
});
document.execCommand('copy');
// Read clipboard contents.
await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
const clipboardItems = await navigator.clipboard.read();
// The unsupported unsupported-type type should be removed, leaving only
// the supported text/plain type.
const clipboardItem = clipboardItems[0];
assert_equals(clipboardItem.types.length, 1);
assert_true(clipboardItem.types.includes('text/plain'));
assert_false(clipboardItem.types.includes('unsupported-type'));
}, 'Verify that clipboard read removes unsupported types.');
</script>
</body>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment