Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / renderer / dom_storage / dom_storage_dispatcher.cc
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.
4
5 #include "content/renderer/dom_storage/dom_storage_dispatcher.h"
6
7 #include <list>
8 #include <map>
9
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"
23
24 namespace content {
25
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) {}
37
38   void SendThrottled(IPC::Message* message);
39   void Shutdown() { sender_ = NULL; }
40
41  private:
42   ~MessageThrottlingFilter() override {}
43
44   bool OnMessageReceived(const IPC::Message& message) override;
45
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_;
53   }
54
55   base::Lock lock_;
56   int pending_count_;
57   RenderThreadImpl* sender_;
58 };
59
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;
71   }
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());
81   }
82 }
83
84 bool MessageThrottlingFilter::OnMessageReceived(const IPC::Message& message) {
85   if (message.type() == DOMStorageMsg_AsyncOperationComplete::ID) {
86     DecrementPendingCount();
87     DCHECK_LE(0, GetPendingCount());
88   }
89   return false;
90 }
91 }  // namespace
92
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);
100
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();
110
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;
131
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) {}
143   };
144   typedef std::map<std::string, CachedAreaHolder> CachedAreaMap;
145   typedef std::list<CompletionCallback> CallbackList;
146
147   ~ProxyImpl() override {}
148
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);
155   }
156
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;
163   }
164
165   std::string GetCachedAreaKey(int64 namespace_id, const GURL& origin) {
166     return base::Int64ToString(namespace_id) + origin.spec();
167   }
168
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);
174   }
175
176   RenderThreadImpl* sender_;
177   CachedAreaMap cached_areas_;
178   CallbackList pending_callbacks_;
179   scoped_refptr<MessageThrottlingFilter> throttling_filter_;
180 };
181
182 DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender)
183     : sender_(sender),
184       throttling_filter_(new MessageThrottlingFilter(sender)) {
185   sender_->AddFilter(throttling_filter_.get());
186 }
187
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();
194   }
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();
199 }
200
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);
210   }
211 }
212
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();
220 }
221
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();
228   }
229 }
230
231 void DomStorageDispatcher::ProxyImpl::CompleteOnePendingCallback(bool success) {
232   PopPendingCallback().Run(success);
233 }
234
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();
241 }
242
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));
249 }
250
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));
258 }
259
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));
264 }
265
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));
272 }
273
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));
280 }
281
282 // DomStorageDispatcher ------------------------------------------------
283
284 DomStorageDispatcher::DomStorageDispatcher()
285     : proxy_(new ProxyImpl(RenderThreadImpl::current())) {
286 }
287
288 DomStorageDispatcher::~DomStorageDispatcher() {
289   proxy_->Shutdown();
290 }
291
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);
298 }
299
300 void DomStorageDispatcher::CloseCachedArea(
301     int connection_id, DOMStorageCachedArea* area) {
302   RenderThreadImpl::current()->Send(
303       new DOMStorageHostMsg_CloseStorageArea(connection_id));
304   proxy_->CloseCachedArea(area);
305 }
306
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;
318 }
319
320 void DomStorageDispatcher::OnStorageEvent(
321     const DOMStorageMsg_Event_Params& params) {
322   RenderThreadImpl::current()->EnsureWebKitInitialized();
323
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);
334   }
335
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);
357   }
358 }
359
360 void DomStorageDispatcher::OnAsyncOperationComplete(bool success) {
361   proxy_->CompleteOnePendingCallback(success);
362 }
363
364 void DomStorageDispatcher::OnResetCachedValues(int64 namespace_id) {
365   proxy_->ResetAllCachedAreas(namespace_id);
366 }
367
368 }  // namespace content