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