blob: 69adaf383089dc7a0ec2059694a55a3d399c95a9 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_RENDERER_HOST_CLIPBOARD_HOST_IMPL_H_
#define CONTENT_BROWSER_RENDERER_HOST_CLIPBOARD_HOST_IMPL_H_
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/common/content_export.h"
#include "content/public/browser/clipboard_types.h"
#include "content/public/browser/document_service.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "third_party/blink/public/mojom/clipboard/clipboard.mojom.h"
#include "ui/base/clipboard/clipboard.h"
class GURL;
namespace ui {
class ScopedClipboardWriter;
} // namespace ui
namespace content {
class ClipboardHostImplTest;
// Returns a representation of the last source ClipboardEndpoint. This will
// either match the last clipboard write if `seqno` matches the last browser tab
// write, or an endpoint built from `Clipboard::GetSource()` called with
// `clipboard_buffer` otherwise.
//
// //content maintains additional metadata on top of what the //ui layer already
// tracks about clipboard data's source, e.g. the WebContents that provided the
// data. This function allows retrieving both the //ui metadata and the
// //content metadata in a single call.
//
// To avoid returning stale //content metadata if the writer has changed, the
// sequence number is used to validate if the writer has changed or not since
// the //content metadata was last updated.
CONTENT_EXPORT ClipboardEndpoint
GetSourceClipboardEndpoint(ui::ClipboardSequenceNumberToken seqno,
ui::ClipboardBuffer clipboard_buffer);
class CONTENT_EXPORT ClipboardHostImpl
: public DocumentService<blink::mojom::ClipboardHost> {
public:
~ClipboardHostImpl() override;
static void Create(
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::ClipboardHost> receiver);
using ClipboardPasteData = content::ClipboardPasteData;
protected:
// These types and methods are protected for testing.
using IsClipboardPasteAllowedCallback =
RenderFrameHostImpl::IsClipboardPasteAllowedCallback;
// Represents the underlying type of the argument passed to
// IsClipboardPasteAllowedCallback without the const& part.
using IsClipboardPasteAllowedCallbackArgType =
std::optional<ClipboardPasteData>;
// Keeps track of a request to see if some clipboard content, identified by
// its sequence number, is allowed to be pasted into the RenderFrameHost
// 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. Returns true
// if this is the first callback added and a request should be started,
// returns false otherwise.
bool AddCallback(IsClipboardPasteAllowedCallback callback);
// Merge `data` into the existing internal `data_` member so that the
// currently pending request will have the appropriate fields for all added
// callbacks, not just the initial one that created the request.
void AddData(ClipboardPasteData data);
// Mark this request as completed with the specified result.
// Invoke all callbacks now.
void Complete(IsClipboardPasteAllowedCallbackArgType data);
// Returns true if the request has completed.
bool is_complete() const { return data_allowed_.has_value(); }
// 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 completed. If called
// before the request is completed the return value is undefined.
base::Time completed_time();
private:
// Calls all the callbacks in |callbacks_| with the current value of
// |allowed_|. |allowed_| must not be empty.
void InvokeCallbacks();
// The time at which the request was completed. Before completion this
// value is undefined.
base::Time completed_time_;
// This member is null until Complete() is called.
std::optional<bool> data_allowed_;
// The data argument to pass to the IsClipboardPasteAllowedCallback.
ClipboardPasteData data_;
std::vector<IsClipboardPasteAllowedCallback> callbacks_;
};
// A paste allowed request is obsolete if it is older than this time.
static const base::TimeDelta kIsPasteAllowedRequestTooOld;
explicit ClipboardHostImpl(
RenderFrameHost& render_frame_host,
mojo::PendingReceiver<blink::mojom::ClipboardHost> receiver);
// Performs a check to see if pasting `data` is allowed by data transfer
// policies and invokes FinishPasteIfAllowed upon completion.
void PasteIfPolicyAllowed(ui::ClipboardBuffer clipboard_buffer,
const ui::ClipboardFormatType& data_type,
ClipboardPasteData clipboard_paste_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(
const ui::ClipboardSequenceNumberToken& seqno,
std::optional<ClipboardPasteData> clipboard_paste_data);
const std::map<ui::ClipboardSequenceNumberToken, IsPasteAllowedRequest>&
is_paste_allowed_requests_for_testing() {
return is_allowed_requests_;
}
// Called by PerformPasteIfAllowed() when an is allowed request is
// needed. Virtual to be overridden in tests.
virtual void StartIsPasteAllowedRequest(
const ui::ClipboardSequenceNumberToken& seqno,
const ui::ClipboardFormatType& data_type,
ui::ClipboardBuffer clipboard_buffer,
ClipboardPasteData clipboard_paste_data);
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, WriteText);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteText_Empty);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteHtml);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteHtml_Empty);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteSvg);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteSvg_Empty);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteBitmap);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteBitmap_Empty);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteCustomData);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, WriteCustomData_Empty);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest,
PerformPasteIfAllowed_EmptyData);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, PerformPasteIfAllowed);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest,
PerformPasteIfAllowed_SameHost_NotStarted);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest,
PerformPasteIfAllowed_External_Started);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest, GetSourceEndpoint);
// mojom::ClipboardHost
void GetSequenceNumber(ui::ClipboardBuffer clipboard_buffer,
GetSequenceNumberCallback callback) override;
void IsFormatAvailable(blink::mojom::ClipboardFormat format,
ui::ClipboardBuffer clipboard_buffer,
IsFormatAvailableCallback callback) override;
void ReadAvailableTypes(ui::ClipboardBuffer clipboard_buffer,
ReadAvailableTypesCallback callback) override;
void ReadText(ui::ClipboardBuffer clipboard_buffer,
ReadTextCallback callback) override;
void ReadHtml(ui::ClipboardBuffer clipboard_buffer,
ReadHtmlCallback callback) override;
void ReadSvg(ui::ClipboardBuffer clipboard_buffer,
ReadSvgCallback callback) override;
void ReadRtf(ui::ClipboardBuffer clipboard_buffer,
ReadRtfCallback callback) override;
void ReadPng(ui::ClipboardBuffer clipboard_buffer,
ReadPngCallback callback) override;
void ReadFiles(ui::ClipboardBuffer clipboard_buffer,
ReadFilesCallback callback) override;
void ReadCustomData(ui::ClipboardBuffer clipboard_buffer,
const std::u16string& type,
ReadCustomDataCallback callback) override;
void ReadAvailableCustomAndStandardFormats(
ReadAvailableCustomAndStandardFormatsCallback callback) override;
void ReadUnsanitizedCustomFormat(
const std::u16string& format,
ReadUnsanitizedCustomFormatCallback callback) override;
void WriteUnsanitizedCustomFormat(const std::u16string& format,
mojo_base::BigBuffer data) override;
void WriteText(const std::u16string& text) override;
void WriteHtml(const std::u16string& markup, const GURL& url) override;
void WriteSvg(const std::u16string& markup) override;
void WriteSmartPasteMarker() override;
void WriteCustomData(
const base::flat_map<std::u16string, std::u16string>& data) override;
void WriteBookmark(const std::string& url,
const std::u16string& title) override;
void WriteImage(const SkBitmap& unsafe_bitmap) override;
void CommitWrite() override;
#if BUILDFLAG(IS_MAC)
void WriteStringToFindPboard(const std::u16string& text) override;
#endif
// Checks if the renderer allows pasting. This check is skipped if called
// soon after a successful content allowed request.
bool IsRendererPasteAllowed(ui::ClipboardBuffer clipboard_buffer,
RenderFrameHost& render_frame_host);
// Helper to be used when checking if data is allowed to be copied.
//
// If `replacement_data` is null, `clipboard_writer_` will be used to write
// `data` to the clipboard. `data` should only have one of its fields set
// depending on which "Write" method lead to `OnCopyAllowedResult()` being
// called. That field should correspond to `data_type`.
//
// If `replacement_data` is not null, instead that replacement string is
// written to the clipboard as plaintext.
//
// This method can be called asynchronously.
void OnCopyAllowedResult(const ui::ClipboardFormatType& data_type,
const ClipboardPasteData& data,
std::optional<std::u16string> replacement_data);
// Does the same thing as the previous function with an extra `source_url`
// used to propagate the URL obtained in the `WriteHtml()` method call.
//
// This method can be called asynchronously.
void OnCopyHtmlAllowedResult(const GURL& source_url,
const ui::ClipboardFormatType& data_type,
const ClipboardPasteData& data,
std::optional<std::u16string> replacement_data);
using CopyAllowedCallback = base::OnceCallback<void()>;
void OnReadPng(ui::ClipboardBuffer clipboard_buffer,
ReadPngCallback callback,
const std::vector<uint8_t>& data);
// Creates a `ui::DataTransferEndpoint` representing the last committed URL.
std::unique_ptr<ui::DataTransferEndpoint> CreateDataEndpoint();
// Creates a `content::ClipboardEndpoint` representing the last committed URL.
ClipboardEndpoint CreateClipboardEndpoint();
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<ui::ClipboardSequenceNumberToken, IsPasteAllowedRequest>
is_allowed_requests_;
base::WeakPtrFactory<ClipboardHostImpl> weak_ptr_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_CLIPBOARD_HOST_IMPL_H_