1// Copyright 2013 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/browser/dom_storage/dom_storage_host.h"
6
7#include "content/browser/dom_storage/dom_storage_area.h"
8#include "content/browser/dom_storage/dom_storage_context_impl.h"
9#include "content/browser/dom_storage/dom_storage_namespace.h"
10#include "content/common/dom_storage/dom_storage_types.h"
11#include "url/gurl.h"
12
13namespace content {
14
15DOMStorageHost::DOMStorageHost(DOMStorageContextImpl* context,
16                               int render_process_id)
17    : context_(context),
18      render_process_id_(render_process_id) {
19}
20
21DOMStorageHost::~DOMStorageHost() {
22  AreaMap::const_iterator it = connections_.begin();
23  for (; it != connections_.end(); ++it)
24    it->second.namespace_->CloseStorageArea(it->second.area_.get());
25  connections_.clear();  // Clear prior to releasing the context_
26}
27
28bool DOMStorageHost::OpenStorageArea(int connection_id, int namespace_id,
29                                     const GURL& origin) {
30  DCHECK(!GetOpenArea(connection_id));
31  if (GetOpenArea(connection_id))
32    return false;  // Indicates the renderer gave us very bad data.
33  NamespaceAndArea references;
34  references.namespace_ = context_->GetStorageNamespace(namespace_id);
35  if (!references.namespace_.get())
36    return false;
37  references.area_ = references.namespace_->OpenStorageArea(origin);
38  DCHECK(references.area_.get());
39  connections_[connection_id] = references;
40  return true;
41}
42
43void DOMStorageHost::CloseStorageArea(int connection_id) {
44  AreaMap::iterator found = connections_.find(connection_id);
45  if (found == connections_.end())
46    return;
47  found->second.namespace_->CloseStorageArea(found->second.area_.get());
48  connections_.erase(found);
49}
50
51bool DOMStorageHost::ExtractAreaValues(
52    int connection_id, DOMStorageValuesMap* map, bool* send_log_get_messages) {
53  map->clear();
54  DOMStorageArea* area = GetOpenArea(connection_id);
55  if (!area)
56    return false;
57  if (!area->IsLoadedInMemory()) {
58    DOMStorageNamespace* ns = GetNamespace(connection_id);
59    DCHECK(ns);
60    if (ns->CountInMemoryAreas() > kMaxInMemoryStorageAreas) {
61      ns->PurgeMemory(DOMStorageNamespace::PURGE_UNOPENED);
62      if (ns->CountInMemoryAreas() > kMaxInMemoryStorageAreas)
63        ns->PurgeMemory(DOMStorageNamespace::PURGE_AGGRESSIVE);
64    }
65  }
66  area->ExtractValues(map);
67  *send_log_get_messages = false;
68  DOMStorageNamespace* ns = GetNamespace(connection_id);
69  DCHECK(ns);
70  *send_log_get_messages = ns->IsLoggingRenderer(render_process_id_);
71  return true;
72}
73
74unsigned DOMStorageHost::GetAreaLength(int connection_id) {
75  DOMStorageArea* area = GetOpenArea(connection_id);
76  if (!area)
77    return 0;
78  return area->Length();
79}
80
81base::NullableString16 DOMStorageHost::GetAreaKey(int connection_id,
82                                                  unsigned index) {
83  DOMStorageArea* area = GetOpenArea(connection_id);
84  if (!area)
85    return base::NullableString16();
86  return area->Key(index);
87}
88
89base::NullableString16 DOMStorageHost::GetAreaItem(int connection_id,
90                                                   const base::string16& key) {
91  DOMStorageArea* area = GetOpenArea(connection_id);
92  if (!area)
93    return base::NullableString16();
94  return area->GetItem(key);
95}
96
97bool DOMStorageHost::SetAreaItem(
98    int connection_id, const base::string16& key,
99    const base::string16& value, const GURL& page_url,
100    base::NullableString16* old_value) {
101  DOMStorageArea* area = GetOpenArea(connection_id);
102  if (!area)
103    return false;
104  if (!area->SetItem(key, value, old_value))
105    return false;
106  if (old_value->is_null() || old_value->string() != value)
107    context_->NotifyItemSet(area, key, value, *old_value, page_url);
108  MaybeLogTransaction(connection_id,
109                      DOMStorageNamespace::TRANSACTION_WRITE,
110                      area->origin(), page_url, key,
111                      base::NullableString16(value, false));
112  return true;
113}
114
115void DOMStorageHost::LogGetAreaItem(
116    int connection_id, const base::string16& key,
117    const base::NullableString16& value) {
118  DOMStorageArea* area = GetOpenArea(connection_id);
119  if (!area)
120    return;
121  MaybeLogTransaction(connection_id,
122                      DOMStorageNamespace::TRANSACTION_READ,
123                      area->origin(), GURL(), key, value);
124}
125
126bool DOMStorageHost::RemoveAreaItem(
127    int connection_id, const base::string16& key, const GURL& page_url,
128    base::string16* old_value) {
129  DOMStorageArea* area = GetOpenArea(connection_id);
130  if (!area)
131    return false;
132  if (!area->RemoveItem(key, old_value))
133    return false;
134  context_->NotifyItemRemoved(area, key, *old_value, page_url);
135  MaybeLogTransaction(connection_id,
136                      DOMStorageNamespace::TRANSACTION_REMOVE,
137                      area->origin(), page_url, key, base::NullableString16());
138  return true;
139}
140
141bool DOMStorageHost::ClearArea(int connection_id, const GURL& page_url) {
142  DOMStorageArea* area = GetOpenArea(connection_id);
143  if (!area)
144    return false;
145  if (!area->Clear())
146    return false;
147  context_->NotifyAreaCleared(area, page_url);
148  MaybeLogTransaction(connection_id,
149                      DOMStorageNamespace::TRANSACTION_CLEAR,
150                      area->origin(), page_url, base::string16(),
151                      base::NullableString16());
152  return true;
153}
154
155bool DOMStorageHost::HasAreaOpen(
156    int64 namespace_id, const GURL& origin, int64* alias_namespace_id) const {
157  AreaMap::const_iterator it = connections_.begin();
158  for (; it != connections_.end(); ++it) {
159    if (namespace_id == it->second.area_->namespace_id() &&
160        origin == it->second.area_->origin()) {
161      *alias_namespace_id = it->second.namespace_->namespace_id();
162      return true;
163    }
164  }
165  return false;
166}
167
168bool DOMStorageHost::ResetOpenAreasForNamespace(int64 namespace_id) {
169  bool result = false;
170  AreaMap::iterator it = connections_.begin();
171  for (; it != connections_.end(); ++it) {
172    if (namespace_id == it->second.namespace_->namespace_id()) {
173      GURL origin = it->second.area_->origin();
174      it->second.namespace_->CloseStorageArea(it->second.area_.get());
175      it->second.area_ = it->second.namespace_->OpenStorageArea(origin);
176      result = true;
177    }
178  }
179  return result;
180}
181
182DOMStorageArea* DOMStorageHost::GetOpenArea(int connection_id) {
183  AreaMap::iterator found = connections_.find(connection_id);
184  if (found == connections_.end())
185    return NULL;
186  return found->second.area_.get();
187}
188
189DOMStorageNamespace* DOMStorageHost::GetNamespace(int connection_id) {
190  AreaMap::iterator found = connections_.find(connection_id);
191  if (found == connections_.end())
192    return NULL;
193  return found->second.namespace_.get();
194}
195
196void DOMStorageHost::MaybeLogTransaction(
197    int connection_id,
198    DOMStorageNamespace::LogType transaction_type,
199    const GURL& origin,
200    const GURL& page_url,
201    const base::string16& key,
202    const base::NullableString16& value) {
203  DOMStorageNamespace* ns = GetNamespace(connection_id);
204  DCHECK(ns);
205  if (!ns->IsLoggingRenderer(render_process_id_))
206    return;
207  DOMStorageNamespace::TransactionRecord transaction;
208  transaction.transaction_type = transaction_type;
209  transaction.origin = origin;
210  transaction.page_url = page_url;
211  transaction.key = key;
212  transaction.value = value;
213  ns->AddTransaction(render_process_id_, transaction);
214}
215
216// NamespaceAndArea
217
218DOMStorageHost::NamespaceAndArea::NamespaceAndArea() {}
219DOMStorageHost::NamespaceAndArea::~NamespaceAndArea() {}
220
221}  // namespace content
222