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 "android_webview/browser/aw_permission_manager.h"

#include <memory>
#include <string>
#include <unordered_map>
#include <utility>

#include "android_webview/browser/aw_browser_permission_request_delegate.h"
#include "android_webview/browser/aw_contents.h"
#include "android_webview/browser/aw_context_permissions_delegate.h"
#include "android_webview/browser/aw_settings.h"
#include "android_webview/common/aw_features.h"
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/notimplemented.h"
#include "base/notreached.h"
#include "base/sequence_checker.h"
#include "base/task/thread_pool.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_util.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_result.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"

using blink::PermissionType;
using blink::mojom::PermissionStatus;

using RequestPermissionsCallback =
    base::OnceCallback<void(const std::vector<content::PermissionResult>&)>;

namespace android_webview {

class LastRequestResultCache {
 public:
  LastRequestResultCache() = default;

  LastRequestResultCache(const LastRequestResultCache&) = delete;
  LastRequestResultCache& operator=(const LastRequestResultCache&) = delete;

  void SetResult(PermissionType permission,
                 const GURL& requesting_origin,
                 const GURL& embedding_origin,
                 PermissionStatus status) {
    DCHECK(status == PermissionStatus::GRANTED ||
           status == PermissionStatus::DENIED);

    // TODO(ddorwin): We should be denying empty origins at a higher level.
    if (requesting_origin.is_empty() || embedding_origin.is_empty()) {
      DLOG(WARNING) << "Not caching result because of empty origin.";
      return;
    }

    if (!requesting_origin.is_valid()) {
      NOTREACHED() << requesting_origin.possibly_invalid_spec();
    }
    if (!embedding_origin.is_valid()) {
      NOTREACHED() << embedding_origin.possibly_invalid_spec();
    }

    switch (permission) {
      case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
      case PermissionType::TOP_LEVEL_STORAGE_ACCESS:
        break;
      // Other permissions are not cached.
      default:
        return;
    }

    std::string key = GetCacheKey(requesting_origin, embedding_origin);
    CHECK(!key.empty());
    pmi_result_cache_[key] = status;
  }

  PermissionStatus GetResult(
      const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
      const GURL& requesting_origin,
      const GURL& embedding_origin) const {
    // TODO(ddorwin): We should be denying empty origins at a higher level.
    if (requesting_origin.is_empty() || embedding_origin.is_empty()) {
      return PermissionStatus::ASK;
    }

    DCHECK(requesting_origin.is_valid())
        << requesting_origin.possibly_invalid_spec();
    DCHECK(embedding_origin.is_valid())
        << embedding_origin.possibly_invalid_spec();

    const PermissionType permission =
        blink::PermissionDescriptorToPermissionType(permission_descriptor);
    switch (permission) {
      case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
      case PermissionType::TOP_LEVEL_STORAGE_ACCESS:
        break;
      // Other permissions are not cached.
      default:
        NOTREACHED()
            << "Results are only cached for PROTECTED_MEDIA_IDENTIFIER";
    }

    std::string key = GetCacheKey(requesting_origin, embedding_origin);
    StatusMap::const_iterator it = pmi_result_cache_.find(key);
    if (it == pmi_result_cache_.end()) {
      DLOG(WARNING) << "GetResult() called for uncached origins: " << key;
      return PermissionStatus::ASK;
    }

    DCHECK(!key.empty());
    return it->second;
  }

  void ClearResult(PermissionType permission,
                   const GURL& requesting_origin,
                   const GURL& embedding_origin) {
    // TODO(ddorwin): We should be denying empty origins at a higher level.
    if (requesting_origin.is_empty() || embedding_origin.is_empty()) {
      return;
    }

    DCHECK(requesting_origin.is_valid())
        << requesting_origin.possibly_invalid_spec();
    DCHECK(embedding_origin.is_valid())
        << embedding_origin.possibly_invalid_spec();


    if (permission != PermissionType::PROTECTED_MEDIA_IDENTIFIER) {
      // Other permissions are not cached, so nothing to clear.
      return;
    }

    std::string key = GetCacheKey(requesting_origin, embedding_origin);
    pmi_result_cache_.erase(key);
  }

