cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / content / renderer / dom_storage / dom_storage_dispatcher.cc
blob94332709d4009b129f3ceebd8faab8815f0e24d0
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 ResetAllCachedAreas(int64 namespace_id);
108 void CompleteOnePendingCallback(bool success);
109 void Shutdown();
111 // DOMStorageProxy interface for use by DOMStorageCachedArea.
112 void LoadArea(int connection_id,
113 DOMStorageValuesMap* values,
114 bool* send_log_get_messages,
115 const CompletionCallback& callback) override;
116 void SetItem(int connection_id,
117 const base::string16& key,
118 const base::string16& value,
119 const GURL& page_url,
120 const CompletionCallback& callback) override;
121 void LogGetItem(int connection_id,
122 const base::string16& key,
123 const base::NullableString16& value) override;
124 void RemoveItem(int connection_id,
125 const base::string16& key,
126 const GURL& page_url,
127 const CompletionCallback& callback) override;
128 void ClearArea(int connection_id,
129 const GURL& page_url,
130 const CompletionCallback& callback) override;
132 private:
133 // Struct to hold references to our contained areas and
134 // to keep track of how many tabs have a given area open.
135 struct CachedAreaHolder {
136 scoped_refptr<DOMStorageCachedArea> area_;
137 int open_count_;
138 int64 namespace_id_;
139 CachedAreaHolder() : open_count_(0) {}
140 CachedAreaHolder(DOMStorageCachedArea* area, int count,
141 int64 namespace_id)
142 : area_(area), open_count_(count), namespace_id_(namespace_id) {}
144 typedef std::map<std::string, CachedAreaHolder> CachedAreaMap;
145 typedef std::list<CompletionCallback> CallbackList;
147 ~ProxyImpl() override {}
149 // Sudden termination is disabled when there are callbacks pending
150 // to more reliably commit changes during shutdown.
151 void PushPendingCallback(const CompletionCallback& callback) {
152 if (pending_callbacks_.empty())
153 blink::Platform::current()->suddenTerminationChanged(false);
154 pending_callbacks_.push_back(callback);
157 CompletionCallback PopPendingCallback() {
158 CompletionCallback callback = pending_callbacks_.front();
159 pending_callbacks_.pop_front();
160 if (pending_callbacks_.empty())
161 blink::Platform::current()->suddenTerminationChanged(true);
162 return callback;
165 std::string GetCachedAreaKey(int64 namespace_id, const GURL& origin) {
166 return base::Int64ToString(namespace_id) + origin.spec();
169 CachedAreaHolder* GetAreaHolder(const std::string& key) {
170 CachedAreaMap::iterator found = cached_areas_.find(key);
171 if (found == cached_areas_.end())
172 return NULL;
173 return &(found->second);
176 RenderThreadImpl* sender_;
177 CachedAreaMap cached_areas_;
178 CallbackList pending_callbacks_;
179 scoped_refptr<MessageThrottlingFilter> throttling_filter_;
182 DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender)
183 : sender_(sender),
184 throttling_filter_(new MessageThrottlingFilter(sender)) {
185 sender_->AddFilter(throttling_filter_.get());
188 DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::OpenCachedArea(
189 int64 namespace_id, const GURL& origin) {
190 std::string key = GetCachedAreaKey(namespace_id, origin);
191 if (CachedAreaHolder* holder = GetAreaHolder(key)) {
192 ++(holder->open_count_);
193 return holder->area_.get();
195 scoped_refptr<DOMStorageCachedArea> area =
196 new DOMStorageCachedArea(namespace_id, origin, this);
197 cached_areas_[key] = CachedAreaHolder(area.get(), 1, namespace_id);
198 return area.get();
201 void DomStorageDispatcher::ProxyImpl::CloseCachedArea(
202 DOMStorageCachedArea* area) {
203 std::string key = GetCachedAreaKey(area->namespace_id(), area->origin());
204 CachedAreaHolder* holder = GetAreaHolder(key);
205 DCHECK(holder);
206 DCHECK_EQ(holder->area_.get(), area);
207 DCHECK_GT(holder->open_count_, 0);
208 if (--(holder->open_count_) == 0) {
209 cached_areas_.erase(key);
213 DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::LookupCachedArea(
214 int64 namespace_id, const GURL& origin) {
215 std::string key = GetCachedAreaKey(namespace_id, origin);
216 CachedAreaHolder* holder = GetAreaHolder(key);
217 if (!holder)
218 return NULL;
219 return holder->area_.get();
222 void DomStorageDispatcher::ProxyImpl::ResetAllCachedAreas(int64 namespace_id) {
223 for (CachedAreaMap::iterator it = cached_areas_.begin();
224 it != cached_areas_.end();
225 ++it) {
226 if (it->second.namespace_id_ == namespace_id)
227 it->second.area_->Reset();
231 void DomStorageDispatcher::ProxyImpl::CompleteOnePendingCallback(bool success) {
232 PopPendingCallback().Run(success);
235 void DomStorageDispatcher::ProxyImpl::Shutdown() {
236 throttling_filter_->Shutdown();
237 sender_->RemoveFilter(throttling_filter_.get());
238 sender_ = NULL;
239 cached_areas_.clear();
240 pending_callbacks_.clear();
243 void DomStorageDispatcher::ProxyImpl::LoadArea(
244 int connection_id, DOMStorageValuesMap* values, bool* send_log_get_messages,
245 const CompletionCallback& callback) {
246 PushPendingCallback(callback);
247 throttling_filter_->SendThrottled(new DOMStorageHostMsg_LoadStorageArea(
248 connection_id, values, send_log_get_messages));
251 void DomStorageDispatcher::ProxyImpl::SetItem(
252 int connection_id, const base::string16& key,
253 const base::string16& value, const GURL& page_url,
254 const CompletionCallback& callback) {
255 PushPendingCallback(callback);
256 throttling_filter_->SendThrottled(new DOMStorageHostMsg_SetItem(
257 connection_id, key, value, page_url));
260 void DomStorageDispatcher::ProxyImpl::LogGetItem(
261 int connection_id, const base::string16& key,
262 const base::NullableString16& value) {
263 sender_->Send(new DOMStorageHostMsg_LogGetItem(connection_id, key, value));
266 void DomStorageDispatcher::ProxyImpl::RemoveItem(
267 int connection_id, const base::string16& key, const GURL& page_url,
268 const CompletionCallback& callback) {
269 PushPendingCallback(callback);
270 throttling_filter_->SendThrottled(new DOMStorageHostMsg_RemoveItem(
271 connection_id, key, page_url));
274 void DomStorageDispatcher::ProxyImpl::ClearArea(int connection_id,
275 const GURL& page_url,
276 const CompletionCallback& callback) {
277 PushPendingCallback(callback);
278 throttling_filter_->SendThrottled(new DOMStorageHostMsg_Clear(
279 connection_id, page_url));
282 // DomStorageDispatcher ------------------------------------------------
284 DomStorageDispatcher::DomStorageDispatcher()
285 : proxy_(new ProxyImpl(RenderThreadImpl::current())) {
288 DomStorageDispatcher::~DomStorageDispatcher() {
289 proxy_->Shutdown();
292 scoped_refptr<DOMStorageCachedArea> DomStorageDispatcher::OpenCachedArea(
293 int connection_id, int64 namespace_id, const GURL& origin) {
294 RenderThreadImpl::current()->Send(
295 new DOMStorageHostMsg_OpenStorageArea(
296 connection_id, namespace_id, origin));
297 return proxy_->OpenCachedArea(namespace_id, origin);
300 void DomStorageDispatcher::CloseCachedArea(
301 int connection_id, DOMStorageCachedArea* area) {
302 RenderThreadImpl::current()->Send(
303 new DOMStorageHostMsg_CloseStorageArea(connection_id));
304 proxy_->CloseCachedArea(area);
307 bool DomStorageDispatcher::OnMessageReceived(const IPC::Message& msg) {
308 bool handled = true;
309 IPC_BEGIN_MESSAGE_MAP(DomStorageDispatcher, msg)
310 IPC_MESSAGE_HANDLER(DOMStorageMsg_Event, OnStorageEvent)
311 IPC_MESSAGE_HANDLER(DOMStorageMsg_AsyncOperationComplete,
312 OnAsyncOperationComplete)
313 IPC_MESSAGE_HANDLER(DOMStorageMsg_ResetCachedValues,
314 OnResetCachedValues)
315 IPC_MESSAGE_UNHANDLED(handled = false)
316 IPC_END_MESSAGE_MAP()
317 return handled;
320 void DomStorageDispatcher::OnStorageEvent(
321 const DOMStorageMsg_Event_Params& params) {
322 RenderThreadImpl::current()->EnsureWebKitInitialized();
324 bool originated_in_process = params.connection_id != 0;
325 WebStorageAreaImpl* originating_area = NULL;
326 if (originated_in_process) {
327 originating_area = WebStorageAreaImpl::FromConnectionId(
328 params.connection_id);
329 } else {
330 DOMStorageCachedArea* cached_area = proxy_->LookupCachedArea(
331 params.namespace_id, params.origin);
332 if (cached_area)
333 cached_area->ApplyMutation(params.key, params.new_value);
336 if (params.namespace_id == kLocalStorageNamespaceId) {
337 blink::WebStorageEventDispatcher::dispatchLocalStorageEvent(
338 params.key,
339 params.old_value,
340 params.new_value,
341 params.origin,
342 params.page_url,
343 originating_area,
344 originated_in_process);
345 } else {
346 WebStorageNamespaceImpl
347 session_namespace_for_event_dispatch(params.namespace_id);
348 blink::WebStorageEventDispatcher::dispatchSessionStorageEvent(
349 params.key,
350 params.old_value,
351 params.new_value,
352 params.origin,
353 params.page_url,
354 session_namespace_for_event_dispatch,
355 originating_area,
356 originated_in_process);
360 void DomStorageDispatcher::OnAsyncOperationComplete(bool success) {
361 proxy_->CompleteOnePendingCallback(success);
364 void DomStorageDispatcher::OnResetCachedValues(int64 namespace_id) {
365 proxy_->ResetAllCachedAreas(namespace_id);
368 } // namespace content