blob: 6d2ee5a93e84ef88fab2bcf03b4680bf4e36e138 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_CLIENT_H_
#define COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_CLIENT_H_
#include <stdint.h>
#include <memory>
#include "base/compiler_specific.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "components/paint_preview/common/capture_result.h"
#include "components/paint_preview/common/mojom/paint_preview_recorder.mojom.h"
#include "components/paint_preview/common/proto/paint_preview.pb.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "ui/gfx/geometry/rect.h"
namespace paint_preview {
// Client responsible for making requests to the mojom::PaintPreviewRecorder. A
// client coordinates between multiple frames and handles capture and
// aggreagation of data from both the main frame and subframes.
//
// Should be created and accessed from the UI thread as WebContentsUserData
// requires this behavior.
class PaintPreviewClient
: public content::WebContentsUserData<PaintPreviewClient>,
public content::WebContentsObserver {
public:
using PaintPreviewCallback =
base::OnceCallback<void(base::UnguessableToken,
mojom::PaintPreviewStatus,
std::unique_ptr<CaptureResult>)>;
using RecordingRequestParamsReadyCallback = base::OnceCallback<void(
RecordingParams,
base::expected<mojom::PaintPreviewCaptureParamsPtr,
mojom::PaintPreviewStatus>)>;
// Augmented version of mojom::PaintPreviewServiceParams.
struct PaintPreviewParams {
explicit PaintPreviewParams(RecordingPersistence persistence);
PaintPreviewParams(const PaintPreviewParams&) = delete;
PaintPreviewParams& operator=(const PaintPreviewParams&) = delete;
PaintPreviewParams(PaintPreviewParams&&) = default;
PaintPreviewParams& operator=(PaintPreviewParams&&) = default;
~PaintPreviewParams();
PaintPreviewParams Clone() const;
// Indicates where the PaintPreviewRecorder should store its intermediate
// artifacts.
RecordingPersistence persistence;
// The root directory in which to store paint_previews. This should be
// a subdirectory inside the active user profile's directory.
base::FilePath root_dir;
RecordingParams inner;
static PaintPreviewParams CreateForTesting(
RecordingPersistence persistence,
base::UnguessableToken document_guid);
private:
PaintPreviewParams(RecordingPersistence persistence,
base::UnguessableToken document_guid);
};
~PaintPreviewClient() override;
// IMPORTANT: The Capture* methods must be called on the UI thread!
// Captures a paint preview corresponding to the content of
// |render_frame_host|. This will work for capturing entire documents if
// passed the main frame or for just a specific subframe depending on
// |render_frame_host|. |callback| is invoked on completion, and must be
// non-null.
void CapturePaintPreview(PaintPreviewParams params,
content::RenderFrameHost* render_frame_host,
PaintPreviewCallback callback);
// Captures a paint preview of the subframe corresponding to
// |render_subframe_host|.
void CaptureSubframePaintPreview(
const base::UnguessableToken& guid,
const gfx::Rect& rect,
content::RenderFrameHost* render_subframe_host);
// WebContentsObserver implementation ---------------------------------------
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
private:
explicit PaintPreviewClient(content::WebContents* web_contents);
friend class content::WebContentsUserData<PaintPreviewClient>;
// Internal Storage Classes -------------------------------------------------
// Ephemeral state for a document being captured. This will be accumulated to
// as the capture progresses and results in a |CaptureResult|.
struct InProgressDocumentCaptureState {
public:
InProgressDocumentCaptureState();
~InProgressDocumentCaptureState();
RecordingPersistence persistence;
// If |RecordingPersistence::kFileSystem|, the root directory to store
// artifacts to. Ignored if |RecordingPersistence::kMemoryBuffer|.
base::FilePath root_dir;
// This corresponds to RenderFrameHost::EmbeddingToken.
base::UnguessableToken root_frame_token;
// From the first recording params. Whether to capture links and the
// size limit per capture respectively.
bool capture_links = true;
size_t max_per_capture_size = 0;
uint64_t max_decoded_image_size_bytes{std::numeric_limits<uint64_t>::max()};
// UKM Source ID of the WebContent.
ukm::SourceId source_id;
// Main frame capture time.
base::TimeDelta main_frame_blink_recording_time;
// Callback that is invoked on completion of data. Must be non-null for the
// lifetime of the InProgressDocumentCaptureState.
PaintPreviewCallback callback;
// All the render frames that are still required.
base::flat_set<base::UnguessableToken> awaiting_subframes;
// All the render frames that have finished.
base::flat_set<base::UnguessableToken> finished_subframes;
// All the render frames that are allowed to be captured.
base::flat_set<base::UnguessableToken> accepted_tokens;
// If |RecordingPersistence::kMemoryBuffer|, this will contain the
// successful recordings. Empty if |RecordingPersistence::FileSystem|
base::flat_map<base::UnguessableToken, mojo_base::BigBuffer>
serialized_skps;
PaintPreviewProto proto;
// Indicates that at least one subframe finished unsuccessfully.
bool had_error = false;
// Indicates that at least one subframe finished successfully.
bool had_success = false;
// Indicates if we should clean up files associated with awaiting frames on
// destruction
bool should_clean_up_files = false;
// This flag will skip GPU accelerated content where applicable when
// capturing. This reduces hangs, capture time and may also reduce OOM
// crashes, but results in a lower fideltiy capture (i.e. the contents
// captured may not accurately reflect the content visible to the user at
// time of capture). See PaintPreviewBaseService::CaptureParams for a
// description of the effects of this flag.
bool skip_accelerated_content = false;
// Controls optional redaction of iframes.
RedactionParams redaction_params;
// Returns whether the given frame is allowed to be captured.
bool IsAllowedToCapture(const base::UnguessableToken& frame_token) const;
// Returns whether the given frame is finished being captured.
bool IsFinishedCapturing(const base::UnguessableToken& frame_token) const;
// Returns whether the given frame's capture is in progress.
bool IsCaptureInProgress(const base::UnguessableToken& frame_token) const;
// Generates a file path based off |root_dir| and |frame_guid|. Will be in
// the form "{hexadecimal}.skp".
base::FilePath FilePathForFrame(
const base::UnguessableToken& frame_guid) const;
// Prepares the PaintPreviewRecorder mojo params request object. If
// |persistence| is |RecordingPersistence::kFileSystem|, this will create
// the file that will act as the sink for the recording.
void PrepareRecordingRequestParams(
RecordingParams capture_params,
const base::UnguessableToken& frame_guid,
RecordingRequestParamsReadyCallback ready_callback) const;
// Record a successful recording into this capture state.
void RecordSuccessfulFrame(const base::UnguessableToken& frame_guid,
bool is_main_frame,
mojom::PaintPreviewCaptureResponsePtr response);
// Convert this capture state into a form that can be returned to the
// original paint preview capture request.
std::unique_ptr<CaptureResult> IntoCaptureResult() &&;
InProgressDocumentCaptureState& operator=(
InProgressDocumentCaptureState&& other) noexcept;
InProgressDocumentCaptureState(
InProgressDocumentCaptureState&& other) noexcept;
private:
InProgressDocumentCaptureState(const InProgressDocumentCaptureState&) =
delete;
InProgressDocumentCaptureState& operator=(
const InProgressDocumentCaptureState&) = delete;
};
// Sets up for a capture of a frame on |render_frame_host| according to
// |params|.
void CapturePaintPreviewInternal(
RecordingParams params,
content::RenderFrameHost* render_frame_host,
const InProgressDocumentCaptureState& document_data);
// Initiates capture via the PaintPreviewRecorder associated with
// |render_frame_host| using |params| to configure the request. |frame_guid|
// is the GUID associated with the frame. |path| is file path associated with
// the File stored in |result| (base::File isn't aware of its file path).
void RequestCaptureOnUIThread(
const base::UnguessableToken& frame_guid,
const content::GlobalRenderFrameHostId& render_frame_id,
RecordingParams params,
base::expected<mojom::PaintPreviewCaptureParamsPtr,
mojom::PaintPreviewStatus> capture_params);
// Handles recording the frame and updating client state when capture is
// complete.
void OnPaintPreviewCapturedCallback(
const base::UnguessableToken& frame_guid,
const content::GlobalRenderFrameHostId& render_frame_id,
RecordingParams params,
base::expected<mojom::PaintPreviewCaptureResponsePtr,
mojom::PaintPreviewStatus> response);
// Marks a frame as having been processed, this should occur regardless of
// whether the processed frame is valid as there is no retry.
void MarkFrameAsProcessed(base::UnguessableToken guid,
const base::UnguessableToken& frame_guid,
InProgressDocumentCaptureState* document_data);
// Handles finishing the capture once all frames are received.
void OnFinished(base::UnguessableToken guid,
InProgressDocumentCaptureState& document_data);
// Performs bookkeeping and requests geometry metadata necessary for iframe
// redaction.
void BeginSubframeRedaction(const base::UnguessableToken& guid,
RecordingParams params,
content::RenderFrameHost* render_subframe_host,
InProgressDocumentCaptureState& document_data);
// Synthesizes a redacted subframe and persists it appropriately, then resumes
// the capture.
void RedactSubframe(const base::UnguessableToken& frame_guid,
const content::GlobalRenderFrameHostId& render_frame_id,
RecordingParams params,
base::OnceCallback<void(
RecordingParams,
base::expected<mojom::PaintPreviewCaptureResponsePtr,
mojom::PaintPreviewStatus>)> callback,
mojom::GeometryMetadataResponsePtr response);
// Performs bookkeeping to keep track of the fact that this frame's capture is
// still pending.
void AwaitSubframeCapture(const base::UnguessableToken& frame_guid,
const RecordingParams& params,
InProgressDocumentCaptureState& document_data);
// Returns a reference to the PaintPreviewRecorder remote for the given frame.
// If no such remote exists yet, one is created.
mojo::AssociatedRemote<mojom::PaintPreviewRecorder>& GetOrInsertRecorder(
const base::UnguessableToken& frame_guid,
content ::RenderFrameHost& render_frame_host) LIFETIME_BOUND;
// Storage ------------------------------------------------------------------
// Maps a RenderFrameHost and document to a remote interface.
base::flat_map<base::UnguessableToken,
mojo::AssociatedRemote<mojom::PaintPreviewRecorder>>
interface_ptrs_;
// Maps render frame's GUID and document cookies that requested the frame.
base::flat_map<base::UnguessableToken, base::flat_set<base::UnguessableToken>>
pending_previews_on_subframe_;
// Maps a document GUID to its capture state while it is in-progress. Entries
// in this map should be cleaned up when a capture completes (either
// successfully or not).
base::flat_map<base::UnguessableToken, InProgressDocumentCaptureState>
all_document_data_;
base::WeakPtrFactory<PaintPreviewClient> weak_ptr_factory_{this};
WEB_CONTENTS_USER_DATA_KEY_DECL();
PaintPreviewClient(const PaintPreviewClient&) = delete;
PaintPreviewClient& operator=(const PaintPreviewClient&) = delete;
};
} // namespace paint_preview
#endif // COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_CLIENT_H_