Make USB permissions work in the new permission message system
[chromium-blink-merge.git] / content / renderer / dom_storage / dom_storage_dispatcher.cc
bloba4d877f715f77c2bad71b6a320bee75db307bd22
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 "content/renderer/dom_storage/dom_storage_dispatcher.h"
7 #include <list>
8 #include <map>
10 #include "base/strings/string_number_conversions.h"
11 #include "base/synchronization/lock.h"
12 #include "content/common/dom_storage/dom_storage_messages.h"
13 #include "content/common/dom_storage/dom_storage_types.h"
14 #include "content/renderer/dom_storage/dom_storage_cached_area.h"
15 #include "content/renderer/dom_storage/dom_storage_proxy.h"
16 #include "content/renderer/dom_storage/webstoragearea_impl.h"
17 #include "content/renderer/dom_storage/webstoragenamespace_impl.h"
18 #include "content/renderer/render_thread_impl.h"
19 #include "ipc/message_filter.h"
20 #include "third_party/WebKit/public/platform/Platform.h"
21 #include "third_party/WebKit/public/web/WebKit.h"
22 #include "third_party/WebKit/public/web/WebStorageEventDispatcher.h"
24 namespace content {
26 namespace {
27 // MessageThrottlingFilter -------------------------------------------
28 // Used to limit the number of ipc messages pending completion so we
29 // don't overwhelm the main browser process. When the limit is reached,
30 // a synchronous message is sent to flush all pending messages thru.
31 // We expect to receive an 'ack' for each message sent. This object
32 // observes receipt of the acks on the IPC thread to decrement a counter.
33 class MessageThrottlingFilter : public IPC::MessageFilter {
34 public:
35 explicit MessageThrottlingFilter(RenderThreadImpl* sender)
36 : pending_count_(0), sender_(sender) {}
38 void SendThrottled(IPC::Message* message);
39 void Shutdown() { sender_ = NULL; }
41 private:
42 ~MessageThrottlingFilter() override {}
44 bool OnMessageReceived(const IPC::Message& message) override;
46 int GetPendingCount() { return IncrementPendingCountN(0); }
47 int IncrementPendingCount() { return IncrementPendingCountN(1); }
48 int DecrementPendingCount() { return IncrementPendingCountN(-1); }
49 int IncrementPendingCountN(int increment) {
50 base::AutoLock locker(lock_);
51 pending_count_ += increment;
52 return pending_count_;
55 base::Lock lock_;
56 int pending_count_;
57 RenderThreadImpl* sender_;
60 void MessageThrottlingFilter::SendThrottled(IPC::Message* message) {
61 // Should only be used for sending of messages which will be acknowledged
62 // with a separate DOMStorageMsg_AsyncOperationComplete message.
63 DCHECK(message->type() == DOMStorageHostMsg_LoadStorageArea::ID ||
64 message->type() == DOMStorageHostMsg_SetItem::ID ||
65 message->type() == DOMStorageHostMsg_RemoveItem::ID ||
66 message->type() == DOMStorageHostMsg_Clear::ID);
67 DCHECK(sender_);
68 if (!sender_) {
69 delete message;
70 return;
72 const int kMaxPendingMessages = 1000;
73 bool need_to_flush = (IncrementPendingCount() > kMaxPendingMessages) &&
74 !message->is_sync();
75 sender_->Send(message);
76 if (need_to_flush) {
77 sender_->Send(new DOMStorageHostMsg_FlushMessages);
78 DCHECK_EQ(0, GetPendingCount());
79 } else {
80 DCHECK_LE(0, GetPendingCount());
84 bool MessageThrottlingFilter::OnMessageReceived(const IPC::Message& message) {
85 if (message.type() == DOMStorageMsg_AsyncOperationComplete::ID) {
86 DecrementPendingCount();
87 DCHECK_LE(0, GetPendingCount());
89 return false;
91 } // namespace
93 // ProxyImpl -----------------------------------------------------
94 // An implementation of the DOMStorageProxy interface in terms of IPC.
95 // This class also manages the collection of cached areas and pending
96 // operations awaiting completion callbacks.
97 class DomStorageDispatcher::ProxyImpl : public DOMStorageProxy {
98 public:
99 explicit ProxyImpl(RenderThreadImpl* sender);
101 // Methods for use by DomStorageDispatcher directly.
102 DOMStorageCachedArea* OpenCachedArea(
103 int64 namespace_id, const GURL& origin);
104 void CloseCachedArea(DOMStorageCachedArea* area);
105 DOMStorageCachedArea* LookupCachedArea(
106 int64 namespace_id, const GURL& origin);
107 void CompleteOnePendingCallback(bool success);
108 void Shutdown();
110 // DOMStorageProxy interface for use by DOMStorageCachedArea.
111 void LoadArea(int connection_id,
112 DOMStorageValuesMap* values,
113 const CompletionCallback& callback) override;
114 void SetItem(int connection_id,
115 const base::string16& key,
116 const base::string16& value,
117 const GURL& page_url,
118 const CompletionCallback& callback) override;
119 void RemoveItem(int connection_id,
120 const base::string16& key,
121 const GURL& page_url,
122 const CompletionCallback& callback) override;
123 void ClearArea(int connection_id,
124 const GURL& page_url,
125 const CompletionCallback& callback) override;
127 private:
128 // Struct to hold references to our contained areas and
129 // to keep track of how many tabs have a given area open.
130 struct CachedAreaHolder {
131 scoped_refptr<DOMStorageCachedArea> area_;
132 int open_count_;
133 CachedAreaHolder() : open_count_(0) {}
134 CachedAreaHolder(DOMStorageCachedArea* area, int count)
135 : area_(area), open_count_(count) {}
137 typedef std::map<std::string, CachedAreaHolder> CachedAreaMap;
138 typedef std::list<CompletionCallback> CallbackList;
140 ~ProxyImpl() override {}
142 // Sudden termination is disabled when there are callbacks pending
143 // to more reliably commit changes during shutdown.
144 void PushPendingCallback(const CompletionCallback& callback) {
145 if (pending_callbacks_.empty())
146 blink::Platform::current()->suddenTerminationChanged(false);
147 pending_callbacks_.push_back(callback);
150 CompletionCallback PopPendingCallback() {
151 CompletionCallback callback = pending_callbacks_.front();
152 pending_callbacks_.pop_front();
153 if (pending_callbacks_.empty())
154 blink::Platform::current()->suddenTerminationChanged(true);
155 return callback;
158 std::string GetCachedAreaKey(int64 namespace_id, const GURL& origin) {
159 return base::Int64ToString(namespace_id) + origin.spec();
162 CachedAreaHolder* GetAreaHolder(const std::string& key) {
163 CachedAreaMap::iterator found = cached_areas_.find(key);
164 if (found == cached_areas_.end())
165 return NULL;
166 return &(found->second);
169 RenderThreadImpl* sender_;
170 CachedAreaMap cached_areas_;
171 CallbackList pending_callbacks_;
172 scoped_refptr<MessageThrottlingFilter> throttling_filter_;
175 DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender)
176 : sender_(sender),
177 throttling_filter_(new MessageThrottlingFilter(sender)) {
178 sender_->AddFilter(throttling_filter_.get());
181 DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::OpenCachedArea(
182 int64 namespace_id, const GURL& origin) {
183 std::string key = GetCachedAreaKey(namespace_id, origin);
184 if (CachedAreaHolder* holder = GetAreaHolder(key)) {
185 ++(holder->open_count_);
186 return holder->area_.get();
188 scoped_refptr<DOMStorageCachedArea> area =
189 new DOMStorageCachedArea(namespace_id, origin, this);
190 cached_areas_[key] = CachedAreaHolder(area.get(), 1);
191 return area.get();
194 void DomStorageDispatcher::ProxyImpl::CloseCachedArea(
195 DOMStorageCachedArea* area) {
196 std::string key = GetCachedAreaKey(area->namespace_id(), area->origin());
197 CachedAreaHolder* holder = GetAreaHolder(key);
198 DCHECK(holder);
199 DCHECK_EQ(holder->area_.get(), area);
200 DCHECK_GT(holder->open_count_, 0);
201 if (--(holder->open_count_) == 0) {
202 cached_areas_.erase(key);
206 DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::LookupCachedArea(
207 int64 namespace_id, const GURL& origin) {
208 std::string key = GetCachedAreaKey(namespace_id, origin);
209 CachedAreaHolder* holder = GetAreaHolder(key);
210 if (!holder)
211 return NULL;
212 return holder->area_.get();
215 void DomStorageDispatcher::ProxyImpl::CompleteOnePendingCallback(bool success) {
216 PopPendingCallback().Run(success);
219 void DomStorageDispatcher::ProxyImpl::Shutdown() {
220 throttling_filter_->Shutdown();
221 sender_->RemoveFilter(throttling_filter_.get());
222 sender_ = NULL;
223 cached_areas_.clear();
224 pending_callbacks_.clear();
227 void DomStorageDispatcher::ProxyImpl::LoadArea(
228 int connection_id, DOMStorageValuesMap* values,
229 const CompletionCallback& callback) {
230 PushPendingCallback(callback);
231 throttling_filter_->SendThrottled(new DOMStorageHostMsg_LoadStorageArea(
232 connection_id, values));
235 void DomStorageDispatcher::ProxyImpl::SetItem(
236 int connection_id, const base::string16& key,
237 const base::string16& value, const GURL& page_url,
238 const CompletionCallback& callback) {
239 PushPendingCallback(callback);
240 throttling_filter_->SendThrottled(new DOMStorageHostMsg_SetItem(
241 connection_id, key, value, page_url));
244 void DomStorageDispatcher::ProxyImpl::RemoveItem(
245 int connection_id, const base::string16& key, const GURL& page_url,
246 const CompletionCallback& callback) {
247 PushPendingCallback(callback);
248 throttling_filter_->SendThrottled(new DOMStorageHostMsg_RemoveItem(
249 connection_id, key, page_url));
252 void DomStorageDispatcher::ProxyImpl::ClearArea(int connection_id,
253 const GURL& page_url,
254 const CompletionCallback& callback) {
255 PushPendingCallback(callback);
256 throttling_filter_->SendThrottled(new DOMStorageHostMsg_Clear(
257 connection_id, page_url));
260 // DomStorageDispatcher ------------------------------------------------
262 DomStorageDispatcher::DomStorageDispatcher()
263 : proxy_(new ProxyImpl(RenderThreadImpl::current())) {
266 DomStorageDispatcher::~DomStorageDispatcher() {
267 proxy_->Shutdown();
270 scoped_refptr<DOMStorageCachedArea> DomStorageDispatcher::OpenCachedArea(
271 int connection_id, int64 namespace_id, const GURL& origin) {
272 RenderThreadImpl::current()->Send(
273 new DOMStorageHostMsg_OpenStorageArea(
274 connection_id, namespace_id, origin));
275 return proxy_->OpenCachedArea(namespace_id, origin);
278 void DomStorageDispatcher::CloseCachedArea(
279 int connection_id, DOMStorageCachedArea* area) {
280 RenderThreadImpl::current()->Send(
281 new DOMStorageHostMsg_CloseStorageArea(connection_id));
282 proxy_->CloseCachedArea(area);
285 bool DomStorageDispatcher::OnMessageReceived(const IPC::Message& msg) {
286 bool handled = true;
287 IPC_BEGIN_MESSAGE_MAP(DomStorageDispatcher, msg)
288 IPC_MESSAGE_HANDLER(DOMStorageMsg_Event, OnStorageEvent)
289 IPC_MESSAGE_HANDLER(DOMStorageMsg_AsyncOperationComplete,
290 OnAsyncOperationComplete)
291 IPC_MESSAGE_UNHANDLED(handled = false)
292 IPC_END_MESSAGE_MAP()
293 return handled;
296 void DomStorageDispatcher::OnStorageEvent(
297 const DOMStorageMsg_Event_Params& params) {
298 RenderThreadImpl::current()->EnsureWebKitInitialized();
300 bool originated_in_process = params.connection_id != 0;
301 WebStorageAreaImpl* originating_area = NULL;
302 if (originated_in_process) {
303 originating_area = WebStorageAreaImpl::FromConnectionId(
304 params.connection_id);
305 } else {
306 DOMStorageCachedArea* cached_area = proxy_->LookupCachedArea(
307 params.namespace_id, params.origin);
308 if (cached_area)
309 cached_area->ApplyMutation(params.key, params.new_value);
312 if (params.namespace_id == kLocalStorageNamespaceId) {
313 blink::WebStorageEventDispatcher::dispatchLocalStorageEvent(
314 params.key,
315 params.old_value,
316 params.new_value,
317 params.origin,
318 params.page_url,
319 originating_area,
320 originated_in_process);
321 } else {
322 WebStorageNamespaceImpl
323 session_namespace_for_event_dispatch(params.namespace_id);
324 blink::WebStorageEventDispatcher::dispatchSessionStorageEvent(
325 params.key,
326 params.old_value,
327 params.new_value,
328 params.origin,
329 params.page_url,
330 session_namespace_for_event_dispatch,
331 originating_area,
332 originated_in_process);
336 void DomStorageDispatcher::OnAsyncOperationComplete(bool success) {
337 proxy_->CompleteOnePendingCallback(success);
340 } // namespace content