blob: 7003fcec8b9c2cd06b28fe1fe835258e76fd73cb [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_IMAGE_EDITOR_SCREENSHOT_FLOW_H_
#define CHROME_BROWSER_IMAGE_EDITOR_SCREENSHOT_FLOW_H_
#include <memory>
#include <string>
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/supports_user_data.h"
#include "build/build_config.h"
#include "content/public/browser/web_contents_observer.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_delegate.h"
#include "ui/events/event.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/image/image.h"
#if BUILDFLAG(IS_MAC)
#include "chrome/browser/image_editor/event_capture_mac.h"
#else
#include "base/scoped_observation.h"
#endif
namespace content {
class WebContents;
enum class Visibility;
} // namespace content
namespace gfx {
class Canvas;
} // namespace gfx
namespace ui {
class EventTarget;
class Layer;
} // namespace ui
namespace image_editor {
// Class to associate screenshot capture data with Profile across navigation.
class ScreenshotCapturedData : public base::SupportsUserData::Data {
public:
ScreenshotCapturedData();
~ScreenshotCapturedData() override;
ScreenshotCapturedData(const ScreenshotCapturedData&) = delete;
ScreenshotCapturedData& operator=(const ScreenshotCapturedData&) = delete;
static constexpr char kDataKey[] = "share_hub_screenshot_data";
base::FilePath screenshot_filepath;
};
// Result codes to distinguish between how the capture mode was closed.
enum class ScreenshotCaptureResultCode {
// Successful capture.
SUCCESS = 0,
// User navigated away from the primary web contents page while the capture
// mode was active.
USER_NAVIGATED_EXIT = 1,
// User exited the capture mode via key press.
USER_ESCAPE_EXIT = 2,
kMaxValue = USER_ESCAPE_EXIT
};
// Structure containing image data and any future metadata.
struct ScreenshotCaptureResult {
// The image obtained from capture. Empty on failure.
gfx::Image image;
// The bounds of the screen during which capture took place. Empty on failure.
gfx::Rect screen_bounds;
// The result code of the capture describing why the user exited.
ScreenshotCaptureResultCode result_code;
};
// Callback for obtaining image data.
// OnceCallback: This class currently expects to capture only one screenshot,
// then the caller proceeds to the next steps in its user-facing flow.
typedef base::OnceCallback<void(const ScreenshotCaptureResult&)>
ScreenshotCaptureCallback;
// ScreenshotFlow allows the user to enter a capture mode where they may drag
// a capture rectangle over a WebContents. The class calls a OnceCallback with
// the screenshot data contained in the region of interest.
class ScreenshotFlow : public content::WebContentsObserver,
public ui::LayerDelegate,
public ui::EventHandler {
public:
enum class CaptureMode {
// Default, capture is not active.
NOT_CAPTURING = 0,
// User can drag a rectangle to select a region.
SELECTION_RECTANGLE,
// User can hover over the DOM to select individual elements.
SELECTION_ELEMENT
};
explicit ScreenshotFlow(content::WebContents* web_contents);
ScreenshotFlow(const ScreenshotFlow&) = delete;
ScreenshotFlow& operator=(const ScreenshotFlow&) = delete;
~ScreenshotFlow() override;
// Runs the entire screen capture and optional image editing flow:
// Enters screenshot capture mode, allowing the user to choose a region of
// the browser window to capture. Shows postprocessing options including
// copying to the clipboard or saving.
void Start(ScreenshotCaptureCallback flow_callback);
// Runs the screen capture flow, capturing the entire viewport rather than
// a region selected by the user.
void StartFullscreenCapture(ScreenshotCaptureCallback flow_callback);
// Exits capture mode without running any callbacks.
void CancelCapture();
// Returns whether the capture mode is open or not.
bool IsCaptureModeActive();
// content::WebContentsObserver:
void WebContentsDestroyed() override;
void OnVisibilityChanged(content::Visibility visibility) override;
private:
class UnderlyingWebContentsObserver;
// ui:EventHandler:
void OnKeyEvent(ui::KeyEvent* event) override;
void OnMouseEvent(ui::MouseEvent* event) override;
void OnScrollEvent(ui::ScrollEvent* event) override;
// ui::LayerDelegate:
void OnPaintLayer(const ui::PaintContext& context) override;
void OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) override {}
// Requests a repaint of the selection layer.
// |region| should contain the invalidation area.
void RequestRepaint(gfx::Rect region);
// Creates and adds the overlay over the webcontnts to handle selection.
// Adds mouse listeners.
void CreateAndAddUIOverlay();
// Checks whether the UI overlay is visible.
bool IsUIOverlayShown();
// Resizes the UI overlay. It's used to make the UI overlay responsive to
// the frame size changes.
void ResetUIOverlayBounds();
// Removes the UI overlay and any listeners.
void RemoveUIOverlay();
// Captures a new screenshot for the chosen region, and runs the completion
// callback.
void CaptureAndRunScreenshotCompleteCallback(
ScreenshotCaptureResultCode result_code,
gfx::Rect region);
// Completes the capture process for |region| and runs the callback provided
// to Start().
void CompleteCapture(ScreenshotCaptureResultCode result_code,
const gfx::Rect& region);
// Completes the capture process and runs the |flow_callback| with provided
// |image| data sourced from |bounds|.
void RunScreenshotCompleteCallback(ScreenshotCaptureResultCode result_code,
gfx::Rect bounds,
gfx::Image image);
// Paints the screenshot selection layer. The user's selection is left
// unpainted to be hollowed out. |invalidation_region| specifies an optional
// region to clip to for performance; if empty, paints the entire window.
void PaintSelectionLayer(gfx::Canvas* canvas,
const gfx::Rect& selection,
const gfx::Rect& invalidation_region);
// Requests to set the cursor type.
void SetCursor(ui::mojom::CursorType cursor_type);
// Attempts to capture the region defined by |drag_start_| and |drag_end_|
// while also making sure the points are within the web contents view bounds.
void AttemptRegionCapture(gfx::Rect view_bounds);
// Setter for |is_dragging_|.
void SetIsDragging(bool value);
base::WeakPtr<ScreenshotFlow> weak_this_;
// Whether we are in drag mode on this layer.
ScreenshotFlow::CaptureMode capture_mode_ =
ScreenshotFlow::CaptureMode::NOT_CAPTURING;
// Web Contents that we are capturing.
base::WeakPtr<content::WebContents> web_contents_;
// Observer for |web_contents_|.
std::unique_ptr<UnderlyingWebContentsObserver> web_contents_observer_;
// Callback provided to Start().
ScreenshotCaptureCallback flow_callback_;
// Mac-specific
#if BUILDFLAG(IS_MAC)
std::unique_ptr<EventCaptureMac> event_capture_mac_;
#else
base::ScopedObservation<ui::EventTarget, ui::EventHandler> event_capture_{
this};
#endif
// Selection rectangle coordinates.
gfx::Point drag_start_;
gfx::Point drag_end_;
// Whether the user is currently dragging on the capture UI.
bool is_dragging_ = false;
// Invalidation area; empty for entire region.
gfx::Rect paint_invalidation_;
// Our top-level layer that is superimposed over the browser window's root
// layer while screen capture mode is active.
std::unique_ptr<ui::Layer> screen_capture_layer_;
base::WeakPtrFactory<ScreenshotFlow> weak_factory_{this};
};
} // namespace image_editor
#endif // CHROME_BROWSER_IMAGE_EDITOR_SCREENSHOT_FLOW_H_