Commit 4d8435dc authored by dominicc's avatar dominicc Committed by Commit bot

Expose RTF content on the clipboard as strings to pages when pasting.

BUG=317807
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_site_isolation

Review-Url: https://codereview.chromium.org/2146323002
Cr-Commit-Position: refs/heads/master@{#407039}
parent 6427de29
......@@ -8,20 +8,17 @@
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "build/build_config.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/clipboard_messages.h"
#include "content/common/frame_messages.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/interstitial_page_delegate.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "ipc/message_filter.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/test/test_clipboard.h"
namespace content {
......@@ -135,46 +132,6 @@ class InterstitialPageImplTest : public ContentBrowserTest {
~InterstitialPageImplTest() override {}
protected:
void SetUpTestClipboard() {
#if defined(OS_WIN)
// On Windows, clipboard reads are handled on the IO thread. So, the test
// clipboard should be created for the IO thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
RunTaskOnIOThreadAndWait(
base::Bind(&InterstitialPageImplTest::SetUpTestClipboard, this));
return;
}
#endif
ui::TestClipboard::CreateForCurrentThread();
}
void TearDownTestClipboard() {
#if defined(OS_WIN)
// On Windows, test clipboard is created for the IO thread. So, destroy it
// for the IO thread, too.
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
RunTaskOnIOThreadAndWait(
base::Bind(&InterstitialPageImplTest::TearDownTestClipboard, this));
return;
}
#endif
ui::Clipboard::DestroyClipboardForCurrentThread();
}
void SetClipboardText(const std::string& text) {
#if defined(OS_WIN)
// On Windows, clipboard reads are handled on the IO thread. So, set the
// text for the IO thread clipboard.
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
RunTaskOnIOThreadAndWait(
base::Bind(&InterstitialPageImplTest::SetClipboardText, this, text));
return;
}
#endif
ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
clipboard_writer.WriteText(base::ASCIIToUTF16(text));
}
void SetUpInterstitialPage() {
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
......@@ -277,21 +234,6 @@ class InterstitialPageImplTest : public ContentBrowserTest {
}
private:
void RunTaskOnIOThreadAndWait(const base::Closure& task) {
base::WaitableEvent completion(
base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&InterstitialPageImplTest::RunTask, this,
task, &completion));
completion.Wait();
}
void RunTask(const base::Closure& task, base::WaitableEvent* completion) {
task.Run();
completion->Signal();
}
std::unique_ptr<InterstitialPageImpl> interstitial_;
scoped_refptr<ClipboardMessageWatcher> clipboard_message_watcher_;
......@@ -331,10 +273,10 @@ IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Copy) {
}
IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Paste) {
SetUpTestClipboard();
BrowserTestClipboardScope clipboard;
SetUpInterstitialPage();
SetClipboardText("text-to-paste");
clipboard.SetText("text-to-paste");
ASSERT_TRUE(CreateInputAndSetText(std::string()));
ASSERT_TRUE(FocusInputAndSelectText());
......@@ -346,7 +288,6 @@ IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Paste) {
EXPECT_EQ("text-to-paste", input_text);
TearDownInterstitialPage();
TearDownTestClipboard();
}
IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, SelectAll) {
......
......@@ -300,6 +300,7 @@
'renderer/render_view_browsertest_mac.mm',
'renderer/render_widget_browsertest.cc',
'renderer/visual_state_browsertest.cc',
'renderer/webclipboard_impl_browsertest.cc',
'test/browser_test_utils_browsertest.cc',
'test/content_browser_test_test.cc',
'test/webui_resource_browsertest.cc',
......
......@@ -41,6 +41,7 @@
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/histogram_fetcher.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_service.h"
......@@ -62,7 +63,10 @@
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/test/test_clipboard.h"
#include "ui/compositor/test/draw_waiter_for_test.h"
#include "ui/events/gesture_detection/gesture_configuration.h"
#include "ui/events/keycodes/dom/dom_code.h"
......@@ -1431,4 +1435,76 @@ uint32_t InputMsgWatcher::WaitForAck() {
return ack_result_;
}
#if defined(OS_WIN)
static void RunTaskAndSignalCompletion(const base::Closure& task,
base::WaitableEvent* completion) {
task.Run();
completion->Signal();
}
static void RunTaskOnIOThreadAndWait(const base::Closure& task) {
base::WaitableEvent completion(
base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&RunTaskAndSignalCompletion, task, &completion));
completion.Wait();
}
#endif
static void SetUpTestClipboard() {
#if defined(OS_WIN)
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
RunTaskOnIOThreadAndWait(base::Bind(&SetUpTestClipboard));
return;
}
#endif
ui::TestClipboard::CreateForCurrentThread();
}
// TODO(dcheng): Make the test clipboard on different threads share the
// same backing store. crbug.com/629765
BrowserTestClipboardScope::BrowserTestClipboardScope() {
SetUpTestClipboard();
}
static void TearDownTestClipboard() {
#if defined(OS_WIN)
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
RunTaskOnIOThreadAndWait(base::Bind(&TearDownTestClipboard));
return;
}
#endif
ui::Clipboard::DestroyClipboardForCurrentThread();
}
BrowserTestClipboardScope::~BrowserTestClipboardScope() {
TearDownTestClipboard();
}
void BrowserTestClipboardScope::SetRtf(const std::string& rtf) {
#if defined(OS_WIN)
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
RunTaskOnIOThreadAndWait(base::Bind(&BrowserTestClipboardScope::SetRtf,
base::Unretained(this), rtf));
return;
}
#endif
ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
clipboard_writer.WriteRTF(rtf);
}
void BrowserTestClipboardScope::SetText(const std::string& text) {
#if defined(OS_WIN)
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
RunTaskOnIOThreadAndWait(base::Bind(&BrowserTestClipboardScope::SetText,
base::Unretained(this), text));
return;
}
#endif
ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
clipboard_writer.WriteText(base::ASCIIToUTF16(text));
}
} // namespace content
......@@ -553,6 +553,27 @@ class InputMsgWatcher : public BrowserMessageFilter {
DISALLOW_COPY_AND_ASSIGN(InputMsgWatcher);
};
// Sets up a ui::TestClipboard for use in browser tests. On Windows,
// clipboard is handled on the IO thread, BrowserTestClipboardScope
// hops messages onto the right thread.
class BrowserTestClipboardScope {
public:
// Sets up a ui::TestClipboard.
BrowserTestClipboardScope();
// Tears down the clipboard.
~BrowserTestClipboardScope();
// Puts text/rtf |rtf| on the clipboard.
void SetRtf(const std::string& rtf);
// Puts plain text |text| on the clipboard.
void SetText(const std::string& text);
private:
DISALLOW_COPY_AND_ASSIGN(BrowserTestClipboardScope);
};
} // namespace content
#endif // CONTENT_PUBLIC_TEST_BROWSER_TEST_UTILS_H_
......@@ -109,6 +109,16 @@ WebString WebClipboardImpl::readHTML(Buffer buffer, WebURL* source_url,
return html_stdstr;
}
WebString WebClipboardImpl::readRTF(Buffer buffer) {
ui::ClipboardType clipboard_type;
if (!ConvertBufferType(buffer, &clipboard_type))
return WebString();
std::string rtf;
delegate_->ReadRTF(clipboard_type, &rtf);
return WebString::fromLatin1(rtf);
}
WebBlobInfo WebClipboardImpl::readImage(Buffer buffer) {
ui::ClipboardType clipboard_type;
if (!ConvertBufferType(buffer, &clipboard_type))
......
......@@ -33,6 +33,7 @@ class WebClipboardImpl : public blink::WebClipboard {
blink::WebURL* source_url,
unsigned* fragment_start,
unsigned* fragment_end) override;
blink::WebString readRTF(Buffer buffer) override;
blink::WebBlobInfo readImage(Buffer buffer) override;
blink::WebString readCustomData(Buffer buffer,
const blink::WebString& type) override;
......
// Copyright 2016 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 "content/renderer/webclipboard_impl.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
namespace content {
namespace {
class WebClipboardImplTest : public ContentBrowserTest {
public:
WebClipboardImplTest() = default;
~WebClipboardImplTest() override = default;
};
IN_PROC_BROWSER_TEST_F(WebClipboardImplTest, PasteRTF) {
BrowserTestClipboardScope clipboard;
const std::string rtf_content = "{\\rtf1\\ansi Hello, {\\b world.}}";
clipboard.SetRtf(rtf_content);
// paste_listener.html takes RTF from the clipboard and sets the title.
NavigateToURL(shell(), GetTestUrl(".", "paste_listener.html"));
const base::string16 expected_title = base::UTF8ToUTF16(rtf_content);
content::TitleWatcher title_watcher(shell()->web_contents(), expected_title);
shell()->web_contents()->Paste();
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
}
} // namespace content
<title>Waiting for paste</title>
<body>
<script>
document.body.contentEditable = true;
document.body.addEventListener('paste', (event) => {
Array.prototype.forEach.call(event.clipboardData.items, (item) => {
if (item.type != 'text/rtf') {
return;
}
item.getAsString((s) => {
document.title = s;
});
});
});
</script>
......@@ -139,6 +139,8 @@ String DataObjectItem::getAsString() const
// This is ugly but there's no real alternative.
if (m_type == mimeTypeTextPlain) {
data = Platform::current()->clipboard()->readPlainText(buffer);
} else if (m_type == mimeTypeTextRTF) {
data = Platform::current()->clipboard()->readRTF(buffer);
} else if (m_type == mimeTypeTextHTML) {
WebURL ignoredSourceURL;
unsigned ignored;
......
......@@ -36,6 +36,7 @@ const char mimeTypeText[] = "text";
const char mimeTypeTextPlain[] = "text/plain";
const char mimeTypeTextPlainEtc[] = "text/plain;";
const char mimeTypeTextHTML[] = "text/html";
const char mimeTypeTextRTF[] = "text/rtf";
const char mimeTypeURL[] = "url";
const char mimeTypeTextURIList[] = "text/uri-list";
const char mimeTypeDownloadURL[] = "downloadurl";
......
......@@ -39,6 +39,7 @@ PLATFORM_EXPORT extern const char mimeTypeText[];
PLATFORM_EXPORT extern const char mimeTypeTextPlain[];
PLATFORM_EXPORT extern const char mimeTypeTextPlainEtc[];
PLATFORM_EXPORT extern const char mimeTypeTextHTML[];
PLATFORM_EXPORT extern const char mimeTypeTextRTF[];
PLATFORM_EXPORT extern const char mimeTypeURL[];
PLATFORM_EXPORT extern const char mimeTypeTextURIList[];
PLATFORM_EXPORT extern const char mimeTypeDownloadURL[];
......
......@@ -77,6 +77,7 @@ public:
virtual WebString readHTML(
Buffer buffer, WebURL* pageURL, unsigned* fragmentStart,
unsigned* fragmentEnd) { return WebString(); }
virtual WebString readRTF(Buffer) { return WebString(); }
virtual WebBlobInfo readImage(Buffer) { return WebBlobInfo(); }
virtual WebString readCustomData(
Buffer, const WebString& type) { return WebString(); }
......
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