| Index: content/renderer/dom_storage/dom_storage_dispatcher.cc
|
| ===================================================================
|
| --- content/renderer/dom_storage/dom_storage_dispatcher.cc (revision 139572)
|
| +++ content/renderer/dom_storage/dom_storage_dispatcher.cc (working copy)
|
| @@ -4,16 +4,281 @@
|
|
|
| #include "content/renderer/dom_storage/dom_storage_dispatcher.h"
|
|
|
| +#include <list>
|
| +#include <map>
|
| +
|
| +#include "base/string_number_conversions.h"
|
| +#include "base/synchronization/lock.h"
|
| #include "content/common/dom_storage_messages.h"
|
| #include "content/renderer/dom_storage/webstoragearea_impl.h"
|
| #include "content/renderer/dom_storage/webstoragenamespace_impl.h"
|
| #include "content/renderer/render_thread_impl.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebKitPlatformSupport.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
|
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebStorageEventDispatcher.h"
|
| +#include "webkit/dom_storage/dom_storage_cached_area.h"
|
| +#include "webkit/dom_storage/dom_storage_proxy.h"
|
| +#include "webkit/dom_storage/dom_storage_types.h"
|
|
|
| +using dom_storage::DomStorageCachedArea;
|
| +using dom_storage::DomStorageProxy;
|
| +using dom_storage::ValuesMap;
|
| +
|
| +namespace {
|
| +// MessageThrottlingFilter -------------------------------------------
|
| +// Used to limit the number of ipc messages pending completion so we
|
| +// don't overwhelm the main browser process. When the limit is reached,
|
| +// a synchronous message is sent to flush all pending messages thru.
|
| +// We expect to receive an 'ack' for each message sent. This object
|
| +// observes receipt of the acks on the IPC thread to decrement a counter.
|
| +class MessageThrottlingFilter : public IPC::ChannelProxy::MessageFilter {
|
| + public:
|
| + explicit MessageThrottlingFilter(RenderThreadImpl* sender)
|
| + : pending_count_(0), sender_(sender) {}
|
| +
|
| + void SendThrottled(IPC::Message* message);
|
| +
|
| + private:
|
| + virtual ~MessageThrottlingFilter() {}
|
| +
|
| + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
|
| +
|
| + int GetPendingCount() { return IncrementPendingCountN(0); }
|
| + int IncrementPendingCount() { return IncrementPendingCountN(1); }
|
| + int DecrementPendingCount() { return IncrementPendingCountN(-1); }
|
| + int IncrementPendingCountN(int increment) {
|
| + base::AutoLock locker(lock_);
|
| + pending_count_ += increment;
|
| + return pending_count_;
|
| + }
|
| +
|
| + base::Lock lock_;
|
| + int pending_count_;
|
| + RenderThreadImpl* sender_;
|
| +};
|
| +
|
| +void MessageThrottlingFilter::SendThrottled(IPC::Message* message) {
|
| + // Should only be used for sending of messages which will be acknowledged
|
| + // with a separate DOMStorageMsg_AsyncOperationComplete message.
|
| + DCHECK(message->type() == DOMStorageHostMsg_LoadStorageArea::ID ||
|
| + message->type() == DOMStorageHostMsg_SetItem::ID ||
|
| + message->type() == DOMStorageHostMsg_RemoveItem::ID ||
|
| + message->type() == DOMStorageHostMsg_Clear::ID);
|
| + const int kMaxPendingMessages = 1000;
|
| + bool need_to_flush = IncrementPendingCount() > kMaxPendingMessages;
|
| + sender_->Send(message);
|
| + if (need_to_flush && !message->is_sync()) {
|
| + sender_->Send(new DOMStorageHostMsg_FlushMessages);
|
| + DCHECK_EQ(0, GetPendingCount());
|
| + } else {
|
| + DCHECK_LE(0, GetPendingCount());
|
| + }
|
| +}
|
| +
|
| +bool MessageThrottlingFilter::OnMessageReceived(const IPC::Message& message) {
|
| + if (message.type() == DOMStorageMsg_AsyncOperationComplete::ID) {
|
| + DecrementPendingCount();
|
| + DCHECK_LE(0, GetPendingCount());
|
| + }
|
| + return false;
|
| +}
|
| +} // namespace
|
| +
|
| +// ProxyImpl -----------------------------------------------------
|
| +// An implementation of the DomStorageProxy interface in terms of IPC.
|
| +// This class also manages the collection of cached areas and pending
|
| +// operations awaiting completion callbacks.
|
| +class DomStorageDispatcher::ProxyImpl : public DomStorageProxy {
|
| + public:
|
| + explicit ProxyImpl(RenderThreadImpl* sender);
|
| +
|
| + // Methods for use by DomStorageDispatcher directly.
|
| + DomStorageCachedArea* OpenCachedArea(
|
| + int64 namespace_id, const GURL& origin);
|
| + void CloseCachedArea(DomStorageCachedArea* area);
|
| + DomStorageCachedArea* LookupCachedArea(
|
| + int64 namespace_id, const GURL& origin);
|
| + void CompleteOnePendingCallback(bool success);
|
| + void Shutdown();
|
| +
|
| + // DomStorageProxy interface for use by DomStorageCachedArea.
|
| + virtual void LoadArea(int connection_id, ValuesMap* values,
|
| + const CompletionCallback& callback) OVERRIDE;
|
| + virtual void SetItem(int connection_id, const string16& key,
|
| + const string16& value, const GURL& page_url,
|
| + const CompletionCallback& callback) OVERRIDE;
|
| + virtual void RemoveItem(int connection_id, const string16& key,
|
| + const GURL& page_url,
|
| + const CompletionCallback& callback) OVERRIDE;
|
| + virtual void ClearArea(int connection_id,
|
| + const GURL& page_url,
|
| + const CompletionCallback& callback) OVERRIDE;
|
| +
|
| + private:
|
| + // Struct to hold references to our contained areas and
|
| + // to keep track of how many tabs have a given area open.
|
| + struct CachedAreaHolder {
|
| + scoped_refptr<DomStorageCachedArea> area_;
|
| + int open_count_;
|
| + CachedAreaHolder() : open_count_(0) {}
|
| + CachedAreaHolder(DomStorageCachedArea* area, int count)
|
| + : area_(area), open_count_(count) {}
|
| + };
|
| + typedef std::map<std::string, CachedAreaHolder> CachedAreaMap;
|
| + typedef std::list<CompletionCallback> CallbackList;
|
| +
|
| + virtual ~ProxyImpl() {
|
| + }
|
| +
|
| + // Sudden termination is disabled when there are callbacks pending
|
| + // to more reliably commit changes during shutdown.
|
| + void PushPendingCallback(const CompletionCallback& callback) {
|
| + if (pending_callbacks_.empty())
|
| + WebKit::webKitPlatformSupport()->suddenTerminationChanged(false);
|
| + pending_callbacks_.push_back(callback);
|
| + }
|
| +
|
| + CompletionCallback PopPendingCallback() {
|
| + CompletionCallback callback = pending_callbacks_.front();
|
| + pending_callbacks_.pop_front();
|
| + if (pending_callbacks_.empty())
|
| + WebKit::webKitPlatformSupport()->suddenTerminationChanged(true);
|
| + return callback;
|
| + }
|
| +
|
| + std::string GetCachedAreaKey(int64 namespace_id, const GURL& origin) {
|
| + return base::Int64ToString(namespace_id) + origin.spec();
|
| + }
|
| +
|
| + CachedAreaHolder* GetAreaHolder(const std::string& key) {
|
| + CachedAreaMap::iterator found = cached_areas_.find(key);
|
| + if (found == cached_areas_.end())
|
| + return NULL;
|
| + return &(found->second);
|
| + }
|
| +
|
| +
|
| + RenderThreadImpl* sender_;
|
| + CachedAreaMap cached_areas_;
|
| + CallbackList pending_callbacks_;
|
| + scoped_refptr<MessageThrottlingFilter> throttling_filter_;
|
| +};
|
| +
|
| +DomStorageDispatcher::ProxyImpl::ProxyImpl(RenderThreadImpl* sender)
|
| + : sender_(sender),
|
| + throttling_filter_(new MessageThrottlingFilter(sender)) {
|
| + sender_->AddFilter(throttling_filter_);
|
| +}
|
| +
|
| +DomStorageCachedArea* DomStorageDispatcher::ProxyImpl::OpenCachedArea(
|
| + int64 namespace_id, const GURL& origin) {
|
| + std::string key = GetCachedAreaKey(namespace_id, origin);
|
| + if (CachedAreaHolder* holder = GetAreaHolder(key)) {
|
| + ++(holder->open_count_);
|
| + return holder->area_;
|
| + }
|
| + scoped_refptr<DomStorageCachedArea> area =
|
| + new DomStorageCachedArea(namespace_id, origin, this);
|
| + cached_areas_[key] = CachedAreaHolder(area, 1);
|
| + return area.get();
|
| +}
|
| +
|
| +void DomStorageDispatcher::ProxyImpl::CloseCachedArea(
|
| + DomStorageCachedArea* area) {
|
| + std::string key = GetCachedAreaKey(area->namespace_id(), area->origin());
|
| + CachedAreaHolder* holder = GetAreaHolder(key);
|
| + DCHECK(holder);
|
| + DCHECK_EQ(holder->area_.get(), area);
|
| + DCHECK_GT(holder->open_count_, 0);
|
| + if (--(holder->open_count_) == 0) {
|
| + cached_areas_.erase(key);
|
| + }
|
| +}
|
| +
|
| +DomStorageCachedArea* DomStorageDispatcher::ProxyImpl::LookupCachedArea(
|
| + int64 namespace_id, const GURL& origin) {
|
| + std::string key = GetCachedAreaKey(namespace_id, origin);
|
| + CachedAreaHolder* holder = GetAreaHolder(key);
|
| + if (!holder)
|
| + return NULL;
|
| + return holder->area_.get();
|
| +}
|
| +
|
| +void DomStorageDispatcher::ProxyImpl::CompleteOnePendingCallback(bool success) {
|
| + PopPendingCallback().Run(success);
|
| +}
|
| +
|
| +void DomStorageDispatcher::ProxyImpl::Shutdown() {
|
| + sender_->RemoveFilter(throttling_filter_);
|
| + sender_ = NULL;
|
| + cached_areas_.clear();
|
| + pending_callbacks_.clear();
|
| +}
|
| +
|
| +void DomStorageDispatcher::ProxyImpl::LoadArea(
|
| + int connection_id, ValuesMap* values,
|
| + const CompletionCallback& callback) {
|
| + PushPendingCallback(callback);
|
| + throttling_filter_->SendThrottled(new DOMStorageHostMsg_LoadStorageArea(
|
| + connection_id, values));
|
| +}
|
| +
|
| +void DomStorageDispatcher::ProxyImpl::SetItem(
|
| + int connection_id, const string16& key,
|
| + const string16& value, const GURL& page_url,
|
| + const CompletionCallback& callback) {
|
| + PushPendingCallback(callback);
|
| + throttling_filter_->SendThrottled(new DOMStorageHostMsg_SetItem(
|
| + connection_id, key, value, page_url));
|
| +}
|
| +
|
| +void DomStorageDispatcher::ProxyImpl::RemoveItem(
|
| + int connection_id, const string16& key, const GURL& page_url,
|
| + const CompletionCallback& callback) {
|
| + PushPendingCallback(callback);
|
| + throttling_filter_->SendThrottled(new DOMStorageHostMsg_RemoveItem(
|
| + connection_id, key, page_url));
|
| +}
|
| +
|
| +void DomStorageDispatcher::ProxyImpl::ClearArea(int connection_id,
|
| + const GURL& page_url,
|
| + const CompletionCallback& callback) {
|
| + PushPendingCallback(callback);
|
| + throttling_filter_->SendThrottled(new DOMStorageHostMsg_Clear(
|
| + connection_id, page_url));
|
| +}
|
| +
|
| +// DomStorageDispatcher ------------------------------------------------
|
| +
|
| +DomStorageDispatcher::DomStorageDispatcher()
|
| + : proxy_(new ProxyImpl(RenderThreadImpl::current())) {
|
| +}
|
| +
|
| +DomStorageDispatcher::~DomStorageDispatcher() {
|
| + proxy_->Shutdown();
|
| +}
|
| +
|
| +scoped_refptr<DomStorageCachedArea> DomStorageDispatcher::OpenCachedArea(
|
| + int connection_id, int64 namespace_id, const GURL& origin) {
|
| + RenderThreadImpl::current()->Send(
|
| + new DOMStorageHostMsg_OpenStorageArea(
|
| + connection_id, namespace_id, origin));
|
| + return proxy_->OpenCachedArea(namespace_id, origin);
|
| +}
|
| +
|
| +void DomStorageDispatcher::CloseCachedArea(
|
| + int connection_id, DomStorageCachedArea* area) {
|
| + RenderThreadImpl::current()->Send(
|
| + new DOMStorageHostMsg_CloseStorageArea(connection_id));
|
| + proxy_->CloseCachedArea(area);
|
| +}
|
| +
|
| bool DomStorageDispatcher::OnMessageReceived(const IPC::Message& msg) {
|
| bool handled = true;
|
| IPC_BEGIN_MESSAGE_MAP(DomStorageDispatcher, msg)
|
| IPC_MESSAGE_HANDLER(DOMStorageMsg_Event, OnStorageEvent)
|
| + IPC_MESSAGE_HANDLER(DOMStorageMsg_AsyncOperationComplete,
|
| + OnAsyncOperationComplete)
|
| IPC_MESSAGE_UNHANDLED(handled = false)
|
| IPC_END_MESSAGE_MAP()
|
| return handled;
|
| @@ -28,6 +293,11 @@
|
| if (originated_in_process) {
|
| originating_area = WebStorageAreaImpl::FromConnectionId(
|
| params.connection_id);
|
| + } else {
|
| + DomStorageCachedArea* cached_area = proxy_->LookupCachedArea(
|
| + params.namespace_id, params.origin);
|
| + if (cached_area)
|
| + cached_area->ApplyMutation(params.key, params.new_value);
|
| }
|
|
|
| if (params.namespace_id == dom_storage::kLocalStorageNamespaceId) {
|
| @@ -39,10 +309,7 @@
|
| params.page_url,
|
| originating_area,
|
| originated_in_process);
|
| - } else if (originated_in_process) {
|
| - // TODO(michaeln): For now, we only raise session storage events into the
|
| - // process which caused the event to occur. However there are cases where
|
| - // sessions can span process boundaries, so there are correctness issues.
|
| + } else {
|
| WebStorageNamespaceImpl
|
| session_namespace_for_event_dispatch(params.namespace_id);
|
| WebKit::WebStorageEventDispatcher::dispatchSessionStorageEvent(
|
| @@ -56,3 +323,7 @@
|
| originated_in_process);
|
| }
|
| }
|
| +
|
| +void DomStorageDispatcher::OnAsyncOperationComplete(bool success) {
|
| + proxy_->CompleteOnePendingCallback(success);
|
| +}
|
|
|