Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 10 | #include "base/strings/string_number_conversions.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 11 | #include "base/synchronization/lock.h" |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 12 | #include "content/common/dom_storage/dom_storage_messages.h" |
| 13 | #include "content/common/dom_storage/dom_storage_types.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 14 | #include "content/renderer/dom_storage/dom_storage_cached_area.h" |
| 15 | #include "content/renderer/dom_storage/dom_storage_proxy.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 19 | #include "third_party/WebKit/public/platform/Platform.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 20 | #include "third_party/WebKit/public/web/WebKit.h" |
| 21 | #include "third_party/WebKit/public/web/WebStorageEventDispatcher.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 22 | |
| 23 | namespace content { |
| 24 | |
| 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) {} |
| 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 | |
| 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; |
| 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 | |
| 83 | bool 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 Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 93 | // An implementation of the DOMStorageProxy interface in terms of IPC. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 94 | // This class also manages the collection of cached areas and pending |
| 95 | // operations awaiting completion callbacks. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 96 | class DomStorageDispatcher::ProxyImpl : public DOMStorageProxy { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 97 | public: |
| 98 | explicit ProxyImpl(RenderThreadImpl* sender); |
| 99 | |
| 100 | // Methods for use by DomStorageDispatcher directly. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 101 | DOMStorageCachedArea* OpenCachedArea( |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 102 | int64 namespace_id, const GURL& origin); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 103 | void CloseCachedArea(DOMStorageCachedArea* area); |
| 104 | DOMStorageCachedArea* LookupCachedArea( |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 105 | int64 namespace_id, const GURL& origin); |
| 106 | void CompleteOnePendingCallback(bool success); |
| 107 | void Shutdown(); |
| 108 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 109 | // DOMStorageProxy interface for use by DOMStorageCachedArea. |
| 110 | virtual void LoadArea(int connection_id, DOMStorageValuesMap* values, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 111 | 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 Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 126 | scoped_refptr<DOMStorageCachedArea> area_; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 127 | int open_count_; |
| 128 | CachedAreaHolder() : open_count_(0) {} |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 129 | CachedAreaHolder(DOMStorageCachedArea* area, int count) |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 130 | : 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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 142 | WebKit::Platform::current()->suddenTerminationChanged(false); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 143 | 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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 150 | WebKit::Platform::current()->suddenTerminationChanged(true); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 151 | 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 | |
| 171 | DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender) |
| 172 | : sender_(sender), |
| 173 | throttling_filter_(new MessageThrottlingFilter(sender)) { |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 174 | sender_->AddFilter(throttling_filter_.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 175 | } |
| 176 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 177 | DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::OpenCachedArea( |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 178 | 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) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 182 | return holder->area_.get(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 183 | } |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 184 | scoped_refptr<DOMStorageCachedArea> area = |
| 185 | new DOMStorageCachedArea(namespace_id, origin, this); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 186 | cached_areas_[key] = CachedAreaHolder(area.get(), 1); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 187 | return area.get(); |
| 188 | } |
| 189 | |
| 190 | void DomStorageDispatcher::ProxyImpl::CloseCachedArea( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 191 | DOMStorageCachedArea* area) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 192 | 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 Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 202 | DOMStorageCachedArea* DomStorageDispatcher::ProxyImpl::LookupCachedArea( |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 203 | 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 | |
| 211 | void DomStorageDispatcher::ProxyImpl::CompleteOnePendingCallback(bool success) { |
| 212 | PopPendingCallback().Run(success); |
| 213 | } |
| 214 | |
| 215 | void DomStorageDispatcher::ProxyImpl::Shutdown() { |
| 216 | throttling_filter_->Shutdown(); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 217 | sender_->RemoveFilter(throttling_filter_.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 218 | sender_ = NULL; |
| 219 | cached_areas_.clear(); |
| 220 | pending_callbacks_.clear(); |
| 221 | } |
| 222 | |
| 223 | void DomStorageDispatcher::ProxyImpl::LoadArea( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 224 | int connection_id, DOMStorageValuesMap* values, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 225 | const CompletionCallback& callback) { |
| 226 | PushPendingCallback(callback); |
| 227 | throttling_filter_->SendThrottled(new DOMStorageHostMsg_LoadStorageArea( |
| 228 | connection_id, values)); |
| 229 | } |
| 230 | |
| 231 | void 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 | |
| 240 | void 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 | |
| 248 | void 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 | |
| 258 | DomStorageDispatcher::DomStorageDispatcher() |
| 259 | : proxy_(new ProxyImpl(RenderThreadImpl::current())) { |
| 260 | } |
| 261 | |
| 262 | DomStorageDispatcher::~DomStorageDispatcher() { |
| 263 | proxy_->Shutdown(); |
| 264 | } |
| 265 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 266 | scoped_refptr<DOMStorageCachedArea> DomStorageDispatcher::OpenCachedArea( |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 267 | 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 | |
| 274 | void DomStorageDispatcher::CloseCachedArea( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 275 | int connection_id, DOMStorageCachedArea* area) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 276 | RenderThreadImpl::current()->Send( |
| 277 | new DOMStorageHostMsg_CloseStorageArea(connection_id)); |
| 278 | proxy_->CloseCachedArea(area); |
| 279 | } |
| 280 | |
| 281 | bool 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 | |
| 292 | void 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 Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 302 | DOMStorageCachedArea* cached_area = proxy_->LookupCachedArea( |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 303 | params.namespace_id, params.origin); |
| 304 | if (cached_area) |
| 305 | cached_area->ApplyMutation(params.key, params.new_value); |
| 306 | } |
| 307 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 308 | if (params.namespace_id == kLocalStorageNamespaceId) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 309 | 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 | |
| 332 | void DomStorageDispatcher::OnAsyncOperationComplete(bool success) { |
| 333 | proxy_->CompleteOnePendingCallback(success); |
| 334 | } |
| 335 | |
| 336 | } // namespace content |