1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/renderer/pepper/pepper_device_enumeration_host_helper.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "base/memory/weak_ptr.h" 10 #include "base/message_loop/message_loop.h" 11 #include "ipc/ipc_message.h" 12 #include "ppapi/c/pp_errors.h" 13 #include "ppapi/host/dispatch_host_message.h" 14 #include "ppapi/host/host_message_context.h" 15 #include "ppapi/host/ppapi_host.h" 16 #include "ppapi/host/resource_host.h" 17 #include "ppapi/proxy/ppapi_messages.h" 18 #include "ppapi/shared_impl/ppb_device_ref_shared.h" 19 20 using ppapi::host::HostMessageContext; 21 22 namespace content { 23 24 // Makes sure that StopEnumerateDevices() is called for each EnumerateDevices(). 25 class PepperDeviceEnumerationHostHelper::ScopedRequest 26 : public base::SupportsWeakPtr<ScopedRequest> { 27 public: 28 // |owner| must outlive this object. 29 ScopedRequest( 30 PepperDeviceEnumerationHostHelper* owner, 31 const Delegate::EnumerateDevicesCallback& callback) 32 : owner_(owner), 33 callback_(callback), 34 requested_(false), 35 request_id_(0), 36 sync_call_(false) { 37 if (!owner_->document_url_.is_valid()) 38 return; 39 40 requested_ = true; 41 42 // Note that the callback passed into 43 // PepperDeviceEnumerationHostHelper::Delegate::EnumerateDevices() may be 44 // called synchronously. In that case, |request_id_| hasn't been updated 45 // when the callback is called. Moreover, |callback| may destroy this 46 // object. So we don't pass in |callback| directly. Instead, we use 47 // EnumerateDevicesCallbackBody() to ensure that we always call |callback| 48 // asynchronously. 49 sync_call_ = true; 50 request_id_ = owner_->delegate_->EnumerateDevices( 51 owner_->device_type_, 52 owner_->document_url_, 53 base::Bind(&ScopedRequest::EnumerateDevicesCallbackBody, AsWeakPtr())); 54 sync_call_ = false; 55 } 56 57 ~ScopedRequest() { 58 if (requested_) { 59 owner_->delegate_->StopEnumerateDevices(request_id_); 60 } 61 } 62 63 bool requested() const { return requested_; } 64 65 private: 66 void EnumerateDevicesCallbackBody( 67 int request_id, 68 const std::vector<ppapi::DeviceRefData>& devices) { 69 if (sync_call_) { 70 base::MessageLoop::current()->PostTask( 71 FROM_HERE, 72 base::Bind(&ScopedRequest::EnumerateDevicesCallbackBody, 73 AsWeakPtr(), 74 request_id, 75 devices)); 76 } else { 77 DCHECK_EQ(request_id_, request_id); 78 callback_.Run(request_id, devices); 79 // This object may have been destroyed at this point. 80 } 81 } 82 83 PepperDeviceEnumerationHostHelper* owner_; 84 PepperDeviceEnumerationHostHelper::Delegate::EnumerateDevicesCallback 85 callback_; 86 bool requested_; 87 int request_id_; 88 bool sync_call_; 89 90 DISALLOW_COPY_AND_ASSIGN(ScopedRequest); 91 }; 92 93 PepperDeviceEnumerationHostHelper::PepperDeviceEnumerationHostHelper( 94 ppapi::host::ResourceHost* resource_host, 95 Delegate* delegate, 96 PP_DeviceType_Dev device_type, 97 const GURL& document_url) 98 : resource_host_(resource_host), 99 delegate_(delegate), 100 device_type_(device_type), 101 document_url_(document_url) { 102 } 103 104 PepperDeviceEnumerationHostHelper::~PepperDeviceEnumerationHostHelper() { 105 } 106 107 bool PepperDeviceEnumerationHostHelper::HandleResourceMessage( 108 const IPC::Message& msg, 109 HostMessageContext* context, 110 int32_t* result) { 111 bool return_value = false; 112 *result = InternalHandleResourceMessage(msg, context, &return_value); 113 return return_value; 114 } 115 116 int32_t PepperDeviceEnumerationHostHelper::InternalHandleResourceMessage( 117 const IPC::Message& msg, 118 HostMessageContext* context, 119 bool* handled) { 120 *handled = true; 121 IPC_BEGIN_MESSAGE_MAP(PepperDeviceEnumerationHostHelper, msg) 122 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( 123 PpapiHostMsg_DeviceEnumeration_EnumerateDevices, OnEnumerateDevices) 124 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 125 PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange, 126 OnMonitorDeviceChange) 127 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( 128 PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange, 129 OnStopMonitoringDeviceChange) 130 IPC_END_MESSAGE_MAP() 131 132 *handled = false; 133 return PP_ERROR_FAILED; 134 } 135 136 int32_t PepperDeviceEnumerationHostHelper::OnEnumerateDevices( 137 HostMessageContext* context) { 138 if (enumerate_devices_context_) 139 return PP_ERROR_INPROGRESS; 140 141 enumerate_.reset(new ScopedRequest( 142 this, 143 base::Bind(&PepperDeviceEnumerationHostHelper::OnEnumerateDevicesComplete, 144 base::Unretained(this)))); 145 if (!enumerate_->requested()) 146 return PP_ERROR_FAILED; 147 148 enumerate_devices_context_.reset( 149 new ppapi::host::ReplyMessageContext(context->MakeReplyMessageContext())); 150 return PP_OK_COMPLETIONPENDING; 151 } 152 153 int32_t PepperDeviceEnumerationHostHelper::OnMonitorDeviceChange( 154 HostMessageContext* /* context */, 155 uint32_t callback_id) { 156 monitor_.reset(new ScopedRequest( 157 this, 158 base::Bind(&PepperDeviceEnumerationHostHelper::OnNotifyDeviceChange, 159 base::Unretained(this), callback_id))); 160 161 return monitor_->requested() ? PP_OK : PP_ERROR_FAILED; 162 } 163 164 int32_t PepperDeviceEnumerationHostHelper::OnStopMonitoringDeviceChange( 165 HostMessageContext* /* context */) { 166 monitor_.reset(NULL); 167 return PP_OK; 168 } 169 170 void PepperDeviceEnumerationHostHelper::OnEnumerateDevicesComplete( 171 int /* request_id */, 172 const std::vector<ppapi::DeviceRefData>& devices) { 173 DCHECK(enumerate_devices_context_.get()); 174 175 enumerate_.reset(NULL); 176 177 enumerate_devices_context_->params.set_result(PP_OK); 178 resource_host_->host()->SendReply( 179 *enumerate_devices_context_, 180 PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply(devices)); 181 enumerate_devices_context_.reset(); 182 } 183 184 void PepperDeviceEnumerationHostHelper::OnNotifyDeviceChange( 185 uint32_t callback_id, 186 int /* request_id */, 187 const std::vector<ppapi::DeviceRefData>& devices) { 188 resource_host_->host()->SendUnsolicitedReply( 189 resource_host_->pp_resource(), 190 PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange( 191 callback_id, 192 devices)); 193 } 194 195 } // namespace content 196