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 "ppapi/proxy/device_enumeration_resource_helper.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "ipc/ipc_message.h" 11 #include "ipc/ipc_message_macros.h" 12 #include "ppapi/c/pp_array_output.h" 13 #include "ppapi/c/pp_errors.h" 14 #include "ppapi/proxy/dispatch_reply_message.h" 15 #include "ppapi/proxy/plugin_resource.h" 16 #include "ppapi/proxy/ppapi_messages.h" 17 #include "ppapi/proxy/resource_message_params.h" 18 #include "ppapi/shared_impl/array_writer.h" 19 #include "ppapi/shared_impl/ppapi_globals.h" 20 #include "ppapi/shared_impl/ppb_device_ref_shared.h" 21 #include "ppapi/shared_impl/proxy_lock.h" 22 #include "ppapi/shared_impl/resource_tracker.h" 23 #include "ppapi/shared_impl/tracked_callback.h" 24 25 namespace ppapi { 26 namespace proxy { 27 28 DeviceEnumerationResourceHelper::DeviceEnumerationResourceHelper( 29 PluginResource* owner) 30 : owner_(owner), 31 pending_enumerate_devices_(false), 32 monitor_callback_id_(0), 33 monitor_user_data_(NULL) { 34 } 35 36 DeviceEnumerationResourceHelper::~DeviceEnumerationResourceHelper() { 37 } 38 39 int32_t DeviceEnumerationResourceHelper::EnumerateDevices0_2( 40 PP_Resource* devices, 41 scoped_refptr<TrackedCallback> callback) { 42 if (pending_enumerate_devices_) 43 return PP_ERROR_INPROGRESS; 44 if (!devices) 45 return PP_ERROR_BADARGUMENT; 46 47 pending_enumerate_devices_ = true; 48 PpapiHostMsg_DeviceEnumeration_EnumerateDevices msg; 49 owner_->Call<PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply>( 50 PluginResource::RENDERER, msg, 51 base::Bind( 52 &DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply0_2, 53 AsWeakPtr(), devices, callback)); 54 return PP_OK_COMPLETIONPENDING; 55 } 56 57 int32_t DeviceEnumerationResourceHelper::EnumerateDevices( 58 const PP_ArrayOutput& output, 59 scoped_refptr<TrackedCallback> callback) { 60 if (pending_enumerate_devices_) 61 return PP_ERROR_INPROGRESS; 62 63 pending_enumerate_devices_ = true; 64 PpapiHostMsg_DeviceEnumeration_EnumerateDevices msg; 65 owner_->Call<PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply>( 66 PluginResource::RENDERER, msg, 67 base::Bind( 68 &DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply, 69 AsWeakPtr(), output, callback)); 70 return PP_OK_COMPLETIONPENDING; 71 } 72 73 int32_t DeviceEnumerationResourceHelper::EnumerateDevicesSync( 74 const PP_ArrayOutput& output) { 75 std::vector<DeviceRefData> devices; 76 int32_t result = 77 owner_->SyncCall<PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply>( 78 PluginResource::RENDERER, 79 PpapiHostMsg_DeviceEnumeration_EnumerateDevices(), 80 &devices); 81 82 if (result == PP_OK) 83 result = WriteToArrayOutput(devices, output); 84 85 return result; 86 } 87 88 int32_t DeviceEnumerationResourceHelper::MonitorDeviceChange( 89 PP_MonitorDeviceChangeCallback callback, 90 void* user_data) { 91 monitor_callback_id_++; 92 monitor_user_data_ = user_data; 93 if (callback) { 94 monitor_callback_.reset( 95 ThreadAwareCallback<PP_MonitorDeviceChangeCallback>::Create(callback)); 96 if (!monitor_callback_.get()) 97 return PP_ERROR_NO_MESSAGE_LOOP; 98 99 owner_->Post(PluginResource::RENDERER, 100 PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange( 101 monitor_callback_id_)); 102 } else { 103 monitor_callback_.reset(NULL); 104 105 owner_->Post(PluginResource::RENDERER, 106 PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange()); 107 } 108 return PP_OK; 109 } 110 111 bool DeviceEnumerationResourceHelper::HandleReply( 112 const ResourceMessageReplyParams& params, 113 const IPC::Message& msg) { 114 IPC_BEGIN_MESSAGE_MAP(DeviceEnumerationResourceHelper, msg) 115 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 116 PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange, 117 OnPluginMsgNotifyDeviceChange) 118 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(return false) 119 IPC_END_MESSAGE_MAP() 120 121 return true; 122 } 123 124 void DeviceEnumerationResourceHelper::LastPluginRefWasDeleted() { 125 // Make sure that no further notifications are sent to the plugin. 126 monitor_callback_id_++; 127 monitor_callback_.reset(NULL); 128 monitor_user_data_ = NULL; 129 130 // There is no need to do anything with pending callback of 131 // EnumerateDevices(), because OnPluginMsgEnumerateDevicesReply*() will handle 132 // that properly. 133 } 134 135 void DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply0_2( 136 PP_Resource* devices_resource, 137 scoped_refptr<TrackedCallback> callback, 138 const ResourceMessageReplyParams& params, 139 const std::vector<DeviceRefData>& devices) { 140 pending_enumerate_devices_ = false; 141 142 // We shouldn't access |devices_resource| if the callback has been called, 143 // which is possible if the last plugin reference to the corresponding 144 // resource has gone away, and the callback has been aborted. 145 if (!TrackedCallback::IsPending(callback)) 146 return; 147 148 if (params.result() == PP_OK) { 149 *devices_resource = PPB_DeviceRef_Shared::CreateResourceArray( 150 OBJECT_IS_PROXY, owner_->pp_instance(), devices); 151 } 152 153 callback->Run(params.result()); 154 } 155 156 void DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply( 157 const PP_ArrayOutput& output, 158 scoped_refptr<TrackedCallback> callback, 159 const ResourceMessageReplyParams& params, 160 const std::vector<DeviceRefData>& devices) { 161 pending_enumerate_devices_ = false; 162 163 // We shouldn't access |output| if the callback has been called, which is 164 // possible if the last plugin reference to the corresponding resource has 165 // gone away, and the callback has been aborted. 166 if (!TrackedCallback::IsPending(callback)) 167 return; 168 169 int32_t result = params.result(); 170 if (result == PP_OK) 171 result = WriteToArrayOutput(devices, output); 172 173 callback->Run(result); 174 } 175 176 void DeviceEnumerationResourceHelper::OnPluginMsgNotifyDeviceChange( 177 const ResourceMessageReplyParams& /* params */, 178 uint32_t callback_id, 179 const std::vector<DeviceRefData>& devices) { 180 if (monitor_callback_id_ != callback_id) { 181 // A new callback or NULL has been set. 182 return; 183 } 184 185 CHECK(monitor_callback_.get()); 186 187 scoped_ptr<PP_Resource[]> elements; 188 uint32_t size = devices.size(); 189 if (size > 0) { 190 elements.reset(new PP_Resource[size]); 191 for (size_t index = 0; index < size; ++index) { 192 PPB_DeviceRef_Shared* device_object = new PPB_DeviceRef_Shared( 193 OBJECT_IS_PROXY, owner_->pp_instance(), devices[index]); 194 elements[index] = device_object->GetReference(); 195 } 196 } 197 198 monitor_callback_->RunOnTargetThread(monitor_user_data_, size, 199 elements.get()); 200 for (size_t index = 0; index < size; ++index) 201 PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(elements[index]); 202 } 203 204 int32_t DeviceEnumerationResourceHelper::WriteToArrayOutput( 205 const std::vector<DeviceRefData>& devices, 206 const PP_ArrayOutput& output) { 207 ArrayWriter writer(output); 208 if (!writer.is_valid()) 209 return PP_ERROR_BADARGUMENT; 210 211 std::vector<scoped_refptr<Resource> > device_resources; 212 for (size_t i = 0; i < devices.size(); ++i) { 213 device_resources.push_back(new PPB_DeviceRef_Shared( 214 OBJECT_IS_PROXY, owner_->pp_instance(), devices[i])); 215 } 216 if (!writer.StoreResourceVector(device_resources)) 217 return PP_ERROR_FAILED; 218 219 return PP_OK; 220 } 221 222 } // namespace proxy 223 } // namespace ppapi 224