IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / renderer / dom_storage / dom_storage_dispatcher.cc
blob477251c33a9b7c745242f3b1dff2c0e7edc73a58
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 "third_party/WebKit/public/platform/Platform.h"
20 #include "third_party/WebKit/public/web/WebKit.h"
21 #include "third_party/WebKit/public/web/WebStorageEventDispatcher.h"
23 namespace content {
25 namespace {
26 // MessageThrottlingFilter -------------------------------------------
27 // Used to limit the number of ipc messages pending completion so we
28 // don't overwhelm the main browser process. When the limit is reached,
29 // a synchronous message is sent to flush all pending messages thru.
30 // We expect to receive an 'ack' for each message sent. This object
31 // observes receipt of the acks on the IPC thread to decrement a counter.
32 class MessageThrottlingFilter : public IPC::ChannelProxy::MessageFilter {
33 public:
34 explicit MessageThrottlingFilter(RenderThreadImpl* sender)
35 : pending_count_(0), sender_(sender) {}
37 void SendThrottled(IPC::Message* message);
38 void Shutdown() { sender_ = NULL; }
40 private:
41 virtual ~MessageThrottlingFilter() {}
43 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
45 int GetPendingCount() { return IncrementPendingCountN(0); }
46 int IncrementPendingCount() { return IncrementPendingCountN(1); }
47 int DecrementPendingCount() { return IncrementPendingCountN(-1); }
48 int IncrementPendingCountN(int increment) {
49 base::AutoLock locker(lock_);
50 pending_count_ += increment;
51 return pending_count_;
54 base::Lock lock_;
55 int pending_count_;
56 RenderThreadImpl* sender_;
59 void MessageThrottlingFilter::SendThrottled(IPC::Message* message) {
60 // Should only be used for sending of messages which will be acknowledged
61 // with a separate DOMStorageMsg_AsyncOperationComplete message.
62 DCHECK(message->type() == DOMStorageHostMsg_LoadStorageArea::ID ||
63 message->type() == DOMStorageHostMsg_SetItem::ID ||
64 message->type() == DOMStorageHostMsg_RemoveItem::ID ||
65 message->type() == DOMStorageHostMsg_Clear::ID);
66 DCHECK(sender_);
67 if (!sender_) {
68 delete message;
69 return;
71 const int kMaxPendingMessages = 1000;
72 bool need_to_flush = (IncrementPendingCount() > kMaxPendingMessages) &&
73 !message->is_sync();
74 sender_->Send(message);
75 if (need_to_flush) {
76 sender_->Send(new DOMStorageHostMsg_FlushMessages);
77 DCHECK_EQ(0, GetPendingCount());
78 } else {
79 DCHECK_LE(0, GetPendingCount());
83 bool MessageThrottlingFilter::OnMessageReceived(const IPC::Message& message) {
84 if (message.type() == DOMStorageMsg_AsyncOperationComplete::ID) {
85 DecrementPendingCount();
86 DCHECK_LE(0, GetPendingCount());
88 return false;
90 } // namespace
92 // ProxyImpl -----------------------------------------------------
93 // An implementation of the DOMStorageProxy interface in terms of IPC.
94 // This class also manages the collection of cached areas and pending
95 // operations awaiting completion callbacks.
96 class DomStorageDispatcher::ProxyImpl : public DOMStorageProxy {
97 public:
98 explicit ProxyImpl(RenderThreadImpl* sender);
100 // Methods for use by DomStorageDispatcher directly.
101 DOMStorageCachedArea* OpenCachedArea(
102 int64 namespace_id, const GURL& origin);
103 void CloseCachedArea(DOMStorageCachedArea* area);
104 DOMStorageCachedArea* LookupCachedArea(
105 int64 namespace_id, const GURL& origin);
106 void ResetAllCachedAreas(int64 namespace_id);
107 void CompleteOnePendingCallback(bool success);
108 void Shutdown();
110 // DOMStorageProxy interface for use by DOMStorageCachedArea.
111 virtual void LoadArea(int connection_id, DOMStorageValuesMap* values,
112 bool* send_log_get_messages,
113 const CompletionCallback& callback) OVERRIDE;
114 virtual void SetItem(int connection_id, const base::string16& key,
115 const base::string16& value, const GURL& page_url,
116 const CompletionCallback& callback) OVERRIDE;
117 virtual void LogGetItem(int connection_id, const base::string16& key,
118 const base::NullableString16& value) OVERRIDE;
119 virtual void RemoveItem(int connection_id, const base::string16& key,
120 const GURL& page_url,
121 const CompletionCallback& callback) OVERRIDE;
122 virtual void ClearArea(int connection_id,
123 const GURL& page_url,
124 const CompletionCallback& callback) OVERRIDE;
126 private:
127 // Struct to hold references to our contained areas and
128 // to keep track of how many tabs have a given area open.
129 struct CachedAreaHolder {
130 scoped_refptr<DOMStorageCachedArea> area_;
131 int open_count_;
132 int64 namespace_id_;
133 CachedAreaHolder() : open_count_(0) {}
134 CachedAreaHolder(DOMStorageCachedArea* area, int count,
135 int64 namespace_id)
136 : area_(area), open_count_(count), namespace_id_(namespace_id) {}
138 typedef std::map<std::string, CachedAreaHolder> CachedAreaMap;
139 typedef std::list<CompletionCallback> CallbackList;
141 virtual ~ProxyImpl() {
144 // Sudden termination is disabled when there are callbacks pending
145 // to more reliably commit changes during shutdown.
146 void PushPendingCallback(const CompletionCallback& callback) {
147 if (pending_callbacks_.empty())
148 blink::Platform::current()->suddenTerminationChanged(false);
149 pending_callbacks_.push_back(callback);
152 CompletionCallback PopPendingCallback() {
153 CompletionCallback callback = pending_callbacks_.front();
154 pending_callbacks_.pop_front();
155 if (pending_callbacks_.empty())
156 blink::Platform::current()->suddenTerminationChanged(true);
157 return callback;
160 std::string GetCachedAreaKey(int64 namespace_id, const GURL& origin) {
161 return base::Int64ToString(namespace_id) + origin.spec();
164 CachedAreaHolder* GetAreaHolder(const std::string& key) {
165 CachedAreaMap::iterator found = cached_areas_.find(key);
166 if (found == cached_areas_.end())
167 return NULL;
168 return &(found->second);
171 RenderThreadImpl* sender_;
172 CachedAreaMap cached_areas_;
173 CallbackList pending_callbacks_;
174 scoped_refptr<MessageThrottlingFilter> throttling_filter_;
177 DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender)
178 : sender_(sender),
179 throttling_filter_(new MessageThrottlingFilter(sender)) {
180 sender_->AddFilter(throttling_filter_.get());
183 DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::OpenCachedArea(
184 int64 namespace_id, const GURL& origin) {
185 std::string key = GetCachedAreaKey(namespace_id, origin);
186 if (CachedAreaHolder* holder = GetAreaHolder(key)) {
187 ++(holder->open_count_);
188 return holder->area_.get();
190 scoped_refptr<DOMStorageCachedArea> area =
191 new DOMStorageCachedArea(namespace_id, origin, this);
192 cached_areas_[key] = CachedAreaHolder(area.get(), 1, namespace_id);
193 return area.get();
196 void DomStorageDispatcher::ProxyImpl::CloseCachedArea(
197 DOMStorageCachedArea* area) {
198 std::string key = GetCachedAreaKey(area->namespace_id(), area->origin());
199 CachedAreaHolder* holder = GetAreaHolder(key);
200 DCHECK(holder);
201 DCHECK_EQ(holder->area_.get(), area);
202 DCHECK_GT(holder->open_count_, 0);
203 if (--(holder->open_count_) == 0) {
204 cached_areas_.erase(key);
208 DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::LookupCachedArea(
209 int64 namespace_id, const GURL& origin) {
210 std::string key = GetCachedAreaKey(namespace_id, origin);
211 CachedAreaHolder* holder = GetAreaHolder(key);
212 if (!holder)
213 return NULL;
214 return holder->area_.get();
217 void DomStorageDispatcher::ProxyImpl::ResetAllCachedAreas(int64 namespace_id) {
218 for (CachedAreaMap::iterator it = cached_areas_.begin();
219 it != cached_areas_.end();
220 ++it) {
221 if (it->second.namespace_id_ == namespace_id)
222 it->second.area_->Reset();
226 void DomStorageDispatcher::ProxyImpl::CompleteOnePendingCallback(bool success) {
227 PopPendingCallback().Run(success);
230 void DomStorageDispatcher::ProxyImpl::Shutdown() {
231 throttling_filter_->Shutdown();
232 sender_->RemoveFilter(throttling_filter_.get());
233 sender_ = NULL;
234 cached_areas_.clear();
235 pending_callbacks_.clear();
238 void DomStorageDispatcher::ProxyImpl::LoadArea(
239 int connection_id, DOMStorageValuesMap* values, bool* send_log_get_messages,
240 const CompletionCallback& callback) {
241 PushPendingCallback(callback);
242 throttling_filter_->SendThrottled(new DOMStorageHostMsg_LoadStorageArea(
243 connection_id, values, send_log_get_messages));
246 void DomStorageDispatcher::ProxyImpl::SetItem(
247 int connection_id, const base::string16& key,
248 const base::string16& value, const GURL& page_url,
249 const CompletionCallback& callback) {
250 PushPendingCallback(callback);
251 throttling_filter_->SendThrottled(new DOMStorageHostMsg_SetItem(
252 connection_id, key, value, page_url));
255 void DomStorageDispatcher::ProxyImpl::LogGetItem(
256 int connection_id, const base::string16& key,
257 const base::NullableString16& value) {
258 sender_->Send(new DOMStorageHostMsg_LogGetItem(connection_id, key, value));
261 void DomStorageDispatcher::ProxyImpl::RemoveItem(
262 int connection_id, const base::string16& key, const GURL& page_url,
263 const CompletionCallback& callback) {
264 PushPendingCallback(callback);
265 throttling_filter_->SendThrottled(new DOMStorageHostMsg_RemoveItem(
266 connection_id, key, page_url));
269 void DomStorageDispatcher::ProxyImpl::ClearArea(int connection_id,
270 const GURL& page_url,
271 const CompletionCallback& callback) {
272 PushPendingCallback(callback);
273 throttling_filter_->SendThrottled(new DOMStorageHostMsg_Clear(
274 connection_id, page_url));
277 // DomStorageDispatcher ------------------------------------------------
279 DomStorageDispatcher::DomStorageDispatcher()
280 : proxy_(new ProxyImpl(RenderThreadImpl::current())) {
283 DomStorageDispatcher::~DomStorageDispatcher() {
284 proxy_->Shutdown();
287 scoped_refptr<DOMStorageCachedArea> DomStorageDispatcher::OpenCachedArea(
288 int connection_id, int64 namespace_id, const GURL& origin) {
289 RenderThreadImpl::current()->Send(
290 new DOMStorageHostMsg_OpenStorageArea(
291 connection_id, namespace_id, origin));
292 return proxy_->OpenCachedArea(namespace_id, origin);
295 void DomStorageDispatcher::CloseCachedArea(
296 int connection_id, DOMStorageCachedArea* area) {
297 RenderThreadImpl::current()->Send(
298 new DOMStorageHostMsg_CloseStorageArea(connection_id));
299 proxy_->CloseCachedArea(area);
302 bool DomStorageDispatcher::OnMessageReceived(const IPC::Message& msg) {
303 bool handled = true;
304 IPC_BEGIN_MESSAGE_MAP(DomStorageDispatcher, msg)
305 IPC_MESSAGE_HANDLER(DOMStorageMsg_Event, OnStorageEvent)
306 IPC_MESSAGE_HANDLER(DOMStorageMsg_AsyncOperationComplete,
307 OnAsyncOperationComplete)
308 IPC_MESSAGE_HANDLER(DOMStorageMsg_ResetCachedValues,
309 OnResetCachedValues)
310 IPC_MESSAGE_UNHANDLED(handled = false)
311 IPC_END_MESSAGE_MAP()
312 return handled;
315 void DomStorageDispatcher::OnStorageEvent(
316 const DOMStorageMsg_Event_Params& params) {
317 RenderThreadImpl::current()->EnsureWebKitInitialized();
319 bool originated_in_process = params.connection_id != 0;
320 WebStorageAreaImpl* originating_area = NULL;
321 if (originated_in_process) {
322 originating_area = WebStorageAreaImpl::FromConnectionId(
323 params.connection_id);
324 } else {
325 DOMStorageCachedArea* cached_area = proxy_->LookupCachedArea(
326 params.namespace_id, params.origin);
327 if (cached_area)
328 cached_area->ApplyMutation(params.key, params.new_value);
331 if (params.namespace_id == kLocalStorageNamespaceId) {
332 blink::WebStorageEventDispatcher::dispatchLocalStorageEvent(
333 params.key,
334 params.old_value,
335 params.new_value,
336 params.origin,
337 params.page_url,
338 originating_area,
339 originated_in_process);
340 } else {
341 WebStorageNamespaceImpl
342 session_namespace_for_event_dispatch(params.namespace_id);
343 blink::WebStorageEventDispatcher::dispatchSessionStorageEvent(
344 params.key,
345 params.old_value,
346 params.new_value,
347 params.origin,
348 params.page_url,
349 session_namespace_for_event_dispatch,
350 originating_area,
351 originated_in_process);
355 void DomStorageDispatcher::OnAsyncOperationComplete(bool success) {
356 proxy_->CompleteOnePendingCallback(success);
359 void DomStorageDispatcher::OnResetCachedValues(int64 namespace_id) {
360 proxy_->ResetAllCachedAreas(namespace_id);
363 } // namespace content