1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2006
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Neil Deakin <enndeakin@sympatico.ca>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsCOMPtr.h"
40 : #include "nsDOMError.h"
41 : #include "nsDOMStorage.h"
42 : #include "nsDOMStorageDBWrapper.h"
43 : #include "nsDOMStoragePersistentDB.h"
44 : #include "nsIFile.h"
45 : #include "nsIVariant.h"
46 : #include "nsIEffectiveTLDService.h"
47 : #include "nsAppDirectoryServiceDefs.h"
48 : #include "mozStorageCID.h"
49 : #include "mozStorageHelper.h"
50 : #include "mozIStorageService.h"
51 : #include "mozIStorageBindingParamsArray.h"
52 : #include "mozIStorageBindingParams.h"
53 : #include "mozIStorageValueArray.h"
54 : #include "mozIStorageFunction.h"
55 : #include "nsNetUtil.h"
56 :
57 : using namespace mozilla;
58 :
59 : // Temporary tables for a storage scope will be flushed if found older
60 : // then this time in seconds since the load
61 : #define TEMP_TABLE_MAX_AGE (10) // seconds
62 :
63 : class nsReverseStringSQLFunction : public mozIStorageFunction
64 78 : {
65 : NS_DECL_ISUPPORTS
66 : NS_DECL_MOZISTORAGEFUNCTION
67 : };
68 :
69 936 : NS_IMPL_ISUPPORTS1(nsReverseStringSQLFunction, mozIStorageFunction)
70 :
71 : NS_IMETHODIMP
72 0 : nsReverseStringSQLFunction::OnFunctionCall(
73 : mozIStorageValueArray *aFunctionArguments, nsIVariant **aResult)
74 : {
75 : nsresult rv;
76 :
77 0 : nsCAutoString stringToReverse;
78 0 : rv = aFunctionArguments->GetUTF8String(0, stringToReverse);
79 0 : NS_ENSURE_SUCCESS(rv, rv);
80 :
81 0 : nsCAutoString result;
82 0 : ReverseString(stringToReverse, result);
83 :
84 : nsCOMPtr<nsIWritableVariant> outVar(do_CreateInstance(
85 0 : NS_VARIANT_CONTRACTID, &rv));
86 0 : NS_ENSURE_SUCCESS(rv, rv);
87 :
88 0 : rv = outVar->SetAsAUTF8String(result);
89 0 : NS_ENSURE_SUCCESS(rv, rv);
90 :
91 0 : *aResult = outVar.get();
92 0 : outVar.forget();
93 0 : return NS_OK;
94 : }
95 :
96 : class nsIsOfflineSQLFunction : public mozIStorageFunction
97 78 : {
98 : NS_DECL_ISUPPORTS
99 : NS_DECL_MOZISTORAGEFUNCTION
100 : };
101 :
102 936 : NS_IMPL_ISUPPORTS1(nsIsOfflineSQLFunction, mozIStorageFunction)
103 :
104 154 : nsDOMStoragePersistentDB::nsDOMStoragePersistentDB()
105 154 : : mStatements(mConnection)
106 : {
107 154 : mTempTableLoads.Init(16);
108 154 : }
109 :
110 : NS_IMETHODIMP
111 0 : nsIsOfflineSQLFunction::OnFunctionCall(
112 : mozIStorageValueArray *aFunctionArguments, nsIVariant **aResult)
113 : {
114 : nsresult rv;
115 :
116 0 : nsCAutoString scope;
117 0 : rv = aFunctionArguments->GetUTF8String(0, scope);
118 0 : NS_ENSURE_SUCCESS(rv, rv);
119 :
120 0 : nsCAutoString domain;
121 0 : rv = nsDOMStorageDBWrapper::GetDomainFromScopeKey(scope, domain);
122 0 : NS_ENSURE_SUCCESS(rv, rv);
123 :
124 0 : bool hasOfflinePermission = IsOfflineAllowed(domain);
125 0 : NS_ENSURE_SUCCESS(rv, rv);
126 :
127 : nsCOMPtr<nsIWritableVariant> outVar(do_CreateInstance(
128 0 : NS_VARIANT_CONTRACTID, &rv));
129 0 : NS_ENSURE_SUCCESS(rv, rv);
130 :
131 0 : rv = outVar->SetAsBool(hasOfflinePermission);
132 0 : NS_ENSURE_SUCCESS(rv, rv);
133 :
134 0 : *aResult = outVar.get();
135 0 : outVar.forget();
136 0 : return NS_OK;
137 : }
138 :
139 : nsresult
140 116 : nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
141 : {
142 : nsresult rv;
143 :
144 232 : nsCOMPtr<nsIFile> storageFile;
145 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
146 116 : getter_AddRefs(storageFile));
147 116 : NS_ENSURE_SUCCESS(rv, rv);
148 78 : rv = storageFile->Append(aDatabaseName);
149 78 : NS_ENSURE_SUCCESS(rv, rv);
150 :
151 156 : nsCOMPtr<mozIStorageService> service;
152 :
153 78 : service = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
154 78 : NS_ENSURE_SUCCESS(rv, rv);
155 78 : rv = service->OpenUnsharedDatabase(storageFile, getter_AddRefs(mConnection));
156 78 : if (rv == NS_ERROR_FILE_CORRUPTED) {
157 : // delete the db and try opening again
158 0 : rv = storageFile->Remove(false);
159 0 : NS_ENSURE_SUCCESS(rv, rv);
160 0 : rv = service->OpenUnsharedDatabase(storageFile, getter_AddRefs(mConnection));
161 : }
162 78 : NS_ENSURE_SUCCESS(rv, rv);
163 :
164 156 : rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
165 78 : MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA temp_store = MEMORY"));
166 78 : NS_ENSURE_SUCCESS(rv, rv);
167 :
168 156 : mozStorageTransaction transaction(mConnection, false);
169 :
170 : // Ensure Gecko 1.9.1 storage table
171 156 : rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
172 : "CREATE TABLE IF NOT EXISTS webappsstore2 ("
173 : "scope TEXT, "
174 : "key TEXT, "
175 : "value TEXT, "
176 : "secure INTEGER, "
177 78 : "owner TEXT)"));
178 78 : NS_ENSURE_SUCCESS(rv, rv);
179 :
180 156 : rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
181 : "CREATE UNIQUE INDEX IF NOT EXISTS scope_key_index"
182 78 : " ON webappsstore2(scope, key)"));
183 78 : NS_ENSURE_SUCCESS(rv, rv);
184 :
185 156 : rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
186 : "CREATE TEMPORARY TABLE webappsstore2_temp ("
187 : "scope TEXT, "
188 : "key TEXT, "
189 : "value TEXT, "
190 : "secure INTEGER, "
191 : "owner TEXT, "
192 78 : "modified INTEGER DEFAULT 0)"));
193 78 : NS_ENSURE_SUCCESS(rv, rv);
194 :
195 156 : rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
196 : "CREATE UNIQUE INDEX scope_key_index_temp"
197 78 : " ON webappsstore2_temp(scope, key)"));
198 78 : NS_ENSURE_SUCCESS(rv, rv);
199 :
200 :
201 156 : rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
202 : "CREATE TEMPORARY VIEW webappsstore2_view AS "
203 : "SELECT scope, key, value, secure, owner FROM webappsstore2_temp "
204 : "UNION ALL "
205 : "SELECT scope, key, value, secure, owner FROM webappsstore2 "
206 : "WHERE NOT EXISTS ("
207 : "SELECT scope, key FROM webappsstore2_temp "
208 78 : "WHERE scope = webappsstore2.scope AND key = webappsstore2.key)"));
209 78 : NS_ENSURE_SUCCESS(rv, rv);
210 :
211 : // carry deletion to both the temporary table and the disk table
212 156 : rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
213 : "CREATE TEMPORARY TRIGGER webappsstore2_view_delete_trigger "
214 : "INSTEAD OF DELETE ON webappsstore2_view "
215 : "BEGIN "
216 : "DELETE FROM webappsstore2_temp "
217 : "WHERE scope = OLD.scope AND key = OLD.key; "
218 : "DELETE FROM webappsstore2 "
219 : "WHERE scope = OLD.scope AND key = OLD.key; "
220 78 : "END"));
221 78 : NS_ENSURE_SUCCESS(rv, rv);
222 :
223 156 : nsCOMPtr<mozIStorageFunction> function1(new nsReverseStringSQLFunction());
224 78 : NS_ENSURE_TRUE(function1, NS_ERROR_OUT_OF_MEMORY);
225 :
226 78 : rv = mConnection->CreateFunction(NS_LITERAL_CSTRING("REVERSESTRING"), 1, function1);
227 78 : NS_ENSURE_SUCCESS(rv, rv);
228 :
229 156 : nsCOMPtr<mozIStorageFunction> function2(new nsIsOfflineSQLFunction());
230 78 : NS_ENSURE_TRUE(function2, NS_ERROR_OUT_OF_MEMORY);
231 :
232 78 : rv = mConnection->CreateFunction(NS_LITERAL_CSTRING("ISOFFLINE"), 1, function2);
233 78 : NS_ENSURE_SUCCESS(rv, rv);
234 :
235 : bool exists;
236 :
237 : // Check if there is storage of Gecko 1.9.0 and if so, upgrade that storage
238 : // to actual webappsstore2 table and drop the obsolete table. First process
239 : // this newer table upgrade to priority potential duplicates from older
240 : // storage table.
241 156 : rv = mConnection->TableExists(NS_LITERAL_CSTRING("webappsstore"),
242 78 : &exists);
243 78 : NS_ENSURE_SUCCESS(rv, rv);
244 :
245 78 : if (exists) {
246 0 : rv = mConnection->ExecuteSimpleSQL(
247 0 : NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
248 : "webappsstore2(scope, key, value, secure, owner) "
249 : "SELECT REVERSESTRING(domain) || '.:', key, value, secure, owner "
250 0 : "FROM webappsstore"));
251 0 : NS_ENSURE_SUCCESS(rv, rv);
252 :
253 0 : rv = mConnection->ExecuteSimpleSQL(
254 0 : NS_LITERAL_CSTRING("DROP TABLE webappsstore"));
255 0 : NS_ENSURE_SUCCESS(rv, rv);
256 : }
257 :
258 : // Check if there is storage of Gecko 1.8 and if so, upgrade that storage
259 : // to actual webappsstore2 table and drop the obsolete table. Potential
260 : // duplicates will be ignored.
261 156 : rv = mConnection->TableExists(NS_LITERAL_CSTRING("moz_webappsstore"),
262 78 : &exists);
263 78 : NS_ENSURE_SUCCESS(rv, rv);
264 :
265 78 : if (exists) {
266 0 : rv = mConnection->ExecuteSimpleSQL(
267 0 : NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
268 : "webappsstore2(scope, key, value, secure, owner) "
269 : "SELECT REVERSESTRING(domain) || '.:', key, value, secure, domain "
270 0 : "FROM moz_webappsstore"));
271 0 : NS_ENSURE_SUCCESS(rv, rv);
272 :
273 0 : rv = mConnection->ExecuteSimpleSQL(
274 0 : NS_LITERAL_CSTRING("DROP TABLE moz_webappsstore"));
275 0 : NS_ENSURE_SUCCESS(rv, rv);
276 : }
277 :
278 78 : rv = transaction.Commit();
279 78 : NS_ENSURE_SUCCESS(rv, rv);
280 :
281 78 : return NS_OK;
282 : }
283 :
284 : void
285 74 : nsDOMStoragePersistentDB::Close()
286 : {
287 : // Finalize the cached statements.
288 74 : mStatements.FinalizeStatements();
289 :
290 148 : DebugOnly<nsresult> rv = mConnection->Close();
291 74 : MOZ_ASSERT(NS_SUCCEEDED(rv));
292 74 : }
293 :
294 : nsresult
295 106 : nsDOMStoragePersistentDB::EnsureLoadTemporaryTableForStorage(DOMStorageImpl* aStorage)
296 : {
297 106 : TimeStamp timeStamp;
298 :
299 106 : if (!mTempTableLoads.Get(aStorage->GetScopeDBKey(), &timeStamp)) {
300 : nsresult rv;
301 :
302 8 : rv = MaybeCommitInsertTransaction();
303 8 : NS_ENSURE_SUCCESS(rv, rv);
304 :
305 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
306 : "INSERT INTO webappsstore2_temp (scope, key, value, secure, owner) "
307 : "SELECT scope, key, value, secure, owner FROM webappsstore2 "
308 : "WHERE scope = :scope "
309 : "AND NOT EXISTS ( "
310 : "SELECT scope, key FROM webappsstore2_temp "
311 : "WHERE scope = webappsstore2.scope AND key = webappsstore2.key "
312 : ") "
313 24 : );
314 8 : NS_ENSURE_STATE(stmt);
315 16 : mozStorageStatementScoper scope(stmt);
316 :
317 16 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
318 8 : aStorage->GetScopeDBKey());
319 8 : NS_ENSURE_SUCCESS(rv, rv);
320 :
321 8 : rv = stmt->Execute();
322 8 : NS_ENSURE_SUCCESS(rv, rv);
323 :
324 8 : mTempTableLoads.Put(aStorage->GetScopeDBKey(), TimeStamp::Now());
325 :
326 16 : DOMStorageImpl::gStorageDB->EnsureTempTableFlushTimer();
327 : }
328 :
329 106 : return NS_OK;
330 : }
331 :
332 : /* static */
333 : PLDHashOperator
334 8 : nsDOMStoragePersistentDB::FlushTemporaryTable(nsCStringHashKey::KeyType aKey,
335 : TimeStamp& aData,
336 : void* aUserArg)
337 : {
338 8 : FlushTemporaryTableData* data = (FlushTemporaryTableData*)aUserArg;
339 :
340 8 : if (!data->mForce &&
341 0 : ((TimeStamp::Now() - aData).ToSeconds() < TEMP_TABLE_MAX_AGE))
342 0 : return PL_DHASH_NEXT;
343 :
344 : {
345 : nsCOMPtr<mozIStorageStatement> stmt =
346 : data->mDB->mStatements.GetCachedStatement(
347 : "INSERT OR REPLACE INTO webappsstore2 "
348 : "SELECT scope, key, value, secure, owner FROM webappsstore2_temp "
349 : "WHERE scope = :scope AND modified = 1"
350 24 : );
351 8 : NS_ENSURE_TRUE(stmt, PL_DHASH_STOP);
352 16 : mozStorageStatementScoper scope(stmt);
353 :
354 8 : data->mRV = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), aKey);
355 8 : NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
356 :
357 8 : data->mRV = stmt->Execute();
358 8 : NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
359 : }
360 :
361 : {
362 : nsCOMPtr<mozIStorageStatement> stmt =
363 : data->mDB->mStatements.GetCachedStatement(
364 : "DELETE FROM webappsstore2_temp "
365 : "WHERE scope = :scope "
366 24 : );
367 8 : NS_ENSURE_TRUE(stmt, PL_DHASH_STOP);
368 16 : mozStorageStatementScoper scope(stmt);
369 :
370 8 : data->mRV = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), aKey);
371 8 : NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
372 :
373 8 : data->mRV = stmt->Execute();
374 8 : NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
375 : }
376 :
377 8 : return PL_DHASH_REMOVE;
378 : }
379 :
380 : nsresult
381 74 : nsDOMStoragePersistentDB::FlushTemporaryTables(bool force)
382 : {
383 148 : mozStorageTransaction trans(mConnection, false);
384 :
385 : nsresult rv;
386 :
387 : FlushTemporaryTableData data;
388 74 : data.mDB = this;
389 74 : data.mForce = force;
390 74 : data.mRV = NS_OK;
391 :
392 74 : mTempTableLoads.Enumerate(FlushTemporaryTable, &data);
393 74 : NS_ENSURE_SUCCESS(data.mRV, data.mRV);
394 :
395 74 : rv = trans.Commit();
396 74 : NS_ENSURE_SUCCESS(rv, rv);
397 :
398 74 : rv = MaybeCommitInsertTransaction();
399 74 : NS_ENSURE_SUCCESS(rv, rv);
400 :
401 74 : return NS_OK;
402 : }
403 :
404 : nsresult
405 38 : nsDOMStoragePersistentDB::GetAllKeys(DOMStorageImpl* aStorage,
406 : nsTHashtable<nsSessionStorageEntry>* aKeys)
407 : {
408 : nsresult rv;
409 :
410 38 : rv = MaybeCommitInsertTransaction();
411 38 : NS_ENSURE_SUCCESS(rv, rv);
412 :
413 38 : rv = EnsureLoadTemporaryTableForStorage(aStorage);
414 38 : NS_ENSURE_SUCCESS(rv, rv);
415 :
416 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
417 : "SELECT key, value, secure FROM webappsstore2_temp "
418 : "WHERE scope = :scope "
419 114 : );
420 38 : NS_ENSURE_STATE(stmt);
421 76 : mozStorageStatementScoper scope(stmt);
422 :
423 76 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
424 38 : aStorage->GetScopeDBKey());
425 38 : NS_ENSURE_SUCCESS(rv, rv);
426 :
427 : bool exists;
428 92 : while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&exists)) && exists) {
429 32 : nsAutoString key;
430 16 : rv = stmt->GetString(0, key);
431 16 : NS_ENSURE_SUCCESS(rv, rv);
432 :
433 32 : nsAutoString value;
434 16 : rv = stmt->GetString(1, value);
435 16 : NS_ENSURE_SUCCESS(rv, rv);
436 :
437 16 : PRInt32 secureInt = 0;
438 16 : rv = stmt->GetInt32(2, &secureInt);
439 16 : NS_ENSURE_SUCCESS(rv, rv);
440 :
441 16 : nsSessionStorageEntry* entry = aKeys->PutEntry(key);
442 16 : NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
443 :
444 16 : entry->mItem = new nsDOMStorageItem(aStorage, key, value, secureInt);
445 16 : if (!entry->mItem) {
446 0 : aKeys->RawRemoveEntry(entry);
447 0 : return NS_ERROR_OUT_OF_MEMORY;
448 : }
449 : }
450 :
451 38 : return NS_OK;
452 : }
453 :
454 : nsresult
455 50 : nsDOMStoragePersistentDB::GetKeyValue(DOMStorageImpl* aStorage,
456 : const nsAString& aKey,
457 : nsAString& aValue,
458 : bool* aSecure)
459 : {
460 : nsresult rv;
461 :
462 50 : rv = MaybeCommitInsertTransaction();
463 50 : NS_ENSURE_SUCCESS(rv, rv);
464 :
465 50 : rv = EnsureLoadTemporaryTableForStorage(aStorage);
466 50 : NS_ENSURE_SUCCESS(rv, rv);
467 :
468 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
469 : "SELECT value, secure FROM webappsstore2_temp "
470 : "WHERE scope = :scope "
471 : "AND key = :key "
472 150 : );
473 50 : NS_ENSURE_STATE(stmt);
474 100 : mozStorageStatementScoper scope(stmt);
475 :
476 100 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
477 50 : aStorage->GetScopeDBKey());
478 50 : NS_ENSURE_SUCCESS(rv, rv);
479 100 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
480 50 : aKey);
481 50 : NS_ENSURE_SUCCESS(rv, rv);
482 :
483 : bool exists;
484 50 : rv = stmt->ExecuteStep(&exists);
485 50 : NS_ENSURE_SUCCESS(rv, rv);
486 :
487 50 : PRInt32 secureInt = 0;
488 50 : if (exists) {
489 32 : rv = stmt->GetString(0, aValue);
490 32 : NS_ENSURE_SUCCESS(rv, rv);
491 :
492 32 : rv = stmt->GetInt32(1, &secureInt);
493 32 : NS_ENSURE_SUCCESS(rv, rv);
494 : }
495 : else {
496 18 : rv = NS_ERROR_DOM_NOT_FOUND_ERR;
497 : }
498 :
499 50 : *aSecure = !!secureInt;
500 :
501 50 : return rv;
502 : }
503 :
504 : nsresult
505 18 : nsDOMStoragePersistentDB::SetKey(DOMStorageImpl* aStorage,
506 : const nsAString& aKey,
507 : const nsAString& aValue,
508 : bool aSecure,
509 : PRInt32 aQuota,
510 : bool aExcludeOfflineFromUsage,
511 : PRInt32 *aNewUsage)
512 : {
513 : nsresult rv;
514 :
515 18 : rv = EnsureLoadTemporaryTableForStorage(aStorage);
516 18 : NS_ENSURE_SUCCESS(rv, rv);
517 :
518 18 : PRInt32 usage = 0;
519 18 : if (!aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage).IsEmpty()) {
520 6 : rv = GetUsage(aStorage, aExcludeOfflineFromUsage, &usage);
521 6 : NS_ENSURE_SUCCESS(rv, rv);
522 : }
523 :
524 18 : usage += aKey.Length() + aValue.Length();
525 :
526 36 : nsAutoString previousValue;
527 : bool secure;
528 18 : rv = aStorage->GetCachedValue(aKey, previousValue, &secure);
529 18 : if (NS_SUCCEEDED(rv)) {
530 0 : if (!aSecure && secure)
531 0 : return NS_ERROR_DOM_SECURITY_ERR;
532 0 : usage -= aKey.Length() + previousValue.Length();
533 : }
534 :
535 18 : if (usage > aQuota) {
536 0 : return NS_ERROR_DOM_QUOTA_REACHED;
537 : }
538 :
539 18 : rv = EnsureInsertTransaction();
540 18 : NS_ENSURE_SUCCESS(rv, rv);
541 :
542 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
543 : "INSERT OR REPLACE INTO webappsstore2_temp (scope, key, value, secure, modified) "
544 : "VALUES (:scope, :key, :value, :secure, 1) "
545 54 : );
546 18 : NS_ENSURE_STATE(stmt);
547 36 : mozStorageStatementScoper scopeinsert(stmt);
548 :
549 36 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
550 18 : aStorage->GetScopeDBKey());
551 18 : NS_ENSURE_SUCCESS(rv, rv);
552 36 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
553 18 : aKey);
554 18 : NS_ENSURE_SUCCESS(rv, rv);
555 36 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("value"),
556 18 : aValue);
557 18 : NS_ENSURE_SUCCESS(rv, rv);
558 36 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
559 18 : aSecure ? 1 : 0);
560 18 : NS_ENSURE_SUCCESS(rv, rv);
561 :
562 18 : rv = stmt->Execute();
563 18 : NS_ENSURE_SUCCESS(rv, rv);
564 :
565 18 : if (!aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage).IsEmpty()) {
566 : // No need to set mCachedOwner since it was set by GetUsage()
567 6 : mCachedUsage = usage;
568 : }
569 :
570 18 : *aNewUsage = usage;
571 :
572 18 : MarkScopeDirty(aStorage);
573 :
574 18 : return NS_OK;
575 : }
576 :
577 : nsresult
578 0 : nsDOMStoragePersistentDB::SetSecure(DOMStorageImpl* aStorage,
579 : const nsAString& aKey,
580 : const bool aSecure)
581 : {
582 : nsresult rv;
583 :
584 0 : rv = EnsureLoadTemporaryTableForStorage(aStorage);
585 0 : NS_ENSURE_SUCCESS(rv, rv);
586 :
587 0 : rv = EnsureInsertTransaction();
588 0 : NS_ENSURE_SUCCESS(rv, rv);
589 :
590 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
591 : "UPDATE webappsstore2_temp "
592 : "SET secure = :secure, modified = 1 "
593 : "WHERE scope = :scope "
594 : "AND key = :key "
595 0 : );
596 0 : NS_ENSURE_STATE(stmt);
597 0 : mozStorageStatementScoper scope(stmt);
598 :
599 0 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
600 0 : aStorage->GetScopeDBKey());
601 0 : NS_ENSURE_SUCCESS(rv, rv);
602 0 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
603 0 : aKey);
604 0 : NS_ENSURE_SUCCESS(rv, rv);
605 0 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
606 0 : aSecure ? 1 : 0);
607 0 : NS_ENSURE_SUCCESS(rv, rv);
608 :
609 0 : rv = stmt->Execute();
610 0 : NS_ENSURE_SUCCESS(rv, rv);
611 :
612 0 : MarkScopeDirty(aStorage);
613 :
614 0 : return NS_OK;
615 : }
616 :
617 : nsresult
618 6 : nsDOMStoragePersistentDB::RemoveKey(DOMStorageImpl* aStorage,
619 : const nsAString& aKey,
620 : bool aExcludeOfflineFromUsage,
621 : PRInt32 aKeyUsage)
622 : {
623 : nsresult rv;
624 :
625 6 : rv = MaybeCommitInsertTransaction();
626 6 : NS_ENSURE_SUCCESS(rv, rv);
627 :
628 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
629 : "DELETE FROM webappsstore2_view "
630 : "WHERE scope = :scope "
631 : "AND key = :key "
632 18 : );
633 6 : NS_ENSURE_STATE(stmt);
634 12 : mozStorageStatementScoper scope(stmt);
635 :
636 6 : if (DomainMaybeCached(
637 6 : aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage))) {
638 0 : mCachedUsage = 0;
639 0 : mCachedOwner.Truncate();
640 : }
641 :
642 12 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
643 6 : aStorage->GetScopeDBKey());
644 6 : NS_ENSURE_SUCCESS(rv, rv);
645 12 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
646 6 : aKey);
647 6 : NS_ENSURE_SUCCESS(rv, rv);
648 :
649 6 : rv = stmt->Execute();
650 6 : NS_ENSURE_SUCCESS(rv, rv);
651 :
652 6 : MarkScopeDirty(aStorage);
653 :
654 6 : return NS_OK;
655 : }
656 :
657 : nsresult
658 6 : nsDOMStoragePersistentDB::ClearStorage(DOMStorageImpl* aStorage)
659 : {
660 : nsresult rv;
661 :
662 6 : rv = MaybeCommitInsertTransaction();
663 6 : NS_ENSURE_SUCCESS(rv, rv);
664 :
665 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
666 : "DELETE FROM webappsstore2_view "
667 : "WHERE scope = :scope "
668 18 : );
669 6 : NS_ENSURE_STATE(stmt);
670 12 : mozStorageStatementScoper scope(stmt);
671 :
672 6 : mCachedUsage = 0;
673 6 : mCachedOwner.Truncate();
674 :
675 12 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
676 6 : aStorage->GetScopeDBKey());
677 6 : NS_ENSURE_SUCCESS(rv, rv);
678 :
679 6 : rv = stmt->Execute();
680 6 : NS_ENSURE_SUCCESS(rv, rv);
681 :
682 6 : MarkScopeDirty(aStorage);
683 :
684 6 : return NS_OK;
685 : }
686 :
687 : nsresult
688 48 : nsDOMStoragePersistentDB::RemoveOwner(const nsACString& aOwner,
689 : bool aIncludeSubDomains)
690 : {
691 : nsresult rv;
692 :
693 48 : rv = MaybeCommitInsertTransaction();
694 48 : NS_ENSURE_SUCCESS(rv, rv);
695 :
696 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
697 : "DELETE FROM webappsstore2_view "
698 : "WHERE scope GLOB :scope "
699 144 : );
700 48 : NS_ENSURE_STATE(stmt);
701 96 : mozStorageStatementScoper scope(stmt);
702 :
703 96 : nsCAutoString subdomainsDBKey;
704 48 : nsDOMStorageDBWrapper::CreateDomainScopeDBKey(aOwner, subdomainsDBKey);
705 :
706 48 : if (DomainMaybeCached(subdomainsDBKey)) {
707 0 : mCachedUsage = 0;
708 0 : mCachedOwner.Truncate();
709 : }
710 :
711 48 : if (!aIncludeSubDomains)
712 0 : subdomainsDBKey.AppendLiteral(":");
713 48 : subdomainsDBKey.AppendLiteral("*");
714 :
715 96 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
716 48 : subdomainsDBKey);
717 48 : NS_ENSURE_SUCCESS(rv, rv);
718 :
719 48 : rv = stmt->Execute();
720 48 : NS_ENSURE_SUCCESS(rv, rv);
721 :
722 48 : MarkAllScopesDirty();
723 :
724 48 : return NS_OK;
725 : }
726 :
727 :
728 : nsresult
729 12 : nsDOMStoragePersistentDB::RemoveOwners(const nsTArray<nsString> &aOwners,
730 : bool aIncludeSubDomains,
731 : bool aMatch)
732 : {
733 12 : if (aOwners.Length() == 0) {
734 12 : if (aMatch) {
735 1 : return NS_OK;
736 : }
737 :
738 11 : return RemoveAll();
739 : }
740 :
741 : // Using nsString here because it is going to be very long
742 0 : nsCString expression;
743 :
744 0 : if (aMatch) {
745 0 : expression.AppendLiteral("DELETE FROM webappsstore2_view WHERE scope IN (");
746 : } else {
747 0 : expression.AppendLiteral("DELETE FROM webappsstore2_view WHERE scope NOT IN (");
748 : }
749 :
750 0 : for (PRUint32 i = 0; i < aOwners.Length(); i++) {
751 0 : if (i)
752 0 : expression.AppendLiteral(" UNION ");
753 :
754 : expression.AppendLiteral(
755 0 : "SELECT DISTINCT scope FROM webappsstore2_temp WHERE scope GLOB :scope");
756 0 : expression.AppendInt(i);
757 0 : expression.AppendLiteral(" UNION ");
758 : expression.AppendLiteral(
759 0 : "SELECT DISTINCT scope FROM webappsstore2 WHERE scope GLOB :scope");
760 0 : expression.AppendInt(i);
761 : }
762 0 : expression.AppendLiteral(");");
763 :
764 0 : nsCOMPtr<mozIStorageStatement> statement;
765 :
766 : nsresult rv;
767 :
768 0 : rv = MaybeCommitInsertTransaction();
769 0 : NS_ENSURE_SUCCESS(rv, rv);
770 :
771 0 : rv = mConnection->CreateStatement(expression,
772 0 : getter_AddRefs(statement));
773 0 : NS_ENSURE_SUCCESS(rv, rv);
774 :
775 0 : for (PRUint32 i = 0; i < aOwners.Length(); i++) {
776 0 : nsCAutoString quotaKey;
777 : rv = nsDOMStorageDBWrapper::CreateDomainScopeDBKey(
778 0 : NS_ConvertUTF16toUTF8(aOwners[i]), quotaKey);
779 :
780 0 : if (DomainMaybeCached(quotaKey)) {
781 0 : mCachedUsage = 0;
782 0 : mCachedOwner.Truncate();
783 : }
784 :
785 0 : if (!aIncludeSubDomains)
786 0 : quotaKey.AppendLiteral(":");
787 0 : quotaKey.AppendLiteral("*");
788 :
789 0 : nsCAutoString paramName;
790 0 : paramName.Assign("scope");
791 0 : paramName.AppendInt(i);
792 :
793 0 : rv = statement->BindUTF8StringByName(paramName, quotaKey);
794 0 : NS_ENSURE_SUCCESS(rv, rv);
795 : }
796 :
797 0 : rv = statement->Execute();
798 0 : NS_ENSURE_SUCCESS(rv, rv);
799 :
800 0 : MarkAllScopesDirty();
801 :
802 0 : return NS_OK;
803 : }
804 :
805 : nsresult
806 11 : nsDOMStoragePersistentDB::RemoveAll()
807 : {
808 : nsresult rv;
809 :
810 11 : rv = MaybeCommitInsertTransaction();
811 11 : NS_ENSURE_SUCCESS(rv, rv);
812 :
813 : nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
814 : "DELETE FROM webappsstore2_view "
815 33 : );
816 11 : NS_ENSURE_STATE(stmt);
817 22 : mozStorageStatementScoper scope(stmt);
818 :
819 11 : rv = stmt->Execute();
820 11 : NS_ENSURE_SUCCESS(rv, rv);
821 :
822 11 : MarkAllScopesDirty();
823 :
824 11 : return NS_OK;
825 : }
826 :
827 : nsresult
828 6 : nsDOMStoragePersistentDB::GetUsage(DOMStorageImpl* aStorage,
829 : bool aExcludeOfflineFromUsage,
830 : PRInt32 *aUsage)
831 : {
832 6 : return GetUsageInternal(aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage),
833 : aExcludeOfflineFromUsage,
834 12 : aUsage);
835 : }
836 :
837 : nsresult
838 0 : nsDOMStoragePersistentDB::GetUsage(const nsACString& aDomain,
839 : bool aIncludeSubDomains,
840 : PRInt32 *aUsage)
841 : {
842 : nsresult rv;
843 :
844 0 : nsCAutoString quotadomainDBKey;
845 : rv = nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomain,
846 : aIncludeSubDomains,
847 : false,
848 0 : quotadomainDBKey);
849 0 : NS_ENSURE_SUCCESS(rv, rv);
850 :
851 0 : return GetUsageInternal(quotadomainDBKey, false, aUsage);
852 : }
853 :
854 : nsresult
855 6 : nsDOMStoragePersistentDB::GetUsageInternal(const nsACString& aQuotaDomainDBKey,
856 : bool aExcludeOfflineFromUsage,
857 : PRInt32 *aUsage)
858 : {
859 6 : if (aQuotaDomainDBKey == mCachedOwner) {
860 2 : *aUsage = mCachedUsage;
861 2 : return NS_OK;
862 : }
863 :
864 : nsresult rv;
865 :
866 4 : rv = MaybeCommitInsertTransaction();
867 4 : NS_ENSURE_SUCCESS(rv, rv);
868 :
869 8 : nsCOMPtr<mozIStorageStatement> stmt;
870 4 : if (aExcludeOfflineFromUsage) {
871 : stmt = mStatements.GetCachedStatement(
872 : "SELECT SUM(LENGTH(key) + LENGTH(value)) "
873 : "FROM ( "
874 : "SELECT key, value FROM webappsstore2_temp "
875 : "WHERE scope GLOB :scope "
876 : "AND NOT ISOFFLINE(scope) "
877 : "UNION ALL "
878 : "SELECT key, value FROM webappsstore2 "
879 : "WHERE scope GLOB :scope "
880 : "AND NOT ISOFFLINE(scope) "
881 : "AND NOT EXISTS ( "
882 : "SELECT scope, key "
883 : "FROM webappsstore2_temp "
884 : "WHERE scope = webappsstore2.scope "
885 : "AND key = webappsstore2.key "
886 : ") "
887 : ") "
888 8 : );
889 : } else {
890 : stmt = mStatements.GetCachedStatement(
891 : "SELECT SUM(LENGTH(key) + LENGTH(value)) "
892 : "FROM ( "
893 : "SELECT key,value FROM webappsstore2_temp "
894 : "WHERE scope GLOB :scope "
895 : "UNION ALL "
896 : "SELECT key,value FROM webappsstore2 "
897 : "WHERE scope GLOB :scope "
898 : "AND NOT EXISTS ( "
899 : "SELECT scope, key "
900 : "FROM webappsstore2_temp "
901 : "WHERE scope = webappsstore2.scope "
902 : "AND key = webappsstore2.key "
903 : ") "
904 : ") "
905 0 : );
906 : }
907 4 : NS_ENSURE_STATE(stmt);
908 8 : mozStorageStatementScoper scope(stmt);
909 :
910 8 : nsCAutoString scopeValue(aQuotaDomainDBKey);
911 4 : scopeValue += NS_LITERAL_CSTRING("*");
912 :
913 4 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), scopeValue);
914 4 : NS_ENSURE_SUCCESS(rv, rv);
915 :
916 : bool exists;
917 4 : rv = stmt->ExecuteStep(&exists);
918 4 : NS_ENSURE_SUCCESS(rv, rv);
919 :
920 4 : if (!exists) {
921 0 : *aUsage = 0;
922 0 : return NS_OK;
923 : }
924 :
925 4 : rv = stmt->GetInt32(0, aUsage);
926 4 : NS_ENSURE_SUCCESS(rv, rv);
927 :
928 4 : if (!aQuotaDomainDBKey.IsEmpty()) {
929 4 : mCachedOwner = aQuotaDomainDBKey;
930 4 : mCachedUsage = *aUsage;
931 : }
932 :
933 4 : return NS_OK;
934 : }
935 :
936 : nsresult
937 18 : nsDOMStoragePersistentDB::EnsureInsertTransaction()
938 : {
939 18 : if (!mConnection)
940 0 : return NS_ERROR_UNEXPECTED;
941 :
942 : bool transactionInProgress;
943 18 : nsresult rv = mConnection->GetTransactionInProgress(&transactionInProgress);
944 18 : NS_ENSURE_SUCCESS(rv, rv);
945 :
946 18 : if (transactionInProgress)
947 0 : return NS_OK;
948 :
949 18 : rv = mConnection->BeginTransaction();
950 18 : NS_ENSURE_SUCCESS(rv, rv);
951 :
952 18 : return NS_OK;
953 : }
954 :
955 : nsresult
956 245 : nsDOMStoragePersistentDB::MaybeCommitInsertTransaction()
957 : {
958 245 : if (!mConnection)
959 0 : return NS_ERROR_UNEXPECTED;
960 :
961 : bool transactionInProgress;
962 245 : nsresult rv = mConnection->GetTransactionInProgress(&transactionInProgress);
963 245 : if (NS_FAILED(rv)) {
964 : NS_WARNING("nsDOMStoragePersistentDB::MaybeCommitInsertTransaction: "
965 0 : "connection probably already dead");
966 : }
967 :
968 245 : if (NS_FAILED(rv) || !transactionInProgress)
969 227 : return NS_OK;
970 :
971 18 : rv = mConnection->CommitTransaction();
972 18 : NS_ENSURE_SUCCESS(rv, rv);
973 :
974 18 : return NS_OK;
975 : }
976 :
977 : bool
978 54 : nsDOMStoragePersistentDB::DomainMaybeCached(const nsACString& aDomain)
979 : {
980 54 : if (mCachedOwner.IsEmpty())
981 50 : return false;
982 :
983 : // if cached owner contains colon we must ignore it
984 4 : if (mCachedOwner[mCachedOwner.Length() - 1] == ':')
985 : return StringBeginsWith(aDomain, Substring(mCachedOwner, 0,
986 0 : mCachedOwner.Length() - 1));
987 : else
988 4 : return StringBeginsWith(aDomain, mCachedOwner);
989 : }
|