mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	API versioning requires that enumerations end with a count value (`*_NUM_VALUES`) and structs begin with a size value (`size_t size`). Wrapper templates are updated to support structs with different size values indicating different versions. To test: Run `ceftests --gtest_filter=ApiVersionTest.StructVersion*`
		
			
				
	
	
		
			325 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			325 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2022 The Chromium Embedded Framework Authors. Portions copyright
 | 
						|
// 2016 The Chromium Authors. All rights reserved. Use of this source code is
 | 
						|
// governed by a BSD-style license that can be found in the LICENSE file.
 | 
						|
 | 
						|
#include "cef/libcef/browser/permission_prompt.h"
 | 
						|
 | 
						|
#include "base/logging.h"
 | 
						|
#include "base/memory/weak_ptr.h"
 | 
						|
#include "base/notreached.h"
 | 
						|
#include "cef/libcef/browser/browser_host_base.h"
 | 
						|
#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
 | 
						|
 | 
						|
namespace permission_prompt {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
uint64_t g_next_prompt_id = 0;
 | 
						|
 | 
						|
using DelegateCallback =
 | 
						|
    base::OnceCallback<void(cef_permission_request_result_t)>;
 | 
						|
 | 
						|
class CefPermissionPromptCallbackImpl : public CefPermissionPromptCallback {
 | 
						|
 public:
 | 
						|
  using CallbackType = base::OnceCallback<void(cef_permission_request_result_t,
 | 
						|
                                               bool /*notify_delegate*/)>;
 | 
						|
 | 
						|
  explicit CefPermissionPromptCallbackImpl(CallbackType&& callback)
 | 
						|
      : callback_(std::move(callback)) {}
 | 
						|
 | 
						|
  CefPermissionPromptCallbackImpl(const CefPermissionPromptCallbackImpl&) =
 | 
						|
      delete;
 | 
						|
  CefPermissionPromptCallbackImpl& operator=(
 | 
						|
      const CefPermissionPromptCallbackImpl&) = delete;
 | 
						|
 | 
						|
  // Don't need to execute the callback in this destructor because this object
 | 
						|
  // will always be kept alive until after the CefPermissionPrompt is destroyed,
 | 
						|
  // and that object will disconnect/execute the callback in its destructor.
 | 
						|
  ~CefPermissionPromptCallbackImpl() override = default;
 | 
						|
 | 
						|
  void Continue(cef_permission_request_result_t result) override {
 | 
						|
    if (CEF_CURRENTLY_ON_UIT()) {
 | 
						|
      if (!callback_.is_null()) {
 | 
						|
        auto callback = base::BindOnce(std::move(callback_), result,
 | 
						|
                                       /*notify_delegate=*/true);
 | 
						|
        if (safe_to_run_sync_) {
 | 
						|
          std::move(callback).Run();
 | 
						|
        } else {
 | 
						|
          CEF_POST_TASK(CEF_UIT, std::move(callback));
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      CEF_POST_TASK(CEF_UIT,
 | 
						|
                    base::BindOnce(&CefPermissionPromptCallbackImpl::Continue,
 | 
						|
                                   this, result));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  [[nodiscard]] CallbackType Disconnect() { return std::move(callback_); }
 | 
						|
  bool IsDisconnected() const { return callback_.is_null(); }
 | 
						|
 | 
						|
  void MarkSafeToRunSync() { safe_to_run_sync_ = true; }
 | 
						|
 | 
						|
 private:
 | 
						|
  // Callback execution from inside CreatePermissionPromptImpl must be async,
 | 
						|
  // otherwise PermissionRequestManager state will be incorrect.
 | 
						|
  bool safe_to_run_sync_ = false;
 | 
						|
 | 
						|
  CallbackType callback_;
 | 
						|
 | 
						|
  IMPLEMENT_REFCOUNTING(CefPermissionPromptCallbackImpl);
 | 
						|
};
 | 
						|
 | 
						|
// Implementation based on PermissionPromptAndroid.
 | 
						|
class CefPermissionPrompt : public permissions::PermissionPrompt {
 | 
						|
 public:
 | 
						|
  explicit CefPermissionPrompt(Delegate* delegate) : delegate_(delegate) {
 | 
						|
    DCHECK(delegate_);
 | 
						|
  }
 | 
						|
 | 
						|
  CefPermissionPrompt(const CefPermissionPrompt&) = delete;
 | 
						|
  CefPermissionPrompt& operator=(const CefPermissionPrompt&) = delete;
 | 
						|
 | 
						|
  // Expect to be destroyed (and the UI needs to go) when:
 | 
						|
  // 1. A navigation happens, tab/webcontents is being closed; with the current
 | 
						|
  //    GetTabSwitchingBehavior() implementation, this instance survives the tab
 | 
						|
  //    being backgrounded.
 | 
						|
  // 2. The permission request is resolved (accept, deny, dismiss).
 | 
						|
  // 3. A higher priority request comes in.
 | 
						|
  ~CefPermissionPrompt() override {
 | 
						|
    CEF_REQUIRE_UIT();
 | 
						|
    if (callback_) {
 | 
						|
      // If the callback is non-null at this point then we still need to execute
 | 
						|
      // it in order to notify the client.
 | 
						|
      auto callback = callback_->Disconnect();
 | 
						|
      if (!callback.is_null()) {
 | 
						|
        std::move(callback).Run(CEF_PERMISSION_RESULT_IGNORE,
 | 
						|
                                /*notify_delegate=*/false);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Used to associate the client callback when OnShowPermissionPrompt is
 | 
						|
  // handled.
 | 
						|
  void AttachClientCallback(
 | 
						|
      CefRefPtr<CefPermissionPromptCallbackImpl> callback) {
 | 
						|
    DCHECK(callback);
 | 
						|
    callback_ = callback;
 | 
						|
    callback_->MarkSafeToRunSync();
 | 
						|
  }
 | 
						|
 | 
						|
  // Used to tie Delegate access to this object's lifespan.
 | 
						|
  DelegateCallback MakeDelegateCallback() {
 | 
						|
    return base::BindOnce(&CefPermissionPrompt::NotifyDelegate,
 | 
						|
                          weak_ptr_factory_.GetWeakPtr());
 | 
						|
  }
 | 
						|
 | 
						|
  // PermissionPrompt methods:
 | 
						|
  bool UpdateAnchor() override { return true; }
 | 
						|
  TabSwitchingBehavior GetTabSwitchingBehavior() override {
 | 
						|
    return TabSwitchingBehavior::kKeepPromptAlive;
 | 
						|
  }
 | 
						|
  permissions::PermissionPromptDisposition GetPromptDisposition()
 | 
						|
      const override {
 | 
						|
    return permissions::PermissionPromptDisposition::CUSTOM_MODAL_DIALOG;
 | 
						|
  }
 | 
						|
  std::optional<gfx::Rect> GetViewBoundsInScreen() const override {
 | 
						|
    return std::nullopt;
 | 
						|
  }
 | 
						|
  bool ShouldFinalizeRequestAfterDecided() const override { return true; }
 | 
						|
  std::vector<permissions::ElementAnchoredBubbleVariant> GetPromptVariants()
 | 
						|
      const override {
 | 
						|
    return {};
 | 
						|
  }
 | 
						|
  std::optional<permissions::feature_params::PermissionElementPromptPosition>
 | 
						|
  GetPromptPosition() const override {
 | 
						|
    return std::nullopt;
 | 
						|
  }
 | 
						|
  bool IsAskPrompt() const override { return true; }
 | 
						|
 | 
						|
 private:
 | 
						|
  // We don't expose AcceptThisTime() because it's a special case for
 | 
						|
  // Geolocation (see DCHECK in PrefProvider::SetWebsiteSetting).
 | 
						|
  void NotifyDelegate(cef_permission_request_result_t result) {
 | 
						|
    switch (result) {
 | 
						|
      case CEF_PERMISSION_RESULT_ACCEPT:
 | 
						|
        delegate_->Accept();
 | 
						|
        break;
 | 
						|
      case CEF_PERMISSION_RESULT_DENY:
 | 
						|
        delegate_->Deny();
 | 
						|
        break;
 | 
						|
      case CEF_PERMISSION_RESULT_DISMISS:
 | 
						|
        delegate_->Dismiss();
 | 
						|
        break;
 | 
						|
      case CEF_PERMISSION_RESULT_IGNORE:
 | 
						|
        delegate_->Ignore();
 | 
						|
        break;
 | 
						|
      case CEF_PERMISSION_RESULT_NUM_VALUES:
 | 
						|
        DCHECK(false);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // |delegate_| is the PermissionRequestManager, which owns this object.
 | 
						|
  const raw_ptr<Delegate> delegate_;
 | 
						|
 | 
						|
  CefRefPtr<CefPermissionPromptCallbackImpl> callback_;
 | 
						|
 | 
						|
  base::WeakPtrFactory<CefPermissionPrompt> weak_ptr_factory_{this};
 | 
						|
};
 | 
						|
 | 
						|
// |notify_delegate| will be false if called from the CefPermissionPrompt
 | 
						|
// destructor.
 | 
						|
void ExecuteResult(CefRefPtr<CefBrowserHostBase> browser,
 | 
						|
                   uint64_t prompt_id,
 | 
						|
                   DelegateCallback delegate_callback,
 | 
						|
                   cef_permission_request_result_t result,
 | 
						|
                   bool notify_delegate) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
 | 
						|
  if (auto client = browser->GetClient()) {
 | 
						|
    if (auto handler = client->GetPermissionHandler()) {
 | 
						|
      handler->OnDismissPermissionPrompt(browser, prompt_id, result);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (notify_delegate) {
 | 
						|
    // Will be a no-op if this executes after the CefPermissionPrompt was
 | 
						|
    // destroyed.
 | 
						|
    std::move(delegate_callback).Run(result);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
cef_permission_request_types_t GetCefRequestType(
 | 
						|
    permissions::RequestType type) {
 | 
						|
  switch (type) {
 | 
						|
    case permissions::RequestType::kArSession:
 | 
						|
      return CEF_PERMISSION_TYPE_AR_SESSION;
 | 
						|
    case permissions::RequestType::kCameraPanTiltZoom:
 | 
						|
      return CEF_PERMISSION_TYPE_CAMERA_PAN_TILT_ZOOM;
 | 
						|
    case permissions::RequestType::kCameraStream:
 | 
						|
      return CEF_PERMISSION_TYPE_CAMERA_STREAM;
 | 
						|
    case permissions::RequestType::kCapturedSurfaceControl:
 | 
						|
      return CEF_PERMISSION_TYPE_CAPTURED_SURFACE_CONTROL;
 | 
						|
    case permissions::RequestType::kClipboard:
 | 
						|
      return CEF_PERMISSION_TYPE_CLIPBOARD;
 | 
						|
    case permissions::RequestType::kDiskQuota:
 | 
						|
      return CEF_PERMISSION_TYPE_DISK_QUOTA;
 | 
						|
    case permissions::RequestType::kLocalFonts:
 | 
						|
      return CEF_PERMISSION_TYPE_LOCAL_FONTS;
 | 
						|
    case permissions::RequestType::kGeolocation:
 | 
						|
      return CEF_PERMISSION_TYPE_GEOLOCATION;
 | 
						|
    case permissions::RequestType::kHandTracking:
 | 
						|
      return CEF_PERMISSION_TYPE_HAND_TRACKING;
 | 
						|
    case permissions::RequestType::kIdentityProvider:
 | 
						|
      return CEF_PERMISSION_TYPE_IDENTITY_PROVIDER;
 | 
						|
    case permissions::RequestType::kIdleDetection:
 | 
						|
      return CEF_PERMISSION_TYPE_IDLE_DETECTION;
 | 
						|
    case permissions::RequestType::kMicStream:
 | 
						|
      return CEF_PERMISSION_TYPE_MIC_STREAM;
 | 
						|
    case permissions::RequestType::kMidiSysex:
 | 
						|
      return CEF_PERMISSION_TYPE_MIDI_SYSEX;
 | 
						|
    case permissions::RequestType::kMultipleDownloads:
 | 
						|
      return CEF_PERMISSION_TYPE_MULTIPLE_DOWNLOADS;
 | 
						|
    case permissions::RequestType::kNotifications:
 | 
						|
      return CEF_PERMISSION_TYPE_NOTIFICATIONS;
 | 
						|
    case permissions::RequestType::kKeyboardLock:
 | 
						|
      return CEF_PERMISSION_TYPE_KEYBOARD_LOCK;
 | 
						|
    case permissions::RequestType::kPointerLock:
 | 
						|
      return CEF_PERMISSION_TYPE_POINTER_LOCK;
 | 
						|
#if BUILDFLAG(IS_WIN)
 | 
						|
    case permissions::RequestType::kProtectedMediaIdentifier:
 | 
						|
      return CEF_PERMISSION_TYPE_PROTECTED_MEDIA_IDENTIFIER;
 | 
						|
#endif
 | 
						|
    case permissions::RequestType::kRegisterProtocolHandler:
 | 
						|
      return CEF_PERMISSION_TYPE_REGISTER_PROTOCOL_HANDLER;
 | 
						|
    case permissions::RequestType::kStorageAccess:
 | 
						|
      return CEF_PERMISSION_TYPE_STORAGE_ACCESS;
 | 
						|
    case permissions::RequestType::kTopLevelStorageAccess:
 | 
						|
      return CEF_PERMISSION_TYPE_TOP_LEVEL_STORAGE_ACCESS;
 | 
						|
    case permissions::RequestType::kVrSession:
 | 
						|
      return CEF_PERMISSION_TYPE_VR_SESSION;
 | 
						|
    case permissions::RequestType::kWebAppInstallation:
 | 
						|
      return CEF_PERMISSION_TYPE_WEB_APP_INSTALLATION;
 | 
						|
    case permissions::RequestType::kWindowManagement:
 | 
						|
      return CEF_PERMISSION_TYPE_WINDOW_MANAGEMENT;
 | 
						|
    case permissions::RequestType::kFileSystemAccess:
 | 
						|
      return CEF_PERMISSION_TYPE_FILE_SYSTEM_ACCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  DCHECK(false);
 | 
						|
  return CEF_PERMISSION_TYPE_NONE;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t GetRequestedPermissions(
 | 
						|
    permissions::PermissionPrompt::Delegate* delegate) {
 | 
						|
  uint32_t permissions = CEF_PERMISSION_TYPE_NONE;
 | 
						|
  for (const auto& request : delegate->Requests()) {
 | 
						|
    permissions |= GetCefRequestType(request->request_type());
 | 
						|
  }
 | 
						|
  return permissions;
 | 
						|
}
 | 
						|
 | 
						|
std::unique_ptr<permissions::PermissionPrompt> CreatePermissionPromptImpl(
 | 
						|
    content::WebContents* web_contents,
 | 
						|
    permissions::PermissionPrompt::Delegate* delegate,
 | 
						|
    bool* default_handling) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
 | 
						|
  bool is_alloy_style = false;
 | 
						|
 | 
						|
  if (auto browser = CefBrowserHostBase::GetBrowserForContents(web_contents)) {
 | 
						|
    is_alloy_style = browser->IsAlloyStyle();
 | 
						|
 | 
						|
    if (auto client = browser->GetClient()) {
 | 
						|
      if (auto handler = client->GetPermissionHandler()) {
 | 
						|
        auto permission_prompt =
 | 
						|
            std::make_unique<CefPermissionPrompt>(delegate);
 | 
						|
 | 
						|
        const auto prompt_id = ++g_next_prompt_id;
 | 
						|
        auto callback =
 | 
						|
            base::BindOnce(&ExecuteResult, browser, prompt_id,
 | 
						|
                           permission_prompt->MakeDelegateCallback());
 | 
						|
 | 
						|
        CefRefPtr<CefPermissionPromptCallbackImpl> callbackImpl(
 | 
						|
            new CefPermissionPromptCallbackImpl(std::move(callback)));
 | 
						|
        bool handled = handler->OnShowPermissionPrompt(
 | 
						|
            browser, prompt_id, delegate->GetRequestingOrigin().spec(),
 | 
						|
            GetRequestedPermissions(delegate), callbackImpl.get());
 | 
						|
 | 
						|
        if (callbackImpl->IsDisconnected() || handled) {
 | 
						|
          // Callback execution will be async.
 | 
						|
          LOG_IF(ERROR, !handled)
 | 
						|
              << "Should return true from OnShowPermissionPrompt when "
 | 
						|
                 "executing the callback";
 | 
						|
          *default_handling = false;
 | 
						|
          permission_prompt->AttachClientCallback(callbackImpl);
 | 
						|
          return permission_prompt;
 | 
						|
        } else {
 | 
						|
          // Proceed with default handling. |callback| is discarded without
 | 
						|
          // execution.
 | 
						|
          callback = callbackImpl->Disconnect();
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (is_alloy_style) {
 | 
						|
    LOG(INFO) << "Implement OnShowPermissionPrompt to override default IGNORE "
 | 
						|
                 "handling of permission prompts.";
 | 
						|
  }
 | 
						|
 | 
						|
  // Proceed with default handling. This will be IGNORE with Alloy style and
 | 
						|
  // default UI prompt with Chrome style.
 | 
						|
  *default_handling = true;
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
void RegisterCreateCallback() {
 | 
						|
  SetCreatePermissionPromptFunction(&CreatePermissionPromptImpl);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace permission_prompt
 |