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
25namespace ppapi {
26namespace proxy {
27
28DeviceEnumerationResourceHelper::DeviceEnumerationResourceHelper(
29    PluginResource* owner)
30    : owner_(owner),
31      pending_enumerate_devices_(false),
32      monitor_callback_id_(0),
33      monitor_user_data_(NULL) {
34}
35
36DeviceEnumerationResourceHelper::~DeviceEnumerationResourceHelper() {
37}
38
39int32_t DeviceEnumerationResourceHelper::EnumerateDevices(
40    const PP_ArrayOutput& output,
41    scoped_refptr<TrackedCallback> callback) {
42  if (pending_enumerate_devices_)
43    return PP_ERROR_INPROGRESS;
44
45  pending_enumerate_devices_ = true;
46  PpapiHostMsg_DeviceEnumeration_EnumerateDevices msg;
47  owner_->Call<PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply>(
48      PluginResource::RENDERER, msg,
49      base::Bind(
50          &DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply,
51          AsWeakPtr(), output, callback));
52  return PP_OK_COMPLETIONPENDING;
53}
54
55int32_t DeviceEnumerationResourceHelper::EnumerateDevicesSync(
56    const PP_ArrayOutput& output) {
57  std::vector<DeviceRefData> devices;
58  int32_t result =
59      owner_->SyncCall<PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply>(
60          PluginResource::RENDERER,
61          PpapiHostMsg_DeviceEnumeration_EnumerateDevices(),
62          &devices);
63
64  if (result == PP_OK)
65    result = WriteToArrayOutput(devices, output);
66
67  return result;
68}
69
70int32_t DeviceEnumerationResourceHelper::MonitorDeviceChange(
71    PP_MonitorDeviceChangeCallback callback,
72    void* user_data) {
73  monitor_callback_id_++;
74  monitor_user_data_ = user_data;
75  if (callback) {
76    monitor_callback_.reset(
77        ThreadAwareCallback<PP_MonitorDeviceChangeCallback>::Create(callback));
78    if (!monitor_callback_.get())
79      return PP_ERROR_NO_MESSAGE_LOOP;
80
81    owner_->Post(PluginResource::RENDERER,
82                 PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange(
83                     monitor_callback_id_));
84  } else {
85    monitor_callback_.reset(NULL);
86
87    owner_->Post(PluginResource::RENDERER,
88                 PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange());
89  }
90  return PP_OK;
91}
92
93bool DeviceEnumerationResourceHelper::HandleReply(
94    const ResourceMessageReplyParams& params,
95    const IPC::Message& msg) {
96  PPAPI_BEGIN_MESSAGE_MAP(DeviceEnumerationResourceHelper, msg)
97    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
98        PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange,
99        OnPluginMsgNotifyDeviceChange)
100    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(return false)
101  PPAPI_END_MESSAGE_MAP()
102
103  return true;
104}
105
106void DeviceEnumerationResourceHelper::LastPluginRefWasDeleted() {
107  // Make sure that no further notifications are sent to the plugin.
108  monitor_callback_id_++;
109  monitor_callback_.reset(NULL);
110  monitor_user_data_ = NULL;
111
112  // There is no need to do anything with pending callback of
113  // EnumerateDevices(), because OnPluginMsgEnumerateDevicesReply*() will handle
114  // that properly.
115}
116
117void DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply(
118    const PP_ArrayOutput& output,
119    scoped_refptr<TrackedCallback> callback,
120    const ResourceMessageReplyParams& params,
121    const std::vector<DeviceRefData>& devices) {
122  pending_enumerate_devices_ = false;
123
124  // We shouldn't access |output| if the callback has been called, which is
125  // possible if the last plugin reference to the corresponding resource has
126  // gone away, and the callback has been aborted.
127  if (!TrackedCallback::IsPending(callback))
128    return;
129
130  int32_t result = params.result();
131  if (result == PP_OK)
132    result = WriteToArrayOutput(devices, output);
133
134  callback->Run(result);
135}
136
137void DeviceEnumerationResourceHelper::OnPluginMsgNotifyDeviceChange(
138    const ResourceMessageReplyParams& /* params */,
139    uint32_t callback_id,
140    const std::vector<DeviceRefData>& devices) {
141  if (monitor_callback_id_ != callback_id) {
142    // A new callback or NULL has been set.
143    return;
144  }
145
146  CHECK(monitor_callback_.get());
147
148  scoped_ptr<PP_Resource[]> elements;
149  uint32_t size = devices.size();
150  if (size > 0) {
151    elements.reset(new PP_Resource[size]);
152    for (size_t index = 0; index < size; ++index) {
153      PPB_DeviceRef_Shared* device_object = new PPB_DeviceRef_Shared(
154          OBJECT_IS_PROXY, owner_->pp_instance(), devices[index]);
155      elements[index] = device_object->GetReference();
156    }
157  }
158
159  monitor_callback_->RunOnTargetThread(monitor_user_data_, size,
160                                       elements.get());
161  for (size_t index = 0; index < size; ++index)
162    PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(elements[index]);
163}
164
165int32_t DeviceEnumerationResourceHelper::WriteToArrayOutput(
166    const std::vector<DeviceRefData>& devices,
167    const PP_ArrayOutput& output) {
168  ArrayWriter writer(output);
169  if (!writer.is_valid())
170    return PP_ERROR_BADARGUMENT;
171
172  std::vector<scoped_refptr<Resource> > device_resources;
173  for (size_t i = 0; i < devices.size(); ++i) {
174    device_resources.push_back(new PPB_DeviceRef_Shared(
175        OBJECT_IS_PROXY, owner_->pp_instance(), devices[i]));
176  }
177  if (!writer.StoreResourceVector(device_resources))
178    return PP_ERROR_FAILED;
179
180  return PP_OK;
181}
182
183}  // namespace proxy
184}  // namespace ppapi
185