Commit 03ffdbd8 authored by Roger Tawa's avatar Roger Tawa Committed by Commit Bot

Hook clipboard paste code to the upload content checks.

Bug: 999150
Change-Id: I7f2d8ea797f06fde21713e47f18528218e56c115
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1935837
Commit-Queue: Roger Tawa <rogerta@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#732466}
parent 25622727
......@@ -329,6 +329,7 @@
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/widevine/cdm/buildflags.h"
#include "ui/base/buildflags.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_features.h"
......@@ -580,6 +581,10 @@
#include "chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.h"
#endif
#if BUILDFLAG(FULL_SAFE_BROWSING)
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h"
#endif
using base::FileDescriptor;
using content::BrowserThread;
using content::BrowserURLHandler;
......@@ -5441,3 +5446,43 @@ void ChromeContentBrowserClient::FetchRemoteSms(
::FetchRemoteSms(browser_context, origin, std::move(callback));
}
#endif
void ChromeContentBrowserClient::IsClipboardPasteAllowed(
content::WebContents* web_contents,
const GURL& url,
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback) {
#if BUILDFLAG(FULL_SAFE_BROWSING)
// Safe browsing does not support images, so accept without checking.
// TODO(crbug.com/1013584): check policy on what to do about unsupported
// types when it is implemented.
if (data_type.Equals(ui::ClipboardFormatType::GetBitmapType())) {
std::move(callback).Run(ClipboardPasteAllowed(true));
return;
}
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
safe_browsing::DeepScanningDialogDelegate::Data dialog_data;
if (safe_browsing::DeepScanningDialogDelegate::IsEnabled(profile, url,
&dialog_data)) {
dialog_data.text.push_back(base::UTF8ToUTF16(data));
safe_browsing::DeepScanningDialogDelegate::ShowForWebContents(
web_contents, std::move(dialog_data),
base::BindOnce(
[](IsClipboardPasteAllowedCallback callback,
const safe_browsing::DeepScanningDialogDelegate::Data& data,
const safe_browsing::DeepScanningDialogDelegate::Result&
result) {
std::move(callback).Run(
ClipboardPasteAllowed(result.text_results[0]));
},
std::move(callback)));
} else {
std::move(callback).Run(ClipboardPasteAllowed(true));
}
#else
std::move(callback).Run(ClipboardPasteAllowed(true));
#endif // BUILDFLAG(FULL_SAFE_BROWSING)
}
......@@ -641,6 +641,13 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
base::OnceCallback<void(base::Optional<std::string>)> callback) override;
#endif
void IsClipboardPasteAllowed(
content::WebContents* web_contents,
const GURL& url,
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback) override;
protected:
static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context);
static bool HandleWebUIReverse(GURL* url,
......
......@@ -22,12 +22,58 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "url/gurl.h"
namespace content {
// TODO(rogerta): how old is too old will need to be determined, maybe via
// experimentation. For now using 1 minute.
const base::TimeDelta ClipboardHostImpl::kIsPasteAllowedRequestTooOld =
base::TimeDelta::FromMinutes(1);
ClipboardHostImpl::IsPasteAllowedRequest::IsPasteAllowedRequest() = default;
ClipboardHostImpl::IsPasteAllowedRequest::~IsPasteAllowedRequest() = default;
bool ClipboardHostImpl::IsPasteAllowedRequest::AddCallback(
IsClipboardPasteAllowedCallback callback) {
// If this request has already completed, invoke the callback immediately
// and return.
if (allowed_.has_value()) {
std::move(callback).Run(allowed_.value());
return false;
}
callbacks_.push_back(std::move(callback));
// If this is the first callback registered tell the caller to start the scan.
return callbacks_.size() == 1;
}
void ClipboardHostImpl::IsPasteAllowedRequest::Complete(
ClipboardPasteAllowed allowed) {
allowed_ = allowed;
InvokeCallbacks();
}
bool ClipboardHostImpl::IsPasteAllowedRequest::IsObsolete(base::Time now) {
// If the request is old and no longer has any registered callbacks it is
// obsolete.
return (now - time_) > kIsPasteAllowedRequestTooOld && callbacks_.empty();
}
void ClipboardHostImpl::IsPasteAllowedRequest::InvokeCallbacks() {
DCHECK(allowed_);
auto callbacks = std::move(callbacks_);
for (auto& callback : callbacks) {
if (!callback.is_null())
std::move(callback).Run(allowed_.value());
}
}
ClipboardHostImpl::ClipboardHostImpl(
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::ClipboardHost> receiver)
......@@ -52,7 +98,9 @@ void ClipboardHostImpl::Create(
// loops. Use manual memory management instead of SelfOwnedReceiver<T> which
// synchronously destroys on failure and can result in some unfortunate
// use-after-frees after the nested message loops exit.
auto* host = new ClipboardHostImpl(render_frame_host, std::move(receiver));
auto* host = new ClipboardHostImpl(
static_cast<RenderFrameHostImpl*>(render_frame_host),
std::move(receiver));
host->receiver_.set_disconnect_handler(base::BindOnce(
[](ClipboardHostImpl* host) {
base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, host);
......@@ -127,7 +175,19 @@ void ClipboardHostImpl::ReadText(ui::ClipboardBuffer clipboard_buffer,
}
#endif
}
std::move(callback).Run(result);
std::string data = base::UTF16ToUTF8(result);
PerformPasteIfAllowed(clipboard_->GetSequenceNumber(clipboard_buffer),
ui::ClipboardFormatType::GetPlainTextType(),
std::move(data),
base::BindOnce(
[](base::string16 result, ReadTextCallback callback,
ClipboardPasteAllowed allowed) {
if (!allowed)
result.clear();
std::move(callback).Run(result);
},
std::move(result), std::move(callback)));
}
void ClipboardHostImpl::ReadHtml(ui::ClipboardBuffer clipboard_buffer,
......@@ -138,21 +198,60 @@ void ClipboardHostImpl::ReadHtml(ui::ClipboardBuffer clipboard_buffer,
uint32_t fragment_end = 0;
clipboard_->ReadHTML(clipboard_buffer, &markup, &src_url_str, &fragment_start,
&fragment_end);
std::move(callback).Run(std::move(markup), GURL(src_url_str), fragment_start,
fragment_end);
std::string data = base::UTF16ToUTF8(markup);
PerformPasteIfAllowed(
clipboard_->GetSequenceNumber(clipboard_buffer),
ui::ClipboardFormatType::GetHtmlType(), std::move(data),
base::BindOnce(
[](base::string16 markup, std::string src_url_str,
uint32_t fragment_start, uint32_t fragment_end,
ReadHtmlCallback callback, ClipboardPasteAllowed allowed) {
if (!allowed)
markup.clear();
std::move(callback).Run(std::move(markup), GURL(src_url_str),
fragment_start, fragment_end);
},
std::move(markup), std::move(src_url_str), fragment_start,
fragment_end, std::move(callback)));
}
void ClipboardHostImpl::ReadRtf(ui::ClipboardBuffer clipboard_buffer,
ReadRtfCallback callback) {
std::string result;
clipboard_->ReadRTF(clipboard_buffer, &result);
std::move(callback).Run(result);
std::string data = result;
PerformPasteIfAllowed(clipboard_->GetSequenceNumber(clipboard_buffer),
ui::ClipboardFormatType::GetRtfType(), std::move(data),
base::BindOnce(
[](std::string result, ReadRtfCallback callback,
ClipboardPasteAllowed allowed) {
if (!allowed)
result.clear();
std::move(callback).Run(result);
},
std::move(result), std::move(callback)));
}
void ClipboardHostImpl::ReadImage(ui::ClipboardBuffer clipboard_buffer,
ReadImageCallback callback) {
SkBitmap result = clipboard_->ReadImage(clipboard_buffer);
std::move(callback).Run(result);
std::string data =
std::string(reinterpret_cast<const char*>(result.getPixels()),
result.computeByteSize());
PerformPasteIfAllowed(clipboard_->GetSequenceNumber(clipboard_buffer),
ui::ClipboardFormatType::GetBitmapType(),
std::move(data),
base::BindOnce(
[](SkBitmap result, ReadImageCallback callback,
ClipboardPasteAllowed allowed) {
if (!allowed)
result.reset();
std::move(callback).Run(result);
},
std::move(result), std::move(callback)));
}
void ClipboardHostImpl::ReadCustomData(ui::ClipboardBuffer clipboard_buffer,
......@@ -160,7 +259,19 @@ void ClipboardHostImpl::ReadCustomData(ui::ClipboardBuffer clipboard_buffer,
ReadCustomDataCallback callback) {
base::string16 result;
clipboard_->ReadCustomData(clipboard_buffer, type, &result);
std::move(callback).Run(result);
std::string data = base::UTF16ToUTF8(result);
PerformPasteIfAllowed(
clipboard_->GetSequenceNumber(clipboard_buffer),
ui::ClipboardFormatType::GetWebCustomDataType(), std::move(data),
base::BindOnce(
[](base::string16 result, ReadCustomDataCallback callback,
ClipboardPasteAllowed allowed) {
if (!allowed)
result.clear();
std::move(callback).Run(result);
},
std::move(result), std::move(callback)));
}
void ClipboardHostImpl::WriteText(const base::string16& text) {
......@@ -224,4 +335,58 @@ void ClipboardHostImpl::CommitWrite() {
new ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste));
}
void ClipboardHostImpl::PerformPasteIfAllowed(
uint64_t seqno,
const ui::ClipboardFormatType& data_type,
std::string data,
IsClipboardPasteAllowedCallback callback) {
CleanupObsoleteRequests();
if (data.empty()) {
std::move(callback).Run(ClipboardPasteAllowed(true));
return;
}
// Add |callback| to the callbacks associated to the sequence number, adding
// an entry to the map if one does not exist.
auto& request = is_allowed_requests_[seqno];
if (request.AddCallback(std::move(callback)))
StartIsPasteAllowedRequest(seqno, data_type, std::move(data));
}
void ClipboardHostImpl::StartIsPasteAllowedRequest(
uint64_t seqno,
const ui::ClipboardFormatType& data_type,
std::string data) {
// May not have a RenderFrameHost in tests.
RenderFrameHostImpl* render_frame_host =
RenderFrameHostImpl::FromID(render_frame_pid_, render_frame_routing_id_);
if (render_frame_host) {
render_frame_host->IsClipboardPasteAllowed(
data_type, data,
base::BindOnce(&ClipboardHostImpl::FinishPasteIfAllowed,
base::Unretained(this), seqno));
} else {
FinishPasteIfAllowed(seqno, ClipboardPasteAllowed(true));
}
}
void ClipboardHostImpl::FinishPasteIfAllowed(uint64_t seqno,
ClipboardPasteAllowed allowed) {
if (is_allowed_requests_.count(seqno) == 0)
return;
auto& request = is_allowed_requests_[seqno];
request.Complete(allowed);
}
void ClipboardHostImpl::CleanupObsoleteRequests() {
for (auto it = is_allowed_requests_.begin();
it != is_allowed_requests_.end();) {
it = it->second.IsObsolete(base::Time::Now())
? is_allowed_requests_.erase(it)
: std::next(it);
}
}
} // namespace content
......@@ -5,11 +5,16 @@
#ifndef CONTENT_BROWSER_FRAME_HOST_CLIPBOARD_HOST_IMPL_H_
#define CONTENT_BROWSER_FRAME_HOST_CLIPBOARD_HOST_IMPL_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "mojo/public/cpp/bindings/receiver.h"
......@@ -25,7 +30,6 @@ class ScopedClipboardWriter;
namespace content {
class ClipboardHostImplTest;
class RenderFrameHost;
class CONTENT_EXPORT ClipboardHostImpl : public blink::mojom::ClipboardHost {
public:
......@@ -35,14 +39,100 @@ class CONTENT_EXPORT ClipboardHostImpl : public blink::mojom::ClipboardHost {
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::ClipboardHost> receiver);
private:
friend class ClipboardHostImplTest;
protected:
// These types and methods are protected for testing.
using ClipboardPasteAllowed = RenderFrameHostImpl::ClipboardPasteAllowed;
using IsClipboardPasteAllowedCallback =
RenderFrameHostImpl::IsClipboardPasteAllowedCallback;
// Keeps track of a request to see if some clipboard content, identified by
// its sequence number, is allowed to be pasted into the render frame host
// that owns this clipboard host.
//
// A request starts in the state incomplete until Complete() is called with
// a value. Callbacks can be added to the request before or after it has
// completed.
class CONTENT_EXPORT IsPasteAllowedRequest {
public:
IsPasteAllowedRequest();
~IsPasteAllowedRequest();
// Adds |callback| to be notified when the request completes. If the
// request is already completed |callback| is invoked immediately. Returns
// true if a request should be started after adding this callback.
bool AddCallback(IsClipboardPasteAllowedCallback callback);
// Mark this request as completed with the specified result.
// Invoke all callbacks now.
void Complete(ClipboardPasteAllowed allowed);
// Returns true if this request is obsolete. An obsolete request
// is one that is completed, all registered callbacks have been
// called, and is considered old.
//
// |now| represents the current time. It is an argument to ease testing.
bool IsObsolete(base::Time now);
// Returns the time at which this request was created.
base::Time time() { return time_; }
private:
// Calls all the callbacks in |callbacks_| with the current value of
// |allowed_|. |allowed_| must not be empty.
void InvokeCallbacks();
base::Time time_{base::Time::Now()};
base::Optional<ClipboardPasteAllowed> allowed_;
std::vector<IsClipboardPasteAllowedCallback> callbacks_;
};
// A paste allowed request is obsolete if it is older than this time.
static const base::TimeDelta kIsPasteAllowedRequestTooOld;
ClipboardHostImpl(
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::ClipboardHost> receiver);
// content::mojom::ClipboardHost
// Performs a check to see if pasting |data| is allowed and invokes |callback|
// upon completion. |callback| maybe be invoked immediately if the data has
// already been checked. |data| and |seqno| should corresponds to the same
// clipboard data.
void PerformPasteIfAllowed(uint64_t seqno,
const ui::ClipboardFormatType& data_type,
std::string data,
IsClipboardPasteAllowedCallback callback);
// Remove obsolete entries from the outstanding requests map.
// A request is obsolete if:
// - its sequence number is less than |seqno|
// - it has no callbacks
// - it is too old
void CleanupObsoleteRequests();
// Completion callback of PerformPasteIfAllowed(). Sets the allowed
// status for the clipboard data corresponding to sequence number |seqno|.
void FinishPasteIfAllowed(uint64_t seqno, ClipboardPasteAllowed allowed);
const std::map<uint64_t, IsPasteAllowedRequest>&
is_paste_allowed_requests_for_testing() {
return is_allowed_requests_;
}
private:
friend class ClipboardHostImplTest;
friend class ClipboardHostImplScanTest;
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplTest,
IsPasteAllowedRequest_AddCallback);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplTest,
IsPasteAllowedRequest_Complete);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplTest,
IsPasteAllowedRequest_IsObsolete);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest,
PerformPasteIfAllowed_EmptyData);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, PerformPasteIfAllowed);
// mojom::ClipboardHost
void GetSequenceNumber(ui::ClipboardBuffer clipboard_buffer,
GetSequenceNumberCallback callback) override;
void IsFormatAvailable(blink::mojom::ClipboardFormat format,
......@@ -75,11 +165,22 @@ class CONTENT_EXPORT ClipboardHostImpl : public blink::mojom::ClipboardHost {
void WriteStringToFindPboard(const base::string16& text) override;
#endif
// Called by PerformPasteIfAllowed() when an is allowed request is needed.
// Virtual to be overridden in tests.
virtual void StartIsPasteAllowedRequest(
uint64_t seqno,
const ui::ClipboardFormatType& data_type,
std::string data);
mojo::Receiver<blink::mojom::ClipboardHost> receiver_;
ui::Clipboard* const clipboard_; // Not owned
int render_frame_routing_id_;
int render_frame_pid_;
std::unique_ptr<ui::ScopedClipboardWriter> clipboard_writer_;
// Outstanding is allowed requests per clipboard contents. Maps a clipboard
// sequence number to an outstanding request.
std::map<uint64_t, IsPasteAllowedRequest> is_allowed_requests_;
};
} // namespace content
......
......@@ -21,6 +21,31 @@
namespace content {
namespace {
// A ClipboardHostImpl that mocks out the dependency on RenderFrameHost.
class ClipboardHostImplNoRFH : public ClipboardHostImpl {
public:
ClipboardHostImplNoRFH(
mojo::PendingReceiver<blink::mojom::ClipboardHost> receiver)
: ClipboardHostImpl(/*render_frame_host=*/nullptr, std::move(receiver)) {}
void StartIsPasteAllowedRequest(uint64_t seqno,
const ui::ClipboardFormatType& data_type,
std::string data) override {}
void CompleteRequest(uint64_t seqno) {
FinishPasteIfAllowed(seqno, ClipboardHostImpl::ClipboardPasteAllowed(true));
}
using ClipboardHostImpl::CleanupObsoleteRequests;
using ClipboardHostImpl::is_paste_allowed_requests_for_testing;
using ClipboardHostImpl::kIsPasteAllowedRequestTooOld;
using ClipboardHostImpl::PerformPasteIfAllowed;
};
} // namespace
class ClipboardHostImplTest : public ::testing::Test {
protected:
ClipboardHostImplTest()
......@@ -95,4 +120,157 @@ TEST_F(ClipboardHostImplTest, ReentrancyInSyncCall) {
EXPECT_FALSE(mojo_clipboard().is_connected());
}
TEST_F(ClipboardHostImplTest, IsPasteAllowedRequest_AddCallback) {
ClipboardHostImpl::IsPasteAllowedRequest request;
int count = 0;
// First call to AddCallback should return true, the next false.
EXPECT_TRUE(request.AddCallback(base::BindLambdaForTesting(
[&count](ClipboardHostImpl::ClipboardPasteAllowed allowed) {
++count;
})));
EXPECT_FALSE(request.AddCallback(base::BindLambdaForTesting(
[&count](ClipboardHostImpl::ClipboardPasteAllowed allowed) {
++count;
})));
// In both cases, the callbacks should noy be called since the request is
// not complete.
EXPECT_EQ(0, count);
}
TEST_F(ClipboardHostImplTest, IsPasteAllowedRequest_Complete) {
ClipboardHostImpl::IsPasteAllowedRequest request;
int count = 0;
// Add a callback. It should not fire right away.
request.AddCallback(base::BindLambdaForTesting(
[&count](ClipboardHostImpl::ClipboardPasteAllowed allowed) {
++count;
ASSERT_EQ(ClipboardHostImpl::ClipboardPasteAllowed(true), allowed);
}));
EXPECT_EQ(0, count);
// Complete the request. Callback should fire. Whether paste is allowed
// or not is not important.
request.Complete(ClipboardHostImpl::ClipboardPasteAllowed(true));
EXPECT_EQ(1, count);
// Adding a new callback after completion invokes it immediately.
request.AddCallback(base::BindLambdaForTesting(
[&count](ClipboardHostImpl::ClipboardPasteAllowed allowed) {
++count;
ASSERT_EQ(ClipboardHostImpl::ClipboardPasteAllowed(true), allowed);
}));
EXPECT_EQ(2, count);
}
TEST_F(ClipboardHostImplTest, IsPasteAllowedRequest_IsObsolete) {
ClipboardHostImpl::IsPasteAllowedRequest request;
// A request that is not too old is not obsolete, even if it has no callbacks.
EXPECT_FALSE(request.IsObsolete(
request.time() + ClipboardHostImpl::kIsPasteAllowedRequestTooOld / 2));
// A request that still has callbacks is not obsolete, even if older than
// "too old".
request.AddCallback(base::DoNothing());
EXPECT_FALSE(request.IsObsolete(
request.time() + ClipboardHostImpl::kIsPasteAllowedRequestTooOld +
base::TimeDelta::FromMicroseconds(1)));
// A request is obsolete once it is too old and has no callbacks.
// Whether paste is allowed or not is not important.
request.Complete(ClipboardHostImpl::ClipboardPasteAllowed(true));
EXPECT_TRUE(request.IsObsolete(
request.time() + ClipboardHostImpl::kIsPasteAllowedRequestTooOld +
base::TimeDelta::FromMicroseconds(1)));
}
class ClipboardHostImplScanTest : public ::testing::Test {
protected:
ClipboardHostImplScanTest()
: clipboard_(ui::TestClipboard::CreateForCurrentThread()),
fake_clipboard_host_impl_(remote_.BindNewPipeAndPassReceiver()) {}
~ClipboardHostImplScanTest() override {
ui::Clipboard::DestroyClipboardForCurrentThread();
}
ClipboardHostImplNoRFH* clipboard_host_impl() {
return &fake_clipboard_host_impl_;
}
BrowserTaskEnvironment* task_environment() { return &task_environment_; }
private:
BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
mojo::Remote<blink::mojom::ClipboardHost> remote_;
ui::Clipboard* const clipboard_;
ClipboardHostImplNoRFH fake_clipboard_host_impl_;
};
TEST_F(ClipboardHostImplScanTest, PerformPasteIfAllowed_EmptyData) {
int count = 0;
// When data is empty, the callback is invoked right away.
clipboard_host_impl()->PerformPasteIfAllowed(
1, ui::ClipboardFormatType::GetPlainTextType(), "",
base::BindLambdaForTesting(
[&count](ClipboardHostImpl::ClipboardPasteAllowed allowed) {
++count;
}));
EXPECT_EQ(
0u,
clipboard_host_impl()->is_paste_allowed_requests_for_testing().size());
EXPECT_EQ(1, count);
}
TEST_F(ClipboardHostImplScanTest, PerformPasteIfAllowed) {
int count = 0;
clipboard_host_impl()->PerformPasteIfAllowed(
1, ui::ClipboardFormatType::GetPlainTextType(), "data",
base::BindLambdaForTesting(
[&count](ClipboardHostImpl::ClipboardPasteAllowed allowed) {
++count;
}));
EXPECT_EQ(
1u,
clipboard_host_impl()->is_paste_allowed_requests_for_testing().size());
EXPECT_EQ(0, count);
// Completing the request invokes the callback. The request will
// remain pending until it is cleaned up.
clipboard_host_impl()->CompleteRequest(1);
EXPECT_EQ(
1u,
clipboard_host_impl()->is_paste_allowed_requests_for_testing().size());
EXPECT_EQ(1, count);
}
TEST_F(ClipboardHostImplScanTest, CleanupObsoleteScanRequests) {
// Perform a request and complete it.
clipboard_host_impl()->PerformPasteIfAllowed(
1, ui::ClipboardFormatType::GetPlainTextType(), "data",
base::DoNothing());
clipboard_host_impl()->CompleteRequest(1);
EXPECT_EQ(
1u,
clipboard_host_impl()->is_paste_allowed_requests_for_testing().size());
// Make sure an appropriate amount of time passes to make the request old.
// It should be cleaned up.
task_environment()->FastForwardBy(
ClipboardHostImplNoRFH::kIsPasteAllowedRequestTooOld +
base::TimeDelta::FromMicroseconds(1));
clipboard_host_impl()->CleanupObsoleteRequests();
EXPECT_EQ(
0u,
clipboard_host_impl()->is_paste_allowed_requests_for_testing().size());
}
} // namespace content
......@@ -180,4 +180,12 @@ bool RenderFrameHostDelegate::IsFrameLowPriority(
return false;
}
void RenderFrameHostDelegate::IsClipboardPasteAllowed(
const GURL& url,
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback) {
std::move(callback).Run(ClipboardPasteAllowed(true));
}
} // namespace content
......@@ -10,10 +10,12 @@
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/i18n/rtl.h"
#include "base/optional.h"
#include "build/build_config.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/common/content_export.h"
#include "content/public/browser/media_player_watch_time.h"
......@@ -69,12 +71,15 @@ class FullscreenOptions;
}
} // namespace blink
namespace ui {
struct ClipboardFormatType;
}
namespace content {
class FileSelectListener;
class FrameTreeNode;
class InterstitialPage;
class PageState;
class RenderFrameHost;
class RenderFrameHostImpl;
class SessionStorageNamespace;
class WebContents;
......@@ -91,6 +96,13 @@ class CreateNewWindowParams;
// of the RenderFrameHost.
class CONTENT_EXPORT RenderFrameHostDelegate {
public:
// Callback used with HandleClipboardPaste() method. If the clipboard paste
// is allowed to proceed, the callback is called with true. Otherwise the
// callback is called with false.
using ClipboardPasteAllowed = RenderFrameHostImpl::ClipboardPasteAllowed;
using IsClipboardPasteAllowedCallback =
RenderFrameHostImpl::IsClipboardPasteAllowedCallback;
// This is used to give the delegate a chance to filter IPC messages.
virtual bool OnMessageReceived(RenderFrameHostImpl* render_frame_host,
const IPC::Message& message);
......@@ -492,6 +504,31 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
virtual media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
GetRecordAggregateWatchTimeCallback();
// Determines if a clipboard paste using |data| of type |data_type| is allowed
// in this renderer frame. Possible data types supported for paste can be
// seen in the ClipboardHostImpl class. Text based formats will use the
// data_type ui::ClipboardFormatType::GetPlainTextType() unless it is known
// to be of a more specific type, like RTF or HTML, in which case a type
// such as ui::ClipboardFormatType::GetRtfType() or
// ui::ClipboardFormatType::GetHtmlType() is used.
//
// It is also possible for the data type to be
// ui::ClipboardFormatType::GetWebCustomDataType() indicating that the paste
// uses a custom data format. It is up to the implementation to attempt to
// understand the type if possible. It is acceptable to deny pastes of
// unknown data types.
//
// The implementation is expected to show UX to the user if needed. If
// shown, the UX should be associated with the specific render frame host.
//
// The callback is called, possibly asynchronously, with a status indicating
// whether the operation is allowed or not.
virtual void IsClipboardPasteAllowed(
const GURL& url,
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback);
protected:
virtual ~RenderFrameHostDelegate() {}
};
......
......@@ -8000,6 +8000,14 @@ bool RenderFrameHostImpl::IsDOMContentLoaded() {
return dom_content_loaded_;
}
void RenderFrameHostImpl::IsClipboardPasteAllowed(
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback) {
delegate_->IsClipboardPasteAllowed(GetLastCommittedURL(), data_type, data,
std::move(callback));
}
RenderFrameHostImpl* RenderFrameHostImpl::ParentOrOuterDelegateFrame() {
// Find the parent in the FrameTree (iframe).
if (parent_)
......
......@@ -166,6 +166,10 @@ namespace network {
class ResourceRequestBody;
} // namespace network
namespace ui {
struct ClipboardFormatType;
}
namespace content {
class AppCacheNavigationHandle;
class AuthenticatorImpl;
......@@ -223,6 +227,11 @@ class CONTENT_EXPORT RenderFrameHostImpl
using AXTreeSnapshotCallback =
base::OnceCallback<void(const ui::AXTreeUpdate&)>;
// Callback used with IsClipboardPasteAllowed() method.
using ClipboardPasteAllowed = ContentBrowserClient::ClipboardPasteAllowed;
using IsClipboardPasteAllowedCallback =
ContentBrowserClient::IsClipboardPasteAllowedCallback;
// An accessibility reset is only allowed to prevent very rare corner cases
// or race conditions where the browser and renderer get out of sync. If
// this happens more than this many times, kill the renderer.
......@@ -327,6 +336,14 @@ class CONTENT_EXPORT RenderFrameHostImpl
void Reload() override;
bool IsDOMContentLoaded() override;
// Determines if a clipboard paste using |data| of type |data_type| is allowed
// in this renderer frame. The implementation delegates to
// RenderFrameHostDelegate::IsClipboardPasteAllowed(). See the description of
// the latter method for complete details.
void IsClipboardPasteAllowed(const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback);
void SendAccessibilityEventsToManager(
const AXEventNotificationDetails& details);
......
......@@ -7251,6 +7251,15 @@ bool WebContentsImpl::IsFrameLowPriority(
return delegate_->IsFrameLowPriority(this, render_frame_host);
}
void WebContentsImpl::IsClipboardPasteAllowed(
const GURL& url,
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback) {
GetContentClient()->browser()->IsClipboardPasteAllowed(
this, url, data_type, data, std::move(callback));
}
void WebContentsImpl::UpdateWebContentsVisibility(Visibility visibility) {
// Occlusion is disabled when |features::kWebContentsOcclusion| is disabled
// (for power and speed impact assessment) or when
......
......@@ -667,6 +667,11 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
const std::string& protocol,
const GURL& url,
bool user_gesture) override;
void IsClipboardPasteAllowed(
const GURL& url,
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback) override;
// RenderViewHostDelegate ----------------------------------------------------
RenderViewHostDelegateView* GetDelegateView() override;
......
......@@ -1026,4 +1026,13 @@ void ContentBrowserClient::FetchRemoteSms(
const url::Origin& origin,
base::OnceCallback<void(base::Optional<std::string>)> callback) {}
void ContentBrowserClient::IsClipboardPasteAllowed(
content::WebContents* web_contents,
const GURL& url,
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback) {
std::move(callback).Run(ClipboardPasteAllowed(true));
}
} // namespace content
......@@ -21,6 +21,7 @@
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
#include "base/util/type_safety/strong_alias.h"
#include "content/common/content_export.h"
#include "content/public/browser/certificate_request_result_type.h"
#include "content/public/browser/generated_code_cache_settings.h"
......@@ -150,6 +151,7 @@ class TargetPolicy;
namespace ui {
class SelectFilePolicy;
struct ClipboardFormatType;
} // namespace ui
namespace url {
......@@ -217,6 +219,12 @@ struct WebPreferences;
// the observer interfaces.)
class CONTENT_EXPORT ContentBrowserClient {
public:
// Callback used with IsClipboardPasteAllowed() method.
using ClipboardPasteAllowed =
util::StrongAlias<class ClipboardPasteAllowedTag, bool>;
using IsClipboardPasteAllowedCallback =
base::OnceCallback<void(ClipboardPasteAllowed)>;
virtual ~ContentBrowserClient() {}
// Allows the embedder to set any number of custom BrowserMainParts
......@@ -1743,6 +1751,32 @@ class CONTENT_EXPORT ContentBrowserClient {
content::BrowserContext* browser_context,
const url::Origin& origin,
base::OnceCallback<void(base::Optional<std::string>)> callback);
// Determines if a clipboard paste using |data| of type |data_type| is allowed
// in this renderer frame. Possible data types supported for paste can be
// seen in the ClipboardHostImpl class. Text based formats will use the
// data_type ui::ClipboardFormatType::GetPlainTextType() unless it is known
// to be of a more specific type, like RTF or HTML, in which case a type
// such as ui::ClipboardFormatType::GetRtfType() or
// ui::ClipboardFormatType::GetHtmlType() is used.
//
// It is also possible for the data type to be
// ui::ClipboardFormatType::GetWebCustomDataType() indicating that the paste
// uses a custom data format. It is up to the implementation to attempt to
// understand the type if possible. It is acceptable to deny pastes of
// unknown data types.
//
// The implementation is expected to show UX to the user if needed. If
// shown, the UX should be associated with the specific WebContents.
//
// The callback is called, possibly asynchronously, with a status indicating
// whether the operation is allowed or not.
virtual void IsClipboardPasteAllowed(
content::WebContents* web_contents,
const GURL& url,
const ui::ClipboardFormatType& data_type,
const std::string& data,
IsClipboardPasteAllowedCallback callback);
};
} // namespace content
......
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