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
20using ppapi::host::HostMessageContext;
21
22namespace content {
23
24// Makes sure that StopEnumerateDevices() is called for each EnumerateDevices().
25class PepperDeviceEnumerationHostHelper::ScopedRequest
26    : public base::SupportsWeakPtr<ScopedRequest> {
27 public:
28  // |owner| must outlive this object.
29  ScopedRequest(PepperDeviceEnumerationHostHelper* owner,
30                const Delegate::EnumerateDevicesCallback& callback)
31      : owner_(owner),
32        callback_(callback),
33        requested_(false),
34        request_id_(0),
35        sync_call_(false) {
36    if (!owner_->document_url_.is_valid())
37      return;
38
39    requested_ = true;
40
41    // Note that the callback passed into
42    // PepperDeviceEnumerationHostHelper::Delegate::EnumerateDevices() may be
43    // called synchronously. In that case, |request_id_| hasn't been updated
44    // when the callback is called. Moreover, |callback| may destroy this
45    // object. So we don't pass in |callback| directly. Instead, we use
46    // EnumerateDevicesCallbackBody() to ensure that we always call |callback|
47    // asynchronously.
48    sync_call_ = true;
49    DCHECK(owner_->delegate_);
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_ && owner_->delegate_) {
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
93PepperDeviceEnumerationHostHelper::PepperDeviceEnumerationHostHelper(
94    ppapi::host::ResourceHost* resource_host,
95    base::WeakPtr<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
103PepperDeviceEnumerationHostHelper::~PepperDeviceEnumerationHostHelper() {}
104
105bool PepperDeviceEnumerationHostHelper::HandleResourceMessage(
106    const IPC::Message& msg,
107    HostMessageContext* context,
108    int32_t* result) {
109  bool return_value = false;
110  *result = InternalHandleResourceMessage(msg, context, &return_value);
111  return return_value;
112}
113
114int32_t PepperDeviceEnumerationHostHelper::InternalHandleResourceMessage(
115    const IPC::Message& msg,
116    HostMessageContext* context,
117    bool* handled) {
118  *handled = true;
119  PPAPI_BEGIN_MESSAGE_MAP(PepperDeviceEnumerationHostHelper, msg)
120    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
121        PpapiHostMsg_DeviceEnumeration_EnumerateDevices, OnEnumerateDevices)
122    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
123        PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange,
124        OnMonitorDeviceChange)
125    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
126        PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange,
127        OnStopMonitoringDeviceChange)
128  PPAPI_END_MESSAGE_MAP()
129
130  *handled = false;
131  return PP_ERROR_FAILED;
132}
133
134int32_t PepperDeviceEnumerationHostHelper::OnEnumerateDevices(
135    HostMessageContext* context) {
136  if (enumerate_devices_context_.is_valid())
137    return PP_ERROR_INPROGRESS;
138
139  enumerate_.reset(new ScopedRequest(
140      this,
141      base::Bind(&PepperDeviceEnumerationHostHelper::OnEnumerateDevicesComplete,
142                 base::Unretained(this))));
143  if (!enumerate_->requested())
144    return PP_ERROR_FAILED;
145
146  enumerate_devices_context_ = context->MakeReplyMessageContext();
147  return PP_OK_COMPLETIONPENDING;
148}
149
150int32_t PepperDeviceEnumerationHostHelper::OnMonitorDeviceChange(
151    HostMessageContext* /* context */,
152    uint32_t callback_id) {
153  monitor_.reset(new ScopedRequest(
154      this,
155      base::Bind(&PepperDeviceEnumerationHostHelper::OnNotifyDeviceChange,
156                 base::Unretained(this),
157                 callback_id)));
158
159  return monitor_->requested() ? PP_OK : PP_ERROR_FAILED;
160}
161
162int32_t PepperDeviceEnumerationHostHelper::OnStopMonitoringDeviceChange(
163    HostMessageContext* /* context */) {
164  monitor_.reset(NULL);
165  return PP_OK;
166}
167
168void PepperDeviceEnumerationHostHelper::OnEnumerateDevicesComplete(
169    int /* request_id */,
170    const std::vector<ppapi::DeviceRefData>& devices) {
171  DCHECK(enumerate_devices_context_.is_valid());
172
173  enumerate_.reset(NULL);
174
175  enumerate_devices_context_.params.set_result(PP_OK);
176  resource_host_->host()->SendReply(
177      enumerate_devices_context_,
178      PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply(devices));
179  enumerate_devices_context_ = ppapi::host::ReplyMessageContext();
180}
181
182void PepperDeviceEnumerationHostHelper::OnNotifyDeviceChange(
183    uint32_t callback_id,
184    int /* request_id */,
185    const std::vector<ppapi::DeviceRefData>& devices) {
186  resource_host_->host()->SendUnsolicitedReply(
187      resource_host_->pp_resource(),
188      PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange(callback_id,
189                                                          devices));
190}
191
192}  // namespace content
193