Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/dom_storage/dom_storage_dispatcher.h" | 5 #include "content/renderer/dom_storage/dom_storage_dispatcher.h" |
| 6 | 6 |
| 7 #include <list> | |
| 8 #include <map> | |
| 9 | |
| 10 #include "base/string_number_conversions.h" | |
| 11 #include "base/synchronization/lock.h" | |
| 7 #include "content/common/dom_storage_messages.h" | 12 #include "content/common/dom_storage_messages.h" |
| 8 #include "content/renderer/dom_storage/webstoragearea_impl.h" | 13 #include "content/renderer/dom_storage/webstoragearea_impl.h" |
| 9 #include "content/renderer/dom_storage/webstoragenamespace_impl.h" | 14 #include "content/renderer/dom_storage/webstoragenamespace_impl.h" |
| 10 #include "content/renderer/render_thread_impl.h" | 15 #include "content/renderer/render_thread_impl.h" |
| 11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebStorageEventDispat cher.h" | 16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebStorageEventDispat cher.h" |
| 17 #include "webkit/dom_storage/dom_storage_cached_area.h" | |
| 18 #include "webkit/dom_storage/dom_storage_proxy.h" | |
| 19 #include "webkit/dom_storage/dom_storage_types.h" | |
| 20 | |
| 21 using dom_storage::DomStorageCachedArea; | |
| 22 using dom_storage::DomStorageProxy; | |
| 23 using dom_storage::ValuesMap; | |
| 24 | |
| 25 namespace { | |
| 26 class MessageThrottlingFilter : public IPC::ChannelProxy::MessageFilter { | |
| 27 public: | |
| 28 explicit MessageThrottlingFilter(RenderThreadImpl* sender) | |
| 29 : pending_count_(0), sender_(sender) {} | |
| 30 | |
| 31 void SendThrottled(IPC::Message* message); | |
| 32 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; | |
| 33 | |
| 34 private: | |
| 35 virtual ~MessageThrottlingFilter() {} | |
| 36 | |
| 37 int IncrementPendingCount(int increment) { | |
| 38 base::AutoLock locker(lock_); | |
| 39 pending_count_ += increment; | |
| 40 return pending_count_; | |
| 41 } | |
| 42 | |
| 43 base::Lock lock_; | |
| 44 int pending_count_; | |
| 45 RenderThreadImpl* sender_; | |
| 46 }; | |
| 47 | |
| 48 void MessageThrottlingFilter::SendThrottled(IPC::Message* message) { | |
| 49 bool need_to_flush = IncrementPendingCount(1) > 1000; | |
| 50 sender_->Send(message); | |
| 51 if (need_to_flush) { | |
| 52 sender_->Send(new DOMStorageHostMsg_FlushMessages); | |
| 53 DCHECK_EQ(0, IncrementPendingCount(0)); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 bool MessageThrottlingFilter::OnMessageReceived(const IPC::Message& message) { | |
| 58 if (message.type() == DOMStorageMsg_AsyncOperationComplete::ID) | |
| 59 IncrementPendingCount(-1); | |
| 60 return false; | |
| 61 } | |
| 62 } // namespace | |
| 63 | |
| 64 // An implementation of the DomStorageProxy interface in terms of IPC. | |
| 65 // This class also manages the collection of cached areas and pending | |
| 66 // operations awaiting completion callbacks. | |
| 67 class DomStorageDispatcher::ProxyImpl : public DomStorageProxy { | |
| 68 public: | |
| 69 explicit ProxyImpl(RenderThreadImpl* sender); | |
| 70 | |
| 71 // Methods for use by DomStorageDispatcher directly. | |
| 72 scoped_refptr<DomStorageCachedArea> OpenCachedArea( | |
| 73 int64 namespace_id, const GURL& origin); | |
| 74 void CloseCachedArea(DomStorageCachedArea* area); | |
| 75 DomStorageCachedArea* LookupCachedArea( | |
|
ericu
2012/05/22 23:23:35
Why does this return a raw pointer, whereas OpenCa
michaeln
2012/05/23 00:38:51
The open method may create a new instance whereas
michaeln
2012/05/23 22:37:28
Done.
ericu
2012/05/30 00:44:11
I'd probably have gone the other way, and switched
| |
| 76 int64 namespace_id, const GURL& origin); | |
| 77 void Shutdown(); | |
| 78 void CompleteAsyncOperation(bool success); | |
| 79 | |
| 80 // DomStorageProxy interface for use by DomStorageCachedArea. | |
| 81 virtual void LoadArea(int connection_id, ValuesMap* values, | |
| 82 const AsyncOperationCallback& callback) OVERRIDE; | |
| 83 virtual void SetItem(int connection_id, const string16& key, | |
| 84 const string16& value, const GURL& page_url, | |
| 85 const AsyncOperationCallback& callback) OVERRIDE; | |
| 86 virtual void RemoveItem(int connection_id, const string16& key, | |
| 87 const GURL& page_url, | |
| 88 const AsyncOperationCallback& callback) OVERRIDE; | |
| 89 virtual void ClearArea(int connection_id, | |
| 90 const GURL& page_url, | |
| 91 const AsyncOperationCallback& callback) OVERRIDE; | |
| 92 | |
| 93 private: | |
| 94 // Struct to hold references to our contained areas and | |
| 95 // to keep track of how many tabs have a given area open. | |
| 96 struct CachedAreaHolder { | |
| 97 scoped_refptr<DomStorageCachedArea> area_; | |
| 98 int open_count_; | |
| 99 CachedAreaHolder() : open_count_(0) {} | |
| 100 CachedAreaHolder(DomStorageCachedArea* area, int count) | |
| 101 : area_(area), open_count_(count) {} | |
| 102 }; | |
| 103 typedef std::map<std::string, CachedAreaHolder> CachedAreaMap; | |
| 104 typedef std::list<AsyncOperationCallback> OperationList; | |
| 105 | |
| 106 virtual ~ProxyImpl() { | |
| 107 } | |
| 108 | |
| 109 std::string GetCachedAreaKey(int64 namespace_id, const GURL& origin) { | |
| 110 return base::Int64ToString(namespace_id) + origin.spec(); | |
| 111 } | |
| 112 | |
| 113 CachedAreaHolder* GetAreaHolder(const std::string& key) { | |
| 114 CachedAreaMap::iterator found = cached_areas_.find(key); | |
| 115 if (found == cached_areas_.end()) | |
| 116 return NULL; | |
| 117 return &(found->second); | |
| 118 } | |
| 119 | |
| 120 RenderThreadImpl* sender_; | |
| 121 CachedAreaMap cached_areas_; | |
| 122 OperationList pending_operations_; | |
| 123 scoped_refptr<MessageThrottlingFilter> throttling_filter_; | |
| 124 }; | |
| 125 | |
| 126 DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender) | |
| 127 : sender_(sender), | |
| 128 throttling_filter_(new MessageThrottlingFilter(sender)) { | |
| 129 sender_->AddFilter(throttling_filter_); | |
| 130 } | |
| 131 | |
| 132 scoped_refptr<DomStorageCachedArea> | |
| 133 DomStorageDispatcher::ProxyImpl::OpenCachedArea( | |
| 134 int64 namespace_id, const GURL& origin) { | |
| 135 std::string key = GetCachedAreaKey(namespace_id, origin); | |
| 136 if (CachedAreaHolder* holder = GetAreaHolder(key)) { | |
| 137 ++(holder->open_count_); | |
| 138 return holder->area_; | |
| 139 } | |
| 140 scoped_refptr<DomStorageCachedArea> area = | |
| 141 new DomStorageCachedArea(namespace_id, origin, this); | |
| 142 cached_areas_[key] = CachedAreaHolder(area, 1); | |
| 143 return area; | |
| 144 } | |
| 145 | |
| 146 void DomStorageDispatcher::ProxyImpl::CloseCachedArea( | |
| 147 DomStorageCachedArea* area) { | |
| 148 std::string key = GetCachedAreaKey(area->namespace_id(), area->origin()); | |
| 149 CachedAreaHolder* holder = GetAreaHolder(key); | |
| 150 DCHECK(holder); | |
| 151 DCHECK_EQ(holder->area_.get(), area); | |
|
ericu
2012/05/22 23:23:35
DCHECK_GT(holder->open_count_, 0) ?
michaeln
2012/05/23 22:37:28
Done.
| |
| 152 if (--(holder->open_count_) == 0) { | |
| 153 cached_areas_.erase(key); | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 DomStorageCachedArea* DomStorageDispatcher::ProxyImpl::LookupCachedArea( | |
| 158 int64 namespace_id, const GURL& origin) { | |
| 159 std::string key = GetCachedAreaKey(namespace_id, origin); | |
| 160 CachedAreaHolder* holder = GetAreaHolder(key); | |
| 161 if (!holder) | |
| 162 return NULL; | |
| 163 return holder->area_.get(); | |
| 164 } | |
| 165 | |
| 166 void DomStorageDispatcher::ProxyImpl::Shutdown() { | |
| 167 sender_->RemoveFilter(throttling_filter_); | |
| 168 sender_ = NULL; | |
| 169 cached_areas_.clear(); | |
| 170 pending_operations_.clear(); | |
| 171 } | |
| 172 | |
| 173 void DomStorageDispatcher::ProxyImpl::CompleteAsyncOperation(bool success) { | |
| 174 pending_operations_.front().Run(success); | |
| 175 pending_operations_.pop_front(); | |
| 176 } | |
| 177 | |
| 178 void DomStorageDispatcher::ProxyImpl::LoadArea( | |
| 179 int connection_id, ValuesMap* values, | |
| 180 const AsyncOperationCallback& callback) { | |
| 181 pending_operations_.push_back(callback); | |
| 182 sender_->Send(new DOMStorageHostMsg_LoadStorageArea( | |
| 183 connection_id, values)); | |
|
ericu
2012/05/22 23:23:35
Nit: It would be kind of nice to use the throttlin
michaeln
2012/05/23 00:38:51
sgtm, should be simple enough to determine 'needsT
michaeln
2012/05/23 22:37:28
Done... kindof...
* added DCHECKs in SendThrottled
| |
| 184 } | |
| 185 | |
| 186 void DomStorageDispatcher::ProxyImpl::SetItem( | |
| 187 int connection_id, const string16& key, | |
| 188 const string16& value, const GURL& page_url, | |
| 189 const AsyncOperationCallback& callback) { | |
| 190 pending_operations_.push_back(callback); | |
| 191 throttling_filter_->SendThrottled(new DOMStorageHostMsg_SetItem( | |
| 192 connection_id, key, value, page_url)); | |
| 193 } | |
| 194 | |
| 195 void DomStorageDispatcher::ProxyImpl::RemoveItem( | |
| 196 int connection_id, const string16& key, const GURL& page_url, | |
| 197 const AsyncOperationCallback& callback) { | |
| 198 pending_operations_.push_back(callback); | |
| 199 throttling_filter_->SendThrottled(new DOMStorageHostMsg_RemoveItem( | |
| 200 connection_id, key, page_url)); | |
| 201 } | |
| 202 | |
| 203 void DomStorageDispatcher::ProxyImpl::ClearArea(int connection_id, | |
| 204 const GURL& page_url, | |
| 205 const AsyncOperationCallback& callback) { | |
| 206 pending_operations_.push_back(callback); | |
| 207 throttling_filter_->SendThrottled(new DOMStorageHostMsg_Clear( | |
| 208 connection_id, page_url)); | |
| 209 } | |
| 210 | |
| 211 // DomStorageDispatcher ------------------------------------------------ | |
| 212 | |
| 213 DomStorageDispatcher::DomStorageDispatcher() | |
| 214 : proxy_(new ProxyImpl(RenderThreadImpl::current())) { | |
| 215 } | |
| 216 | |
| 217 DomStorageDispatcher::~DomStorageDispatcher() { | |
| 218 proxy_->Shutdown(); | |
| 219 } | |
| 220 | |
| 221 scoped_refptr<DomStorageCachedArea> DomStorageDispatcher::OpenCachedArea( | |
| 222 int connection_id, int64 namespace_id, const GURL& origin) { | |
| 223 RenderThreadImpl::current()->Send( | |
| 224 new DOMStorageHostMsg_OpenStorageArea( | |
| 225 connection_id, namespace_id, origin)); | |
| 226 return proxy_->OpenCachedArea(namespace_id, origin); | |
| 227 } | |
| 228 | |
| 229 void DomStorageDispatcher::CloseCachedArea( | |
| 230 int connection_id, DomStorageCachedArea* area) { | |
| 231 RenderThreadImpl::current()->Send( | |
| 232 new DOMStorageHostMsg_CloseStorageArea(connection_id)); | |
| 233 proxy_->CloseCachedArea(area); | |
| 234 } | |
| 12 | 235 |
| 13 bool DomStorageDispatcher::OnMessageReceived(const IPC::Message& msg) { | 236 bool DomStorageDispatcher::OnMessageReceived(const IPC::Message& msg) { |
| 14 bool handled = true; | 237 bool handled = true; |
| 15 IPC_BEGIN_MESSAGE_MAP(DomStorageDispatcher, msg) | 238 IPC_BEGIN_MESSAGE_MAP(DomStorageDispatcher, msg) |
| 16 IPC_MESSAGE_HANDLER(DOMStorageMsg_Event, OnStorageEvent) | 239 IPC_MESSAGE_HANDLER(DOMStorageMsg_Event, OnStorageEvent) |
| 240 IPC_MESSAGE_HANDLER(DOMStorageMsg_AsyncOperationComplete, | |
| 241 OnAsyncOperationComplete) | |
| 17 IPC_MESSAGE_UNHANDLED(handled = false) | 242 IPC_MESSAGE_UNHANDLED(handled = false) |
| 18 IPC_END_MESSAGE_MAP() | 243 IPC_END_MESSAGE_MAP() |
| 19 return handled; | 244 return handled; |
| 20 } | 245 } |
| 21 | 246 |
| 22 void DomStorageDispatcher::OnStorageEvent( | 247 void DomStorageDispatcher::OnStorageEvent( |
| 23 const DOMStorageMsg_Event_Params& params) { | 248 const DOMStorageMsg_Event_Params& params) { |
| 24 RenderThreadImpl::current()->EnsureWebKitInitialized(); | 249 RenderThreadImpl::current()->EnsureWebKitInitialized(); |
| 25 | 250 |
| 26 bool originated_in_process = params.connection_id != 0; | 251 bool originated_in_process = params.connection_id != 0; |
| 27 WebStorageAreaImpl* originating_area = NULL; | 252 WebStorageAreaImpl* originating_area = NULL; |
| 28 if (originated_in_process) { | 253 if (originated_in_process) { |
| 29 originating_area = WebStorageAreaImpl::FromConnectionId( | 254 originating_area = WebStorageAreaImpl::FromConnectionId( |
| 30 params.connection_id); | 255 params.connection_id); |
| 256 } else { | |
| 257 DomStorageCachedArea* cached_area = proxy_->LookupCachedArea( | |
| 258 params.namespace_id, params.origin); | |
| 259 if (cached_area) { | |
| 260 cached_area->ApplyMutation(params.key, params.old_value, | |
| 261 params.new_value); | |
| 262 } | |
| 31 } | 263 } |
| 32 | 264 |
| 33 if (params.namespace_id == dom_storage::kLocalStorageNamespaceId) { | 265 if (params.namespace_id == dom_storage::kLocalStorageNamespaceId) { |
| 34 WebKit::WebStorageEventDispatcher::dispatchLocalStorageEvent( | 266 WebKit::WebStorageEventDispatcher::dispatchLocalStorageEvent( |
| 35 params.key, | 267 params.key, |
| 36 params.old_value, | 268 params.old_value, |
| 37 params.new_value, | 269 params.new_value, |
| 38 params.origin, | 270 params.origin, |
| 39 params.page_url, | 271 params.page_url, |
| 40 originating_area, | 272 originating_area, |
| 41 originated_in_process); | 273 originated_in_process); |
| 42 } else if (originated_in_process) { | 274 } else { |
| 43 // TODO(michaeln): For now, we only raise session storage events into the | |
| 44 // process which caused the event to occur. However there are cases where | |
| 45 // sessions can span process boundaries, so there are correctness issues. | |
| 46 WebStorageNamespaceImpl | 275 WebStorageNamespaceImpl |
| 47 session_namespace_for_event_dispatch(params.namespace_id); | 276 session_namespace_for_event_dispatch(params.namespace_id); |
| 48 WebKit::WebStorageEventDispatcher::dispatchSessionStorageEvent( | 277 WebKit::WebStorageEventDispatcher::dispatchSessionStorageEvent( |
| 49 params.key, | 278 params.key, |
| 50 params.old_value, | 279 params.old_value, |
| 51 params.new_value, | 280 params.new_value, |
| 52 params.origin, | 281 params.origin, |
| 53 params.page_url, | 282 params.page_url, |
| 54 session_namespace_for_event_dispatch, | 283 session_namespace_for_event_dispatch, |
| 55 originating_area, | 284 originating_area, |
| 56 originated_in_process); | 285 originated_in_process); |
| 57 } | 286 } |
| 58 } | 287 } |
| 288 | |
| 289 void DomStorageDispatcher::OnAsyncOperationComplete(bool success) { | |
| 290 proxy_->CompleteAsyncOperation(success); | |
| 291 } | |
| OLD | NEW |