| // Copyright 2012 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/pepper/pepper_device_enumeration_host_helper.h" |
| |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/location.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "ipc/ipc_message.h" |
| #include "ppapi/c/pp_errors.h" |
| #include "ppapi/host/dispatch_host_message.h" |
| #include "ppapi/host/host_message_context.h" |
| #include "ppapi/host/ppapi_host.h" |
| #include "ppapi/host/resource_host.h" |
| #include "ppapi/proxy/ppapi_messages.h" |
| #include "ppapi/shared_impl/ppb_device_ref_shared.h" |
| |
| using ppapi::host::HostMessageContext; |
| |
| namespace content { |
| |
| // Makes sure that StopEnumerateDevices() is called for each EnumerateDevices(). |
| class PepperDeviceEnumerationHostHelper::ScopedEnumerationRequest final { |
| public: |
| // |owner| must outlive this object. |
| ScopedEnumerationRequest(PepperDeviceEnumerationHostHelper* owner, |
| Delegate::DevicesOnceCallback callback) |
| : callback_(std::move(callback)), requested_(false), sync_call_(false) { |
| if (!owner->delegate_) { |
| // If no delegate, return an empty list of devices. |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &ScopedEnumerationRequest::EnumerateDevicesCallbackBody, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::vector<ppapi::DeviceRefData>())); |
| return; |
| } |
| |
| requested_ = true; |
| |
| // Note that the callback passed into |
| // PepperDeviceEnumerationHostHelper::Delegate::EnumerateDevices() may be |
| // called synchronously. In that case, |callback| may destroy this |
| // object. So we don't pass in |callback| directly. Instead, we use |
| // EnumerateDevicesCallbackBody() to ensure that we always call |callback| |
| // asynchronously. |
| sync_call_ = true; |
| owner->delegate_->EnumerateDevices( |
| owner->device_type_, |
| base::BindOnce(&ScopedEnumerationRequest::EnumerateDevicesCallbackBody, |
| weak_ptr_factory_.GetWeakPtr())); |
| sync_call_ = false; |
| } |
| |
| ScopedEnumerationRequest(const ScopedEnumerationRequest&) = delete; |
| ScopedEnumerationRequest& operator=(const ScopedEnumerationRequest&) = delete; |
| |
| bool requested() const { return requested_; } |
| |
| private: |
| void EnumerateDevicesCallbackBody( |
| const std::vector<ppapi::DeviceRefData>& devices) { |
| if (sync_call_) { |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &ScopedEnumerationRequest::EnumerateDevicesCallbackBody, |
| weak_ptr_factory_.GetWeakPtr(), devices)); |
| } else { |
| std::move(callback_).Run(devices); |
| // This object may have been destroyed at this point. |
| } |
| } |
| |
| PepperDeviceEnumerationHostHelper::Delegate::DevicesOnceCallback callback_; |
| bool requested_; |
| bool sync_call_; |
| base::WeakPtrFactory<ScopedEnumerationRequest> weak_ptr_factory_{this}; |
| }; |
| |
| // Makes sure that StopMonitoringDevices() is called for each |
| // StartMonitoringDevices(). |
| class PepperDeviceEnumerationHostHelper::ScopedMonitoringRequest { |
| public: |
| // |owner| must outlive this object. |
| ScopedMonitoringRequest(PepperDeviceEnumerationHostHelper* owner, |
| Delegate::DevicesCallback callback) |
| : owner_(owner), |
| callback_(std::move(callback)), |
| requested_(false), |
| subscription_id_(0u) { |
| DCHECK(owner_); |
| if (!owner->delegate_) { |
| return; |
| } |
| |
| requested_ = true; |
| |
| // |callback| is never called synchronously by StartMonitoringDevices(), |
| // so it is OK to pass it directly, even if |callback| destroys |this|. |
| subscription_id_ = owner_->delegate_->StartMonitoringDevices( |
| owner_->device_type_, callback_); |
| } |
| |
| ScopedMonitoringRequest(const ScopedMonitoringRequest&) = delete; |
| ScopedMonitoringRequest& operator=(const ScopedMonitoringRequest&) = delete; |
| |
| ~ScopedMonitoringRequest() { |
| if (requested_ && owner_->delegate_) { |
| owner_->delegate_->StopMonitoringDevices(owner_->device_type_, |
| subscription_id_); |
| } |
| } |
| |
| bool requested() const { return requested_; } |
| |
| private: |
| const raw_ptr<PepperDeviceEnumerationHostHelper> owner_; |
| PepperDeviceEnumerationHostHelper::Delegate::DevicesCallback callback_; |
| bool requested_; |
| size_t subscription_id_; |
| }; |
| |
| PepperDeviceEnumerationHostHelper::PepperDeviceEnumerationHostHelper( |
| ppapi::host::ResourceHost* resource_host, |
| base::WeakPtr<Delegate> delegate, |
| PP_DeviceType_Dev device_type, |
| const GURL& document_url) |
| : resource_host_(resource_host), |
| delegate_(delegate), |
| device_type_(device_type) {} |
| |
| PepperDeviceEnumerationHostHelper::~PepperDeviceEnumerationHostHelper() {} |
| |
| bool PepperDeviceEnumerationHostHelper::HandleResourceMessage( |
| const IPC::Message& msg, |
| HostMessageContext* context, |
| int32_t* result) { |
| bool return_value = false; |
| *result = InternalHandleResourceMessage(msg, context, &return_value); |
| return return_value; |
| } |
| |
| int32_t PepperDeviceEnumerationHostHelper::InternalHandleResourceMessage( |
| const IPC::Message& msg, |
| HostMessageContext* context, |
| bool* handled) { |
| *handled = true; |
| PPAPI_BEGIN_MESSAGE_MAP(PepperDeviceEnumerationHostHelper, msg) |
| PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( |
| PpapiHostMsg_DeviceEnumeration_EnumerateDevices, OnEnumerateDevices) |
| PPAPI_DISPATCH_HOST_RESOURCE_CALL( |
| PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange, |
| OnMonitorDeviceChange) |
| PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( |
| PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange, |
| OnStopMonitoringDeviceChange) |
| PPAPI_END_MESSAGE_MAP() |
| |
| *handled = false; |
| return PP_ERROR_FAILED; |
| } |
| |
| int32_t PepperDeviceEnumerationHostHelper::OnEnumerateDevices( |
| HostMessageContext* context) { |
| if (enumerate_devices_context_.is_valid()) |
| return PP_ERROR_INPROGRESS; |
| |
| enumerate_ = std::make_unique<ScopedEnumerationRequest>( |
| this, base::BindOnce( |
| &PepperDeviceEnumerationHostHelper::OnEnumerateDevicesComplete, |
| base::Unretained(this))); |
| if (!enumerate_->requested()) |
| return PP_ERROR_FAILED; |
| |
| enumerate_devices_context_ = context->MakeReplyMessageContext(); |
| return PP_OK_COMPLETIONPENDING; |
| } |
| |
| int32_t PepperDeviceEnumerationHostHelper::OnMonitorDeviceChange( |
| HostMessageContext* /* context */, |
| uint32_t callback_id) { |
| monitor_ = std::make_unique<ScopedMonitoringRequest>( |
| this, base::BindRepeating( |
| &PepperDeviceEnumerationHostHelper::OnNotifyDeviceChange, |
| base::Unretained(this), callback_id)); |
| |
| return monitor_->requested() ? PP_OK : PP_ERROR_FAILED; |
| } |
| |
| int32_t PepperDeviceEnumerationHostHelper::OnStopMonitoringDeviceChange( |
| HostMessageContext* /* context */) { |
| monitor_.reset(nullptr); |
| return PP_OK; |
| } |
| |
| void PepperDeviceEnumerationHostHelper::OnEnumerateDevicesComplete( |
| const std::vector<ppapi::DeviceRefData>& devices) { |
| DCHECK(enumerate_devices_context_.is_valid()); |
| |
| enumerate_.reset(nullptr); |
| |
| enumerate_devices_context_.params.set_result(PP_OK); |
| resource_host_->host()->SendReply( |
| enumerate_devices_context_, |
| PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply(devices)); |
| enumerate_devices_context_ = ppapi::host::ReplyMessageContext(); |
| } |
| |
| void PepperDeviceEnumerationHostHelper::OnNotifyDeviceChange( |
| uint32_t callback_id, |
| const std::vector<ppapi::DeviceRefData>& devices) { |
| resource_host_->host()->SendUnsolicitedReply( |
| resource_host_->pp_resource(), |
| PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange(callback_id, |
| devices)); |
| } |
| |
| } // namespace content |