blob: a8a37c989a410e89388496ca5da46ec99b0edd65 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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.
4
5#include "content/renderer/dom_storage/dom_storage_dispatcher.h"
6
7#include <list>
8#include <map>
9
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010010#include "base/strings/string_number_conversions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000011#include "base/synchronization/lock.h"
Ben Murdochbb1529c2013-08-08 10:24:53 +010012#include "content/common/dom_storage/dom_storage_messages.h"
13#include "content/common/dom_storage/dom_storage_types.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010014#include "content/renderer/dom_storage/dom_storage_cached_area.h"
15#include "content/renderer/dom_storage/dom_storage_proxy.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000016#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"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010019#include "third_party/WebKit/public/platform/Platform.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010020#include "third_party/WebKit/public/web/WebKit.h"
21#include "third_party/WebKit/public/web/WebStorageEventDispatcher.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000022
23namespace content {
24
25namespace {
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.
32class MessageThrottlingFilter : public IPC::ChannelProxy::MessageFilter {
33 public:
34 explicit MessageThrottlingFilter(RenderThreadImpl* sender)
35 : pending_count_(0), sender_(sender) {}
36
37 void SendThrottled(IPC::Message* message);
38 void Shutdown() { sender_ = NULL; }
39
40 private:
41 virtual ~MessageThrottlingFilter() {}
42
43 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
44
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_;
52 }
53
54 base::Lock lock_;
55 int pending_count_;
56 RenderThreadImpl* sender_;
57};
58
59void 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;
70 }
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());
80 }
81}
82
83bool MessageThrottlingFilter::OnMessageReceived(const IPC::Message& message) {
84 if (message.type() == DOMStorageMsg_AsyncOperationComplete::ID) {
85 DecrementPendingCount();
86 DCHECK_LE(0, GetPendingCount());
87 }
88 return false;
89}
90} // namespace
91
92// ProxyImpl -----------------------------------------------------
Ben Murdochbb1529c2013-08-08 10:24:53 +010093// An implementation of the DOMStorageProxy interface in terms of IPC.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000094// This class also manages the collection of cached areas and pending
95// operations awaiting completion callbacks.
Ben Murdochbb1529c2013-08-08 10:24:53 +010096class DomStorageDispatcher::ProxyImpl : public DOMStorageProxy {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000097 public:
98 explicit ProxyImpl(RenderThreadImpl* sender);
99
100 // Methods for use by DomStorageDispatcher directly.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100101 DOMStorageCachedArea* OpenCachedArea(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000102 int64 namespace_id, const GURL& origin);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100103 void CloseCachedArea(DOMStorageCachedArea* area);
104 DOMStorageCachedArea* LookupCachedArea(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000105 int64 namespace_id, const GURL& origin);
106 void CompleteOnePendingCallback(bool success);
107 void Shutdown();
108
Ben Murdochbb1529c2013-08-08 10:24:53 +0100109 // DOMStorageProxy interface for use by DOMStorageCachedArea.
110 virtual void LoadArea(int connection_id, DOMStorageValuesMap* values,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000111 const CompletionCallback& callback) OVERRIDE;
112 virtual void SetItem(int connection_id, const string16& key,
113 const string16& value, const GURL& page_url,
114 const CompletionCallback& callback) OVERRIDE;
115 virtual void RemoveItem(int connection_id, const string16& key,
116 const GURL& page_url,
117 const CompletionCallback& callback) OVERRIDE;
118 virtual void ClearArea(int connection_id,
119 const GURL& page_url,
120 const CompletionCallback& callback) OVERRIDE;
121
122 private:
123 // Struct to hold references to our contained areas and
124 // to keep track of how many tabs have a given area open.
125 struct CachedAreaHolder {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100126 scoped_refptr<DOMStorageCachedArea> area_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000127 int open_count_;
128 CachedAreaHolder() : open_count_(0) {}
Ben Murdochbb1529c2013-08-08 10:24:53 +0100129 CachedAreaHolder(DOMStorageCachedArea* area, int count)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000130 : area_(area), open_count_(count) {}
131 };
132 typedef std::map<std::string, CachedAreaHolder> CachedAreaMap;
133 typedef std::list<CompletionCallback> CallbackList;
134
135 virtual ~ProxyImpl() {
136 }
137
138 // Sudden termination is disabled when there are callbacks pending
139 // to more reliably commit changes during shutdown.
140 void PushPendingCallback(const CompletionCallback& callback) {
141 if (pending_callbacks_.empty())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000142 WebKit::Platform::current()->suddenTerminationChanged(false);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000143 pending_callbacks_.push_back(callback);
144 }
145
146 CompletionCallback PopPendingCallback() {
147 CompletionCallback callback = pending_callbacks_.front();
148 pending_callbacks_.pop_front();
149 if (pending_callbacks_.empty())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000150 WebKit::Platform::current()->suddenTerminationChanged(true);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000151 return callback;
152 }
153
154 std::string GetCachedAreaKey(int64 namespace_id, const GURL& origin) {
155 return base::Int64ToString(namespace_id) + origin.spec();
156 }
157
158 CachedAreaHolder* GetAreaHolder(const std::string& key) {
159 CachedAreaMap::iterator found = cached_areas_.find(key);
160 if (found == cached_areas_.end())
161 return NULL;
162 return &(found->second);
163 }
164
165 RenderThreadImpl* sender_;
166 CachedAreaMap cached_areas_;
167 CallbackList pending_callbacks_;
168 scoped_refptr<MessageThrottlingFilter> throttling_filter_;
169};
170
171DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender)
172 : sender_(sender),
173 throttling_filter_(new MessageThrottlingFilter(sender)) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100174 sender_->AddFilter(throttling_filter_.get());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000175}
176
Ben Murdochbb1529c2013-08-08 10:24:53 +0100177DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::OpenCachedArea(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000178 int64 namespace_id, const GURL& origin) {
179 std::string key = GetCachedAreaKey(namespace_id, origin);
180 if (CachedAreaHolder* holder = GetAreaHolder(key)) {
181 ++(holder->open_count_);
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100182 return holder->area_.get();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000183 }
Ben Murdochbb1529c2013-08-08 10:24:53 +0100184 scoped_refptr<DOMStorageCachedArea> area =
185 new DOMStorageCachedArea(namespace_id, origin, this);
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100186 cached_areas_[key] = CachedAreaHolder(area.get(), 1);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000187 return area.get();
188}
189
190void DomStorageDispatcher::ProxyImpl::CloseCachedArea(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100191 DOMStorageCachedArea* area) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000192 std::string key = GetCachedAreaKey(area->namespace_id(), area->origin());
193 CachedAreaHolder* holder = GetAreaHolder(key);
194 DCHECK(holder);
195 DCHECK_EQ(holder->area_.get(), area);
196 DCHECK_GT(holder->open_count_, 0);
197 if (--(holder->open_count_) == 0) {
198 cached_areas_.erase(key);
199 }
200}
201
Ben Murdochbb1529c2013-08-08 10:24:53 +0100202DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::LookupCachedArea(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000203 int64 namespace_id, const GURL& origin) {
204 std::string key = GetCachedAreaKey(namespace_id, origin);
205 CachedAreaHolder* holder = GetAreaHolder(key);
206 if (!holder)
207 return NULL;
208 return holder->area_.get();
209}
210
211void DomStorageDispatcher::ProxyImpl::CompleteOnePendingCallback(bool success) {
212 PopPendingCallback().Run(success);
213}
214
215void DomStorageDispatcher::ProxyImpl::Shutdown() {
216 throttling_filter_->Shutdown();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100217 sender_->RemoveFilter(throttling_filter_.get());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000218 sender_ = NULL;
219 cached_areas_.clear();
220 pending_callbacks_.clear();
221}
222
223void DomStorageDispatcher::ProxyImpl::LoadArea(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100224 int connection_id, DOMStorageValuesMap* values,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000225 const CompletionCallback& callback) {
226 PushPendingCallback(callback);
227 throttling_filter_->SendThrottled(new DOMStorageHostMsg_LoadStorageArea(
228 connection_id, values));
229}
230
231void DomStorageDispatcher::ProxyImpl::SetItem(
232 int connection_id, const string16& key,
233 const string16& value, const GURL& page_url,
234 const CompletionCallback& callback) {
235 PushPendingCallback(callback);
236 throttling_filter_->SendThrottled(new DOMStorageHostMsg_SetItem(
237 connection_id, key, value, page_url));
238}
239
240void DomStorageDispatcher::ProxyImpl::RemoveItem(
241 int connection_id, const string16& key, const GURL& page_url,
242 const CompletionCallback& callback) {
243 PushPendingCallback(callback);
244 throttling_filter_->SendThrottled(new DOMStorageHostMsg_RemoveItem(
245 connection_id, key, page_url));
246}
247
248void DomStorageDispatcher::ProxyImpl::ClearArea(int connection_id,
249 const GURL& page_url,
250 const CompletionCallback& callback) {
251 PushPendingCallback(callback);
252 throttling_filter_->SendThrottled(new DOMStorageHostMsg_Clear(
253 connection_id, page_url));
254}
255
256// DomStorageDispatcher ------------------------------------------------
257
258DomStorageDispatcher::DomStorageDispatcher()
259 : proxy_(new ProxyImpl(RenderThreadImpl::current())) {
260}
261
262DomStorageDispatcher::~DomStorageDispatcher() {
263 proxy_->Shutdown();
264}
265
Ben Murdochbb1529c2013-08-08 10:24:53 +0100266scoped_refptr<DOMStorageCachedArea> DomStorageDispatcher::OpenCachedArea(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000267 int connection_id, int64 namespace_id, const GURL& origin) {
268 RenderThreadImpl::current()->Send(
269 new DOMStorageHostMsg_OpenStorageArea(
270 connection_id, namespace_id, origin));
271 return proxy_->OpenCachedArea(namespace_id, origin);
272}
273
274void DomStorageDispatcher::CloseCachedArea(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100275 int connection_id, DOMStorageCachedArea* area) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000276 RenderThreadImpl::current()->Send(
277 new DOMStorageHostMsg_CloseStorageArea(connection_id));
278 proxy_->CloseCachedArea(area);
279}
280
281bool DomStorageDispatcher::OnMessageReceived(const IPC::Message& msg) {
282 bool handled = true;
283 IPC_BEGIN_MESSAGE_MAP(DomStorageDispatcher, msg)
284 IPC_MESSAGE_HANDLER(DOMStorageMsg_Event, OnStorageEvent)
285 IPC_MESSAGE_HANDLER(DOMStorageMsg_AsyncOperationComplete,
286 OnAsyncOperationComplete)
287 IPC_MESSAGE_UNHANDLED(handled = false)
288 IPC_END_MESSAGE_MAP()
289 return handled;
290}
291
292void DomStorageDispatcher::OnStorageEvent(
293 const DOMStorageMsg_Event_Params& params) {
294 RenderThreadImpl::current()->EnsureWebKitInitialized();
295
296 bool originated_in_process = params.connection_id != 0;
297 WebStorageAreaImpl* originating_area = NULL;
298 if (originated_in_process) {
299 originating_area = WebStorageAreaImpl::FromConnectionId(
300 params.connection_id);
301 } else {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100302 DOMStorageCachedArea* cached_area = proxy_->LookupCachedArea(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000303 params.namespace_id, params.origin);
304 if (cached_area)
305 cached_area->ApplyMutation(params.key, params.new_value);
306 }
307
Ben Murdochbb1529c2013-08-08 10:24:53 +0100308 if (params.namespace_id == kLocalStorageNamespaceId) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000309 WebKit::WebStorageEventDispatcher::dispatchLocalStorageEvent(
310 params.key,
311 params.old_value,
312 params.new_value,
313 params.origin,
314 params.page_url,
315 originating_area,
316 originated_in_process);
317 } else {
318 WebStorageNamespaceImpl
319 session_namespace_for_event_dispatch(params.namespace_id);
320 WebKit::WebStorageEventDispatcher::dispatchSessionStorageEvent(
321 params.key,
322 params.old_value,
323 params.new_value,
324 params.origin,
325 params.page_url,
326 session_namespace_for_event_dispatch,
327 originating_area,
328 originated_in_process);
329 }
330}
331
332void DomStorageDispatcher::OnAsyncOperationComplete(bool success) {
333 proxy_->CompleteOnePendingCallback(success);
334}
335
336} // namespace content