blob: 5376cb286aaa0d3dc3cea3b68b56f5cd696454fe [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.
#include "content/browser/media/captured_surface_control_permission_manager.h"
#include "base/command_line.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/task/bind_post_task.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_features.h"
#include "media/base/media_switches.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
namespace content {
namespace {
using ::blink::mojom::PermissionStatus;
using CapturedSurfaceControlPermissionStatus =
::content::CapturedSurfaceControlPermissionManager::
CapturedSurfaceControlPermissionStatus;
// Translate between callbacks expecting different types.
base::OnceCallback<void(content::PermissionResult)> WrapCallback(
base::OnceCallback<void(CapturedSurfaceControlPermissionStatus)> callback) {
return base::BindOnce(
[](base::OnceCallback<void(CapturedSurfaceControlPermissionStatus)>
callback,
content::PermissionResult permission_result) {
std::move(callback).Run(
permission_result.status == PermissionStatus::GRANTED
? CapturedSurfaceControlPermissionStatus::kGranted
: CapturedSurfaceControlPermissionStatus::kDenied);
},
std::move(callback));
}
void CheckPermissionOnUIThread(
GlobalRenderFrameHostId capturer_rfh_id,
base::OnceCallback<void(CapturedSurfaceControlPermissionStatus)> callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderFrameHostImpl* const capturer_rfhi =
RenderFrameHostImpl::FromID(capturer_rfh_id);
if (!capturer_rfhi) {
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kError);
return;
}
BrowserContext* const browser_context = capturer_rfhi->GetBrowserContext();
if (!browser_context) {
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kError);
return;
}
WebContentsImpl* const capturer_wc =
WebContentsImpl::FromRenderFrameHostImpl(capturer_rfhi);
if (!capturer_wc) {
// The capturing frame or tab appears to have closed asynchronously.
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kError);
return;
}
PermissionController* const permission_controller =
browser_context->GetPermissionController();
if (!permission_controller) {
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kError);
return;
}
switch (permission_controller->GetPermissionStatusForCurrentDocument(
content::PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(
blink::PermissionType::CAPTURED_SURFACE_CONTROL),
capturer_rfhi)) {
case PermissionStatus::GRANTED:
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kGranted);
return;
case PermissionStatus::DENIED:
case blink::mojom::PermissionStatus::UNSATISFIED_OPTIONS:
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kDenied);
return;
case PermissionStatus::ASK:
break;
}
const bool user_gesture = capturer_rfhi->HasTransientUserActivation();
if (!user_gesture) {
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kDenied);
return;
}
permission_controller->RequestPermissionFromCurrentDocument(
capturer_rfhi,
PermissionRequestDescription(
content::PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(
blink::PermissionType::CAPTURED_SURFACE_CONTROL),
user_gesture),
WrapCallback(std::move(callback)));
}
} // namespace
CapturedSurfaceControlPermissionManager::
CapturedSurfaceControlPermissionManager(
GlobalRenderFrameHostId capturer_rfh_id)
: capturer_rfh_id_(capturer_rfh_id) {}
CapturedSurfaceControlPermissionManager::
~CapturedSurfaceControlPermissionManager() = default;
void CapturedSurfaceControlPermissionManager::CheckPermission(
base::OnceCallback<void(CapturedSurfaceControlPermissionStatus)> callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAutoGrantCapturedSurfaceControlPrompt)) {
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kGranted);
return;
}
// After CheckPermissionOnUIThread() is done (on the UI thread) it will
// report back to `this` object (on the IO thread), but only after hopping
// through a static method that will ensure that the `callback` is invoked
// even if the capture stops while the prompt is pending.
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&CheckPermissionOnUIThread, capturer_rfh_id_,
base::BindPostTaskToCurrentDefault(base::BindOnce(
&CapturedSurfaceControlPermissionManager::OnCheckResultStatic,
weak_factory_.GetWeakPtr(), std::move(callback)))));
}
// static
void CapturedSurfaceControlPermissionManager::OnCheckResultStatic(
base::WeakPtr<CapturedSurfaceControlPermissionManager> manager,
base::OnceCallback<void(CapturedSurfaceControlPermissionStatus)> callback,
CapturedSurfaceControlPermissionStatus result) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!manager) {
// Intentionally ignore `result`, as the capture-session stopped
// asynchronously and the result is no longer relevant.
std::move(callback).Run(CapturedSurfaceControlPermissionStatus::kError);
return;
}
manager->OnCheckResult(std::move(callback), result);
}
void CapturedSurfaceControlPermissionManager::OnCheckResult(
base::OnceCallback<void(CapturedSurfaceControlPermissionStatus)> callback,
CapturedSurfaceControlPermissionStatus result) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::move(callback).Run(result);
}
} // namespace content