Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(566)

Side by Side Diff: webkit/dom_storage/dom_storage_area.cc

Issue 15990007: Move dom_storage to new locations. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « webkit/dom_storage/dom_storage_area.h ('k') | webkit/dom_storage/dom_storage_area_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "webkit/dom_storage/dom_storage_area.h"
6
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/time.h"
12 #include "base/utf_string_conversions.h"
13 #include "third_party/WebKit/public/platform/WebString.h"
14 #include "webkit/base/file_path_string_conversions.h"
15 #include "webkit/base/origin_url_conversions.h"
16 #include "webkit/browser/database/database_util.h"
17 #include "webkit/common/fileapi/file_system_util.h"
18 #include "webkit/dom_storage/dom_storage_map.h"
19 #include "webkit/dom_storage/dom_storage_namespace.h"
20 #include "webkit/dom_storage/dom_storage_task_runner.h"
21 #include "webkit/dom_storage/dom_storage_types.h"
22 #include "webkit/dom_storage/local_storage_database_adapter.h"
23 #include "webkit/dom_storage/session_storage_database.h"
24 #include "webkit/dom_storage/session_storage_database_adapter.h"
25
26 using webkit_database::DatabaseUtil;
27
28 namespace dom_storage {
29
30 static const int kCommitTimerSeconds = 1;
31
32 DomStorageArea::CommitBatch::CommitBatch()
33 : clear_all_first(false) {
34 }
35 DomStorageArea::CommitBatch::~CommitBatch() {}
36
37
38 // static
39 const base::FilePath::CharType DomStorageArea::kDatabaseFileExtension[] =
40 FILE_PATH_LITERAL(".localstorage");
41
42 // static
43 base::FilePath DomStorageArea::DatabaseFileNameFromOrigin(const GURL& origin) {
44 base::string16 filename = webkit_base::GetOriginIdentifierFromURL(origin);
45 // There is no base::FilePath.AppendExtension() method, so start with just the
46 // extension as the filename, and then InsertBeforeExtension the desired
47 // name.
48 return base::FilePath().Append(kDatabaseFileExtension).
49 InsertBeforeExtensionASCII(UTF16ToUTF8(filename));
50 }
51
52 // static
53 GURL DomStorageArea::OriginFromDatabaseFileName(const base::FilePath& name) {
54 DCHECK(name.MatchesExtension(kDatabaseFileExtension));
55 WebKit::WebString origin_id = webkit_base::FilePathToWebString(
56 name.BaseName().RemoveExtension());
57 return webkit_base::GetOriginURLFromIdentifier(origin_id);
58 }
59
60 DomStorageArea::DomStorageArea(const GURL& origin, const base::FilePath& directo ry,
61 DomStorageTaskRunner* task_runner)
62 : namespace_id_(kLocalStorageNamespaceId), origin_(origin),
63 directory_(directory),
64 task_runner_(task_runner),
65 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)),
66 is_initial_import_done_(true),
67 is_shutdown_(false),
68 commit_batches_in_flight_(0) {
69 if (!directory.empty()) {
70 base::FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_));
71 backing_.reset(new LocalStorageDatabaseAdapter(path));
72 is_initial_import_done_ = false;
73 }
74 }
75
76 DomStorageArea::DomStorageArea(
77 int64 namespace_id,
78 const std::string& persistent_namespace_id,
79 const GURL& origin,
80 SessionStorageDatabase* session_storage_backing,
81 DomStorageTaskRunner* task_runner)
82 : namespace_id_(namespace_id),
83 persistent_namespace_id_(persistent_namespace_id),
84 origin_(origin),
85 task_runner_(task_runner),
86 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)),
87 session_storage_backing_(session_storage_backing),
88 is_initial_import_done_(true),
89 is_shutdown_(false),
90 commit_batches_in_flight_(0) {
91 DCHECK(namespace_id != kLocalStorageNamespaceId);
92 if (session_storage_backing) {
93 backing_.reset(new SessionStorageDatabaseAdapter(
94 session_storage_backing, persistent_namespace_id, origin));
95 is_initial_import_done_ = false;
96 }
97 }
98
99 DomStorageArea::~DomStorageArea() {
100 }
101
102 void DomStorageArea::ExtractValues(ValuesMap* map) {
103 if (is_shutdown_)
104 return;
105 InitialImportIfNeeded();
106 map_->ExtractValues(map);
107 }
108
109 unsigned DomStorageArea::Length() {
110 if (is_shutdown_)
111 return 0;
112 InitialImportIfNeeded();
113 return map_->Length();
114 }
115
116 NullableString16 DomStorageArea::Key(unsigned index) {
117 if (is_shutdown_)
118 return NullableString16(true);
119 InitialImportIfNeeded();
120 return map_->Key(index);
121 }
122
123 NullableString16 DomStorageArea::GetItem(const base::string16& key) {
124 if (is_shutdown_)
125 return NullableString16(true);
126 InitialImportIfNeeded();
127 return map_->GetItem(key);
128 }
129
130 bool DomStorageArea::SetItem(const base::string16& key,
131 const base::string16& value,
132 NullableString16* old_value) {
133 if (is_shutdown_)
134 return false;
135 InitialImportIfNeeded();
136 if (!map_->HasOneRef())
137 map_ = map_->DeepCopy();
138 bool success = map_->SetItem(key, value, old_value);
139 if (success && backing_) {
140 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
141 commit_batch->changed_values[key] = NullableString16(value, false);
142 }
143 return success;
144 }
145
146 bool DomStorageArea::RemoveItem(const base::string16& key,
147 base::string16* old_value) {
148 if (is_shutdown_)
149 return false;
150 InitialImportIfNeeded();
151 if (!map_->HasOneRef())
152 map_ = map_->DeepCopy();
153 bool success = map_->RemoveItem(key, old_value);
154 if (success && backing_) {
155 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
156 commit_batch->changed_values[key] = NullableString16(true);
157 }
158 return success;
159 }
160
161 bool DomStorageArea::Clear() {
162 if (is_shutdown_)
163 return false;
164 InitialImportIfNeeded();
165 if (map_->Length() == 0)
166 return false;
167
168 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance);
169
170 if (backing_) {
171 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
172 commit_batch->clear_all_first = true;
173 commit_batch->changed_values.clear();
174 }
175
176 return true;
177 }
178
179 void DomStorageArea::FastClear() {
180 // TODO(marja): Unify clearing localStorage and sessionStorage. The problem is
181 // to make the following 3 to work together: 1) FastClear, 2) PurgeMemory and
182 // 3) not creating events when clearing an empty area.
183 if (is_shutdown_)
184 return;
185
186 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance);
187 // This ensures no import will happen while we're waiting to clear the data
188 // from the database. This mechanism fails if PurgeMemory is called.
189 is_initial_import_done_ = true;
190
191 if (backing_) {
192 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
193 commit_batch->clear_all_first = true;
194 commit_batch->changed_values.clear();
195 }
196 }
197
198 DomStorageArea* DomStorageArea::ShallowCopy(
199 int64 destination_namespace_id,
200 const std::string& destination_persistent_namespace_id) {
201 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_);
202 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id);
203
204 DomStorageArea* copy = new DomStorageArea(destination_namespace_id,
205 destination_persistent_namespace_id,
206 origin_,
207 session_storage_backing_.get(),
208 task_runner_.get());
209 copy->map_ = map_;
210 copy->is_shutdown_ = is_shutdown_;
211 copy->is_initial_import_done_ = true;
212
213 // All the uncommitted changes to this area need to happen before the actual
214 // shallow copy is made (scheduled by the upper layer). Another OnCommitTimer
215 // call might be in the event queue at this point, but it's handled gracefully
216 // when it fires.
217 if (commit_batch_)
218 OnCommitTimer();
219 return copy;
220 }
221
222 bool DomStorageArea::HasUncommittedChanges() const {
223 DCHECK(!is_shutdown_);
224 return commit_batch_.get() || commit_batches_in_flight_;
225 }
226
227 void DomStorageArea::DeleteOrigin() {
228 DCHECK(!is_shutdown_);
229 // This function shouldn't be called for sessionStorage.
230 DCHECK(!session_storage_backing_.get());
231 if (HasUncommittedChanges()) {
232 // TODO(michaeln): This logically deletes the data immediately,
233 // and in a matter of a second, deletes the rows from the backing
234 // database file, but the file itself will linger until shutdown
235 // or purge time. Ideally, this should delete the file more
236 // quickly.
237 Clear();
238 return;
239 }
240 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance);
241 if (backing_) {
242 is_initial_import_done_ = false;
243 backing_->Reset();
244 backing_->DeleteFiles();
245 }
246 }
247
248 void DomStorageArea::PurgeMemory() {
249 DCHECK(!is_shutdown_);
250 // Purging sessionStorage is not supported; it won't work with FastClear.
251 DCHECK(!session_storage_backing_.get());
252 if (!is_initial_import_done_ || // We're not using any memory.
253 !backing_.get() || // We can't purge anything.
254 HasUncommittedChanges()) // We leave things alone with changes pending.
255 return;
256
257 // Drop the in memory cache, we'll reload when needed.
258 is_initial_import_done_ = false;
259 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance);
260
261 // Recreate the database object, this frees up the open sqlite connection
262 // and its page cache.
263 backing_->Reset();
264 }
265
266 void DomStorageArea::Shutdown() {
267 DCHECK(!is_shutdown_);
268 is_shutdown_ = true;
269 map_ = NULL;
270 if (!backing_)
271 return;
272
273 bool success = task_runner_->PostShutdownBlockingTask(
274 FROM_HERE,
275 DomStorageTaskRunner::COMMIT_SEQUENCE,
276 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this));
277 DCHECK(success);
278 }
279
280 void DomStorageArea::InitialImportIfNeeded() {
281 if (is_initial_import_done_)
282 return;
283
284 DCHECK(backing_.get());
285
286 base::TimeTicks before = base::TimeTicks::Now();
287 ValuesMap initial_values;
288 backing_->ReadAllValues(&initial_values);
289 map_->SwapValues(&initial_values);
290 is_initial_import_done_ = true;
291 base::TimeDelta time_to_import = base::TimeTicks::Now() - before;
292 UMA_HISTOGRAM_TIMES("LocalStorage.BrowserTimeToPrimeLocalStorage",
293 time_to_import);
294
295 size_t local_storage_size_kb = map_->bytes_used() / 1024;
296 // Track localStorage size, from 0-6MB. Note that the maximum size should be
297 // 5MB, but we add some slop since we want to make sure the max size is always
298 // above what we see in practice, since histograms can't change.
299 UMA_HISTOGRAM_CUSTOM_COUNTS("LocalStorage.BrowserLocalStorageSizeInKB",
300 local_storage_size_kb,
301 0, 6 * 1024, 50);
302 if (local_storage_size_kb < 100) {
303 UMA_HISTOGRAM_TIMES(
304 "LocalStorage.BrowserTimeToPrimeLocalStorageUnder100KB",
305 time_to_import);
306 } else if (local_storage_size_kb < 1000) {
307 UMA_HISTOGRAM_TIMES(
308 "LocalStorage.BrowserTimeToPrimeLocalStorage100KBTo1MB",
309 time_to_import);
310 } else {
311 UMA_HISTOGRAM_TIMES(
312 "LocalStorage.BrowserTimeToPrimeLocalStorage1MBTo5MB",
313 time_to_import);
314 }
315 }
316
317 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() {
318 DCHECK(!is_shutdown_);
319 if (!commit_batch_) {
320 commit_batch_.reset(new CommitBatch());
321
322 // Start a timer to commit any changes that accrue in the batch, but only if
323 // no commits are currently in flight. In that case the timer will be
324 // started after the commits have happened.
325 if (!commit_batches_in_flight_) {
326 task_runner_->PostDelayedTask(
327 FROM_HERE,
328 base::Bind(&DomStorageArea::OnCommitTimer, this),
329 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
330 }
331 }
332 return commit_batch_.get();
333 }
334
335 void DomStorageArea::OnCommitTimer() {
336 if (is_shutdown_)
337 return;
338
339 DCHECK(backing_.get());
340
341 // It's possible that there is nothing to commit, since a shallow copy occured
342 // before the timer fired.
343 if (!commit_batch_)
344 return;
345
346 // This method executes on the primary sequence, we schedule
347 // a task for immediate execution on the commit sequence.
348 DCHECK(task_runner_->IsRunningOnPrimarySequence());
349 bool success = task_runner_->PostShutdownBlockingTask(
350 FROM_HERE,
351 DomStorageTaskRunner::COMMIT_SEQUENCE,
352 base::Bind(&DomStorageArea::CommitChanges, this,
353 base::Owned(commit_batch_.release())));
354 ++commit_batches_in_flight_;
355 DCHECK(success);
356 }
357
358 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) {
359 // This method executes on the commit sequence.
360 DCHECK(task_runner_->IsRunningOnCommitSequence());
361 bool success = backing_->CommitChanges(commit_batch->clear_all_first,
362 commit_batch->changed_values);
363 DCHECK(success); // TODO(michaeln): what if it fails?
364 task_runner_->PostTask(
365 FROM_HERE,
366 base::Bind(&DomStorageArea::OnCommitComplete, this));
367 }
368
369 void DomStorageArea::OnCommitComplete() {
370 // We're back on the primary sequence in this method.
371 DCHECK(task_runner_->IsRunningOnPrimarySequence());
372 --commit_batches_in_flight_;
373 if (is_shutdown_)
374 return;
375 if (commit_batch_.get() && !commit_batches_in_flight_) {
376 // More changes have accrued, restart the timer.
377 task_runner_->PostDelayedTask(
378 FROM_HERE,
379 base::Bind(&DomStorageArea::OnCommitTimer, this),
380 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
381 }
382 }
383
384 void DomStorageArea::ShutdownInCommitSequence() {
385 // This method executes on the commit sequence.
386 DCHECK(task_runner_->IsRunningOnCommitSequence());
387 DCHECK(backing_.get());
388 if (commit_batch_) {
389 // Commit any changes that accrued prior to the timer firing.
390 bool success = backing_->CommitChanges(
391 commit_batch_->clear_all_first,
392 commit_batch_->changed_values);
393 DCHECK(success);
394 }
395 commit_batch_.reset();
396 backing_.reset();
397 session_storage_backing_ = NULL;
398 }
399
400 } // namespace dom_storage
OLDNEW
« no previous file with comments | « webkit/dom_storage/dom_storage_area.h ('k') | webkit/dom_storage/dom_storage_area_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698