Added documentation to web_view.js/web_view_experimental.js regarding the webview...
[chromium-blink-merge.git] / ppapi / shared_impl / resource_tracker.cc
blob36aa2e142dc04191bb232c6f18ce2d1907a929af
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.
5 #include "ppapi/shared_impl/resource_tracker.h"
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/message_loop/message_loop.h"
10 #include "ppapi/shared_impl/callback_tracker.h"
11 #include "ppapi/shared_impl/id_assignment.h"
12 #include "ppapi/shared_impl/ppapi_globals.h"
13 #include "ppapi/shared_impl/proxy_lock.h"
14 #include "ppapi/shared_impl/resource.h"
16 namespace ppapi {
18 ResourceTracker::ResourceTracker(ThreadMode thread_mode)
19 : last_resource_value_(0),
20 weak_ptr_factory_(this) {
21 if (thread_mode == SINGLE_THREADED)
22 thread_checker_.reset(new base::ThreadChecker);
25 ResourceTracker::~ResourceTracker() {
28 void ResourceTracker::CheckThreadingPreconditions() const {
29 DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
30 #ifndef NDEBUG
31 ProxyLock::AssertAcquired();
32 #endif
35 Resource* ResourceTracker::GetResource(PP_Resource res) const {
36 CheckThreadingPreconditions();
37 ResourceMap::const_iterator i = live_resources_.find(res);
38 if (i == live_resources_.end())
39 return NULL;
40 return i->second.first;
43 void ResourceTracker::AddRefResource(PP_Resource res) {
44 CheckThreadingPreconditions();
45 DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE))
46 << res << " is not a PP_Resource.";
48 DCHECK(CanOperateOnResource(res));
50 ResourceMap::iterator i = live_resources_.find(res);
51 if (i == live_resources_.end())
52 return;
54 // Prevent overflow of refcount.
55 if (i->second.second ==
56 std::numeric_limits<ResourceAndRefCount::second_type>::max())
57 return;
59 // When we go from 0 to 1 plugin ref count, keep an additional "real" ref
60 // on its behalf.
61 if (i->second.second == 0)
62 i->second.first->AddRef();
64 i->second.second++;
65 return;
68 void ResourceTracker::ReleaseResource(PP_Resource res) {
69 CheckThreadingPreconditions();
70 DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE))
71 << res << " is not a PP_Resource.";
73 DCHECK(CanOperateOnResource(res));
75 ResourceMap::iterator i = live_resources_.find(res);
76 if (i == live_resources_.end())
77 return;
79 // Prevent underflow of refcount.
80 if (i->second.second == 0)
81 return;
83 i->second.second--;
84 if (i->second.second == 0) {
85 LastPluginRefWasDeleted(i->second.first);
87 // When we go from 1 to 0 plugin ref count, free the additional "real" ref
88 // on its behalf. THIS WILL MOST LIKELY RELEASE THE OBJECT AND REMOVE IT
89 // FROM OUR LIST.
90 i->second.first->Release();
94 void ResourceTracker::ReleaseResourceSoon(PP_Resource res) {
95 base::MessageLoop::current()->PostNonNestableTask(
96 FROM_HERE,
97 RunWhileLocked(base::Bind(&ResourceTracker::ReleaseResource,
98 weak_ptr_factory_.GetWeakPtr(),
99 res)));
102 void ResourceTracker::DidCreateInstance(PP_Instance instance) {
103 CheckThreadingPreconditions();
104 // Due to the infrastructure of some tests, the instance is registered
105 // twice in a few cases. It would be nice not to do that and assert here
106 // instead.
107 if (instance_map_.find(instance) != instance_map_.end())
108 return;
109 instance_map_[instance] = linked_ptr<InstanceData>(new InstanceData);
112 void ResourceTracker::DidDeleteInstance(PP_Instance instance) {
113 CheckThreadingPreconditions();
114 InstanceMap::iterator found_instance = instance_map_.find(instance);
116 // Due to the infrastructure of some tests, the instance is unregistered
117 // twice in a few cases. It would be nice not to do that and assert here
118 // instead.
119 if (found_instance == instance_map_.end())
120 return;
122 InstanceData& data = *found_instance->second;
124 // Force release all plugin references to resources associated with the
125 // deleted instance. Make a copy since as we iterate through them, each one
126 // will remove itself from the tracking info individually.
127 ResourceSet to_delete = data.resources;
128 ResourceSet::iterator cur = to_delete.begin();
129 while (cur != to_delete.end()) {
130 // Note that it's remotely possible for the object to already be deleted
131 // from the live resources. One case is if a resource object is holding
132 // the last ref to another. When we release the first one, it will release
133 // the second one. So the second one will be gone when we eventually get
134 // to it.
135 ResourceMap::iterator found_resource = live_resources_.find(*cur);
136 if (found_resource != live_resources_.end()) {
137 Resource* resource = found_resource->second.first;
138 if (found_resource->second.second > 0) {
139 LastPluginRefWasDeleted(resource);
140 found_resource->second.second = 0;
142 // This will most likely delete the resource object and remove it
143 // from the live_resources_ list.
144 resource->Release();
148 cur++;
151 // In general the above pass will delete all the resources and there won't
152 // be any left in the map. However, if parts of the implementation are still
153 // holding on to internal refs, we need to tell them that the instance is
154 // gone.
155 to_delete = data.resources;
156 cur = to_delete.begin();
157 while (cur != to_delete.end()) {
158 ResourceMap::iterator found_resource = live_resources_.find(*cur);
159 if (found_resource != live_resources_.end())
160 found_resource->second.first->NotifyInstanceWasDeleted();
161 cur++;
164 instance_map_.erase(instance);
167 int ResourceTracker::GetLiveObjectsForInstance(PP_Instance instance) const {
168 CheckThreadingPreconditions();
169 InstanceMap::const_iterator found = instance_map_.find(instance);
170 if (found == instance_map_.end())
171 return 0;
172 return static_cast<int>(found->second->resources.size());
175 void ResourceTracker::UseOddResourceValueInDebugMode() {
176 #if !defined(NDEBUG)
177 DCHECK_EQ(0, last_resource_value_);
179 ++last_resource_value_;
180 #endif
183 PP_Resource ResourceTracker::AddResource(Resource* object) {
184 CheckThreadingPreconditions();
185 // If the plugin manages to create too many resources, don't do crazy stuff.
186 if (last_resource_value_ >= kMaxPPId)
187 return 0;
189 // Allocate an ID. Note there's a rare error condition below that means we
190 // could end up not using |new_id|, but that's harmless.
191 PP_Resource new_id = MakeTypedId(GetNextResourceValue(), PP_ID_TYPE_RESOURCE);
193 // Some objects have a 0 instance, meaning they aren't associated with any
194 // instance, so they won't be in |instance_map_|. This is (as of this writing)
195 // only true of the PPB_MessageLoop resource for the main thread.
196 if (object->pp_instance()) {
197 InstanceMap::iterator found = instance_map_.find(object->pp_instance());
198 if (found == instance_map_.end()) {
199 // If you hit this, it's likely somebody forgot to call DidCreateInstance,
200 // the resource was created with an invalid PP_Instance, or the renderer
201 // side tried to create a resource for a plugin that crashed/exited. This
202 // could happen for OOP plugins where due to reentrancies in context of
203 // outgoing sync calls the renderer can send events after a plugin has
204 // exited.
205 DLOG(INFO) << "Failed to find plugin instance in instance map";
206 return 0;
208 found->second->resources.insert(new_id);
211 live_resources_[new_id] = ResourceAndRefCount(object, 0);
212 return new_id;
215 void ResourceTracker::RemoveResource(Resource* object) {
216 CheckThreadingPreconditions();
217 PP_Resource pp_resource = object->pp_resource();
218 InstanceMap::iterator found = instance_map_.find(object->pp_instance());
219 if (found != instance_map_.end())
220 found->second->resources.erase(pp_resource);
221 live_resources_.erase(pp_resource);
224 void ResourceTracker::LastPluginRefWasDeleted(Resource* object) {
225 // Bug http://crbug.com/134611 indicates that sometimes the resource tracker
226 // is null here. This should never be the case since if we have a resource in
227 // the tracker, it should always have a valid instance associated with it
228 // (except for the resource for the main thread's message loop, which has
229 // instance set to 0).
230 // As a result, we do some CHECKs here to see what types of problems the
231 // instance might have before dispatching.
233 // TODO(brettw) remove these checks when this bug is no longer relevant.
234 // Note, we do an imperfect check here; this might be a loop that's not the
235 // main one.
236 const bool is_message_loop = (object->AsPPB_MessageLoop_API() != NULL);
237 CHECK(object->pp_instance() || is_message_loop);
238 CallbackTracker* callback_tracker =
239 PpapiGlobals::Get()->GetCallbackTrackerForInstance(object->pp_instance());
240 CHECK(callback_tracker || is_message_loop);
241 if (callback_tracker)
242 callback_tracker->PostAbortForResource(object->pp_resource());
243 object->NotifyLastPluginRefWasDeleted();
246 int32 ResourceTracker::GetNextResourceValue() {
247 #if defined(NDEBUG)
248 return ++last_resource_value_;
249 #else
250 // In debug mode, the least significant bit indicates which side (renderer
251 // or plugin process) created the resource. Increment by 2 so it's always the
252 // same.
253 last_resource_value_ += 2;
254 return last_resource_value_;
255 #endif
258 bool ResourceTracker::CanOperateOnResource(PP_Resource res) {
259 #if defined(NDEBUG)
260 return true;
261 #else
262 // The invalid PP_Resource value could appear at both sides.
263 if (res == 0)
264 return true;
266 // Skipping the type bits, the least significant bit of |res| should be the
267 // same as that of |last_resource_value_|.
268 return ((res >> kPPIdTypeBits) & 1) == (last_resource_value_ & 1);
269 #endif
273 } // namespace ppapi