910e62b5创建于 1月15日历史提交
// Copyright 2015 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/renderer/media/media_permission_dispatcher.h"

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "base/types/cxx23_to_underlying.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "url/gurl.h"

namespace {

using Type = media::MediaPermission::Type;

blink::mojom::PermissionDescriptorPtr MediaPermissionTypeToPermissionDescriptor(
    Type type) {
  auto descriptor = blink::mojom::PermissionDescriptor::New();
  switch (type) {
    case Type::kProtectedMediaIdentifier:
      descriptor->name =
          blink::mojom::PermissionName::PROTECTED_MEDIA_IDENTIFIER;
      break;
    case Type::kAudioCapture:
      descriptor->name = blink::mojom::PermissionName::AUDIO_CAPTURE;
      break;
    case Type::kVideoCapture:
      descriptor->name = blink::mojom::PermissionName::VIDEO_CAPTURE;
      break;
    default:
      NOTREACHED() << base::to_underlying(type);
  }
  return descriptor;
}

}  // namespace

namespace content {

MediaPermissionDispatcher::MediaPermissionDispatcher(
    RenderFrameImpl* render_frame)
    : task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
      next_request_id_(0),
      render_frame_(render_frame) {
  DCHECK(render_frame_);
  weak_ptr_ = weak_factory_.GetWeakPtr();
}

MediaPermissionDispatcher::~MediaPermissionDispatcher() {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());

  // Clean up pending requests.
  OnPermissionServiceConnectionError();
}

void MediaPermissionDispatcher::OnNavigation() {
  // Behave as if there were a connection error. The browser process will be
  // closing the connection imminently.
  OnPermissionServiceConnectionError();
}

void MediaPermissionDispatcher::HasPermission(
    Type type,
    PermissionStatusCB permission_status_cb) {
  if (!task_runner_->RunsTasksInCurrentSequence()) {
    task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&MediaPermissionDispatcher::HasPermission,
                                  weak_ptr_, type,
                                  base::BindPostTaskToCurrentDefault(
                                      std::move(permission_status_cb))));
    return;
  }

  DCHECK(task_runner_->RunsTasksInCurrentSequence());

  int request_id = RegisterCallback(std::move(permission_status_cb));
  DVLOG(2) << __func__ << ": request ID " << request_id;

  GetPermissionService()->HasPermission(
      MediaPermissionTypeToPermissionDescriptor(type),
      base::BindOnce(&MediaPermissionDispatcher::OnPermissionStatus, weak_ptr_,
                     request_id));
}

void MediaPermissionDispatcher::RequestPermission(
    Type type,
    PermissionStatusCB permission_status_cb) {
  if (!task_runner_->RunsTasksInCurrentSequence()) {
    task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&MediaPermissionDispatcher::RequestPermission,
                                  weak_ptr_, type,
                                  base::BindPostTaskToCurrentDefault(
                                      std::move(permission_status_cb))));
    return;
  }

  DCHECK(task_runner_->RunsTasksInCurrentSequence());

  int request_id = RegisterCallback(std::move(permission_status_cb));
  DVLOG(2) << __func__ << ": request ID " << request_id;

  GetPermissionService()->RequestPermission(
      MediaPermissionTypeToPermissionDescriptor(type),
      render_frame_->GetWebFrame()->HasTransientUserActivation(),
      base::BindOnce(&MediaPermissionDispatcher::OnPermissionStatus, weak_ptr_,
                     request_id));
}

bool MediaPermissionDispatcher::IsEncryptedMediaEnabled() {
  return render_frame_->GetRendererPreferences().enable_encrypted_media;
}

uint32_t MediaPermissionDispatcher::RegisterCallback(
    PermissionStatusCB permission_status_cb) {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());

  uint32_t request_id = next_request_id_++;
  DCHECK(!requests_.count(request_id));
  requests_[request_id] = std::move(permission_status_cb);

  return request_id;
}

blink::mojom::PermissionService*
MediaPermissionDispatcher::GetPermissionService() {
  if (!permission_service_) {
    render_frame_->GetBrowserInterfaceBroker().GetInterface(
        permission_service_.BindNewPipeAndPassReceiver());
    permission_service_.set_disconnect_handler(base::BindOnce(
        &MediaPermissionDispatcher::OnPermissionServiceConnectionError,
        base::Unretained(this)));
  }

  return permission_service_.get();
}

void MediaPermissionDispatcher::OnPermissionStatus(
    uint32_t request_id,
    blink::mojom::PermissionStatus status) {
  DVLOG(2) << __func__ << ": (" << request_id << ", " << status << ")";
  DCHECK(task_runner_->RunsTasksInCurrentSequence());

  auto iter = requests_.find(request_id);
  CHECK(iter != requests_.end());

  PermissionStatusCB permission_status_cb = std::move(iter->second);
  requests_.erase(iter);

  std::move(permission_status_cb)
      .Run(status == blink::mojom::PermissionStatus::GRANTED);
}

#if BUILDFLAG(IS_WIN)
void MediaPermissionDispatcher::IsHardwareSecureDecryptionAllowed(
    IsHardwareSecureDecryptionAllowedCB cb) {
  GetMediaFoundationPreferences()->IsHardwareSecureDecryptionAllowed(
      std::move(cb));
}

media::mojom::MediaFoundationPreferences*
MediaPermissionDispatcher::GetMediaFoundationPreferences() {
  if (!mf_preferences_) {
    render_frame_->GetBrowserInterfaceBroker().GetInterface(
        mf_preferences_.BindNewPipeAndPassReceiver());
    mf_preferences_.set_disconnect_handler(base::BindOnce(
        &MediaPermissionDispatcher::OnMediaFoundationPreferencesConnectionError,
        base::Unretained(this)));
  }

  return mf_preferences_.get();
}

void MediaPermissionDispatcher::OnMediaFoundationPreferencesConnectionError() {
  mf_preferences_.reset();
}
#endif  // BUILDFLAG(IS_WIN)

void MediaPermissionDispatcher::OnPermissionServiceConnectionError() {
  permission_service_.reset();

  // Fire all pending callbacks with |false|.
  RequestMap requests;
  requests.swap(requests_);
  for (auto& request : requests)
    std::move(request.second).Run(false);
}

}  // namespace content