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/plugin_resource.h"
6 
7 #include <limits>
8 
9 #include "ppapi/proxy/plugin_globals.h"
10 #include "ppapi/proxy/ppapi_messages.h"
11 #include "ppapi/shared_impl/ppapi_globals.h"
12 
13 namespace ppapi {
14 namespace proxy {
15 
SafeRunCallback(scoped_refptr<TrackedCallback> * callback,int32_t error)16 void SafeRunCallback(scoped_refptr<TrackedCallback>* callback, int32_t error) {
17   if (TrackedCallback::IsPending(*callback)) {
18     scoped_refptr<TrackedCallback> temp;
19     callback->swap(temp);
20     temp->Run(error);
21   }
22 }
23 
PluginResource(Connection connection,PP_Instance instance)24 PluginResource::PluginResource(Connection connection, PP_Instance instance)
25     : Resource(OBJECT_IS_PROXY, instance),
26       connection_(connection),
27       next_sequence_number_(1),
28       sent_create_to_browser_(false),
29       sent_create_to_renderer_(false),
30       resource_reply_thread_registrar_(
31           PpapiGlobals::Get()->IsPluginGlobals() ?
32               PluginGlobals::Get()->resource_reply_thread_registrar() : NULL) {
33 }
34 
~PluginResource()35 PluginResource::~PluginResource() {
36   if (sent_create_to_browser_) {
37     connection_.browser_sender()->Send(
38         new PpapiHostMsg_ResourceDestroyed(pp_resource()));
39   }
40   if (sent_create_to_renderer_) {
41     connection_.GetRendererSender()->Send(
42         new PpapiHostMsg_ResourceDestroyed(pp_resource()));
43   }
44 
45   if (resource_reply_thread_registrar_.get())
46     resource_reply_thread_registrar_->Unregister(pp_resource());
47 }
48 
OnReplyReceived(const proxy::ResourceMessageReplyParams & params,const IPC::Message & msg)49 void PluginResource::OnReplyReceived(
50     const proxy::ResourceMessageReplyParams& params,
51     const IPC::Message& msg) {
52   TRACE_EVENT2("ppapi proxy", "PluginResource::OnReplyReceived",
53                "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
54                "Line", IPC_MESSAGE_ID_LINE(msg.type()));
55   // Grab the callback for the reply sequence number and run it with |msg|.
56   CallbackMap::iterator it = callbacks_.find(params.sequence());
57   if (it == callbacks_.end()) {
58     DCHECK(false) << "Callback does not exist for an expected sequence number.";
59   } else {
60     scoped_refptr<PluginResourceCallbackBase> callback = it->second;
61     callbacks_.erase(it);
62     callback->Run(params, msg);
63   }
64 }
65 
NotifyLastPluginRefWasDeleted()66 void PluginResource::NotifyLastPluginRefWasDeleted() {
67   Resource::NotifyLastPluginRefWasDeleted();
68 
69   // The callbacks may hold referrences to this object. Normally, we will get
70   // reply messages from the host side and remove them. However, it is possible
71   // that some replies from the host never arrive, e.g., the corresponding
72   // renderer crashes. In that case, we have to clean up the callbacks,
73   // otherwise this object will live forever.
74   callbacks_.clear();
75 }
76 
NotifyInstanceWasDeleted()77 void PluginResource::NotifyInstanceWasDeleted() {
78   Resource::NotifyInstanceWasDeleted();
79 
80   // Please see comments in NotifyLastPluginRefWasDeleted() about why we must
81   // clean up the callbacks.
82   // It is possible that NotifyLastPluginRefWasDeleted() is never called for a
83   // resource. For example, those singleton-style resources such as
84   // GamepadResource never expose references to the plugin and thus won't
85   // receive a NotifyLastPluginRefWasDeleted() call. For those resources, we
86   // need to clean up callbacks when the instance goes away.
87   callbacks_.clear();
88 }
89 
SendCreate(Destination dest,const IPC::Message & msg)90 void PluginResource::SendCreate(Destination dest, const IPC::Message& msg) {
91   TRACE_EVENT2("ppapi proxy", "PluginResource::SendCreate",
92                "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
93                "Line", IPC_MESSAGE_ID_LINE(msg.type()));
94   if (dest == RENDERER) {
95     DCHECK(!sent_create_to_renderer_);
96     sent_create_to_renderer_ = true;
97   } else {
98     DCHECK(!sent_create_to_browser_);
99     sent_create_to_browser_ = true;
100   }
101   ResourceMessageCallParams params(pp_resource(), GetNextSequence());
102   GetSender(dest)->Send(
103       new PpapiHostMsg_ResourceCreated(params, pp_instance(), msg));
104 }
105 
AttachToPendingHost(Destination dest,int pending_host_id)106 void PluginResource::AttachToPendingHost(Destination dest,
107                                          int pending_host_id) {
108   // Connecting to a pending host is a replacement for "create".
109   if (dest == RENDERER) {
110     DCHECK(!sent_create_to_renderer_);
111     sent_create_to_renderer_ = true;
112   } else {
113     DCHECK(!sent_create_to_browser_);
114     sent_create_to_browser_ = true;
115   }
116   GetSender(dest)->Send(
117       new PpapiHostMsg_AttachToPendingHost(pp_resource(), pending_host_id));
118 }
119 
Post(Destination dest,const IPC::Message & msg)120 void PluginResource::Post(Destination dest, const IPC::Message& msg) {
121   TRACE_EVENT2("ppapi proxy", "PluginResource::Post",
122                "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
123                "Line", IPC_MESSAGE_ID_LINE(msg.type()));
124   ResourceMessageCallParams params(pp_resource(), GetNextSequence());
125   SendResourceCall(dest, params, msg);
126 }
127 
SendResourceCall(Destination dest,const ResourceMessageCallParams & call_params,const IPC::Message & nested_msg)128 bool PluginResource::SendResourceCall(
129     Destination dest,
130     const ResourceMessageCallParams& call_params,
131     const IPC::Message& nested_msg) {
132   // For in-process plugins, we need to send the routing ID with the request.
133   // The browser then uses that routing ID when sending the reply so it will be
134   // routed back to the correct RenderFrameImpl.
135   if (dest == BROWSER && connection_.in_process()) {
136     return GetSender(dest)->Send(new PpapiHostMsg_InProcessResourceCall(
137         connection_.browser_sender_routing_id(), call_params, nested_msg));
138   } else {
139     return GetSender(dest)->Send(
140         new PpapiHostMsg_ResourceCall(call_params, nested_msg));
141   }
142 }
143 
GenericSyncCall(Destination dest,const IPC::Message & msg,IPC::Message * reply,ResourceMessageReplyParams * reply_params)144 int32_t PluginResource::GenericSyncCall(
145     Destination dest,
146     const IPC::Message& msg,
147     IPC::Message* reply,
148     ResourceMessageReplyParams* reply_params) {
149   TRACE_EVENT2("ppapi proxy", "PluginResource::GenericSyncCall",
150                "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
151                "Line", IPC_MESSAGE_ID_LINE(msg.type()));
152   ResourceMessageCallParams params(pp_resource(), GetNextSequence());
153   params.set_has_callback();
154   bool success = GetSender(dest)->Send(new PpapiHostMsg_ResourceSyncCall(
155       params, msg, reply_params, reply));
156   if (success)
157     return reply_params->result();
158   return PP_ERROR_FAILED;
159 }
160 
GetNextSequence()161 int32_t PluginResource::GetNextSequence() {
162   // Return the value with wraparound, making sure we don't make a sequence
163   // number with a 0 ID. Note that signed wraparound is undefined in C++ so we
164   // manually check.
165   int32_t ret = next_sequence_number_;
166   if (next_sequence_number_ == std::numeric_limits<int32_t>::max())
167     next_sequence_number_ = 1;  // Skip 0 which is invalid.
168   else
169     next_sequence_number_++;
170   return ret;
171 }
172 
173 }  // namespace proxy
174 }  // namespace ppapi
175