Home | History | Annotate | Download | only in dom_storage
      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_cached_area.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/time/time.h"
     10 #include "content/common/dom_storage/dom_storage_map.h"
     11 #include "content/renderer/dom_storage/dom_storage_proxy.h"
     12 
     13 namespace content {
     14 
     15 namespace {
     16 
     17 static const int kMaxLogGetMessagesToSend = 16 * 1024;
     18 
     19 }  // namespace
     20 
     21 DOMStorageCachedArea::DOMStorageCachedArea(int64 namespace_id,
     22                                            const GURL& origin,
     23                                            DOMStorageProxy* proxy)
     24     : ignore_all_mutations_(false),
     25       namespace_id_(namespace_id),
     26       origin_(origin),
     27       proxy_(proxy),
     28       remaining_log_get_messages_(0),
     29       weak_factory_(this) {}
     30 
     31 DOMStorageCachedArea::~DOMStorageCachedArea() {}
     32 
     33 unsigned DOMStorageCachedArea::GetLength(int connection_id) {
     34   PrimeIfNeeded(connection_id);
     35   return map_->Length();
     36 }
     37 
     38 base::NullableString16 DOMStorageCachedArea::GetKey(int connection_id,
     39                                                     unsigned index) {
     40   PrimeIfNeeded(connection_id);
     41   return map_->Key(index);
     42 }
     43 
     44 base::NullableString16 DOMStorageCachedArea::GetItem(
     45     int connection_id,
     46     const base::string16& key) {
     47   PrimeIfNeeded(connection_id);
     48   base::NullableString16 result = map_->GetItem(key);
     49   if (remaining_log_get_messages_ > 0) {
     50     remaining_log_get_messages_--;
     51     proxy_->LogGetItem(connection_id, key, result);
     52   }
     53   return result;
     54 }
     55 
     56 bool DOMStorageCachedArea::SetItem(int connection_id,
     57                                    const base::string16& key,
     58                                    const base::string16& value,
     59                                    const GURL& page_url) {
     60   // A quick check to reject obviously overbudget items to avoid
     61   // the priming the cache.
     62   if (key.length() + value.length() > kPerStorageAreaQuota)
     63     return false;
     64 
     65   PrimeIfNeeded(connection_id);
     66   base::NullableString16 unused;
     67   if (!map_->SetItem(key, value, &unused))
     68     return false;
     69 
     70   // Ignore mutations to 'key' until OnSetItemComplete.
     71   ignore_key_mutations_[key]++;
     72   proxy_->SetItem(
     73       connection_id, key, value, page_url,
     74       base::Bind(&DOMStorageCachedArea::OnSetItemComplete,
     75                  weak_factory_.GetWeakPtr(), key));
     76   return true;
     77 }
     78 
     79 void DOMStorageCachedArea::RemoveItem(int connection_id,
     80                                       const base::string16& key,
     81                                       const GURL& page_url) {
     82   PrimeIfNeeded(connection_id);
     83   base::string16 unused;
     84   if (!map_->RemoveItem(key, &unused))
     85     return;
     86 
     87   // Ignore mutations to 'key' until OnRemoveItemComplete.
     88   ignore_key_mutations_[key]++;
     89   proxy_->RemoveItem(
     90       connection_id, key, page_url,
     91       base::Bind(&DOMStorageCachedArea::OnRemoveItemComplete,
     92                  weak_factory_.GetWeakPtr(), key));
     93 }
     94 
     95 void DOMStorageCachedArea::Clear(int connection_id, const GURL& page_url) {
     96   // No need to prime the cache in this case.
     97   Reset();
     98   map_ = new DOMStorageMap(kPerStorageAreaQuota);
     99 
    100   // Ignore all mutations until OnClearComplete time.
    101   ignore_all_mutations_ = true;
    102   proxy_->ClearArea(connection_id,
    103                     page_url,
    104                     base::Bind(&DOMStorageCachedArea::OnClearComplete,
    105                                weak_factory_.GetWeakPtr()));
    106 }
    107 
    108 void DOMStorageCachedArea::ApplyMutation(
    109     const base::NullableString16& key,
    110     const base::NullableString16& new_value) {
    111   if (!map_.get() || ignore_all_mutations_)
    112     return;
    113 
    114   if (key.is_null()) {
    115     // It's a clear event.
    116     scoped_refptr<DOMStorageMap> old = map_;
    117     map_ = new DOMStorageMap(kPerStorageAreaQuota);
    118 
    119     // We have to retain local additions which happened after this
    120     // clear operation from another process.
    121     std::map<base::string16, int>::iterator iter =
    122         ignore_key_mutations_.begin();
    123     while (iter != ignore_key_mutations_.end()) {
    124       base::NullableString16 value = old->GetItem(iter->first);
    125       if (!value.is_null()) {
    126         base::NullableString16 unused;
    127         map_->SetItem(iter->first, value.string(), &unused);
    128       }
    129       ++iter;
    130     }
    131     return;
    132   }
    133 
    134   // We have to retain local changes.
    135   if (should_ignore_key_mutation(key.string()))
    136     return;
    137 
    138   if (new_value.is_null()) {
    139     // It's a remove item event.
    140     base::string16 unused;
    141     map_->RemoveItem(key.string(), &unused);
    142     return;
    143   }
    144 
    145   // It's a set item event.
    146   // We turn off quota checking here to accomodate the over budget
    147   // allowance that's provided in the browser process.
    148   base::NullableString16 unused;
    149   map_->set_quota(kint32max);
    150   map_->SetItem(key.string(), new_value.string(), &unused);
    151   map_->set_quota(kPerStorageAreaQuota);
    152 }
    153 
    154 size_t DOMStorageCachedArea::MemoryBytesUsedByCache() const {
    155   return map_.get() ? map_->bytes_used() : 0;
    156 }
    157 
    158 void DOMStorageCachedArea::Prime(int connection_id) {
    159   DCHECK(!map_.get());
    160 
    161   // The LoadArea method is actually synchronous, but we have to
    162   // wait for an asyncly delivered message to know when incoming
    163   // mutation events should be applied. Our valuemap is plucked
    164   // from ipc stream out of order, mutations in front if it need
    165   // to be ignored.
    166 
    167   // Ignore all mutations until OnLoadComplete time.
    168   ignore_all_mutations_ = true;
    169   DOMStorageValuesMap values;
    170   bool send_log_get_messages = false;
    171   base::TimeTicks before = base::TimeTicks::Now();
    172   proxy_->LoadArea(connection_id,
    173                    &values,
    174                    &send_log_get_messages,
    175                    base::Bind(&DOMStorageCachedArea::OnLoadComplete,
    176                               weak_factory_.GetWeakPtr()));
    177   base::TimeDelta time_to_prime = base::TimeTicks::Now() - before;
    178   // Keeping this histogram named the same (without the ForRenderer suffix)
    179   // to maintain histogram continuity.
    180   UMA_HISTOGRAM_TIMES("LocalStorage.TimeToPrimeLocalStorage",
    181                       time_to_prime);
    182   map_ = new DOMStorageMap(kPerStorageAreaQuota);
    183   map_->SwapValues(&values);
    184   if (send_log_get_messages)
    185     remaining_log_get_messages_ = kMaxLogGetMessagesToSend;
    186 
    187   size_t local_storage_size_kb = map_->bytes_used() / 1024;
    188   // Track localStorage size, from 0-6MB. Note that the maximum size should be
    189   // 5MB, but we add some slop since we want to make sure the max size is always
    190   // above what we see in practice, since histograms can't change.
    191   UMA_HISTOGRAM_CUSTOM_COUNTS("LocalStorage.RendererLocalStorageSizeInKB",
    192                               local_storage_size_kb,
    193                               0, 6 * 1024, 50);
    194   if (local_storage_size_kb < 100) {
    195     UMA_HISTOGRAM_TIMES(
    196         "LocalStorage.RendererTimeToPrimeLocalStorageUnder100KB",
    197         time_to_prime);
    198   } else if (local_storage_size_kb < 1000) {
    199     UMA_HISTOGRAM_TIMES(
    200         "LocalStorage.RendererTimeToPrimeLocalStorage100KBTo1MB",
    201         time_to_prime);
    202   } else {
    203     UMA_HISTOGRAM_TIMES(
    204         "LocalStorage.RendererTimeToPrimeLocalStorage1MBTo5MB",
    205         time_to_prime);
    206   }
    207 }
    208 
    209 void DOMStorageCachedArea::Reset() {
    210   map_ = NULL;
    211   weak_factory_.InvalidateWeakPtrs();
    212   ignore_key_mutations_.clear();
    213   ignore_all_mutations_ = false;
    214 }
    215 
    216 void DOMStorageCachedArea::OnLoadComplete(bool success) {
    217   DCHECK(success);
    218   DCHECK(ignore_all_mutations_);
    219   ignore_all_mutations_ = false;
    220 }
    221 
    222 void DOMStorageCachedArea::OnSetItemComplete(const base::string16& key,
    223                                              bool success) {
    224   if (!success) {
    225     Reset();
    226     return;
    227   }
    228   std::map<base::string16, int>::iterator found =
    229       ignore_key_mutations_.find(key);
    230   DCHECK(found != ignore_key_mutations_.end());
    231   if (--found->second == 0)
    232     ignore_key_mutations_.erase(found);
    233 }
    234 
    235 void DOMStorageCachedArea::OnRemoveItemComplete(const base::string16& key,
    236                                                 bool success) {
    237   DCHECK(success);
    238   std::map<base::string16, int>::iterator found =
    239       ignore_key_mutations_.find(key);
    240   DCHECK(found != ignore_key_mutations_.end());
    241   if (--found->second == 0)
    242     ignore_key_mutations_.erase(found);
    243 }
    244 
    245 void DOMStorageCachedArea::OnClearComplete(bool success) {
    246   DCHECK(success);
    247   DCHECK(ignore_all_mutations_);
    248   ignore_all_mutations_ = false;
    249 }
    250 
    251 }  // namespace content
    252