The final CL for plumbing DOM Storage.

Add webKitClient plumbing for getting/creating storage namespaces.  Add a chromium implementation for WebStorageArea and WebStorageNamespace which communicates via IPC with the dom_storage_dispatcher_host in the browser process.  Flesh out the StorageAreaProxy and StorageNamespaceProxy to use the aforementioned implementations.

The WebStorageArea implementation includes decently aggressive caching optimizations.  There's still a lot of work to do, though.

BUG=4360
TEST=none

Review URL: http://codereview.chromium.org/147248

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21495 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/renderer/renderer_webstoragearea_impl.cc b/chrome/renderer/renderer_webstoragearea_impl.cc
new file mode 100644
index 0000000..c438cdfe
--- /dev/null
+++ b/chrome/renderer/renderer_webstoragearea_impl.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.  Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "chrome/renderer/renderer_webstoragearea_impl.h"
+
+#include "chrome/common/render_messages.h"
+#include "chrome/renderer/render_thread.h"
+#include "webkit/api/public/WebString.h"
+
+RendererWebStorageAreaImpl::RendererWebStorageAreaImpl(
+    int64 namespace_id,
+    const WebKit::WebString& origin)
+    : namespace_id_(namespace_id),
+      origin_(origin),
+      storage_area_id_(kUninitializedStorageAreaId),
+      lock_held_(false),
+      bytes_left_in_quota_(0) {
+}
+
+RendererWebStorageAreaImpl::~RendererWebStorageAreaImpl() {
+}
+
+void RendererWebStorageAreaImpl::lock(bool& invalidate_cache,
+                                      size_t& bytes_left_in_quota) {
+  EnsureInitializedAndLocked();
+}
+
+void RendererWebStorageAreaImpl::unlock() {
+  if (storage_area_id_ != kUninitializedStorageAreaId) {
+    RenderThread::current()->Send(
+        new ViewHostMsg_DOMStorageUnlock(storage_area_id_));
+  }
+  lock_held_ = false;
+}
+
+unsigned RendererWebStorageAreaImpl::length() {
+  EnsureInitializedAndLocked();
+  // Right now this is always sync.  We could cache it, but I can't think of
+  // too many use cases where you'd repeatedly look up length() and not be
+  // doing so many key() lookups that the length() calls are the problem.
+  unsigned length;
+  RenderThread::current()->Send(
+      new ViewHostMsg_DOMStorageLength(storage_area_id_, &length));
+  return length;
+}
+
+WebKit::WebString RendererWebStorageAreaImpl::key(unsigned index,
+                                                  bool& key_exception) {
+  EnsureInitializedAndLocked();
+  // Right now this is always sync.  We may want to optimize this by fetching
+  // chunks of keys rather than single keys (and flushing the cache on every
+  // mutation of the storage area) since this will most often be used to fetch
+  // all the keys at once.
+  string16 key;
+  RenderThread::current()->Send(
+      new ViewHostMsg_DOMStorageKey(storage_area_id_, index,
+                                    &key_exception, &key));
+  return key;
+}
+
+WebKit::WebString RendererWebStorageAreaImpl::getItem(
+    const WebKit::WebString& webkit_key) {
+  EnsureInitializedAndLocked();
+  string16 key = webkit_key;
+
+  // Return from our cache if possible.
+  CacheMap::const_iterator iterator = cached_items_.find(key);
+  if (iterator != cached_items_.end())
+    return iterator->second;
+  if (cached_invalid_items_.find(key) != cached_invalid_items_.end())
+    return WebKit::WebString();  // Return a "null" string.
+
+  // The item is not in the cache, so we must do a sync IPC.  Afterwards,
+  // add it to the cache.
+  string16 raw_value;
+  bool value_is_null;
+  RenderThread::current()->Send(
+      new ViewHostMsg_DOMStorageGetItem(storage_area_id_, key,
+                                        &raw_value, &value_is_null));
+  WebKit::WebString value = value_is_null ? WebKit::WebString()
+                                          : WebKit::WebString(raw_value);
+  SetCache(key, value);
+  return value;
+}
+
+void RendererWebStorageAreaImpl::setItem(const WebKit::WebString& key,
+                                         const WebKit::WebString& value,
+                                         bool& quota_exception) {
+  EnsureInitializedAndLocked();
+  quota_exception = !UpdateQuota(key, value);
+  if (quota_exception)
+    return;
+  RenderThread::current()->Send(
+      new ViewHostMsg_DOMStorageSetItem(storage_area_id_, key, value));
+  SetCache(key, value);
+}
+
+void RendererWebStorageAreaImpl::removeItem(const WebKit::WebString& key) {
+  EnsureInitializedAndLocked();
+  bool update_succeeded = UpdateQuota(key, WebKit::WebString());
+  DCHECK(update_succeeded);
+  RenderThread::current()->Send(
+      new ViewHostMsg_DOMStorageRemoveItem(storage_area_id_, key));
+  SetCache(key, WebKit::WebString());
+}
+
+void RendererWebStorageAreaImpl::clear() {
+  EnsureInitializedAndLocked();
+  RenderThread::current()->Send(
+      new ViewHostMsg_DOMStorageClear(storage_area_id_,
+                                      &bytes_left_in_quota_));
+  // A possible optimization is a flag that says our cache is 100% complete.
+  // This could be set here, and then future gets would never require IPC.
+  cached_items_.clear();
+  cached_invalid_items_.clear();
+}
+
+void RendererWebStorageAreaImpl::EnsureInitializedAndLocked() {
+  if (storage_area_id_ == kUninitializedStorageAreaId) {
+    RenderThread::current()->Send(
+        new ViewHostMsg_DOMStorageStorageAreaId(namespace_id_, origin_,
+                                                &storage_area_id_));
+  }
+
+  if (lock_held_)
+    return;
+
+  bool invalidate_cache;
+  RenderThread::current()->Send(
+      new ViewHostMsg_DOMStorageLock(storage_area_id_, &invalidate_cache,
+                                     &bytes_left_in_quota_));
+  lock_held_ = true;
+  if (invalidate_cache) {
+    cached_items_.clear();
+    cached_invalid_items_.clear();
+  }
+}
+
+bool RendererWebStorageAreaImpl::UpdateQuota(const WebKit::WebString& key,
+                                             const WebKit::WebString& value) {
+  // TODO(jorlow): Remove once the bytes_left_in_quota values we're getting
+  //               are accurate.
+  return true;
+
+  size_t existing_bytes = getItem(key).length();
+  size_t new_bytes = value.length();
+
+  if (existing_bytes < new_bytes) {
+    size_t delta = new_bytes - existing_bytes;
+    if (delta > bytes_left_in_quota_)
+      return false;
+    bytes_left_in_quota_ -= delta;
+  } else {
+    size_t delta = existing_bytes - new_bytes;
+    DCHECK(delta + bytes_left_in_quota_ >= bytes_left_in_quota_);
+    bytes_left_in_quota_ += delta;
+  }
+  return true;
+}
+
+void RendererWebStorageAreaImpl::SetCache(const string16& key,
+                                          const WebKit::WebString& value) {
+  cached_items_.erase(key);
+  cached_invalid_items_.erase(key);
+
+  if (!value.isNull())
+    cached_items_[key] = value;
+  else
+    cached_invalid_items_.insert(key);
+}