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 : * Johnny Stenback <jst@mozilla.com>
25 : * Ehsan Akhgari <ehsan.akhgari@gmail.com>
26 : * Honza Bambas <honzab@firemni.cz>
27 : * Josh Matthews <josh@joshmatthews.net>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either of the GNU General Public License Version 2 or later (the "GPL"),
31 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "StorageChild.h"
44 : #include "StorageParent.h"
45 : #include "nsXULAppAPI.h"
46 : using mozilla::dom::StorageChild;
47 :
48 : #include "prnetdb.h"
49 : #include "nsCOMPtr.h"
50 : #include "nsDOMError.h"
51 : #include "nsDOMClassInfoID.h"
52 : #include "nsDOMJSUtils.h"
53 : #include "nsUnicharUtils.h"
54 : #include "nsIDocument.h"
55 : #include "nsDOMStorage.h"
56 : #include "nsEscape.h"
57 : #include "nsContentUtils.h"
58 : #include "nsIScriptSecurityManager.h"
59 : #include "nsIPrincipal.h"
60 : #include "nsIURI.h"
61 : #include "nsReadableUtils.h"
62 : #include "nsIObserverService.h"
63 : #include "nsNetUtil.h"
64 : #include "nsIPrefBranch.h"
65 : #include "nsICookiePermission.h"
66 : #include "nsIPermission.h"
67 : #include "nsIPermissionManager.h"
68 : #include "nsCycleCollectionParticipant.h"
69 : #include "nsIOfflineCacheUpdate.h"
70 : #include "nsIJSContextStack.h"
71 : #include "nsIPrivateBrowsingService.h"
72 : #include "nsDOMString.h"
73 : #include "nsNetCID.h"
74 : #include "mozilla/Preferences.h"
75 : #include "nsThreadUtils.h"
76 : #include "mozilla/Telemetry.h"
77 : #include "DictionaryHelpers.h"
78 :
79 : // calls FlushAndDeleteTemporaryTables(false)
80 : #define NS_DOMSTORAGE_FLUSH_TIMER_TOPIC "domstorage-flush-timer"
81 :
82 : // calls FlushAndDeleteTemporaryTables(true)
83 : #define NS_DOMSTORAGE_FLUSH_FORCE_TOPIC "domstorage-flush-force"
84 :
85 : using namespace mozilla;
86 :
87 : static const PRUint32 ASK_BEFORE_ACCEPT = 1;
88 : static const PRUint32 ACCEPT_SESSION = 2;
89 : static const PRUint32 BEHAVIOR_REJECT = 2;
90 :
91 : static const PRUint32 DEFAULT_QUOTA = 5 * 1024;
92 : // Be generous with offline apps by default...
93 : static const PRUint32 DEFAULT_OFFLINE_APP_QUOTA = 200 * 1024;
94 : // ... but warn if it goes over this amount
95 : static const PRUint32 DEFAULT_OFFLINE_WARN_QUOTA = 50 * 1024;
96 :
97 : // Intervals to flush the temporary table after in seconds
98 : #define NS_DOMSTORAGE_MAXIMUM_TEMPTABLE_INACTIVITY_TIME (5)
99 : #define NS_DOMSTORAGE_MAXIMUM_TEMPTABLE_AGE (30)
100 :
101 : static const char kPermissionType[] = "cookie";
102 : static const char kStorageEnabled[] = "dom.storage.enabled";
103 : static const char kDefaultQuota[] = "dom.storage.default_quota";
104 : static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
105 : static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
106 : static const char kOfflineAppWarnQuota[] = "offline-apps.quota.warn";
107 : static const char kOfflineAppQuota[] = "offline-apps.quota.max";
108 :
109 : // The URI returned is the innermost URI that should be used for
110 : // security-check-like stuff. aHost is its hostname, correctly canonicalized.
111 : static nsresult
112 0 : GetPrincipalURIAndHost(nsIPrincipal* aPrincipal, nsIURI** aURI, nsCString& aHost)
113 : {
114 0 : nsresult rv = aPrincipal->GetDomain(aURI);
115 0 : NS_ENSURE_SUCCESS(rv, rv);
116 :
117 0 : if (!*aURI) {
118 0 : rv = aPrincipal->GetURI(aURI);
119 0 : NS_ENSURE_SUCCESS(rv, rv);
120 : }
121 :
122 0 : if (!*aURI) {
123 0 : return NS_OK;
124 : }
125 :
126 0 : nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(*aURI);
127 0 : if (!innerURI) {
128 0 : return NS_ERROR_UNEXPECTED;
129 : }
130 :
131 0 : rv = innerURI->GetAsciiHost(aHost);
132 0 : if (NS_FAILED(rv)) {
133 0 : return NS_ERROR_DOM_SECURITY_ERR;
134 : }
135 :
136 0 : innerURI.swap(*aURI);
137 :
138 0 : return NS_OK;
139 : }
140 :
141 : //
142 : // Helper that tells us whether the caller is secure or not.
143 : //
144 :
145 : static bool
146 168 : IsCallerSecure()
147 : {
148 336 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
149 168 : nsresult rv = nsContentUtils::GetSecurityManager()->
150 168 : GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
151 168 : NS_ENSURE_SUCCESS(rv, false);
152 :
153 168 : if (!subjectPrincipal) {
154 : // No subject principal means no code is running. Default to not
155 : // being secure in that case.
156 :
157 0 : return false;
158 : }
159 :
160 336 : nsCOMPtr<nsIURI> codebase;
161 168 : subjectPrincipal->GetURI(getter_AddRefs(codebase));
162 :
163 168 : if (!codebase) {
164 168 : return false;
165 : }
166 :
167 0 : nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(codebase);
168 :
169 0 : if (!innerUri) {
170 0 : return false;
171 : }
172 :
173 0 : bool isHttps = false;
174 0 : rv = innerUri->SchemeIs("https", &isHttps);
175 :
176 0 : return NS_SUCCEEDED(rv) && isHttps;
177 : }
178 :
179 : PRUint32
180 24 : GetOfflinePermission(const nsACString &aDomain)
181 : {
182 : // Fake a URI for the permission manager
183 48 : nsCOMPtr<nsIURI> uri;
184 24 : NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aDomain);
185 :
186 : PRUint32 perm;
187 24 : if (uri) {
188 : nsCOMPtr<nsIPermissionManager> permissionManager =
189 48 : do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
190 :
191 48 : if (permissionManager &&
192 48 : NS_SUCCEEDED(permissionManager->TestPermission(uri, "offline-app", &perm)))
193 24 : return perm;
194 : }
195 :
196 0 : return nsIPermissionManager::UNKNOWN_ACTION;
197 : }
198 :
199 : bool
200 6 : IsOfflineAllowed(const nsACString &aDomain)
201 : {
202 6 : PRInt32 perm = GetOfflinePermission(aDomain);
203 6 : return IS_PERMISSION_ALLOWED(perm);
204 : }
205 :
206 : // Returns two quotas - A hard limit for which adding data will be an error,
207 : // and a limit after which a warning event will be sent to the observer
208 : // service. The warn limit may be -1, in which case there will be no warning.
209 : // If aOverrideQuota is set, the larger offline apps quota is used and no
210 : // warning is sent.
211 : static PRUint32
212 18 : GetQuota(const nsACString &aDomain, PRInt32 *aQuota, PRInt32 *aWarnQuota,
213 : bool aOverrideQuota)
214 : {
215 18 : PRUint32 perm = GetOfflinePermission(aDomain);
216 18 : if (IS_PERMISSION_ALLOWED(perm) || aOverrideQuota) {
217 : // This is an offline app, give more space by default.
218 : *aQuota = Preferences::GetInt(kOfflineAppQuota,
219 12 : DEFAULT_OFFLINE_APP_QUOTA) * 1024;
220 :
221 12 : if (perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN ||
222 : aOverrideQuota) {
223 12 : *aWarnQuota = -1;
224 : } else {
225 : *aWarnQuota = Preferences::GetInt(kOfflineAppWarnQuota,
226 0 : DEFAULT_OFFLINE_WARN_QUOTA) * 1024;
227 : }
228 12 : return perm;
229 : }
230 :
231 : // FIXME: per-domain quotas?
232 6 : *aQuota = Preferences::GetInt(kDefaultQuota, DEFAULT_QUOTA) * 1024;
233 6 : *aWarnQuota = -1;
234 :
235 6 : return perm;
236 : }
237 :
238 34 : nsSessionStorageEntry::nsSessionStorageEntry(KeyTypePointer aStr)
239 34 : : nsStringHashKey(aStr), mItem(nsnull)
240 : {
241 34 : }
242 :
243 0 : nsSessionStorageEntry::nsSessionStorageEntry(const nsSessionStorageEntry& aToCopy)
244 0 : : nsStringHashKey(aToCopy), mItem(nsnull)
245 : {
246 0 : NS_ERROR("We're horked.");
247 0 : }
248 :
249 34 : nsSessionStorageEntry::~nsSessionStorageEntry()
250 : {
251 34 : }
252 :
253 : //
254 : // nsDOMStorageManager
255 : //
256 :
257 : nsDOMStorageManager* nsDOMStorageManager::gStorageManager;
258 :
259 1404 : nsDOMStorageManager::nsDOMStorageManager()
260 1404 : : mInPrivateBrowsing(false)
261 : {
262 1404 : }
263 :
264 182619 : NS_IMPL_ISUPPORTS3(nsDOMStorageManager,
265 : nsIDOMStorageManager,
266 : nsIObserver,
267 : nsISupportsWeakReference)
268 :
269 : //static
270 : nsresult
271 1404 : nsDOMStorageManager::Initialize()
272 : {
273 1404 : gStorageManager = new nsDOMStorageManager();
274 1404 : if (!gStorageManager)
275 0 : return NS_ERROR_OUT_OF_MEMORY;
276 :
277 1404 : if (!gStorageManager->mStorages.Init()) {
278 0 : delete gStorageManager;
279 0 : gStorageManager = nsnull;
280 0 : return NS_ERROR_OUT_OF_MEMORY;
281 : }
282 :
283 1404 : NS_ADDREF(gStorageManager);
284 :
285 : // No observers needed in non-chrome
286 1404 : if (XRE_GetProcessType() != GeckoProcessType_Default)
287 0 : return NS_OK;
288 :
289 2808 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
290 1404 : if (!os)
291 0 : return NS_OK;
292 :
293 : nsresult rv;
294 1404 : rv = os->AddObserver(gStorageManager, "cookie-changed", true);
295 1404 : NS_ENSURE_SUCCESS(rv, rv);
296 1404 : rv = os->AddObserver(gStorageManager, "offline-app-removed", true);
297 1404 : NS_ENSURE_SUCCESS(rv, rv);
298 1404 : rv = os->AddObserver(gStorageManager, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
299 1404 : NS_ENSURE_SUCCESS(rv, rv);
300 1404 : rv = os->AddObserver(gStorageManager, "profile-after-change", true);
301 1404 : NS_ENSURE_SUCCESS(rv, rv);
302 1404 : rv = os->AddObserver(gStorageManager, "perm-changed", true);
303 1404 : NS_ENSURE_SUCCESS(rv, rv);
304 1404 : rv = os->AddObserver(gStorageManager, "browser:purge-domain-data", true);
305 1404 : NS_ENSURE_SUCCESS(rv, rv);
306 : // Used for temporary table flushing
307 1404 : rv = os->AddObserver(gStorageManager, "profile-before-change", true);
308 1404 : NS_ENSURE_SUCCESS(rv, rv);
309 1404 : rv = os->AddObserver(gStorageManager, NS_DOMSTORAGE_FLUSH_TIMER_TOPIC, true);
310 1404 : NS_ENSURE_SUCCESS(rv, rv);
311 :
312 1404 : return NS_OK;
313 : }
314 :
315 : //static
316 : nsDOMStorageManager*
317 4 : nsDOMStorageManager::GetInstance()
318 : {
319 4 : NS_ASSERTION(gStorageManager,
320 : "nsDOMStorageManager::GetInstance() called before Initialize()");
321 4 : NS_IF_ADDREF(gStorageManager);
322 4 : return gStorageManager;
323 : }
324 :
325 : //static
326 : void
327 1403 : nsDOMStorageManager::Shutdown()
328 : {
329 1403 : NS_IF_RELEASE(gStorageManager);
330 1403 : gStorageManager = nsnull;
331 :
332 1403 : ShutdownDB();
333 1403 : }
334 :
335 : //static
336 : void
337 1440 : nsDOMStorageManager::ShutdownDB()
338 : {
339 1440 : delete DOMStorageImpl::gStorageDB;
340 1440 : DOMStorageImpl::gStorageDB = nsnull;
341 1440 : }
342 :
343 : static PLDHashOperator
344 10 : ClearStorage(nsDOMStorageEntry* aEntry, void* userArg)
345 : {
346 10 : aEntry->mStorage->ClearAll();
347 10 : return PL_DHASH_REMOVE;
348 : }
349 :
350 : static PLDHashOperator
351 6 : ClearStorageIfDomainMatches(nsDOMStorageEntry* aEntry, void* userArg)
352 : {
353 6 : nsCAutoString* aKey = static_cast<nsCAutoString*> (userArg);
354 6 : if (StringBeginsWith(aEntry->mStorage->GetScopeDBKey(), *aKey)) {
355 4 : aEntry->mStorage->ClearAll();
356 : }
357 6 : return PL_DHASH_REMOVE;
358 : }
359 :
360 : static nsresult
361 13 : GetOfflineDomains(nsTArray<nsString>& aDomains)
362 : {
363 : nsCOMPtr<nsIPermissionManager> permissionManager =
364 26 : do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
365 13 : if (permissionManager) {
366 26 : nsCOMPtr<nsISimpleEnumerator> enumerator;
367 13 : nsresult rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
368 13 : NS_ENSURE_SUCCESS(rv, rv);
369 :
370 : bool hasMore;
371 26 : while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
372 0 : nsCOMPtr<nsISupports> supp;
373 0 : rv = enumerator->GetNext(getter_AddRefs(supp));
374 0 : NS_ENSURE_SUCCESS(rv, rv);
375 :
376 0 : nsCOMPtr<nsIPermission> perm(do_QueryInterface(supp, &rv));
377 0 : NS_ENSURE_SUCCESS(rv, rv);
378 :
379 : PRUint32 capability;
380 0 : rv = perm->GetCapability(&capability);
381 0 : NS_ENSURE_SUCCESS(rv, rv);
382 0 : if (capability != nsIPermissionManager::DENY_ACTION) {
383 0 : nsCAutoString type;
384 0 : rv = perm->GetType(type);
385 0 : NS_ENSURE_SUCCESS(rv, rv);
386 :
387 0 : if (type.EqualsLiteral("offline-app")) {
388 0 : nsCAutoString host;
389 0 : rv = perm->GetHost(host);
390 0 : NS_ENSURE_SUCCESS(rv, rv);
391 :
392 0 : aDomains.AppendElement(NS_ConvertUTF8toUTF16(host));
393 : }
394 : }
395 : }
396 : }
397 :
398 13 : return NS_OK;
399 : }
400 :
401 : nsresult
402 18058 : nsDOMStorageManager::Observe(nsISupports *aSubject,
403 : const char *aTopic,
404 : const PRUnichar *aData)
405 : {
406 18058 : if (!strcmp(aTopic, "profile-after-change")) {
407 : nsCOMPtr<nsIPrivateBrowsingService> pbs =
408 0 : do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
409 0 : if (pbs)
410 0 : pbs->GetPrivateBrowsingEnabled(&gStorageManager->mInPrivateBrowsing);
411 : }
412 18058 : else if (!strcmp(aTopic, "offline-app-removed")) {
413 0 : nsresult rv = DOMStorageImpl::InitDB();
414 0 : NS_ENSURE_SUCCESS(rv, rv);
415 0 : return DOMStorageImpl::gStorageDB->RemoveOwner(NS_ConvertUTF16toUTF8(aData),
416 0 : true);
417 69246 : } else if (!strcmp(aTopic, "cookie-changed") &&
418 51188 : !nsCRT::strcmp(aData, NS_LITERAL_STRING("cleared").get())) {
419 46 : mStorages.EnumerateEntries(ClearStorage, nsnull);
420 :
421 46 : nsresult rv = DOMStorageImpl::InitDB();
422 46 : NS_ENSURE_SUCCESS(rv, rv);
423 :
424 : // Remove global storage for domains that aren't marked for offline use.
425 24 : nsTArray<nsString> domains;
426 12 : rv = GetOfflineDomains(domains);
427 12 : NS_ENSURE_SUCCESS(rv, rv);
428 12 : return DOMStorageImpl::gStorageDB->RemoveOwners(domains, true, false);
429 18012 : } else if (!strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC)) {
430 141 : mStorages.EnumerateEntries(ClearStorage, nsnull);
431 141 : if (!nsCRT::strcmp(aData, NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).get()))
432 74 : mInPrivateBrowsing = true;
433 67 : else if (!nsCRT::strcmp(aData, NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).get()))
434 67 : mInPrivateBrowsing = false;
435 141 : nsresult rv = DOMStorageImpl::InitDB();
436 141 : NS_ENSURE_SUCCESS(rv, rv);
437 :
438 137 : return DOMStorageImpl::gStorageDB->DropPrivateBrowsingStorages();
439 17871 : } else if (!strcmp(aTopic, "perm-changed")) {
440 : // Check for cookie permission change
441 1008 : nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
442 504 : if (perm) {
443 992 : nsCAutoString type;
444 496 : perm->GetType(type);
445 496 : if (type != NS_LITERAL_CSTRING("cookie"))
446 487 : return NS_OK;
447 :
448 9 : PRUint32 cap = 0;
449 9 : perm->GetCapability(&cap);
450 18 : if (!(cap & nsICookiePermission::ACCESS_SESSION) ||
451 9 : nsDependentString(aData) != NS_LITERAL_STRING("deleted"))
452 9 : return NS_OK;
453 :
454 0 : nsCAutoString host;
455 0 : perm->GetHost(host);
456 0 : if (host.IsEmpty())
457 0 : return NS_OK;
458 :
459 0 : nsresult rv = DOMStorageImpl::InitDB();
460 0 : NS_ENSURE_SUCCESS(rv, rv);
461 :
462 0 : return DOMStorageImpl::gStorageDB->DropSessionOnlyStoragesForHost(host);
463 : }
464 17367 : } else if (!strcmp(aTopic, "timer-callback")) {
465 0 : nsCOMPtr<nsIObserverService> obsserv = mozilla::services::GetObserverService();
466 0 : if (obsserv)
467 0 : obsserv->NotifyObservers(nsnull, NS_DOMSTORAGE_FLUSH_TIMER_TOPIC, nsnull);
468 17367 : } else if (!strcmp(aTopic, "browser:purge-domain-data")) {
469 : // Convert the domain name to the ACE format
470 96 : nsCAutoString aceDomain;
471 : nsresult rv;
472 96 : nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
473 48 : if (converter) {
474 48 : rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain);
475 48 : NS_ENSURE_SUCCESS(rv, rv);
476 : } else {
477 : // In case the IDN service is not available, this is the best we can come up with!
478 0 : NS_EscapeURL(NS_ConvertUTF16toUTF8(aData),
479 : esc_OnlyNonASCII | esc_AlwaysCopy,
480 0 : aceDomain);
481 : }
482 :
483 96 : nsCAutoString key;
484 48 : rv = nsDOMStorageDBWrapper::CreateDomainScopeDBKey(aceDomain, key);
485 48 : NS_ENSURE_SUCCESS(rv, rv);
486 :
487 : // Clear the storage entries for matching domains
488 48 : mStorages.EnumerateEntries(ClearStorageIfDomainMatches, &key);
489 :
490 48 : rv = DOMStorageImpl::InitDB();
491 48 : NS_ENSURE_SUCCESS(rv, rv);
492 :
493 96 : DOMStorageImpl::gStorageDB->RemoveOwner(aceDomain, true);
494 17319 : } else if (!strcmp(aTopic, "profile-before-change")) {
495 800 : if (DOMStorageImpl::gStorageDB) {
496 : DebugOnly<nsresult> rv =
497 74 : DOMStorageImpl::gStorageDB->FlushAndDeleteTemporaryTables(true);
498 37 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
499 : "DOMStorage: temporary table commit failed");
500 37 : DOMStorageImpl::gStorageDB->Close();
501 37 : nsDOMStorageManager::ShutdownDB();
502 : }
503 16519 : } else if (!strcmp(aTopic, NS_DOMSTORAGE_FLUSH_TIMER_TOPIC)) {
504 0 : if (DOMStorageImpl::gStorageDB) {
505 : DebugOnly<nsresult> rv =
506 0 : DOMStorageImpl::gStorageDB->FlushAndDeleteTemporaryTables(false);
507 0 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
508 : "DOMStorage: temporary table commit failed");
509 : }
510 16519 : } else if (!strcmp(aTopic, NS_DOMSTORAGE_FLUSH_FORCE_TOPIC)) {
511 0 : if (DOMStorageImpl::gStorageDB) {
512 : DebugOnly<nsresult> rv =
513 0 : DOMStorageImpl::gStorageDB->FlushAndDeleteTemporaryTables(true);
514 0 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
515 : "DOMStorage: temporary table commit failed");
516 : }
517 : }
518 :
519 17375 : return NS_OK;
520 : }
521 :
522 : NS_IMETHODIMP
523 0 : nsDOMStorageManager::GetUsage(const nsAString& aDomain,
524 : PRInt32 *aUsage)
525 : {
526 0 : nsresult rv = DOMStorageImpl::InitDB();
527 0 : NS_ENSURE_SUCCESS(rv, rv);
528 :
529 0 : return DOMStorageImpl::gStorageDB->GetUsage(NS_ConvertUTF16toUTF8(aDomain),
530 0 : false, aUsage);
531 : }
532 :
533 : NS_IMETHODIMP
534 1 : nsDOMStorageManager::ClearOfflineApps()
535 : {
536 1 : nsresult rv = DOMStorageImpl::InitDB();
537 1 : NS_ENSURE_SUCCESS(rv, rv);
538 :
539 2 : nsTArray<nsString> domains;
540 1 : rv = GetOfflineDomains(domains);
541 1 : NS_ENSURE_SUCCESS(rv, rv);
542 1 : return DOMStorageImpl::gStorageDB->RemoveOwners(domains, true, true);
543 : }
544 :
545 : NS_IMETHODIMP
546 20 : nsDOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal *aPrincipal,
547 : const nsSubstring &aDocumentURI,
548 : nsIDOMStorage **aResult)
549 : {
550 20 : NS_ENSURE_ARG_POINTER(aPrincipal);
551 20 : *aResult = nsnull;
552 :
553 : nsresult rv;
554 :
555 40 : nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
556 20 : if (!storage)
557 0 : return NS_ERROR_OUT_OF_MEMORY;
558 :
559 20 : rv = storage->InitAsLocalStorage(aPrincipal, aDocumentURI);
560 20 : if (NS_FAILED(rv))
561 0 : return rv;
562 :
563 20 : *aResult = storage.get();
564 20 : storage.forget();
565 :
566 20 : return NS_OK;
567 : }
568 :
569 : void
570 20 : nsDOMStorageManager::AddToStoragesHash(DOMStorageImpl* aStorage)
571 : {
572 20 : nsDOMStorageEntry* entry = mStorages.PutEntry(aStorage);
573 20 : if (entry)
574 20 : entry->mStorage = aStorage;
575 20 : }
576 :
577 : void
578 18 : nsDOMStorageManager::RemoveFromStoragesHash(DOMStorageImpl* aStorage)
579 : {
580 18 : nsDOMStorageEntry* entry = mStorages.GetEntry(aStorage);
581 18 : if (entry)
582 3 : mStorages.RemoveEntry(aStorage);
583 18 : }
584 :
585 : //
586 : // nsDOMStorage
587 : //
588 :
589 : nsDOMStorageDBWrapper* DOMStorageImpl::gStorageDB = nsnull;
590 :
591 20 : nsDOMStorageEntry::nsDOMStorageEntry(KeyTypePointer aStr)
592 20 : : nsVoidPtrHashKey(aStr), mStorage(nsnull)
593 : {
594 20 : }
595 :
596 0 : nsDOMStorageEntry::nsDOMStorageEntry(const nsDOMStorageEntry& aToCopy)
597 0 : : nsVoidPtrHashKey(aToCopy), mStorage(nsnull)
598 : {
599 0 : NS_ERROR("DOMStorage horked.");
600 0 : }
601 :
602 20 : nsDOMStorageEntry::~nsDOMStorageEntry()
603 : {
604 20 : }
605 :
606 1464 : NS_IMPL_CYCLE_COLLECTION_1(nsDOMStorage, mStorageImpl)
607 :
608 : DOMCI_DATA(StorageObsolete, nsDOMStorage)
609 :
610 20 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStorage)
611 20 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStorage)
612 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage)
613 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorageObsolete)
614 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageObsolete)
615 0 : NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
616 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageObsolete)
617 0 : NS_INTERFACE_MAP_END
618 :
619 : nsresult
620 0 : NS_NewDOMStorage(nsISupports* aOuter, REFNSIID aIID, void** aResult)
621 : {
622 0 : nsDOMStorage* storage = new nsDOMStorage();
623 0 : if (!storage)
624 0 : return NS_ERROR_OUT_OF_MEMORY;
625 :
626 0 : return storage->QueryInterface(aIID, aResult);
627 : }
628 :
629 : nsresult
630 0 : NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult)
631 : {
632 0 : nsDOMStorage2* storage = new nsDOMStorage2();
633 0 : if (!storage)
634 0 : return NS_ERROR_OUT_OF_MEMORY;
635 :
636 0 : return storage->QueryInterface(aIID, aResult);
637 : }
638 :
639 20 : DOMStorageBase::DOMStorageBase()
640 : : mStorageType(nsPIDOMStorage::Unknown)
641 : , mUseDB(false)
642 : , mSessionOnly(true)
643 20 : , mCanUseChromePersist(false)
644 : {
645 20 : }
646 :
647 0 : DOMStorageBase::DOMStorageBase(DOMStorageBase& aThat)
648 : : mStorageType(aThat.mStorageType)
649 : , mUseDB(false) // Clones don't use the DB
650 : , mSessionOnly(true)
651 : , mDomain(aThat.mDomain)
652 : , mScopeDBKey(aThat.mScopeDBKey)
653 : , mQuotaETLDplus1DomainDBKey(aThat.mQuotaETLDplus1DomainDBKey)
654 : , mQuotaDomainDBKey(aThat.mQuotaDomainDBKey)
655 0 : , mCanUseChromePersist(aThat.mCanUseChromePersist)
656 : {
657 0 : }
658 :
659 : void
660 0 : DOMStorageBase::InitAsSessionStorage(nsIURI* aDomainURI)
661 : {
662 : // No need to check for a return value. If this would fail we would not get
663 : // here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
664 : // nsDOMStorage::CanUseStorage before we query the storage manager for a new
665 : // sessionStorage. It calls GetAsciiHost on innermost URI. If it fails, we
666 : // won't get to InitAsSessionStorage.
667 0 : aDomainURI->GetAsciiHost(mDomain);
668 :
669 0 : mUseDB = false;
670 0 : mScopeDBKey.Truncate();
671 0 : mQuotaDomainDBKey.Truncate();
672 0 : mStorageType = nsPIDOMStorage::SessionStorage;
673 0 : }
674 :
675 : void
676 20 : DOMStorageBase::InitAsLocalStorage(nsIURI* aDomainURI,
677 : bool aCanUseChromePersist)
678 : {
679 : // No need to check for a return value. If this would fail we would not get
680 : // here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
681 : // nsDOMStorage::CanUseStorage before we query the storage manager for a new
682 : // localStorage. It calls GetAsciiHost on innermost URI. If it fails, we won't
683 : // get to InitAsLocalStorage. Actually, mDomain will get replaced with
684 : // mPrincipal in bug 455070. It is not even used for localStorage.
685 20 : aDomainURI->GetAsciiHost(mDomain);
686 :
687 20 : nsDOMStorageDBWrapper::CreateOriginScopeDBKey(aDomainURI, mScopeDBKey);
688 :
689 : // XXX Bug 357323, we have to solve the issue how to define
690 : // origin for file URLs. In that case CreateOriginScopeDBKey
691 : // fails (the result is empty) and we must avoid database use
692 : // in that case because it produces broken entries w/o owner.
693 20 : mUseDB = !mScopeDBKey.IsEmpty();
694 :
695 : nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(mDomain,
696 20 : true, false, mQuotaDomainDBKey);
697 : nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(mDomain,
698 20 : true, true, mQuotaETLDplus1DomainDBKey);
699 20 : mCanUseChromePersist = aCanUseChromePersist;
700 20 : mStorageType = nsPIDOMStorage::LocalStorage;
701 20 : }
702 :
703 : void
704 0 : DOMStorageBase::InitAsGlobalStorage(const nsACString& aDomainDemanded)
705 : {
706 0 : mDomain = aDomainDemanded;
707 :
708 0 : nsDOMStorageDBWrapper::CreateDomainScopeDBKey(aDomainDemanded, mScopeDBKey);
709 :
710 : // XXX Bug 357323, we have to solve the issue how to define
711 : // origin for file URLs. In that case CreateOriginScopeDBKey
712 : // fails (the result is empty) and we must avoid database use
713 : // in that case because it produces broken entries w/o owner.
714 0 : if (!(mUseDB = !mScopeDBKey.IsEmpty()))
715 0 : mScopeDBKey.AppendLiteral(":");
716 :
717 : nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomainDemanded,
718 0 : true, false, mQuotaDomainDBKey);
719 : nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomainDemanded,
720 0 : true, true, mQuotaETLDplus1DomainDBKey);
721 0 : mStorageType = nsPIDOMStorage::GlobalStorage;
722 0 : }
723 :
724 : PLDHashOperator
725 4 : SessionStorageTraverser(nsSessionStorageEntry* aEntry, void* userArg) {
726 : nsCycleCollectionTraversalCallback *cb =
727 4 : static_cast<nsCycleCollectionTraversalCallback*>(userArg);
728 :
729 4 : cb->NoteXPCOMChild((nsIDOMStorageItem *) aEntry->mItem);
730 :
731 4 : return PL_DHASH_NEXT;
732 : }
733 :
734 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(DOMStorageImpl)
735 4 : NS_IMPL_CYCLE_COLLECTION_UNLINK_0(DOMStorageImpl)
736 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMStorageImpl)
737 : {
738 4 : if (tmp->mItems.IsInitialized()) {
739 4 : tmp->mItems.EnumerateEntries(SessionStorageTraverser, &cb);
740 : }
741 : }
742 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
743 :
744 58 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMStorageImpl)
745 78 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMStorageImpl)
746 62 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMStorageImpl)
747 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
748 0 : NS_INTERFACE_MAP_END
749 :
750 20 : DOMStorageImpl::DOMStorageImpl(nsDOMStorage* aStorage)
751 : {
752 20 : Init(aStorage);
753 20 : }
754 :
755 0 : DOMStorageImpl::DOMStorageImpl(nsDOMStorage* aStorage, DOMStorageImpl& aThat)
756 0 : : DOMStorageBase(aThat)
757 : {
758 0 : Init(aStorage);
759 0 : }
760 :
761 : void
762 20 : DOMStorageImpl::Init(nsDOMStorage* aStorage)
763 : {
764 20 : mItemsCachedVersion = 0;
765 20 : mItems.Init(8);
766 20 : mOwner = aStorage;
767 20 : if (nsDOMStorageManager::gStorageManager)
768 20 : nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
769 20 : }
770 :
771 40 : DOMStorageImpl::~DOMStorageImpl()
772 : {
773 20 : if (nsDOMStorageManager::gStorageManager)
774 18 : nsDOMStorageManager::gStorageManager->RemoveFromStoragesHash(this);
775 20 : }
776 :
777 : nsresult
778 354 : DOMStorageImpl::InitDB()
779 : {
780 354 : if (!gStorageDB) {
781 77 : gStorageDB = new nsDOMStorageDBWrapper();
782 77 : if (!gStorageDB)
783 0 : return NS_ERROR_OUT_OF_MEMORY;
784 :
785 77 : nsresult rv = gStorageDB->Init();
786 77 : if (NS_FAILED(rv)) {
787 : // Failed to initialize the DB, delete it and null out the
788 : // pointer so we don't end up attempting to use an
789 : // un-initialized DB later on.
790 :
791 38 : delete gStorageDB;
792 38 : gStorageDB = nsnull;
793 :
794 38 : return rv;
795 : }
796 : }
797 :
798 316 : return NS_OK;
799 : }
800 :
801 : void
802 0 : DOMStorageImpl::InitFromChild(bool aUseDB, bool aCanUseChromePersist,
803 : bool aSessionOnly, const nsACString& aDomain,
804 : const nsACString& aScopeDBKey,
805 : const nsACString& aQuotaDomainDBKey,
806 : const nsACString& aQuotaETLDplus1DomainDBKey,
807 : PRUint32 aStorageType)
808 : {
809 0 : mUseDB = aUseDB;
810 0 : mCanUseChromePersist = aCanUseChromePersist;
811 0 : mSessionOnly = aSessionOnly;
812 0 : mDomain = aDomain;
813 0 : mScopeDBKey = aScopeDBKey;
814 0 : mQuotaDomainDBKey = aQuotaDomainDBKey;
815 0 : mQuotaETLDplus1DomainDBKey = aQuotaETLDplus1DomainDBKey;
816 0 : mStorageType = static_cast<nsPIDOMStorage::nsDOMStorageType>(aStorageType);
817 0 : }
818 :
819 : void
820 0 : DOMStorageImpl::SetSessionOnly(bool aSessionOnly)
821 : {
822 0 : mSessionOnly = aSessionOnly;
823 0 : }
824 :
825 : void
826 0 : DOMStorageImpl::InitAsSessionStorage(nsIURI* aDomainURI)
827 : {
828 0 : DOMStorageBase::InitAsSessionStorage(aDomainURI);
829 0 : }
830 :
831 : void
832 20 : DOMStorageImpl::InitAsLocalStorage(nsIURI* aDomainURI,
833 : bool aCanUseChromePersist)
834 : {
835 20 : DOMStorageBase::InitAsLocalStorage(aDomainURI, aCanUseChromePersist);
836 20 : }
837 :
838 : void
839 0 : DOMStorageImpl::InitAsGlobalStorage(const nsACString& aDomainDemanded)
840 : {
841 0 : DOMStorageBase::InitAsGlobalStorage(aDomainDemanded);
842 0 : }
843 :
844 : bool
845 30 : DOMStorageImpl::CacheStoragePermissions()
846 : {
847 : // If this is a cross-process situation, we don't have a real storage owner.
848 : // All the correct checks have been done on the child, so we just need to
849 : // make sure that our session-only status is correctly updated.
850 30 : if (!mOwner)
851 0 : return nsDOMStorage::CanUseStorage(&mSessionOnly);
852 :
853 30 : return mOwner->CacheStoragePermissions();
854 : }
855 :
856 : bool
857 266 : DOMStorageImpl::CanUseChromePersist()
858 : {
859 266 : return mCanUseChromePersist;
860 : }
861 :
862 : nsresult
863 18 : DOMStorageImpl::GetCachedValue(const nsAString& aKey, nsAString& aValue,
864 : bool* aSecure)
865 : {
866 18 : aValue.Truncate();
867 18 : *aSecure = false;
868 :
869 18 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
870 18 : if (!entry)
871 18 : return NS_ERROR_NOT_AVAILABLE;
872 :
873 0 : aValue = entry->mItem->GetValueInternal();
874 0 : *aSecure = entry->mItem->IsSecure();
875 :
876 0 : return NS_OK;
877 : }
878 :
879 : nsresult
880 50 : DOMStorageImpl::GetDBValue(const nsAString& aKey, nsAString& aValue,
881 : bool* aSecure)
882 : {
883 50 : aValue.Truncate();
884 :
885 50 : if (!UseDB())
886 0 : return NS_OK;
887 :
888 50 : nsresult rv = InitDB();
889 50 : NS_ENSURE_SUCCESS(rv, rv);
890 :
891 100 : nsAutoString value;
892 50 : rv = gStorageDB->GetKeyValue(this, aKey, value, aSecure);
893 :
894 50 : if (rv == NS_ERROR_DOM_NOT_FOUND_ERR &&
895 : mStorageType != nsPIDOMStorage::GlobalStorage) {
896 18 : SetDOMStringToNull(aValue);
897 : }
898 :
899 50 : if (NS_FAILED(rv))
900 18 : return rv;
901 :
902 32 : aValue.Assign(value);
903 :
904 32 : return NS_OK;
905 : }
906 :
907 : nsresult
908 18 : DOMStorageImpl::SetDBValue(const nsAString& aKey,
909 : const nsAString& aValue,
910 : bool aSecure)
911 : {
912 18 : if (!UseDB())
913 0 : return NS_OK;
914 :
915 18 : nsresult rv = InitDB();
916 18 : NS_ENSURE_SUCCESS(rv, rv);
917 :
918 : PRInt32 offlineAppPermission;
919 : PRInt32 quota;
920 : PRInt32 warnQuota;
921 : offlineAppPermission = GetQuota(mDomain, "a, &warnQuota,
922 18 : CanUseChromePersist());
923 :
924 18 : CacheKeysFromDB();
925 :
926 : PRInt32 usage;
927 : rv = gStorageDB->SetKey(this, aKey, aValue, aSecure, quota,
928 0 : !IS_PERMISSION_ALLOWED(offlineAppPermission),
929 36 : &usage);
930 18 : NS_ENSURE_SUCCESS(rv, rv);
931 :
932 18 : if (warnQuota >= 0 && usage > warnQuota) {
933 : // try to include the window that exceeded the warn quota
934 0 : nsCOMPtr<nsIDOMWindow> window;
935 : JSContext *cx;
936 : nsCOMPtr<nsIJSContextStack> stack =
937 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1");
938 0 : if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
939 0 : nsCOMPtr<nsIScriptContext> scriptContext;
940 0 : scriptContext = GetScriptContextFromJSContext(cx);
941 0 : if (scriptContext) {
942 0 : window = do_QueryInterface(scriptContext->GetGlobalObject());
943 : }
944 : }
945 :
946 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
947 0 : os->NotifyObservers(window, "dom-storage-warn-quota-exceeded",
948 0 : NS_ConvertUTF8toUTF16(mDomain).get());
949 : }
950 :
951 18 : return NS_OK;
952 : }
953 :
954 : nsresult
955 0 : DOMStorageImpl::SetSecure(const nsAString& aKey, bool aSecure)
956 : {
957 0 : if (UseDB()) {
958 0 : nsresult rv = InitDB();
959 0 : NS_ENSURE_SUCCESS(rv, rv);
960 :
961 0 : return gStorageDB->SetSecure(this, aKey, aSecure);
962 : }
963 :
964 0 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
965 0 : NS_ASSERTION(entry, "Don't use SetSecure() with nonexistent keys!");
966 :
967 0 : if (entry) {
968 0 : entry->mItem->SetSecureInternal(aSecure);
969 : }
970 :
971 0 : return NS_OK;
972 : }
973 :
974 : static PLDHashOperator
975 9 : ClearStorageItem(nsSessionStorageEntry* aEntry, void* userArg)
976 : {
977 9 : aEntry->mItem->SetValueInternal(EmptyString());
978 9 : return PL_DHASH_NEXT;
979 : }
980 :
981 : void
982 14 : DOMStorageImpl::ClearAll()
983 : {
984 14 : mItems.EnumerateEntries(ClearStorageItem, nsnull);
985 14 : mItemsCachedVersion = 0;
986 14 : }
987 :
988 : struct CopyArgs {
989 : DOMStorageImpl* storage;
990 : bool callerSecure;
991 : };
992 :
993 : static PLDHashOperator
994 0 : CopyStorageItems(nsSessionStorageEntry* aEntry, void* userArg)
995 : {
996 : // When copying items from one impl to another, we may not
997 : // have an mOwner that we can call SetItem on. Therefore we need
998 : // to replicate its behaviour.
999 :
1000 0 : CopyArgs* args = static_cast<CopyArgs*>(userArg);
1001 :
1002 0 : nsAutoString unused;
1003 0 : nsresult rv = args->storage->SetValue(args->callerSecure, aEntry->GetKey(),
1004 0 : aEntry->mItem->GetValueInternal(), unused);
1005 0 : if (NS_FAILED(rv))
1006 0 : return PL_DHASH_NEXT;
1007 :
1008 0 : if (aEntry->mItem->IsSecure()) {
1009 0 : args->storage->SetSecure(aEntry->GetKey(), true);
1010 : }
1011 :
1012 0 : return PL_DHASH_NEXT;
1013 : }
1014 :
1015 : nsresult
1016 0 : DOMStorageImpl::CloneFrom(bool aCallerSecure, DOMStorageBase* aThat)
1017 : {
1018 : // For various reasons, we no longer call SetItem in CopyStorageItems,
1019 : // so we need to ensure that the storage permissions are correct.
1020 0 : if (!CacheStoragePermissions())
1021 0 : return NS_ERROR_DOM_SECURITY_ERR;
1022 :
1023 0 : DOMStorageImpl* that = static_cast<DOMStorageImpl*>(aThat);
1024 0 : CopyArgs args = { this, aCallerSecure };
1025 0 : that->mItems.EnumerateEntries(CopyStorageItems, &args);
1026 0 : return NS_OK;
1027 : }
1028 :
1029 : nsresult
1030 92 : DOMStorageImpl::CacheKeysFromDB()
1031 : {
1032 : // cache all the keys in the hash. This is used by the Length and Key methods
1033 : // use this cache for better performance. The disadvantage is that the
1034 : // order may break if someone changes the keys in the database directly.
1035 92 : if (gStorageDB->IsScopeDirty(this)) {
1036 38 : nsresult rv = InitDB();
1037 38 : NS_ENSURE_SUCCESS(rv, rv);
1038 :
1039 38 : mItems.Clear();
1040 :
1041 38 : rv = gStorageDB->GetAllKeys(this, &mItems);
1042 38 : NS_ENSURE_SUCCESS(rv, rv);
1043 :
1044 38 : gStorageDB->MarkScopeCached(this);
1045 : }
1046 :
1047 92 : return NS_OK;
1048 : }
1049 :
1050 : struct KeysArrayBuilderStruct
1051 : {
1052 : bool callerIsSecure;
1053 : nsTArray<nsString> *keys;
1054 : };
1055 :
1056 : static PLDHashOperator
1057 0 : KeysArrayBuilder(nsSessionStorageEntry* aEntry, void* userArg)
1058 : {
1059 0 : KeysArrayBuilderStruct *keystruct = (KeysArrayBuilderStruct *)userArg;
1060 :
1061 0 : if (keystruct->callerIsSecure || !aEntry->mItem->IsSecure())
1062 0 : keystruct->keys->AppendElement(aEntry->GetKey());
1063 :
1064 0 : return PL_DHASH_NEXT;
1065 : }
1066 :
1067 : nsTArray<nsString>*
1068 0 : DOMStorageImpl::GetKeys(bool aCallerSecure)
1069 : {
1070 0 : if (UseDB())
1071 0 : CacheKeysFromDB();
1072 :
1073 : KeysArrayBuilderStruct keystruct;
1074 0 : keystruct.callerIsSecure = aCallerSecure;
1075 0 : keystruct.keys = new nsTArray<nsString>();
1076 0 : if (keystruct.keys)
1077 0 : mItems.EnumerateEntries(KeysArrayBuilder, &keystruct);
1078 :
1079 0 : return keystruct.keys;
1080 : }
1081 :
1082 : class ItemCounterState
1083 : {
1084 : public:
1085 44 : ItemCounterState(bool aIsCallerSecure)
1086 44 : : mIsCallerSecure(aIsCallerSecure), mCount(0)
1087 : {
1088 44 : }
1089 :
1090 : bool mIsCallerSecure;
1091 : PRUint32 mCount;
1092 : private:
1093 : ItemCounterState(); // Not to be implemented
1094 : };
1095 :
1096 : static PLDHashOperator
1097 26 : ItemCounter(nsSessionStorageEntry* aEntry, void* userArg)
1098 : {
1099 26 : ItemCounterState *state = (ItemCounterState *)userArg;
1100 :
1101 26 : if (state->mIsCallerSecure || !aEntry->mItem->IsSecure()) {
1102 26 : ++state->mCount;
1103 : }
1104 :
1105 26 : return PL_DHASH_NEXT;
1106 : }
1107 :
1108 : nsresult
1109 44 : DOMStorageImpl::GetLength(bool aCallerSecure, PRUint32* aLength)
1110 : {
1111 : // Force reload of items from database. This ensures sync localStorages for
1112 : // same origins among different windows.
1113 44 : if (UseDB())
1114 44 : CacheKeysFromDB();
1115 :
1116 44 : ItemCounterState state(aCallerSecure);
1117 :
1118 44 : mItems.EnumerateEntries(ItemCounter, &state);
1119 :
1120 44 : *aLength = state.mCount;
1121 44 : return NS_OK;
1122 : }
1123 :
1124 : class IndexFinderData
1125 : {
1126 : public:
1127 24 : IndexFinderData(bool aIsCallerSecure, PRUint32 aWantedIndex)
1128 : : mIsCallerSecure(aIsCallerSecure), mIndex(0), mWantedIndex(aWantedIndex),
1129 24 : mItem(nsnull)
1130 : {
1131 24 : }
1132 :
1133 : bool mIsCallerSecure;
1134 : PRUint32 mIndex;
1135 : PRUint32 mWantedIndex;
1136 : nsSessionStorageEntry *mItem;
1137 :
1138 : private:
1139 : IndexFinderData(); // Not to be implemented
1140 : };
1141 :
1142 : static PLDHashOperator
1143 24 : IndexFinder(nsSessionStorageEntry* aEntry, void* userArg)
1144 : {
1145 24 : IndexFinderData *data = (IndexFinderData *)userArg;
1146 :
1147 48 : if (data->mIndex == data->mWantedIndex &&
1148 24 : (data->mIsCallerSecure || !aEntry->mItem->IsSecure())) {
1149 24 : data->mItem = aEntry;
1150 :
1151 24 : return PL_DHASH_STOP;
1152 : }
1153 :
1154 0 : ++data->mIndex;
1155 :
1156 0 : return PL_DHASH_NEXT;
1157 : }
1158 :
1159 : nsresult
1160 24 : DOMStorageImpl::GetKey(bool aCallerSecure, PRUint32 aIndex, nsAString& aKey)
1161 : {
1162 : // XXXjst: This is as retarded as the DOM spec is, takes an unsigned
1163 : // int, but the spec talks about what to do if a negative value is
1164 : // passed in.
1165 :
1166 : // XXX: This does a linear search for the key at index, which would
1167 : // suck if there's a large numer of indexes. Do we care? If so,
1168 : // maybe we need to have a lazily populated key array here or
1169 : // something?
1170 :
1171 24 : if (UseDB()) {
1172 24 : CacheKeysFromDB();
1173 : }
1174 :
1175 24 : IndexFinderData data(aCallerSecure, aIndex);
1176 24 : mItems.EnumerateEntries(IndexFinder, &data);
1177 :
1178 24 : if (!data.mItem) {
1179 : // aIndex was larger than the number of accessible keys. Throw.
1180 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1181 : }
1182 :
1183 24 : aKey = data.mItem->GetKey();
1184 24 : return NS_OK;
1185 : }
1186 :
1187 : // The behaviour of this function must be kept in sync with StorageChild::GetValue.
1188 : // See the explanatory comment there for more details.
1189 : nsIDOMStorageItem*
1190 44 : DOMStorageImpl::GetValue(bool aCallerSecure, const nsAString& aKey,
1191 : nsresult* aResult)
1192 : {
1193 44 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
1194 44 : nsIDOMStorageItem* item = nsnull;
1195 44 : if (entry) {
1196 30 : if (aCallerSecure || !entry->mItem->IsSecure()) {
1197 30 : item = entry->mItem;
1198 : }
1199 : }
1200 14 : else if (UseDB()) {
1201 : bool secure;
1202 28 : nsAutoString value;
1203 14 : nsresult rv = GetDBValue(aKey, value, &secure);
1204 : // return null if access isn't allowed or the key wasn't found
1205 14 : if (rv == NS_ERROR_DOM_SECURITY_ERR || rv == NS_ERROR_DOM_NOT_FOUND_ERR ||
1206 0 : (!aCallerSecure && secure))
1207 14 : return nsnull;
1208 :
1209 0 : *aResult = rv;
1210 0 : NS_ENSURE_SUCCESS(rv, nsnull);
1211 :
1212 : nsRefPtr<nsDOMStorageItem> newitem =
1213 14 : new nsDOMStorageItem(this, aKey, value, secure);
1214 0 : if (newitem && (entry = mItems.PutEntry(aKey))) {
1215 0 : item = entry->mItem = newitem;
1216 : }
1217 : else {
1218 0 : *aResult = NS_ERROR_OUT_OF_MEMORY;
1219 : }
1220 : }
1221 30 : return item;
1222 : }
1223 :
1224 : nsresult
1225 18 : DOMStorageImpl::SetValue(bool aIsCallerSecure, const nsAString& aKey,
1226 : const nsAString& aData, nsAString& aOldValue)
1227 : {
1228 18 : if (aKey.IsEmpty())
1229 0 : return NS_OK;
1230 :
1231 : nsresult rv;
1232 36 : nsString oldValue;
1233 18 : SetDOMStringToNull(oldValue);
1234 :
1235 : // First store the value to the database, we need to do this before we update
1236 : // the mItems cache. SetDBValue is using the old cached value to decide
1237 : // on quota checking.
1238 18 : if (UseDB()) {
1239 18 : rv = SetDBValue(aKey, aData, aIsCallerSecure);
1240 18 : NS_ENSURE_SUCCESS(rv, rv);
1241 : }
1242 :
1243 18 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
1244 18 : if (entry) {
1245 0 : if (entry->mItem->IsSecure() && !aIsCallerSecure) {
1246 0 : return NS_ERROR_DOM_SECURITY_ERR;
1247 : }
1248 0 : oldValue = entry->mItem->GetValueInternal();
1249 0 : entry->mItem->SetValueInternal(aData);
1250 : }
1251 : else {
1252 : nsRefPtr<nsDOMStorageItem> newitem =
1253 36 : new nsDOMStorageItem(this, aKey, aData, aIsCallerSecure);
1254 18 : if (!newitem)
1255 0 : return NS_ERROR_OUT_OF_MEMORY;
1256 18 : entry = mItems.PutEntry(aKey);
1257 18 : NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
1258 36 : entry->mItem = newitem;
1259 : }
1260 18 : aOldValue = oldValue;
1261 18 : return NS_OK;
1262 : }
1263 :
1264 : nsresult
1265 6 : DOMStorageImpl::RemoveValue(bool aCallerSecure, const nsAString& aKey,
1266 : nsAString& aOldValue)
1267 : {
1268 12 : nsString oldValue;
1269 6 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
1270 :
1271 6 : if (entry && entry->mItem->IsSecure() && !aCallerSecure) {
1272 0 : return NS_ERROR_DOM_SECURITY_ERR;
1273 : }
1274 :
1275 6 : if (UseDB()) {
1276 6 : nsresult rv = InitDB();
1277 6 : NS_ENSURE_SUCCESS(rv, rv);
1278 :
1279 12 : nsAutoString value;
1280 : bool secureItem;
1281 6 : rv = GetDBValue(aKey, value, &secureItem);
1282 6 : NS_ENSURE_SUCCESS(rv, rv);
1283 6 : if (!aCallerSecure && secureItem)
1284 0 : return NS_ERROR_DOM_SECURITY_ERR;
1285 :
1286 6 : oldValue = value;
1287 :
1288 6 : rv = gStorageDB->RemoveKey(this, aKey, !IsOfflineAllowed(mDomain),
1289 12 : aKey.Length() + value.Length());
1290 6 : NS_ENSURE_SUCCESS(rv, rv);
1291 : }
1292 0 : else if (entry) {
1293 : // clear string as StorageItems may be referencing this item
1294 0 : oldValue = entry->mItem->GetValueInternal();
1295 0 : entry->mItem->ClearValue();
1296 : }
1297 :
1298 6 : if (entry) {
1299 6 : mItems.RawRemoveEntry(entry);
1300 : }
1301 6 : aOldValue = oldValue;
1302 6 : return NS_OK;
1303 : }
1304 :
1305 : PR_STATIC_CALLBACK(PLDHashOperator)
1306 6 : CheckSecure(nsSessionStorageEntry* aEntry, void* userArg)
1307 : {
1308 6 : bool* secure = (bool*)userArg;
1309 6 : if (aEntry->mItem->IsSecure()) {
1310 0 : *secure = true;
1311 0 : return PL_DHASH_STOP;
1312 : }
1313 :
1314 6 : return PL_DHASH_NEXT;
1315 : }
1316 :
1317 : nsresult
1318 6 : DOMStorageImpl::Clear(bool aCallerSecure, PRInt32* aOldCount)
1319 : {
1320 6 : if (UseDB())
1321 6 : CacheKeysFromDB();
1322 :
1323 6 : PRInt32 oldCount = mItems.Count();
1324 :
1325 6 : bool foundSecureItem = false;
1326 6 : mItems.EnumerateEntries(CheckSecure, &foundSecureItem);
1327 :
1328 6 : if (foundSecureItem && !aCallerSecure) {
1329 0 : return NS_ERROR_DOM_SECURITY_ERR;
1330 : }
1331 :
1332 6 : if (UseDB()) {
1333 6 : nsresult rv = InitDB();
1334 6 : NS_ENSURE_SUCCESS(rv, rv);
1335 :
1336 6 : rv = gStorageDB->ClearStorage(this);
1337 6 : NS_ENSURE_SUCCESS(rv, rv);
1338 : }
1339 :
1340 6 : *aOldCount = oldCount;
1341 6 : mItems.Clear();
1342 6 : return NS_OK;
1343 : }
1344 :
1345 20 : nsDOMStorage::nsDOMStorage()
1346 : : mStorageType(nsPIDOMStorage::Unknown)
1347 20 : , mEventBroadcaster(nsnull)
1348 : {
1349 20 : if (XRE_GetProcessType() != GeckoProcessType_Default)
1350 0 : mStorageImpl = new StorageChild(this);
1351 : else
1352 20 : mStorageImpl = new DOMStorageImpl(this);
1353 20 : }
1354 :
1355 0 : nsDOMStorage::nsDOMStorage(nsDOMStorage& aThat)
1356 : : mStorageType(aThat.mStorageType)
1357 : , mPrincipal(aThat.mPrincipal)
1358 0 : , mEventBroadcaster(nsnull)
1359 : {
1360 0 : if (XRE_GetProcessType() != GeckoProcessType_Default) {
1361 0 : StorageChild* other = static_cast<StorageChild*>(aThat.mStorageImpl.get());
1362 0 : mStorageImpl = new StorageChild(this, *other);
1363 : } else {
1364 0 : DOMStorageImpl* other = static_cast<DOMStorageImpl*>(aThat.mStorageImpl.get());
1365 0 : mStorageImpl = new DOMStorageImpl(this, *other);
1366 : }
1367 0 : }
1368 :
1369 40 : nsDOMStorage::~nsDOMStorage()
1370 : {
1371 80 : }
1372 :
1373 : static
1374 : nsresult
1375 20 : GetDomainURI(nsIPrincipal *aPrincipal, bool aIncludeDomain, nsIURI **_domain)
1376 : {
1377 40 : nsCOMPtr<nsIURI> uri;
1378 :
1379 20 : if (aIncludeDomain) {
1380 0 : nsresult rv = aPrincipal->GetDomain(getter_AddRefs(uri));
1381 0 : NS_ENSURE_SUCCESS(rv, rv);
1382 : }
1383 :
1384 20 : if (!uri) {
1385 20 : nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
1386 20 : NS_ENSURE_SUCCESS(rv, rv);
1387 : }
1388 :
1389 : // Check if we really got any URI. System principal doesn't return a URI
1390 : // instance and we would crash in NS_GetInnermostURI below.
1391 20 : if (!uri)
1392 0 : return NS_ERROR_NOT_AVAILABLE;
1393 :
1394 40 : nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
1395 20 : if (!innerURI)
1396 0 : return NS_ERROR_UNEXPECTED;
1397 20 : innerURI.forget(_domain);
1398 :
1399 20 : return NS_OK;
1400 : }
1401 :
1402 : nsresult
1403 0 : nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
1404 : {
1405 0 : nsCOMPtr<nsIURI> domainURI;
1406 0 : nsresult rv = GetDomainURI(aPrincipal, true, getter_AddRefs(domainURI));
1407 0 : NS_ENSURE_SUCCESS(rv, rv);
1408 :
1409 0 : mDocumentURI = aDocumentURI;
1410 0 : mPrincipal = aPrincipal;
1411 :
1412 0 : mStorageType = SessionStorage;
1413 :
1414 0 : mStorageImpl->InitAsSessionStorage(domainURI);
1415 0 : return NS_OK;
1416 : }
1417 :
1418 : nsresult
1419 20 : nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
1420 : {
1421 40 : nsCOMPtr<nsIURI> domainURI;
1422 20 : nsresult rv = GetDomainURI(aPrincipal, false, getter_AddRefs(domainURI));
1423 20 : NS_ENSURE_SUCCESS(rv, rv);
1424 :
1425 20 : mDocumentURI = aDocumentURI;
1426 20 : mPrincipal = aPrincipal;
1427 :
1428 20 : mStorageType = LocalStorage;
1429 :
1430 20 : bool canUseChromePersist = false;
1431 40 : nsCOMPtr<nsIURI> URI;
1432 20 : if (NS_SUCCEEDED(aPrincipal->GetURI(getter_AddRefs(URI))) && URI) {
1433 20 : canUseChromePersist = URICanUseChromePersist(URI);
1434 : }
1435 :
1436 20 : mStorageImpl->InitAsLocalStorage(domainURI, canUseChromePersist);
1437 20 : return NS_OK;
1438 : }
1439 :
1440 : nsresult
1441 0 : nsDOMStorage::InitAsGlobalStorage(const nsACString &aDomainDemanded)
1442 : {
1443 0 : mStorageType = GlobalStorage;
1444 0 : mEventBroadcaster = this;
1445 0 : mStorageImpl->InitAsGlobalStorage(aDomainDemanded);
1446 0 : return NS_OK;
1447 : }
1448 :
1449 : //static
1450 : bool
1451 172 : nsDOMStorage::CanUseStorage(bool* aSessionOnly)
1452 : {
1453 : // check if the calling domain can use storage. Downgrade to session
1454 : // only if only session storage may be used.
1455 172 : NS_ASSERTION(aSessionOnly, "null session flag");
1456 172 : *aSessionOnly = false;
1457 :
1458 172 : if (!Preferences::GetBool(kStorageEnabled)) {
1459 0 : return false;
1460 : }
1461 :
1462 : // chrome can always use storage regardless of permission preferences
1463 172 : if (nsContentUtils::IsCallerChrome())
1464 172 : return true;
1465 :
1466 0 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
1467 0 : nsresult rv = nsContentUtils::GetSecurityManager()->
1468 0 : GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1469 0 : NS_ENSURE_SUCCESS(rv, false);
1470 :
1471 : // if subjectPrincipal were null we'd have returned after
1472 : // IsCallerChrome().
1473 :
1474 0 : nsCOMPtr<nsIURI> subjectURI;
1475 0 : nsCAutoString unused;
1476 0 : if (NS_FAILED(GetPrincipalURIAndHost(subjectPrincipal,
1477 : getter_AddRefs(subjectURI),
1478 : unused))) {
1479 0 : return false;
1480 : }
1481 :
1482 : nsCOMPtr<nsIPermissionManager> permissionManager =
1483 0 : do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
1484 0 : if (!permissionManager)
1485 0 : return false;
1486 :
1487 : PRUint32 perm;
1488 0 : permissionManager->TestPermission(subjectURI, kPermissionType, &perm);
1489 :
1490 0 : if (perm == nsIPermissionManager::DENY_ACTION)
1491 0 : return false;
1492 :
1493 : // In private browsing mode we ougth to behave as in session-only cookies
1494 : // mode to prevent detection of being in private browsing mode and ensuring
1495 : // that there will be no traces left.
1496 0 : if (perm == nsICookiePermission::ACCESS_SESSION ||
1497 0 : nsDOMStorageManager::gStorageManager->InPrivateBrowsingMode()) {
1498 0 : *aSessionOnly = true;
1499 : }
1500 0 : else if (perm != nsIPermissionManager::ALLOW_ACTION) {
1501 0 : PRUint32 cookieBehavior = Preferences::GetUint(kCookiesBehavior);
1502 0 : PRUint32 lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy);
1503 :
1504 : // Treat "ask every time" as "reject always".
1505 : // Chrome persistent pages can bypass this check.
1506 0 : if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT) &&
1507 0 : !URICanUseChromePersist(subjectURI))
1508 0 : return false;
1509 :
1510 0 : if (lifetimePolicy == ACCEPT_SESSION)
1511 0 : *aSessionOnly = true;
1512 : }
1513 :
1514 0 : return true;
1515 : }
1516 :
1517 : bool
1518 172 : nsDOMStorage::CacheStoragePermissions()
1519 : {
1520 : // Bug 488446, disallowing storage use when in session only mode.
1521 : // This is temporary fix before we find complete solution for storage
1522 : // behavior in private browsing mode or session-only cookies mode.
1523 172 : if (!CanUseStorage(&mStorageImpl->mSessionOnly))
1524 0 : return false;
1525 :
1526 172 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1527 172 : if (!ssm)
1528 0 : return false;
1529 :
1530 344 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
1531 172 : nsresult rv = ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1532 172 : NS_ENSURE_SUCCESS(rv, false);
1533 :
1534 172 : return CanAccess(subjectPrincipal);
1535 : }
1536 :
1537 : // static
1538 : bool
1539 20 : nsDOMStorage::URICanUseChromePersist(nsIURI* aURI) {
1540 : bool isAbout;
1541 : return
1542 20 : (NS_SUCCEEDED(aURI->SchemeIs("moz-safe-about", &isAbout)) && isAbout) ||
1543 20 : (NS_SUCCEEDED(aURI->SchemeIs("about", &isAbout)) && isAbout);
1544 : }
1545 :
1546 : NS_IMETHODIMP
1547 44 : nsDOMStorage::GetLength(PRUint32 *aLength)
1548 : {
1549 44 : if (!CacheStoragePermissions())
1550 0 : return NS_ERROR_DOM_SECURITY_ERR;
1551 :
1552 44 : return mStorageImpl->GetLength(IsCallerSecure(), aLength);
1553 : }
1554 :
1555 : NS_IMETHODIMP
1556 24 : nsDOMStorage::Key(PRUint32 aIndex, nsAString& aKey)
1557 : {
1558 24 : if (!CacheStoragePermissions())
1559 0 : return NS_ERROR_DOM_SECURITY_ERR;
1560 :
1561 24 : return mStorageImpl->GetKey(IsCallerSecure(), aIndex, aKey);
1562 : }
1563 :
1564 : nsIDOMStorageItem*
1565 44 : nsDOMStorage::GetNamedItem(const nsAString& aKey, nsresult* aResult)
1566 : {
1567 44 : if (!CacheStoragePermissions()) {
1568 0 : *aResult = NS_ERROR_DOM_SECURITY_ERR;
1569 0 : return nsnull;
1570 : }
1571 :
1572 44 : *aResult = NS_OK;
1573 44 : if (aKey.IsEmpty())
1574 0 : return nsnull;
1575 :
1576 44 : return mStorageImpl->GetValue(IsCallerSecure(), aKey, aResult);
1577 : }
1578 :
1579 : nsresult
1580 44 : nsDOMStorage::GetItem(const nsAString& aKey, nsAString &aData)
1581 : {
1582 : nsresult rv;
1583 :
1584 : // IMPORTANT:
1585 : // CacheStoragePermissions() is called inside of
1586 : // GetItem(nsAString, nsIDOMStorageItem)
1587 : // To call it particularly in this method would just duplicate
1588 : // the call. If the code changes, make sure that call to
1589 : // CacheStoragePermissions() is put here!
1590 :
1591 88 : nsCOMPtr<nsIDOMStorageItem> item;
1592 44 : rv = GetItem(aKey, getter_AddRefs(item));
1593 44 : if (NS_FAILED(rv))
1594 0 : return rv;
1595 :
1596 44 : if (item) {
1597 30 : rv = item->GetValue(aData);
1598 30 : NS_ENSURE_SUCCESS(rv, rv);
1599 : }
1600 : else
1601 14 : SetDOMStringToNull(aData);
1602 :
1603 44 : return NS_OK;
1604 : }
1605 :
1606 : static Telemetry::ID
1607 18 : TelemetryIDForKey(nsPIDOMStorage::nsDOMStorageType type)
1608 : {
1609 18 : switch (type) {
1610 : default:
1611 0 : MOZ_ASSERT(false);
1612 : // We need to return something to satisfy the compiler.
1613 : // Fallthrough.
1614 : case nsPIDOMStorage::GlobalStorage:
1615 0 : return Telemetry::GLOBALDOMSTORAGE_KEY_SIZE_BYTES;
1616 : case nsPIDOMStorage::LocalStorage:
1617 18 : return Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES;
1618 : case nsPIDOMStorage::SessionStorage:
1619 0 : return Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES;
1620 : }
1621 : }
1622 :
1623 : static Telemetry::ID
1624 18 : TelemetryIDForValue(nsPIDOMStorage::nsDOMStorageType type)
1625 : {
1626 18 : switch (type) {
1627 : default:
1628 0 : MOZ_ASSERT(false);
1629 : // We need to return something to satisfy the compiler.
1630 : // Fallthrough.
1631 : case nsPIDOMStorage::GlobalStorage:
1632 0 : return Telemetry::GLOBALDOMSTORAGE_VALUE_SIZE_BYTES;
1633 : case nsPIDOMStorage::LocalStorage:
1634 18 : return Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES;
1635 : case nsPIDOMStorage::SessionStorage:
1636 0 : return Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES;
1637 : }
1638 : }
1639 :
1640 : NS_IMETHODIMP
1641 44 : nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem)
1642 : {
1643 : nsresult rv;
1644 :
1645 44 : NS_IF_ADDREF(*aItem = GetNamedItem(aKey, &rv));
1646 :
1647 44 : return rv;
1648 : }
1649 :
1650 : NS_IMETHODIMP
1651 18 : nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
1652 : {
1653 18 : if (!CacheStoragePermissions())
1654 0 : return NS_ERROR_DOM_SECURITY_ERR;
1655 :
1656 18 : Telemetry::Accumulate(TelemetryIDForKey(mStorageType), aKey.Length());
1657 18 : Telemetry::Accumulate(TelemetryIDForValue(mStorageType), aData.Length());
1658 :
1659 36 : nsString oldValue;
1660 18 : nsresult rv = mStorageImpl->SetValue(IsCallerSecure(), aKey, aData, oldValue);
1661 18 : if (NS_FAILED(rv))
1662 0 : return rv;
1663 :
1664 18 : if ((oldValue != aData || mStorageType == GlobalStorage) && mEventBroadcaster)
1665 18 : mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, aData);
1666 :
1667 18 : return NS_OK;
1668 : }
1669 :
1670 6 : NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
1671 : {
1672 6 : if (!CacheStoragePermissions())
1673 0 : return NS_ERROR_DOM_SECURITY_ERR;
1674 :
1675 6 : if (aKey.IsEmpty())
1676 0 : return NS_OK;
1677 :
1678 12 : nsString oldValue;
1679 6 : nsresult rv = mStorageImpl->RemoveValue(IsCallerSecure(), aKey, oldValue);
1680 6 : if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
1681 0 : return NS_OK;
1682 6 : if (NS_FAILED(rv))
1683 0 : return rv;
1684 :
1685 6 : if ((!oldValue.IsEmpty() && mStorageType != GlobalStorage) && mEventBroadcaster) {
1686 12 : nsAutoString nullString;
1687 6 : SetDOMStringToNull(nullString);
1688 6 : mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, nullString);
1689 : }
1690 :
1691 6 : return NS_OK;
1692 : }
1693 :
1694 : nsresult
1695 6 : nsDOMStorage::Clear()
1696 : {
1697 6 : if (!CacheStoragePermissions())
1698 0 : return NS_ERROR_DOM_SECURITY_ERR;
1699 :
1700 : PRInt32 oldCount;
1701 6 : nsresult rv = mStorageImpl->Clear(IsCallerSecure(), &oldCount);
1702 6 : if (NS_FAILED(rv))
1703 0 : return rv;
1704 :
1705 6 : if (oldCount && mEventBroadcaster) {
1706 12 : nsAutoString nullString;
1707 6 : SetDOMStringToNull(nullString);
1708 6 : mEventBroadcaster->BroadcastChangeNotification(nullString, nullString, nullString);
1709 : }
1710 :
1711 6 : return NS_OK;
1712 : }
1713 :
1714 : already_AddRefed<nsIDOMStorage>
1715 0 : nsDOMStorage::Clone()
1716 : {
1717 0 : NS_ASSERTION(false, "Old DOMStorage doesn't implement cloning");
1718 0 : return nsnull;
1719 : }
1720 :
1721 : already_AddRefed<nsIDOMStorage>
1722 0 : nsDOMStorage::Fork(const nsSubstring &aDocumentURI)
1723 : {
1724 0 : NS_ASSERTION(false, "Old DOMStorage doesn't implement forking");
1725 0 : return nsnull;
1726 : }
1727 :
1728 0 : bool nsDOMStorage::IsForkOf(nsIDOMStorage* aThat)
1729 : {
1730 0 : NS_ASSERTION(false, "Old DOMStorage doesn't implement forking");
1731 0 : return false;
1732 : }
1733 :
1734 : nsresult
1735 0 : nsDOMStorage::CloneFrom(nsDOMStorage* aThat)
1736 : {
1737 0 : return mStorageImpl->CloneFrom(IsCallerSecure(), aThat->mStorageImpl);
1738 : }
1739 :
1740 : nsTArray<nsString> *
1741 0 : nsDOMStorage::GetKeys()
1742 : {
1743 0 : return mStorageImpl->GetKeys(IsCallerSecure());
1744 : }
1745 :
1746 : nsIPrincipal*
1747 0 : nsDOMStorage::Principal()
1748 : {
1749 0 : return nsnull;
1750 : }
1751 :
1752 : bool
1753 0 : nsDOMStorage::CanAccessSystem(nsIPrincipal *aPrincipal)
1754 : {
1755 0 : if (!aPrincipal)
1756 0 : return true;
1757 :
1758 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1759 0 : if (!ssm)
1760 0 : return false;
1761 :
1762 : bool isSystem;
1763 0 : nsresult rv = ssm->IsSystemPrincipal(aPrincipal, &isSystem);
1764 :
1765 0 : return NS_SUCCEEDED(rv) && isSystem;
1766 : }
1767 :
1768 : bool
1769 172 : nsDOMStorage::CanAccess(nsIPrincipal *aPrincipal)
1770 : {
1771 172 : switch (mStorageType) {
1772 : case nsPIDOMStorage::LocalStorage:
1773 : case nsPIDOMStorage::SessionStorage:
1774 : {
1775 : // Allow C++ callers to access the storage
1776 172 : if (!aPrincipal)
1777 0 : return true;
1778 :
1779 : // Allow more powerful principals (e.g. system) to access the storage
1780 : bool subsumes;
1781 172 : nsresult rv = aPrincipal->SubsumesIgnoringDomain(mPrincipal, &subsumes);
1782 172 : if (NS_FAILED(rv))
1783 0 : return false;
1784 :
1785 172 : return subsumes;
1786 : }
1787 :
1788 : case nsPIDOMStorage::GlobalStorage:
1789 : {
1790 : // Allow C++/system callers to access the storage
1791 0 : if (CanAccessSystem(aPrincipal))
1792 0 : return true;
1793 :
1794 0 : nsCAutoString domain;
1795 0 : nsCOMPtr<nsIURI> unused;
1796 : nsresult rv = GetPrincipalURIAndHost(aPrincipal,
1797 0 : getter_AddRefs(unused), domain);
1798 0 : NS_ENSURE_SUCCESS(rv, false);
1799 :
1800 0 : return domain.Equals(mStorageImpl->mDomain);
1801 : }
1802 :
1803 : default:
1804 0 : return false;
1805 : }
1806 :
1807 : return false;
1808 : }
1809 :
1810 : nsPIDOMStorage::nsDOMStorageType
1811 0 : nsDOMStorage::StorageType()
1812 : {
1813 0 : return mStorageType;
1814 : }
1815 :
1816 : void
1817 0 : nsDOMStorage::BroadcastChangeNotification(const nsSubstring &aKey,
1818 : const nsSubstring &aOldValue,
1819 : const nsSubstring &aNewValue)
1820 : {
1821 : nsCOMPtr<nsIObserverService> observerService =
1822 0 : mozilla::services::GetObserverService();
1823 0 : if (!observerService) {
1824 : return;
1825 : }
1826 :
1827 : // Fire off a notification that a storage object changed. If the
1828 : // storage object is a session storage object, we don't pass a
1829 : // domain, but if it's a global storage object we do.
1830 0 : observerService->NotifyObservers((nsIDOMStorageObsolete *)this,
1831 : "dom-storage-changed",
1832 0 : NS_ConvertUTF8toUTF16(mStorageImpl->mDomain).get());
1833 : }
1834 :
1835 : //
1836 : // nsDOMStorage2
1837 : //
1838 :
1839 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorage2)
1840 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStorage2)
1841 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStorage)
1842 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1843 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStorage2)
1844 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mStorage, nsIDOMStorageObsolete)
1845 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1846 :
1847 : DOMCI_DATA(Storage, nsDOMStorage2)
1848 :
1849 262 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStorage2)
1850 282 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStorage2)
1851 596 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage2)
1852 434 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
1853 414 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
1854 222 : NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
1855 222 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
1856 202 : NS_INTERFACE_MAP_END
1857 :
1858 20 : nsDOMStorage2::nsDOMStorage2()
1859 : {
1860 20 : }
1861 :
1862 0 : nsDOMStorage2::nsDOMStorage2(nsDOMStorage2& aThat)
1863 : {
1864 0 : mStorage = new nsDOMStorage(*aThat.mStorage.get());
1865 0 : mPrincipal = aThat.mPrincipal;
1866 0 : }
1867 :
1868 : nsresult
1869 0 : nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
1870 : {
1871 0 : mStorage = new nsDOMStorage();
1872 0 : if (!mStorage)
1873 0 : return NS_ERROR_OUT_OF_MEMORY;
1874 :
1875 0 : mPrincipal = aPrincipal;
1876 0 : mDocumentURI = aDocumentURI;
1877 :
1878 0 : return mStorage->InitAsSessionStorage(aPrincipal, aDocumentURI);
1879 : }
1880 :
1881 : nsresult
1882 20 : nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
1883 : {
1884 20 : mStorage = new nsDOMStorage();
1885 20 : if (!mStorage)
1886 0 : return NS_ERROR_OUT_OF_MEMORY;
1887 :
1888 20 : mPrincipal = aPrincipal;
1889 20 : mDocumentURI = aDocumentURI;
1890 :
1891 20 : return mStorage->InitAsLocalStorage(aPrincipal, aDocumentURI);
1892 : }
1893 :
1894 : nsresult
1895 0 : nsDOMStorage2::InitAsGlobalStorage(const nsACString &aDomainDemanded)
1896 : {
1897 0 : NS_ASSERTION(false, "Should not initialize nsDOMStorage2 as global storage.");
1898 0 : return NS_ERROR_NOT_IMPLEMENTED;
1899 : }
1900 :
1901 : already_AddRefed<nsIDOMStorage>
1902 0 : nsDOMStorage2::Clone()
1903 : {
1904 0 : nsDOMStorage2* storage = new nsDOMStorage2(*this);
1905 0 : if (!storage)
1906 0 : return nsnull;
1907 :
1908 0 : storage->mStorage->CloneFrom(mStorage);
1909 0 : NS_ADDREF(storage);
1910 :
1911 0 : return storage;
1912 : }
1913 :
1914 : already_AddRefed<nsIDOMStorage>
1915 0 : nsDOMStorage2::Fork(const nsSubstring &aDocumentURI)
1916 : {
1917 0 : nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
1918 0 : if (!storage)
1919 0 : return nsnull;
1920 :
1921 0 : nsresult rv = storage->InitAsSessionStorageFork(mPrincipal, aDocumentURI, mStorage);
1922 0 : if (NS_FAILED(rv))
1923 0 : return nsnull;
1924 :
1925 0 : nsIDOMStorage* result = static_cast<nsIDOMStorage*>(storage.get());
1926 0 : storage.forget();
1927 0 : return result;
1928 : }
1929 :
1930 0 : bool nsDOMStorage2::IsForkOf(nsIDOMStorage* aThat)
1931 : {
1932 0 : if (!aThat)
1933 0 : return false;
1934 :
1935 0 : nsDOMStorage2* storage = static_cast<nsDOMStorage2*>(aThat);
1936 0 : return mStorage == storage->mStorage;
1937 : }
1938 :
1939 : nsresult
1940 0 : nsDOMStorage2::InitAsSessionStorageFork(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI, nsIDOMStorageObsolete* aStorage)
1941 : {
1942 0 : mPrincipal = aPrincipal;
1943 0 : mDocumentURI = aDocumentURI;
1944 0 : mStorage = static_cast<nsDOMStorage*>(aStorage);
1945 :
1946 0 : return NS_OK;
1947 : }
1948 :
1949 : nsTArray<nsString> *
1950 0 : nsDOMStorage2::GetKeys()
1951 : {
1952 0 : return mStorage->GetKeys();
1953 : }
1954 :
1955 : nsIPrincipal*
1956 0 : nsDOMStorage2::Principal()
1957 : {
1958 0 : return mPrincipal;
1959 : }
1960 :
1961 : bool
1962 0 : nsDOMStorage2::CanAccess(nsIPrincipal *aPrincipal)
1963 : {
1964 0 : return mStorage->CanAccess(aPrincipal);
1965 : }
1966 :
1967 : nsPIDOMStorage::nsDOMStorageType
1968 0 : nsDOMStorage2::StorageType()
1969 : {
1970 0 : if (mStorage)
1971 0 : return mStorage->StorageType();
1972 :
1973 0 : return nsPIDOMStorage::Unknown;
1974 : }
1975 :
1976 : namespace {
1977 :
1978 : class StorageNotifierRunnable : public nsRunnable
1979 120 : {
1980 : public:
1981 30 : StorageNotifierRunnable(nsISupports* aSubject)
1982 30 : : mSubject(aSubject)
1983 30 : { }
1984 :
1985 : NS_DECL_NSIRUNNABLE
1986 :
1987 : private:
1988 : nsCOMPtr<nsISupports> mSubject;
1989 : };
1990 :
1991 : NS_IMETHODIMP
1992 30 : StorageNotifierRunnable::Run()
1993 : {
1994 : nsCOMPtr<nsIObserverService> observerService =
1995 60 : mozilla::services::GetObserverService();
1996 30 : if (observerService) {
1997 30 : observerService->NotifyObservers(mSubject, "dom-storage2-changed", nsnull);
1998 : }
1999 30 : return NS_OK;
2000 : }
2001 :
2002 : } // anonymous namespace
2003 :
2004 : void
2005 30 : nsDOMStorage2::BroadcastChangeNotification(const nsSubstring &aKey,
2006 : const nsSubstring &aOldValue,
2007 : const nsSubstring &aNewValue)
2008 : {
2009 : nsresult rv;
2010 60 : nsCOMPtr<nsIDOMStorageEvent> event = new nsDOMStorageEvent();
2011 60 : rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
2012 : false,
2013 : false,
2014 : aKey,
2015 : aOldValue,
2016 : aNewValue,
2017 : mDocumentURI,
2018 30 : static_cast<nsIDOMStorage*>(this));
2019 30 : if (NS_FAILED(rv)) {
2020 : return;
2021 : }
2022 :
2023 90 : nsRefPtr<StorageNotifierRunnable> r = new StorageNotifierRunnable(event);
2024 30 : NS_DispatchToMainThread(r);
2025 : }
2026 :
2027 : NS_IMETHODIMP
2028 44 : nsDOMStorage2::GetLength(PRUint32 *aLength)
2029 : {
2030 44 : return mStorage->GetLength(aLength);
2031 : }
2032 :
2033 : NS_IMETHODIMP
2034 24 : nsDOMStorage2::Key(PRUint32 aIndex, nsAString& aKey)
2035 : {
2036 24 : return mStorage->Key(aIndex, aKey);
2037 : }
2038 :
2039 : NS_IMETHODIMP
2040 44 : nsDOMStorage2::GetItem(const nsAString& aKey, nsAString &aData)
2041 : {
2042 44 : return mStorage->GetItem(aKey, aData);
2043 : }
2044 :
2045 : NS_IMETHODIMP
2046 18 : nsDOMStorage2::SetItem(const nsAString& aKey, const nsAString& aData)
2047 : {
2048 18 : mStorage->mEventBroadcaster = this;
2049 18 : return mStorage->SetItem(aKey, aData);
2050 : }
2051 :
2052 : NS_IMETHODIMP
2053 6 : nsDOMStorage2::RemoveItem(const nsAString& aKey)
2054 : {
2055 6 : mStorage->mEventBroadcaster = this;
2056 6 : return mStorage->RemoveItem(aKey);
2057 : }
2058 :
2059 : NS_IMETHODIMP
2060 6 : nsDOMStorage2::Clear()
2061 : {
2062 6 : mStorage->mEventBroadcaster = this;
2063 6 : return mStorage->Clear();
2064 : }
2065 :
2066 : //
2067 : // nsDOMStorageList
2068 : //
2069 :
2070 : DOMCI_DATA(StorageList, nsDOMStorageList)
2071 :
2072 0 : NS_INTERFACE_MAP_BEGIN(nsDOMStorageList)
2073 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
2074 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageList)
2075 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageList)
2076 0 : NS_INTERFACE_MAP_END
2077 :
2078 0 : NS_IMPL_ADDREF(nsDOMStorageList)
2079 0 : NS_IMPL_RELEASE(nsDOMStorageList)
2080 :
2081 : nsIDOMStorageObsolete*
2082 0 : nsDOMStorageList::GetNamedItem(const nsAString& aDomain, nsresult* aResult)
2083 : {
2084 0 : nsCAutoString requestedDomain;
2085 :
2086 : // Normalize the requested domain
2087 0 : nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
2088 0 : if (idn) {
2089 0 : *aResult = idn->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aDomain),
2090 0 : requestedDomain);
2091 0 : NS_ENSURE_SUCCESS(*aResult, nsnull);
2092 : } else {
2093 : // Don't have the IDN service, best we can do is URL escape.
2094 0 : NS_EscapeURL(NS_ConvertUTF16toUTF8(aDomain),
2095 : esc_OnlyNonASCII | esc_AlwaysCopy,
2096 0 : requestedDomain);
2097 : }
2098 0 : ToLowerCase(requestedDomain);
2099 :
2100 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
2101 0 : if (!ssm) {
2102 0 : *aResult = NS_ERROR_FAILURE;
2103 0 : return nsnull;
2104 : }
2105 :
2106 0 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
2107 0 : *aResult = ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
2108 0 : NS_ENSURE_SUCCESS(*aResult, nsnull);
2109 :
2110 0 : nsCAutoString currentDomain;
2111 0 : if (subjectPrincipal) {
2112 0 : nsCOMPtr<nsIURI> unused;
2113 0 : *aResult = GetPrincipalURIAndHost(subjectPrincipal, getter_AddRefs(unused),
2114 0 : currentDomain);
2115 0 : NS_ENSURE_SUCCESS(*aResult, nsnull);
2116 :
2117 : bool sessionOnly;
2118 0 : if (!nsDOMStorage::CanUseStorage(&sessionOnly)) {
2119 0 : *aResult = NS_ERROR_DOM_SECURITY_ERR;
2120 0 : return nsnull;
2121 : }
2122 : }
2123 :
2124 0 : bool isSystem = nsContentUtils::IsCallerTrustedForRead();
2125 0 : if (currentDomain.IsEmpty() && !isSystem) {
2126 0 : *aResult = NS_ERROR_DOM_SECURITY_ERR;
2127 0 : return nsnull;
2128 : }
2129 :
2130 : return GetStorageForDomain(requestedDomain,
2131 0 : currentDomain, isSystem, aResult);
2132 : }
2133 :
2134 : NS_IMETHODIMP
2135 0 : nsDOMStorageList::NamedItem(const nsAString& aDomain,
2136 : nsIDOMStorageObsolete** aStorage)
2137 : {
2138 : nsresult rv;
2139 0 : NS_IF_ADDREF(*aStorage = GetNamedItem(aDomain, &rv));
2140 0 : return rv;
2141 : }
2142 :
2143 : // static
2144 : bool
2145 0 : nsDOMStorageList::CanAccessDomain(const nsACString& aRequestedDomain,
2146 : const nsACString& aCurrentDomain)
2147 : {
2148 0 : return aRequestedDomain.Equals(aCurrentDomain);
2149 : }
2150 :
2151 : nsIDOMStorageObsolete*
2152 0 : nsDOMStorageList::GetStorageForDomain(const nsACString& aRequestedDomain,
2153 : const nsACString& aCurrentDomain,
2154 : bool aNoCurrentDomainCheck,
2155 : nsresult* aResult)
2156 : {
2157 0 : nsTArray<nsCString> requestedDomainArray;
2158 0 : if ((!aNoCurrentDomainCheck &&
2159 0 : !CanAccessDomain(aRequestedDomain, aCurrentDomain)) ||
2160 0 : !ConvertDomainToArray(aRequestedDomain, &requestedDomainArray)) {
2161 0 : *aResult = NS_ERROR_DOM_SECURITY_ERR;
2162 :
2163 0 : return nsnull;
2164 : }
2165 :
2166 : // now rebuild a string for the domain.
2167 0 : nsCAutoString usedDomain;
2168 0 : PRUint32 requestedPos = 0;
2169 0 : for (requestedPos = 0; requestedPos < requestedDomainArray.Length();
2170 : requestedPos++) {
2171 0 : if (!usedDomain.IsEmpty())
2172 0 : usedDomain.Append('.');
2173 0 : usedDomain.Append(requestedDomainArray[requestedPos]);
2174 : }
2175 :
2176 0 : *aResult = NS_OK;
2177 :
2178 : // now have a valid domain, so look it up in the storage table
2179 0 : nsIDOMStorageObsolete* storage = mStorages.GetWeak(usedDomain);
2180 0 : if (!storage) {
2181 0 : nsRefPtr<nsDOMStorage> newstorage;
2182 0 : newstorage = new nsDOMStorage();
2183 0 : if (newstorage && mStorages.Put(usedDomain, newstorage)) {
2184 0 : *aResult = newstorage->InitAsGlobalStorage(usedDomain);
2185 0 : if (NS_FAILED(*aResult)) {
2186 0 : mStorages.Remove(usedDomain);
2187 0 : return nsnull;
2188 : }
2189 0 : storage = newstorage;
2190 : }
2191 : else {
2192 0 : *aResult = NS_ERROR_OUT_OF_MEMORY;
2193 : }
2194 : }
2195 :
2196 0 : return storage;
2197 : }
2198 :
2199 : // static
2200 : bool
2201 0 : nsDOMStorageList::ConvertDomainToArray(const nsACString& aDomain,
2202 : nsTArray<nsCString> *aArray)
2203 : {
2204 0 : PRInt32 length = aDomain.Length();
2205 0 : PRInt32 n = 0;
2206 0 : while (n < length) {
2207 0 : PRInt32 dotpos = aDomain.FindChar('.', n);
2208 0 : nsCAutoString domain;
2209 :
2210 0 : if (dotpos == -1) // no more dots
2211 0 : domain.Assign(Substring(aDomain, n));
2212 0 : else if (dotpos - n == 0) // no point continuing in this case
2213 0 : return false;
2214 0 : else if (dotpos >= 0)
2215 0 : domain.Assign(Substring(aDomain, n, dotpos - n));
2216 :
2217 0 : ToLowerCase(domain);
2218 0 : aArray->AppendElement(domain);
2219 :
2220 0 : if (dotpos == -1)
2221 : break;
2222 :
2223 0 : n = dotpos + 1;
2224 : }
2225 :
2226 : // if n equals the length, there is a dot at the end, so treat it as invalid
2227 0 : return (n != length);
2228 : }
2229 :
2230 : nsresult
2231 0 : NS_NewDOMStorageList(nsIDOMStorageList** aResult)
2232 : {
2233 0 : *aResult = new nsDOMStorageList();
2234 0 : if (!*aResult)
2235 0 : return NS_ERROR_OUT_OF_MEMORY;
2236 :
2237 0 : NS_ADDREF(*aResult);
2238 0 : return NS_OK;
2239 : }
2240 :
2241 : //
2242 : // nsDOMStorageItem
2243 : //
2244 :
2245 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorageItem)
2246 4 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStorageItem)
2247 : {
2248 4 : tmp->mStorage = nsnull;
2249 : }
2250 4 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2251 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStorageItem)
2252 : {
2253 4 : cb.NoteXPCOMChild((nsISupports*) tmp->mStorage);
2254 : }
2255 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2256 :
2257 116 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStorageItem)
2258 116 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStorageItem)
2259 :
2260 : DOMCI_DATA(StorageItem, nsDOMStorageItem)
2261 :
2262 102 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorageItem)
2263 30 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorageItem)
2264 30 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageItem)
2265 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMToString)
2266 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageItem)
2267 0 : NS_INTERFACE_MAP_END
2268 :
2269 34 : nsDOMStorageItem::nsDOMStorageItem(DOMStorageBase* aStorage,
2270 : const nsAString& aKey,
2271 : const nsAString& aValue,
2272 : bool aSecure)
2273 : : mSecure(aSecure),
2274 : mKey(aKey),
2275 : mValue(aValue),
2276 34 : mStorage(aStorage)
2277 : {
2278 34 : }
2279 :
2280 68 : nsDOMStorageItem::~nsDOMStorageItem()
2281 : {
2282 136 : }
2283 :
2284 : NS_IMETHODIMP
2285 0 : nsDOMStorageItem::GetSecure(bool* aSecure)
2286 : {
2287 0 : if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) {
2288 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2289 : }
2290 :
2291 0 : if (mStorage->UseDB()) {
2292 0 : nsAutoString value;
2293 0 : return mStorage->GetDBValue(mKey, value, aSecure);
2294 : }
2295 :
2296 0 : *aSecure = IsSecure();
2297 0 : return NS_OK;
2298 : }
2299 :
2300 : NS_IMETHODIMP
2301 0 : nsDOMStorageItem::SetSecure(bool aSecure)
2302 : {
2303 0 : if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) {
2304 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2305 : }
2306 :
2307 0 : if (mStorage->UseDB()) {
2308 0 : nsresult rv = mStorage->SetSecure(mKey, aSecure);
2309 0 : NS_ENSURE_SUCCESS(rv, rv);
2310 : }
2311 :
2312 0 : mSecure = aSecure;
2313 0 : return NS_OK;
2314 : }
2315 :
2316 : NS_IMETHODIMP
2317 30 : nsDOMStorageItem::GetValue(nsAString& aValue)
2318 : {
2319 30 : if (!mStorage->CacheStoragePermissions())
2320 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2321 :
2322 30 : if (mStorage->UseDB()) {
2323 : bool secure;
2324 30 : nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure);
2325 30 : if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
2326 4 : return NS_OK;
2327 26 : if (NS_SUCCEEDED(rv) && !IsCallerSecure() && secure)
2328 0 : return NS_ERROR_DOM_SECURITY_ERR;
2329 26 : return rv;
2330 : }
2331 :
2332 0 : if (IsSecure() && !IsCallerSecure()) {
2333 0 : return NS_ERROR_DOM_SECURITY_ERR;
2334 : }
2335 :
2336 0 : aValue = mValue;
2337 0 : return NS_OK;
2338 : }
2339 :
2340 : NS_IMETHODIMP
2341 0 : nsDOMStorageItem::SetValue(const nsAString& aValue)
2342 : {
2343 0 : if (!mStorage->CacheStoragePermissions())
2344 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2345 :
2346 0 : bool secureCaller = IsCallerSecure();
2347 :
2348 0 : if (mStorage->UseDB()) {
2349 : // SetDBValue() does the security checks for us.
2350 0 : return mStorage->SetDBValue(mKey, aValue, secureCaller);
2351 : }
2352 :
2353 0 : bool secureItem = IsSecure();
2354 :
2355 0 : if (!secureCaller && secureItem) {
2356 : // The item is secure, but the caller isn't. Throw.
2357 0 : return NS_ERROR_DOM_SECURITY_ERR;
2358 : }
2359 :
2360 0 : mValue = aValue;
2361 0 : mSecure = secureCaller;
2362 0 : return NS_OK;
2363 : }
2364 :
2365 : NS_IMETHODIMP
2366 0 : nsDOMStorageItem::ToString(nsAString& aStr)
2367 : {
2368 0 : return GetValue(aStr);
2369 : }
2370 :
2371 : // Cycle collection implementation for nsDOMStorageEvent
2372 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorageEvent)
2373 :
2374 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMStorageEvent, nsDOMEvent)
2375 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStorageArea)
2376 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2377 :
2378 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMStorageEvent, nsDOMEvent)
2379 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStorageArea)
2380 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2381 :
2382 90 : NS_IMPL_ADDREF_INHERITED(nsDOMStorageEvent, nsDOMEvent)
2383 90 : NS_IMPL_RELEASE_INHERITED(nsDOMStorageEvent, nsDOMEvent)
2384 :
2385 : DOMCI_DATA(StorageEvent, nsDOMStorageEvent)
2386 :
2387 : // QueryInterface implementation for nsDOMStorageEvent
2388 90 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMStorageEvent)
2389 30 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEvent)
2390 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEvent)
2391 0 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
2392 :
2393 :
2394 : /* readonly attribute DOMString key; */
2395 0 : NS_IMETHODIMP nsDOMStorageEvent::GetKey(nsAString & aKey)
2396 : {
2397 0 : aKey = mKey;
2398 0 : return NS_OK;
2399 : }
2400 :
2401 : /* readonly attribute DOMString oldValue; */
2402 0 : NS_IMETHODIMP nsDOMStorageEvent::GetOldValue(nsAString & aOldValue)
2403 : {
2404 0 : aOldValue = mOldValue;
2405 0 : return NS_OK;
2406 : }
2407 :
2408 : /* readonly attribute DOMString newValue; */
2409 0 : NS_IMETHODIMP nsDOMStorageEvent::GetNewValue(nsAString & aNewValue)
2410 : {
2411 0 : aNewValue = mNewValue;
2412 0 : return NS_OK;
2413 : }
2414 :
2415 : /* readonly attribute DOMString url; */
2416 0 : NS_IMETHODIMP nsDOMStorageEvent::GetUrl(nsAString & aUrl)
2417 : {
2418 0 : aUrl = mUrl;
2419 0 : return NS_OK;
2420 : }
2421 :
2422 : /* readonly attribute nsIDOMStorage storageArea; */
2423 0 : NS_IMETHODIMP nsDOMStorageEvent::GetStorageArea(nsIDOMStorage * *aStorageArea)
2424 : {
2425 0 : NS_ENSURE_ARG_POINTER(aStorageArea);
2426 :
2427 0 : NS_IF_ADDREF(*aStorageArea = mStorageArea);
2428 0 : return NS_OK;
2429 : }
2430 :
2431 : /* void initStorageEvent (in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString urlArg, in nsIDOMStorage storageAreaArg); */
2432 30 : NS_IMETHODIMP nsDOMStorageEvent::InitStorageEvent(const nsAString & typeArg,
2433 : bool canBubbleArg,
2434 : bool cancelableArg,
2435 : const nsAString & keyArg,
2436 : const nsAString & oldValueArg,
2437 : const nsAString & newValueArg,
2438 : const nsAString & urlArg,
2439 : nsIDOMStorage *storageAreaArg)
2440 : {
2441 : nsresult rv;
2442 :
2443 30 : rv = InitEvent(typeArg, canBubbleArg, cancelableArg);
2444 30 : NS_ENSURE_SUCCESS(rv, rv);
2445 :
2446 30 : mKey = keyArg;
2447 30 : mOldValue = oldValueArg;
2448 30 : mNewValue = newValueArg;
2449 30 : mUrl = urlArg;
2450 30 : mStorageArea = storageAreaArg;
2451 :
2452 30 : return NS_OK;
2453 : }
2454 :
2455 : nsresult
2456 0 : nsDOMStorageEvent::InitFromCtor(const nsAString& aType,
2457 : JSContext* aCx, jsval* aVal)
2458 : {
2459 0 : mozilla::dom::StorageEventInit d;
2460 0 : nsresult rv = d.Init(aCx, aVal);
2461 0 : NS_ENSURE_SUCCESS(rv, rv);
2462 : return InitStorageEvent(aType, d.bubbles, d.cancelable, d.key, d.oldValue,
2463 0 : d.newValue, d.url, d.storageArea);
2464 : }
2465 :
2466 : // Obsolete globalStorage event
2467 :
2468 : DOMCI_DATA(StorageEventObsolete, nsDOMStorageEventObsolete)
2469 :
2470 : // QueryInterface implementation for nsDOMStorageEventObsolete
2471 0 : NS_INTERFACE_MAP_BEGIN(nsDOMStorageEventObsolete)
2472 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEventObsolete)
2473 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEventObsolete)
2474 0 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
2475 :
2476 0 : NS_IMPL_ADDREF_INHERITED(nsDOMStorageEventObsolete, nsDOMEvent)
2477 0 : NS_IMPL_RELEASE_INHERITED(nsDOMStorageEventObsolete, nsDOMEvent)
2478 :
2479 :
2480 : NS_IMETHODIMP
2481 0 : nsDOMStorageEventObsolete::GetDomain(nsAString& aDomain)
2482 : {
2483 : // mDomain will be #session for session storage for events that fire
2484 : // due to a change in a session storage object.
2485 0 : aDomain = mDomain;
2486 :
2487 0 : return NS_OK;
2488 : }
2489 :
2490 : NS_IMETHODIMP
2491 0 : nsDOMStorageEventObsolete::InitStorageEvent(const nsAString& aTypeArg,
2492 : bool aCanBubbleArg,
2493 : bool aCancelableArg,
2494 : const nsAString& aDomainArg)
2495 : {
2496 0 : nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
2497 0 : NS_ENSURE_SUCCESS(rv, rv);
2498 :
2499 0 : mDomain = aDomainArg;
2500 :
2501 0 : return NS_OK;
2502 4392 : }
|