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"
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"
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
{
35 explicit MessageThrottlingFilter(RenderThreadImpl
* sender
)
36 : pending_count_(0), sender_(sender
) {}
38 void SendThrottled(IPC::Message
* message
);
39 void Shutdown() { sender_
= NULL
; }
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_
;
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
);
72 const int kMaxPendingMessages
= 1000;
73 bool need_to_flush
= (IncrementPendingCount() > kMaxPendingMessages
) &&
75 sender_
->Send(message
);
77 sender_
->Send(new DOMStorageHostMsg_FlushMessages
);
78 DCHECK_EQ(0, GetPendingCount());
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());
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
{
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
);
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
;
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_
;
134 CachedAreaHolder() : open_count_(0) {}
135 CachedAreaHolder(DOMStorageCachedArea
* area
, int count
,
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);
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())
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
)
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
);
197 void DomStorageDispatcher::ProxyImpl::CloseCachedArea(
198 DOMStorageCachedArea
* area
) {
199 std::string key
= GetCachedAreaKey(area
->namespace_id(), area
->origin());
200 CachedAreaHolder
* holder
= GetAreaHolder(key
);
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
);
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();
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());
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() {
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
) {
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
,
311 IPC_MESSAGE_UNHANDLED(handled
= false)
312 IPC_END_MESSAGE_MAP()
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
);
326 DOMStorageCachedArea
* cached_area
= proxy_
->LookupCachedArea(
327 params
.namespace_id
, params
.origin
);
329 cached_area
->ApplyMutation(params
.key
, params
.new_value
);
332 if (params
.namespace_id
== kLocalStorageNamespaceId
) {
333 blink::WebStorageEventDispatcher::dispatchLocalStorageEvent(
340 originated_in_process
);
342 WebStorageNamespaceImpl
343 session_namespace_for_event_dispatch(params
.namespace_id
);
344 blink::WebStorageEventDispatcher::dispatchSessionStorageEvent(
350 session_namespace_for_event_dispatch
,
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