 private:
  // Returns a concatenation of the origins to be used as the index.
  // Returns the empty string if either origin is invalid or empty.
  static std::string GetCacheKey(const GURL& requesting_origin,
                                 const GURL& embedding_origin) {
    const std::string& requesting = requesting_origin.spec();
    const std::string& embedding = embedding_origin.spec();
    if (requesting.empty() || embedding.empty())
      return std::string();
    return requesting + "," + embedding;
  }

  using StatusMap = std::unordered_map<std::string, PermissionStatus>;
  StatusMap pmi_result_cache_;
};

class AwPermissionManager::PendingRequest {
 public:
  PendingRequest(const std::vector<PermissionType>& permissions,
                 GURL requesting_origin,
                 GURL embedding_origin,
                 int render_process_id,
                 int render_frame_id,
                 RequestPermissionsCallback callback)
      : permissions(permissions),
        requesting_origin(requesting_origin),
        embedding_origin(embedding_origin),
        render_process_id(render_process_id),
        render_frame_id(render_frame_id),
        callback(std::move(callback)),
        results(permissions.size(),
                content::PermissionResult(PermissionStatus::DENIED)),
        cancelled_(false) {
    for (size_t i = 0; i < permissions.size(); ++i) {
      permission_index_map_.insert(std::make_pair(permissions[i], i));
    }
  }

  ~PendingRequest() = default;

  void SetPermissionResult(PermissionType type,
                           content::PermissionResult permission_result) {
    auto result = permission_index_map_.find(type);
    if (result == permission_index_map_.end()) {
      NOTREACHED();
    }
    DCHECK(!IsCompleted());
    results[result->second] = permission_result;
    if (base::FeatureList::IsEnabled(blink::features::kBlockMidiByDefault)) {
      if (type == PermissionType::MIDI &&
          permission_result.status == PermissionStatus::GRANTED) {
        content::ChildProcessSecurityPolicy::GetInstance()
            ->GrantSendMidiMessage(render_process_id);
      }
    }
    if (type == PermissionType::MIDI_SYSEX &&
        permission_result.status == PermissionStatus::GRANTED) {
      content::ChildProcessSecurityPolicy::GetInstance()
          ->GrantSendMidiSysExMessage(render_process_id);
    }
    resolved_permissions_.insert(type);
  }

  content::PermissionResult GetPermissionResult(PermissionType type) {
    auto result = permission_index_map_.find(type);
    if (result == permission_index_map_.end()) {
      NOTREACHED();
    }
    return results[result->second];
  }

  bool HasPermissionType(PermissionType type) {
    return base::Contains(permission_index_map_, type);
  }

  bool IsCompleted() const {
    return results.size() == resolved_permissions_.size();
  }

  bool IsCompleted(PermissionType type) const {
    return resolved_permissions_.count(type) != 0;
  }

  void Cancel() { cancelled_ = true; }

  bool IsCancelled() const { return cancelled_; }

  std::vector<PermissionType> permissions;
  GURL requesting_origin;
  GURL embedding_origin;
  int render_process_id;
  int render_frame_id;
  RequestPermissionsCallback callback;
  std::vector<content::PermissionResult> results;

