Replace FINAL and OVERRIDE with their C++11 counterparts in content/renderer
[chromium-blink-merge.git] / content / renderer / dom_storage / dom_storage_dispatcher.cc
blob6d4496632bb1afe8c741dcab453ef7b875145f61
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 virtual ~MessageThrottlingFilter() {}
44 virtual 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 virtual void LoadArea(int connection_id, DOMStorageValuesMap* values,
113 bool* send_log_get_messages,
114 const CompletionCallback& callback) override;
115 virtual void SetItem(int connection_id, const base::string16& key,
116 const base::string16& value, const GURL& page_url,
117 const CompletionCallback& callback) override;
118 virtual void LogGetItem(int connection_id, const base::string16& key,
119 const base::NullableString16& value) override;
120 virtual void RemoveItem(int connection_id, const base::string16& key,
121 const GURL& page_url,
122 const CompletionCallback& callback) override;
123 virtual 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 int64 namespace_id_;
134 CachedAreaHolder() : open_count_(0) {}
135 CachedAreaHolder(DOMStorageCachedArea* area, int count,
136 int64 namespace_id)
137 : area_(area), open_count_(count), namespace_id_(namespace_id) {}
139 typedef std::map<std::string, CachedAreaHolder> CachedAreaMap;
140 typedef std::list<CompletionCallback> CallbackList;
142 virtual ~ProxyImpl() {
145 // Sudden termination is disabled when there are callbacks pending
146 // to more reliably commit changes during shutdown.
147 void PushPendingCallback(const CompletionCallback& callback) {
148 if (pending_callbacks_.empty())
149 blink::Platform::current()->suddenTerminationChanged(false);
150 pending_callbacks_.push_back(callback);
153 CompletionCallback PopPendingCallback() {
154 CompletionCallback callback = pending_callbacks_.front();
155 pending_callbacks_.pop_front();
156 if (pending_callbacks_.empty())
157 blink::Platform::current()->suddenTerminationChanged(true);
158 return callback;
161 std::string GetCachedAreaKey(int64 namespace_id, const GURL& origin) {
162 return base::Int64ToString(namespace_id) + origin.spec();
165 CachedAreaHolder* GetAreaHolder(const std::string& key) {
166 CachedAreaMap::iterator found = cached_areas_.find(key);
167 if (found == cached_areas_.end())
168 return NULL;
169 return &(found->second);
172 RenderThreadImpl* sender_;
173 CachedAreaMap cached_areas_;
174 CallbackList pending_callbacks_;
175 scoped_refptr<MessageThrottlingFilter> throttling_filter_;
178 DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender)
179 : sender_(sender),
180 throttling_filter_(new MessageThrottlingFilter(sender)) {
181 sender_->AddFilter(throttling_filter_.get());
184 DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::OpenCachedArea(
185 int64 namespace_id, const GURL& origin) {
186 std::string key = GetCachedAreaKey(namespace_id, origin);
187 if (CachedAreaHolder* holder = GetAreaHolder(key)) {
188 ++(holder->open_count_);
189 return holder->area_.get();
191 scoped_refptr<DOMStorageCachedArea> area =
192 new DOMStorageCachedArea(namespace_id, origin, this);
193 cached_areas_[key] = CachedAreaHolder(area.get(), 1, namespace_id);
194 return area.get();
197 void DomStorageDispatcher::ProxyImpl::CloseCachedArea(
198 DOMStorageCachedArea* area) {
199 std::string key = GetCachedAreaKey(area->namespace_id(), area->origin());
200 CachedAreaHolder* holder = GetAreaHolder(key);
201 DCHECK(holder);
202 DCHECK_EQ(holder->area_.get(), area);
203 DCHECK_GT(holder->open_count_, 0);
204 if (--(holder->open_count_) == 0) {
205 cached_areas_.erase(key);
209 DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::LookupCachedArea(
210 int64 namespace_id, const GURL& origin) {
211 std::string key = GetCachedAreaKey(namespace_id, origin);
212 CachedAreaHolder* holder = GetAreaHolder(key);
213 if (!holder)
214 return NULL;
215 return holder->area_.get();
218 void DomStorageDispatcher::ProxyImpl::ResetAllCachedAreas(int64 namespace_id) {
219 for (CachedAreaMap::iterator it = cached_areas_.begin();
220 it != cached_areas_.end();
221 ++it) {
222 if (it->second.namespace_id_ == namespace_id)
223 it->second.area_->Reset();
227 void DomStorageDispatcher::ProxyImpl::CompleteOnePendingCallback(bool success) {
228 PopPendingCallback().Run(success);
231 void DomStorageDispatcher::ProxyImpl::Shutdown() {
232 throttling_filter_->Shutdown();
233 sender_->RemoveFilter(throttling_filter_.get());
234 sender_ = NULL;
235 cached_areas_.clear();
236 pending_callbacks_.clear();
239 void DomStorageDispatcher::ProxyImpl::LoadArea(
240 int connection_id, DOMStorageValuesMap* values, bool* send_log_get_messages,
241 const CompletionCallback& callback) {
242 PushPendingCallback(callback);
243 throttling_filter_->SendThrottled(new DOMStorageHostMsg_LoadStorageArea(
244 connection_id, values, send_log_get_messages));
247 void DomStorageDispatcher::ProxyImpl::SetItem(
248 int connection_id, const base::string16& key,
249 const base::string16& value, const GURL& page_url,
250 const CompletionCallback& callback) {
251 PushPendingCallback(callback);
252 throttling_filter_->SendThrottled(new DOMStorageHostMsg_SetItem(
253 connection_id, key, value, page_url));
256 void DomStorageDispatcher::ProxyImpl::LogGetItem(
257 int connection_id, const base::string16& key,
258 const base::NullableString16& value) {
259 sender_->Send(new DOMStorageHostMsg_LogGetItem(connection_id, key, value));
262 void DomStorageDispatcher::ProxyImpl::RemoveItem(
263 int connection_id, const base::string16& key, const GURL& page_url,
264 const CompletionCallback& callback) {
265 PushPendingCallback(callback);
266 throttling_filter_->SendThrottled(new DOMStorageHostMsg_RemoveItem(
267 connection_id, key, page_url));
270 void DomStorageDispatcher::ProxyImpl::ClearArea(int connection_id,
271 const GURL& page_url,
272 const CompletionCallback& callback) {
273 PushPendingCallback(callback);
274 throttling_filter_->SendThrottled(new DOMStorageHostMsg_Clear(
275 connection_id, page_url));
278 // DomStorageDispatcher ------------------------------------------------
280 DomStorageDispatcher::DomStorageDispatcher()
281 : proxy_(new ProxyImpl(RenderThreadImpl::current())) {
284 DomStorageDispatcher::~DomStorageDispatcher() {
285 proxy_->Shutdown();
288 scoped_refptr<DOMStorageCachedArea> DomStorageDispatcher::OpenCachedArea(
289 int connection_id, int64 namespace_id, const GURL& origin) {
290 RenderThreadImpl::current()->Send(
291 new DOMStorageHostMsg_OpenStorageArea(
292 connection_id, namespace_id, origin));
293 return proxy_->OpenCachedArea(namespace_id, origin);
296 void DomStorageDispatcher::CloseCachedArea(
297 int connection_id, DOMStorageCachedArea* area) {
298 RenderThreadImpl::current()->Send(
299 new DOMStorageHostMsg_CloseStorageArea(connection_id));
300 proxy_->CloseCachedArea(area);
303 bool DomStorageDispatcher::OnMessageReceived(const IPC::Message& msg) {
304 bool handled = true;
305 IPC_BEGIN_MESSAGE_MAP(DomStorageDispatcher, msg)
306 IPC_MESSAGE_HANDLER(DOMStorageMsg_Event, OnStorageEvent)
307 IPC_MESSAGE_HANDLER(DOMStorageMsg_AsyncOperationComplete,
308 OnAsyncOperationComplete)
309 IPC_MESSAGE_HANDLER(DOMStorageMsg_ResetCachedValues,
310 OnResetCachedValues)
311 IPC_MESSAGE_UNHANDLED(handled = false)
312 IPC_END_MESSAGE_MAP()
313 return handled;
316 void DomStorageDispatcher::OnStorageEvent(
317 const DOMStorageMsg_Event_Params& params) {
318 RenderThreadImpl::current()->EnsureWebKitInitialized();
320 bool originated_in_process = params.connection_id != 0;
321 WebStorageAreaImpl* originating_area = NULL;
322 if (originated_in_process) {
323 originating_area = WebStorageAreaImpl::FromConnectionId(
324 params.connection_id);
325 } else {
326 DOMStorageCachedArea* cached_area = proxy_->LookupCachedArea(
327 params.namespace_id, params.origin);
328 if (cached_area)
329 cached_area->ApplyMutation(params.key, params.new_value);
332 if (params.namespace_id == kLocalStorageNamespaceId) {
333 blink::WebStorageEventDispatcher::dispatchLocalStorageEvent(
334 params.key,
335 params.old_value,
336 params.new_value,
337 params.origin,
338 params.page_url,
339 originating_area,
340 originated_in_process);
341 } else {
342 WebStorageNamespaceImpl
343 session_namespace_for_event_dispatch(params.namespace_id);
344 blink::WebStorageEventDispatcher::dispatchSessionStorageEvent(
345 params.key,
346 params.old_value,
347 params.new_value,
348 params.origin,
349 params.page_url,
350 session_namespace_for_event_dispatch,
351 originating_area,
352 originated_in_process);
356 void DomStorageDispatcher::OnAsyncOperationComplete(bool success) {
357 proxy_->CompleteOnePendingCallback(success);
360 void DomStorageDispatcher::OnResetCachedValues(int64 namespace_id) {
361 proxy_->ResetAllCachedAreas(namespace_id);
364 } // namespace content