blob: 68b4ff818753fc2394e943afad532dba9834545a [file] [log] [blame]
// Copyright 2023 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_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_
#define CONTENT_BROWSER_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_
#include <memory>
#include "base/callback_list.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/media/captured_surface_control_permission_manager.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents_media_capture_id.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
namespace content {
class WebContents;
// Encapsulates the permission state and logic associated with the Captured
// Surface Control API. Objects of this class live on the IO thread.
class CONTENT_EXPORT CapturedSurfaceController {
public:
using CapturedSurfaceControlResult =
::blink::mojom::CapturedSurfaceControlResult;
// Wheel deltas are clamped to this arbitrary, reasonable value.
// We clamp rather than report an error because "reasonable" is not
// well-defined as of the time being, let alone in an interoperable way.
inline static constexpr int32_t kMaxWheelDeltaMagnitude = 1000000;
static std::unique_ptr<CapturedSurfaceController> CreateForTesting(
GlobalRenderFrameHostId capturer_rfh_id,
WebContentsMediaCaptureId captured_wc_id,
std::unique_ptr<CapturedSurfaceControlPermissionManager>
permission_manager,
base::RepeatingCallback<void(int)> on_zoom_level_change_callback,
base::RepeatingCallback<void(base::WeakPtr<WebContents>)>
wc_resolution_callback);
CapturedSurfaceController(
GlobalRenderFrameHostId capturer_rfh_id,
WebContentsMediaCaptureId captured_wc_id,
base::RepeatingCallback<void(int)> on_zoom_level_change_callback);
virtual ~CapturedSurfaceController();
CapturedSurfaceController(const CapturedSurfaceController&) = delete;
CapturedSurfaceController& operator=(const CapturedSurfaceController&) =
delete;
// Set the captured WebContents this controller is associated with.
// This may be called with a null `WebContentsMediaCaptureId`.
virtual void UpdateCaptureTarget(WebContentsMediaCaptureId captured_wc_id);
// Produce a wheel event on the captured surface.
//
// * `action` represents the wheel event that should be forwarded to the
// captured surface.
// * `reply_callback` reports success/failure back to the caller.
virtual void SendWheel(
blink::mojom::CapturedWheelActionPtr action,
base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback);
// Updates the zoom level of the captured tab.
virtual void UpdateZoomLevel(
blink::mojom::ZoomLevelAction action,
base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback);
virtual void RequestPermission(
base::OnceCallback<void(CapturedSurfaceControlResult)> reply_callback);
struct CapturedSurfaceInfo final {
CapturedSurfaceInfo(
base::WeakPtr<WebContents> captured_wc,
std::unique_ptr<base::CallbackListSubscription,
BrowserThread::DeleteOnUIThread> subscription,
int subscription_version,
int initial_zoom_level);
CapturedSurfaceInfo(CapturedSurfaceInfo&& other);
CapturedSurfaceInfo& operator=(CapturedSurfaceInfo&& other);
~CapturedSurfaceInfo();
base::WeakPtr<WebContents> captured_wc;
std::unique_ptr<base::CallbackListSubscription,
BrowserThread::DeleteOnUIThread>
subscription;
int subscription_version;
int initial_zoom_level;
};
private:
using CapturedSurfaceControlPermissionStatus =
CapturedSurfaceControlPermissionManager::
CapturedSurfaceControlPermissionStatus;
void OnZoomLevelChange(int subscription_version, int zoom_level);
CapturedSurfaceController(
GlobalRenderFrameHostId capturer_rfh_id,
WebContentsMediaCaptureId captured_wc_id,
std::unique_ptr<CapturedSurfaceControlPermissionManager>
permission_manager,
base::RepeatingCallback<void(int)> on_zoom_level_change_callback,
base::RepeatingCallback<void(base::WeakPtr<WebContents>)>
wc_resolution_callback);
// Manage the resolution of WebContents-IDs into base::WeakPtr<WebContents>.
void ResolveCapturedSurface(WebContentsMediaCaptureId captured_wc_id);
void OnCapturedSurfaceResolved(
std::optional<CapturedSurfaceInfo> captured_surface);
const GlobalRenderFrameHostId capturer_rfh_id_;
// References the captured tab through its WebContents.
//
// Set to nullopt when:
// * The captured surface is not a tab.
// * Right after construction, before the ID is first resolved (on the
// UI thread) to a valid base::WeakPtr<WebContents>.
// * Whenever the captured tab changes, and UpdateCaptureTarget() is
// called. This triggers a new resolution, and in the intervening time,
// this will be set back to nullptr.
//
// Set to a concrete value otherwise.
// However, this concrete value can be nullptr, (1) as with any WeakPtr,
// or (2) if the ID failed to resolve to a valid WebContents.
//
// Note that `this` lives on the IO thread, and it is not possible to
// check the value of the underlying WebContents* here, or even compare
// it to nullptr.
//
// In the unlikely-yet-possible case that SendWheel() or SetZoomLevel()
// are called while the task to resolve is pending, those calls will
// fail gracefully. Subsequent calls are valid and can succeed.
// TODO(crbug.com/41493349): Add UMA to measure how often this happens
// and determine whether it's worth the effort to fix.
std::optional<base::WeakPtr<WebContents>> captured_wc_;
// Counts the pending resolutions, so that `captured_wc_` would only
// be set to a concrete values when the last one resolves.
int pending_wc_resolutions_ = 0;
std::unique_ptr<CapturedSurfaceControlPermissionManager> permission_manager_;
// Callback to be invoked whenever an ID's resolution to a
// base::WeakPtr<WebContents> completes.
const base::RepeatingCallback<void(base::WeakPtr<WebContents>)>
wc_resolution_callback_;
// `zoom_level_subscription_` controls the lifetime of a subscriptions to
// zoom-level updates for a captured tab. If the capture is switched over to a
// new tab, the subscription is re-initialized and `subscription_version_` is
// incremented so that callback invocations from the previous subscriptions
// may be ignored.
std::unique_ptr<base::CallbackListSubscription,
BrowserThread::DeleteOnUIThread>
zoom_level_subscription_;
int subscription_version_ = 0;
const base::RepeatingCallback<void(int)> on_zoom_level_change_callback_;
base::WeakPtrFactory<CapturedSurfaceController> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_MEDIA_CAPTURED_SURFACE_CONTROLLER_H_