 private:
  std::map<PermissionType, size_t> permission_index_map_;
  std::set<PermissionType> resolved_permissions_;
  bool cancelled_;
};

AwPermissionManager::AwPermissionManager(
    const AwContextPermissionsDelegate& context_delegate)
    : context_delegate_(context_delegate),
      result_cache_(new LastRequestResultCache) {}

AwPermissionManager::~AwPermissionManager() {
  CancelPermissionRequests();
}

void AwPermissionManager::RequestPermissions(
    content::RenderFrameHost* render_frame_host,
    const content::PermissionRequestDescription& request_description,
    base::OnceCallback<void(const std::vector<content::PermissionResult>&)>
        callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  auto const& permissions = blink::PermissionDescriptorToPermissionTypes(
      request_description.permissions);
  if (permissions.empty()) {
    std::move(callback).Run(std::vector<content::PermissionResult>());
    return;
  }

  const GURL& embedding_origin = LastCommittedMainOrigin(render_frame_host);
  const GURL& requesting_origin = request_description.requesting_origin;

  auto pending_request = std::make_unique<PendingRequest>(
      permissions, requesting_origin, embedding_origin,
      GetRenderProcessID(render_frame_host),
      GetRenderFrameID(render_frame_host), std::move(callback));
  std::vector<bool> should_delegate_requests =
      std::vector<bool>(permissions.size(), true);
  for (size_t i = 0; i < permissions.size(); ++i) {
    for (PendingRequestsMap::Iterator<PendingRequest> it(&pending_requests_);
         !it.IsAtEnd(); it.Advance()) {
      if (it.GetCurrentValue()->HasPermissionType(permissions[i]) &&
          it.GetCurrentValue()->requesting_origin == requesting_origin) {
        if (it.GetCurrentValue()->IsCompleted(permissions[i])) {
          pending_request->SetPermissionResult(
              permissions[i],
              it.GetCurrentValue()->GetPermissionResult(permissions[i]));
        }
        should_delegate_requests[i] = false;
        break;
      }
    }
  }

  // Keep copy of pointer for performing further operations after ownership is
  // transferred to pending_requests_
  PendingRequest* pending_request_raw = pending_request.get();
  const int request_id = pending_requests_.Add(std::move(pending_request));

  AwBrowserPermissionRequestDelegate* delegate =
      GetDelegate(pending_request_raw->render_process_id,
                  pending_request_raw->render_frame_id);

  for (size_t i = 0; i < permissions.size(); ++i) {
    if (!should_delegate_requests[i])
      continue;

    if (!delegate) {
      DVLOG(0) << "Dropping permissions request for "
               << static_cast<int>(permissions[i]);
      pending_request_raw->SetPermissionResult(
          permissions[i], content::PermissionResult(PermissionStatus::DENIED));
      continue;
    }

    switch (permissions[i]) {
      case PermissionType::GEOLOCATION:
        delegate->RequestGeolocationPermission(
            pending_request_raw->requesting_origin,
            base::BindOnce(&OnRequestResponse, weak_ptr_factory_.GetWeakPtr(),
                           request_id, permissions[i]));
        break;
      case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
        delegate->RequestProtectedMediaIdentifierPermission(
            pending_request_raw->requesting_origin,
            base::BindOnce(&OnRequestResponse, weak_ptr_factory_.GetWeakPtr(),
                           request_id, permissions[i]));
        break;
      case PermissionType::MIDI_SYSEX:
        delegate->RequestMIDISysexPermission(
            pending_request_raw->requesting_origin,
            base::BindOnce(&OnRequestResponse, weak_ptr_factory_.GetWeakPtr(),
                           request_id, permissions[i]));
        break;
      case PermissionType::CLIPBOARD_SANITIZED_WRITE:
        // This is the permission for writing vetted data (such as plain text or
        // sanitized images) using the async clipboard API.
        // This permission type implies that user gesture is present, and as
        // such, it can be auto-granted to conform with Chrome logic.
        // Reading from the clipboard or writing
        // custom data is represented with the CLIPBOARD_READ_WRITE permission,
        // and that requires an explicit user approval, which is not implemented
        // yet. See crbug.com/1271620
        pending_request_raw->SetPermissionResult(
            permissions[i],
            content::PermissionResult(PermissionStatus::GRANTED));
        break;
      case PermissionType::AUDIO_CAPTURE:
      case PermissionType::VIDEO_CAPTURE:
      case PermissionType::NOTIFICATIONS:
      case PermissionType::DURABLE_STORAGE:
      case PermissionType::BACKGROUND_SYNC:
      case PermissionType::CLIPBOARD_READ_WRITE:
      case PermissionType::PAYMENT_HANDLER:
      case PermissionType::BACKGROUND_FETCH:
      case PermissionType::IDLE_DETECTION:
      case PermissionType::PERIODIC_BACKGROUND_SYNC:
      case PermissionType::NFC:
      case PermissionType::VR:
      case PermissionType::AR:
      case PermissionType::HAND_TRACKING:
      case PermissionType::CAMERA_PAN_TILT_ZOOM:
      case PermissionType::WINDOW_MANAGEMENT:
      case PermissionType::LOCAL_FONTS:
      case PermissionType::DISPLAY_CAPTURE:
      case PermissionType::CAPTURED_SURFACE_CONTROL:
      case PermissionType::SMART_CARD:
      case PermissionType::WEB_PRINTING:
      case PermissionType::SPEAKER_SELECTION:
      case PermissionType::KEYBOARD_LOCK:
      case PermissionType::POINTER_LOCK:
      case PermissionType::AUTOMATIC_FULLSCREEN:
      case PermissionType::WEB_APP_INSTALLATION:
      case PermissionType::STORAGE_ACCESS_GRANT:
      case PermissionType::TOP_LEVEL_STORAGE_ACCESS:
        NOTIMPLEMENTED() << "RequestPermissions is not implemented for "
                         << static_cast<int>(permissions[i]);
        pending_request_raw->SetPermissionResult(
            permissions[i],
            content::PermissionResult(PermissionStatus::DENIED));
        break;
      case PermissionType::MIDI:
      case PermissionType::SENSORS:
      case PermissionType::WAKE_LOCK_SCREEN:
        // PermissionType::SENSORS requests are always granted so that access
        // to device motion and device orientation data (and underlying
        // sensors) works in the WebView. SensorProviderImpl::GetSensor()
        // filters requests for other types of sensors.
        pending_request_raw->SetPermissionResult(
            permissions[i],
            content::PermissionResult(PermissionStatus::GRANTED));
        break;
      case PermissionType::WAKE_LOCK_SYSTEM:
        pending_request_raw->SetPermissionResult(
            permissions[i],
            content::PermissionResult(PermissionStatus::DENIED));
        break;
      case PermissionType::LOCAL_NETWORK_ACCESS:
        // PermissionType::LOCAL_NETWORK_ACCESS requests are always granted so
        // that local network requests in WebView work as-is. WebView is
        // currently out-of-scope for Local Network Access restrictions.
        pending_request_raw->SetPermissionResult(
            permissions[i],
            content::PermissionResult(PermissionStatus::GRANTED));
        break;
      case PermissionType::NUM:
        NOTREACHED() << "PermissionType::NUM was not expected here.";
    }
  }

  // If delegate resolve the permission synchronously, all requests could be
  // already resolved here.
  if (!pending_requests_.Lookup(request_id)) {
    return;
  }

  // If requests are resolved without calling delegate functions, e.g.
  // PermissionType::MIDI is permitted within the previous for-loop, all
  // requests could be already resolved, but still in the |pending_requests_|
  // without invoking the callback.
  if (pending_request_raw->IsCompleted()) {
    std::vector<content::PermissionResult> results =
        pending_request_raw->results;
    RequestPermissionsCallback completed_callback =
        std::move(pending_request_raw->callback);
    pending_requests_.Remove(request_id);
    std::move(completed_callback).Run(results);
  }
}

// static
void AwPermissionManager::OnRequestResponse(
    const base::WeakPtr<AwPermissionManager>& manager,
    int request_id,
    PermissionType permission,
    bool allowed) {
  // All delegate functions should be cancelled when the manager runs
  // destructor. Therefore |manager| should be always valid here.
  CHECK(manager);

  PermissionStatus status =
      allowed ? PermissionStatus::GRANTED : PermissionStatus::DENIED;
  PendingRequest* pending_request =
      manager->pending_requests_.Lookup(request_id);

  manager->result_cache_->SetResult(permission,
                                    pending_request->requesting_origin,
                                    pending_request->embedding_origin, status);

  std::vector<int> complete_request_ids;
  std::vector<std::pair<RequestPermissionsCallback,
                        std::vector<content::PermissionResult>>>
      complete_request_pairs;
  for (PendingRequestsMap::Iterator<PendingRequest> it(
           &manager->pending_requests_);
       !it.IsAtEnd(); it.Advance()) {
    if (!it.GetCurrentValue()->HasPermissionType(permission) ||
        it.GetCurrentValue()->requesting_origin !=
            pending_request->requesting_origin) {
      continue;
    }
    it.GetCurrentValue()->SetPermissionResult(
        permission, content::PermissionResult(status));
    if (it.GetCurrentValue()->IsCompleted()) {
      complete_request_ids.push_back(it.GetCurrentKey());
      if (!it.GetCurrentValue()->IsCancelled()) {
        complete_request_pairs.emplace_back(
            std::move(it.GetCurrentValue()->callback),
            std::move(it.GetCurrentValue()->results));
      }
    }
  }
  for (auto id : complete_request_ids)
    manager->pending_requests_.Remove(id);
  for (auto& pair : complete_request_pairs)
    std::move(pair.first).Run(pair.second);
}

void AwPermissionManager::ResetPermission(PermissionType permission,
                                          const GURL& requesting_origin,
                                          const GURL& embedding_origin) {
  result_cache_->ClearResult(permission, requesting_origin, embedding_origin);
}

void AwPermissionManager::RequestPermissionsFromCurrentDocument(
    content::RenderFrameHost* render_frame_host,
    const content::PermissionRequestDescription& request_description,
    base::OnceCallback<void(const std::vector<content::PermissionResult>&)>
        callback) {
  RequestPermissions(render_frame_host, request_description,
                     std::move(callback));
}

PermissionStatus AwPermissionManager::GetPermissionStatus(
    const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
    const GURL& requesting_origin,
    const GURL& embedding_origin) {
  return GetPermissionStatusInternal(permission_descriptor, requesting_origin,
                                     embedding_origin,
                                     /*web_contents=*/nullptr);
}

PermissionStatus AwPermissionManager::GetPermissionStatusInternal(
    const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
    const GURL& requesting_origin,
    const GURL& embedding_origin,
    content::WebContents* web_contents) {
  const blink::PermissionType permission_type =
      blink::PermissionDescriptorToPermissionType(permission_descriptor);
  switch (permission_type) {
    case blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
      return result_cache_->GetResult(permission_descriptor, requesting_origin,
                                      embedding_origin);

    case blink::PermissionType::GEOLOCATION:
      return GetGeolocationPermission(requesting_origin, web_contents);

    case blink::PermissionType::CLIPBOARD_SANITIZED_WRITE:
    case blink::PermissionType::MIDI:
    case blink::PermissionType::SENSORS:
    case blink::PermissionType::LOCAL_NETWORK_ACCESS:
      // These permissions are auto-granted by WebView.
      return PermissionStatus::GRANTED;

    case blink::PermissionType::MIDI_SYSEX:
    case blink::PermissionType::AUDIO_CAPTURE:
    case blink::PermissionType::VIDEO_CAPTURE:
      // These permissions are always forwarded to the app to handle.
      return PermissionStatus::ASK;

    case blink::PermissionType::AR:
    case blink::PermissionType::AUTOMATIC_FULLSCREEN:
    case blink::PermissionType::BACKGROUND_FETCH:
    case blink::PermissionType::BACKGROUND_SYNC:
    case blink::PermissionType::CAMERA_PAN_TILT_ZOOM:
    case blink::PermissionType::CAPTURED_SURFACE_CONTROL:
    case blink::PermissionType::CLIPBOARD_READ_WRITE:
    case blink::PermissionType::DISPLAY_CAPTURE:
    case blink::PermissionType::DURABLE_STORAGE:
    case blink::PermissionType::HAND_TRACKING:
    case blink::PermissionType::IDLE_DETECTION:
    case blink::PermissionType::KEYBOARD_LOCK:
    case blink::PermissionType::LOCAL_FONTS:
    case blink::PermissionType::NFC:
    case blink::PermissionType::NOTIFICATIONS:
    case blink::PermissionType::NUM:
    case blink::PermissionType::PAYMENT_HANDLER:
    case blink::PermissionType::PERIODIC_BACKGROUND_SYNC:
    case blink::PermissionType::POINTER_LOCK:
    case blink::PermissionType::SMART_CARD:
    case blink::PermissionType::SPEAKER_SELECTION:
    case blink::PermissionType::VR:
    case blink::PermissionType::WAKE_LOCK_SCREEN:
    case blink::PermissionType::WAKE_LOCK_SYSTEM:
    case blink::PermissionType::WEB_APP_INSTALLATION:
    case blink::PermissionType::WEB_PRINTING:
    case blink::PermissionType::WINDOW_MANAGEMENT:
    case blink::PermissionType::STORAGE_ACCESS_GRANT:
    case blink::PermissionType::TOP_LEVEL_STORAGE_ACCESS:
      return PermissionStatus::DENIED;
  }
  NOTREACHED() << "Unhandled permission type: "
               << static_cast<int>(permission_type);
}

PermissionStatus AwPermissionManager::GetGeolocationPermission(
    const GURL& requesting_origin,
    content::WebContents* web_contents) {
  if (!web_contents) {
    // If we don't have a web_contents, we can't determine if we have
    // permission.
    return PermissionStatus::ASK;
  }

  AwSettings* settings = AwSettings::FromWebContents(web_contents);
  if (!settings) {
    // If we don't have a settings, we can't determine if we have
    // permission.
    return PermissionStatus::ASK;
  }

  if (!settings->geolocation_enabled()) {
    return PermissionStatus::DENIED;
  }

  return context_delegate_->GetGeolocationPermission(requesting_origin);
}

content::PermissionResult
AwPermissionManager::GetPermissionResultForOriginWithoutContext(
    const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
    const url::Origin& requesting_origin,
    const url::Origin& embedding_origin) {
  blink::mojom::PermissionStatus status =
      GetPermissionStatus(permission_descriptor, requesting_origin.GetURL(),
                          embedding_origin.GetURL());

  return content::PermissionResult(status);
}

content::PermissionResult
AwPermissionManager::GetPermissionResultForCurrentDocument(
    const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
    content::RenderFrameHost* render_frame_host,
    bool should_include_device_status) {
  content::WebContents* web_contents =
      content::WebContents::FromRenderFrameHost(render_frame_host);
  return content::PermissionResult(GetPermissionStatusInternal(
      permission_descriptor,
      permissions::PermissionUtil::GetLastCommittedOriginAsURL(
          render_frame_host),
      permissions::PermissionUtil::GetLastCommittedOriginAsURL(
          render_frame_host->GetMainFrame()),
      web_contents));
}

content::PermissionResult AwPermissionManager::GetPermissionResultForWorker(
    const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
    content::RenderProcessHost* render_process_host,
    const GURL& worker_origin) {
  return content::PermissionResult(
      GetPermissionStatus(permission_descriptor, worker_origin, worker_origin));
}

content::PermissionResult
AwPermissionManager::GetPermissionResultForEmbeddedRequester(
    const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
    content::RenderFrameHost* render_frame_host,
    const url::Origin& requesting_origin) {
  return content::PermissionResult(GetPermissionStatusInternal(
      permission_descriptor, requesting_origin.GetURL(),
      permissions::PermissionUtil::GetLastCommittedOriginAsURL(
          render_frame_host->GetMainFrame()),
      content::WebContents::FromRenderFrameHost(render_frame_host)));
}

void AwPermissionManager::CancelPermissionRequest(int request_id) {
  PendingRequest* pending_request = pending_requests_.Lookup(request_id);
  if (!pending_request || pending_request->IsCancelled())
    return;
  pending_request->Cancel();

  const GURL& embedding_origin = pending_request->embedding_origin;
  const GURL& requesting_origin = pending_request->requesting_origin;
  for (auto permission : pending_request->permissions)
    result_cache_->ClearResult(permission, requesting_origin, embedding_origin);

  AwBrowserPermissionRequestDelegate* delegate = GetDelegate(
      pending_request->render_process_id, pending_request->render_frame_id);

  for (auto permission : pending_request->permissions) {
    // If the permission was already resolved, we do not need to cancel it.
    if (pending_request->IsCompleted(permission))
      continue;

    // If another pending_request waits for the same permission being resolved,
    // we should not cancel the delegate's request.
    bool should_not_cancel_ = false;
    for (PendingRequestsMap::Iterator<PendingRequest> it(&pending_requests_);
         !it.IsAtEnd(); it.Advance()) {
      if (it.GetCurrentValue() != pending_request &&
          it.GetCurrentValue()->HasPermissionType(permission) &&
          it.GetCurrentValue()->requesting_origin == requesting_origin &&
          !it.GetCurrentValue()->IsCompleted(permission)) {
        should_not_cancel_ = true;
        break;
      }
    }
    if (should_not_cancel_)
      continue;

    switch (permission) {
      case PermissionType::GEOLOCATION:
        if (delegate)
          delegate->CancelGeolocationPermissionRequests(requesting_origin);
        break;
      case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
        if (delegate)
          delegate->CancelProtectedMediaIdentifierPermissionRequests(
              requesting_origin);
        break;
      case PermissionType::MIDI_SYSEX:
        if (delegate)
          delegate->CancelMIDISysexPermissionRequests(requesting_origin);
        break;
      case PermissionType::NOTIFICATIONS:
      case PermissionType::DURABLE_STORAGE:
      case PermissionType::AUDIO_CAPTURE:
      case PermissionType::VIDEO_CAPTURE:
      case PermissionType::BACKGROUND_SYNC:
      case PermissionType::CLIPBOARD_READ_WRITE:
      case PermissionType::CLIPBOARD_SANITIZED_WRITE:
      case PermissionType::PAYMENT_HANDLER:
      case PermissionType::BACKGROUND_FETCH:
      case PermissionType::IDLE_DETECTION:
      case PermissionType::PERIODIC_BACKGROUND_SYNC:
      case PermissionType::NFC:
      case PermissionType::HAND_TRACKING:
      case PermissionType::VR:
      case PermissionType::AR:
      case PermissionType::STORAGE_ACCESS_GRANT:
      case PermissionType::TOP_LEVEL_STORAGE_ACCESS:
      case PermissionType::CAMERA_PAN_TILT_ZOOM:
      case PermissionType::WINDOW_MANAGEMENT:
      case PermissionType::LOCAL_FONTS:
      case PermissionType::DISPLAY_CAPTURE:
      case PermissionType::CAPTURED_SURFACE_CONTROL:
      case PermissionType::SMART_CARD:
      case PermissionType::WEB_PRINTING:
      case PermissionType::SPEAKER_SELECTION:
      case PermissionType::KEYBOARD_LOCK:
      case PermissionType::POINTER_LOCK:
      case PermissionType::AUTOMATIC_FULLSCREEN:
      case PermissionType::WEB_APP_INSTALLATION:
        NOTIMPLEMENTED() << "CancelPermission not implemented for "
                         << static_cast<int>(permission);
        break;
      case PermissionType::MIDI:
      case PermissionType::SENSORS:
      case PermissionType::WAKE_LOCK_SCREEN:
      case PermissionType::WAKE_LOCK_SYSTEM:
      case PermissionType::LOCAL_NETWORK_ACCESS:
        // There is nothing to cancel so this is simply ignored.
        break;
      case PermissionType::NUM:
        NOTREACHED() << "PermissionType::NUM was not expected here.";
    }
    pending_request->SetPermissionResult(
        permission, content::PermissionResult(PermissionStatus::DENIED));
  }

  // If there are still active requests, we should not remove request_id here,
  // but just do not invoke a relevant callback when the request is resolved in
  // OnRequestResponse().
  if (pending_request->IsCompleted())
    pending_requests_.Remove(request_id);
}

void AwPermissionManager::CancelPermissionRequests() {
  std::vector<int> request_ids;
  for (PendingRequestsMap::Iterator<PendingRequest> it(&pending_requests_);
       !it.IsAtEnd(); it.Advance()) {
    request_ids.push_back(it.GetCurrentKey());
  }
  for (auto request_id : request_ids)
    CancelPermissionRequest(request_id);
  DCHECK(pending_requests_.IsEmpty());
}

void AwPermissionManager::SetOriginCanReadEnumerateDevicesAudioLabels(
    const url::Origin& origin,
    bool audio) {
  auto it = enumerate_devices_labels_cache_.find(origin);
  if (it == enumerate_devices_labels_cache_.end()) {
    enumerate_devices_labels_cache_[origin] = std::make_pair(audio, false);
  } else {
    it->second.first = audio;
  }
}

void AwPermissionManager::SetOriginCanReadEnumerateDevicesVideoLabels(
    const url::Origin& origin,
    bool video) {
  auto it = enumerate_devices_labels_cache_.find(origin);
  if (it == enumerate_devices_labels_cache_.end())
    enumerate_devices_labels_cache_[origin] = std::make_pair(false, video);
  else
    it->second.second = video;
}

bool AwPermissionManager::ShouldShowEnumerateDevicesAudioLabels(
    const url::Origin& origin) {
  auto it = enumerate_devices_labels_cache_.find(origin);
  if (it == enumerate_devices_labels_cache_.end())
    return false;
  return it->second.first;
}

bool AwPermissionManager::ShouldShowEnumerateDevicesVideoLabels(
    const url::Origin& origin) {
  auto it = enumerate_devices_labels_cache_.find(origin);
  if (it == enumerate_devices_labels_cache_.end())
    return false;
  return it->second.second;
}

void AwPermissionManager::ClearEnumerateDevicesCachedPermission(
    const url::Origin& origin,
    bool remove_audio,
    bool remove_video) {
  auto it = enumerate_devices_labels_cache_.find(origin);
  if (it == enumerate_devices_labels_cache_.end())
    return;
  else if (remove_audio && remove_video) {
    enumerate_devices_labels_cache_.erase(origin);
  } else if (remove_audio) {
    it->second.first = false;
  } else if (remove_video) {
    it->second.second = false;
  }
}

int AwPermissionManager::GetRenderProcessID(
    content::RenderFrameHost* render_frame_host) {
  return render_frame_host->GetProcess()->GetDeprecatedID();
}

int AwPermissionManager::GetRenderFrameID(
    content::RenderFrameHost* render_frame_host) {
  return render_frame_host->GetRoutingID();
}

GURL AwPermissionManager::LastCommittedMainOrigin(
    content::RenderFrameHost* render_frame_host) {
  return permissions::PermissionUtil::GetLastCommittedOriginAsURL(
      render_frame_host->GetMainFrame());
}

AwBrowserPermissionRequestDelegate* AwPermissionManager::GetDelegate(
    int render_process_id,
    int render_frame_id) {
  return AwBrowserPermissionRequestDelegate::FromID(render_process_id,
                                                    render_frame_id);
}

}  // namespace android_webview