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 : PLDHashOperator
704 4 : SessionStorageTraverser(nsSessionStorageEntry* aEntry, void* userArg) {
705 : nsCycleCollectionTraversalCallback *cb =
706 4 : static_cast<nsCycleCollectionTraversalCallback*>(userArg);
707 :
708 4 : cb->NoteXPCOMChild((nsIDOMStorageItem *) aEntry->mItem);
709 :
710 4 : return PL_DHASH_NEXT;
711 : }
712 :
713 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(DOMStorageImpl)
714 4 : NS_IMPL_CYCLE_COLLECTION_UNLINK_0(DOMStorageImpl)
715 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMStorageImpl)
716 : {
717 4 : if (tmp->mItems.IsInitialized()) {
718 4 : tmp->mItems.EnumerateEntries(SessionStorageTraverser, &cb);
719 : }
720 : }
721 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
722 :
723 58 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMStorageImpl)
724 78 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMStorageImpl)
725 62 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMStorageImpl)
726 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
727 0 : NS_INTERFACE_MAP_END
728 :
729 20 : DOMStorageImpl::DOMStorageImpl(nsDOMStorage* aStorage)
730 : {
731 20 : Init(aStorage);
732 20 : }
733 :
734 0 : DOMStorageImpl::DOMStorageImpl(nsDOMStorage* aStorage, DOMStorageImpl& aThat)
735 0 : : DOMStorageBase(aThat)
736 : {
737 0 : Init(aStorage);
738 0 : }
739 :
740 : void
741 20 : DOMStorageImpl::Init(nsDOMStorage* aStorage)
742 : {
743 20 : mItemsCachedVersion = 0;
744 20 : mItems.Init(8);
745 20 : mOwner = aStorage;
746 20 : if (nsDOMStorageManager::gStorageManager)
747 20 : nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
748 20 : }
749 :
750 40 : DOMStorageImpl::~DOMStorageImpl()
751 : {
752 20 : if (nsDOMStorageManager::gStorageManager)
753 18 : nsDOMStorageManager::gStorageManager->RemoveFromStoragesHash(this);
754 20 : }
755 :
756 : nsresult
757 354 : DOMStorageImpl::InitDB()
758 : {
759 354 : if (!gStorageDB) {
760 77 : gStorageDB = new nsDOMStorageDBWrapper();
761 77 : if (!gStorageDB)
762 0 : return NS_ERROR_OUT_OF_MEMORY;
763 :
764 77 : nsresult rv = gStorageDB->Init();
765 77 : if (NS_FAILED(rv)) {
766 : // Failed to initialize the DB, delete it and null out the
767 : // pointer so we don't end up attempting to use an
768 : // un-initialized DB later on.
769 :
770 38 : delete gStorageDB;
771 38 : gStorageDB = nsnull;
772 :
773 38 : return rv;
774 : }
775 : }
776 :
777 316 : return NS_OK;
778 : }
779 :
780 : void
781 0 : DOMStorageImpl::InitFromChild(bool aUseDB, bool aCanUseChromePersist,
782 : bool aSessionOnly, const nsACString& aDomain,
783 : const nsACString& aScopeDBKey,
784 : const nsACString& aQuotaDomainDBKey,
785 : const nsACString& aQuotaETLDplus1DomainDBKey,
786 : PRUint32 aStorageType)
787 : {
788 0 : mUseDB = aUseDB;
789 0 : mCanUseChromePersist = aCanUseChromePersist;
790 0 : mSessionOnly = aSessionOnly;
791 0 : mDomain = aDomain;
792 0 : mScopeDBKey = aScopeDBKey;
793 0 : mQuotaDomainDBKey = aQuotaDomainDBKey;
794 0 : mQuotaETLDplus1DomainDBKey = aQuotaETLDplus1DomainDBKey;
795 0 : mStorageType = static_cast<nsPIDOMStorage::nsDOMStorageType>(aStorageType);
796 0 : }
797 :
798 : void
799 0 : DOMStorageImpl::SetSessionOnly(bool aSessionOnly)
800 : {
801 0 : mSessionOnly = aSessionOnly;
802 0 : }
803 :
804 : void
805 0 : DOMStorageImpl::InitAsSessionStorage(nsIURI* aDomainURI)
806 : {
807 0 : DOMStorageBase::InitAsSessionStorage(aDomainURI);
808 0 : }
809 :
810 : void
811 20 : DOMStorageImpl::InitAsLocalStorage(nsIURI* aDomainURI,
812 : bool aCanUseChromePersist)
813 : {
814 20 : DOMStorageBase::InitAsLocalStorage(aDomainURI, aCanUseChromePersist);
815 20 : }
816 :
817 : bool
818 30 : DOMStorageImpl::CacheStoragePermissions()
819 : {
820 : // If this is a cross-process situation, we don't have a real storage owner.
821 : // All the correct checks have been done on the child, so we just need to
822 : // make sure that our session-only status is correctly updated.
823 30 : if (!mOwner)
824 0 : return nsDOMStorage::CanUseStorage(&mSessionOnly);
825 :
826 30 : return mOwner->CacheStoragePermissions();
827 : }
828 :
829 : bool
830 266 : DOMStorageImpl::CanUseChromePersist()
831 : {
832 266 : return mCanUseChromePersist;
833 : }
834 :
835 : nsresult
836 18 : DOMStorageImpl::GetCachedValue(const nsAString& aKey, nsAString& aValue,
837 : bool* aSecure)
838 : {
839 18 : aValue.Truncate();
840 18 : *aSecure = false;
841 :
842 18 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
843 18 : if (!entry)
844 18 : return NS_ERROR_NOT_AVAILABLE;
845 :
846 0 : aValue = entry->mItem->GetValueInternal();
847 0 : *aSecure = entry->mItem->IsSecure();
848 :
849 0 : return NS_OK;
850 : }
851 :
852 : nsresult
853 50 : DOMStorageImpl::GetDBValue(const nsAString& aKey, nsAString& aValue,
854 : bool* aSecure)
855 : {
856 50 : aValue.Truncate();
857 :
858 50 : if (!UseDB())
859 0 : return NS_OK;
860 :
861 50 : nsresult rv = InitDB();
862 50 : NS_ENSURE_SUCCESS(rv, rv);
863 :
864 100 : nsAutoString value;
865 50 : rv = gStorageDB->GetKeyValue(this, aKey, value, aSecure);
866 :
867 50 : if (rv == NS_ERROR_DOM_NOT_FOUND_ERR) {
868 18 : SetDOMStringToNull(aValue);
869 : }
870 :
871 50 : if (NS_FAILED(rv))
872 18 : return rv;
873 :
874 32 : aValue.Assign(value);
875 :
876 32 : return NS_OK;
877 : }
878 :
879 : nsresult
880 18 : DOMStorageImpl::SetDBValue(const nsAString& aKey,
881 : const nsAString& aValue,
882 : bool aSecure)
883 : {
884 18 : if (!UseDB())
885 0 : return NS_OK;
886 :
887 18 : nsresult rv = InitDB();
888 18 : NS_ENSURE_SUCCESS(rv, rv);
889 :
890 : PRInt32 offlineAppPermission;
891 : PRInt32 quota;
892 : PRInt32 warnQuota;
893 : offlineAppPermission = GetQuota(mDomain, "a, &warnQuota,
894 18 : CanUseChromePersist());
895 :
896 18 : CacheKeysFromDB();
897 :
898 : PRInt32 usage;
899 : rv = gStorageDB->SetKey(this, aKey, aValue, aSecure, quota,
900 0 : !IS_PERMISSION_ALLOWED(offlineAppPermission),
901 36 : &usage);
902 18 : NS_ENSURE_SUCCESS(rv, rv);
903 :
904 18 : if (warnQuota >= 0 && usage > warnQuota) {
905 : // try to include the window that exceeded the warn quota
906 0 : nsCOMPtr<nsIDOMWindow> window;
907 : JSContext *cx;
908 : nsCOMPtr<nsIJSContextStack> stack =
909 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1");
910 0 : if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
911 0 : nsCOMPtr<nsIScriptContext> scriptContext;
912 0 : scriptContext = GetScriptContextFromJSContext(cx);
913 0 : if (scriptContext) {
914 0 : window = do_QueryInterface(scriptContext->GetGlobalObject());
915 : }
916 : }
917 :
918 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
919 0 : os->NotifyObservers(window, "dom-storage-warn-quota-exceeded",
920 0 : NS_ConvertUTF8toUTF16(mDomain).get());
921 : }
922 :
923 18 : return NS_OK;
924 : }
925 :
926 : nsresult
927 0 : DOMStorageImpl::SetSecure(const nsAString& aKey, bool aSecure)
928 : {
929 0 : if (UseDB()) {
930 0 : nsresult rv = InitDB();
931 0 : NS_ENSURE_SUCCESS(rv, rv);
932 :
933 0 : return gStorageDB->SetSecure(this, aKey, aSecure);
934 : }
935 :
936 0 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
937 0 : NS_ASSERTION(entry, "Don't use SetSecure() with nonexistent keys!");
938 :
939 0 : if (entry) {
940 0 : entry->mItem->SetSecureInternal(aSecure);
941 : }
942 :
943 0 : return NS_OK;
944 : }
945 :
946 : static PLDHashOperator
947 9 : ClearStorageItem(nsSessionStorageEntry* aEntry, void* userArg)
948 : {
949 9 : aEntry->mItem->SetValueInternal(EmptyString());
950 9 : return PL_DHASH_NEXT;
951 : }
952 :
953 : void
954 14 : DOMStorageImpl::ClearAll()
955 : {
956 14 : mItems.EnumerateEntries(ClearStorageItem, nsnull);
957 14 : mItemsCachedVersion = 0;
958 14 : }
959 :
960 : struct CopyArgs {
961 : DOMStorageImpl* storage;
962 : bool callerSecure;
963 : };
964 :
965 : static PLDHashOperator
966 0 : CopyStorageItems(nsSessionStorageEntry* aEntry, void* userArg)
967 : {
968 : // When copying items from one impl to another, we may not
969 : // have an mOwner that we can call SetItem on. Therefore we need
970 : // to replicate its behaviour.
971 :
972 0 : CopyArgs* args = static_cast<CopyArgs*>(userArg);
973 :
974 0 : nsAutoString unused;
975 0 : nsresult rv = args->storage->SetValue(args->callerSecure, aEntry->GetKey(),
976 0 : aEntry->mItem->GetValueInternal(), unused);
977 0 : if (NS_FAILED(rv))
978 0 : return PL_DHASH_NEXT;
979 :
980 0 : if (aEntry->mItem->IsSecure()) {
981 0 : args->storage->SetSecure(aEntry->GetKey(), true);
982 : }
983 :
984 0 : return PL_DHASH_NEXT;
985 : }
986 :
987 : nsresult
988 0 : DOMStorageImpl::CloneFrom(bool aCallerSecure, DOMStorageBase* aThat)
989 : {
990 : // For various reasons, we no longer call SetItem in CopyStorageItems,
991 : // so we need to ensure that the storage permissions are correct.
992 0 : if (!CacheStoragePermissions())
993 0 : return NS_ERROR_DOM_SECURITY_ERR;
994 :
995 0 : DOMStorageImpl* that = static_cast<DOMStorageImpl*>(aThat);
996 0 : CopyArgs args = { this, aCallerSecure };
997 0 : that->mItems.EnumerateEntries(CopyStorageItems, &args);
998 0 : return NS_OK;
999 : }
1000 :
1001 : nsresult
1002 92 : DOMStorageImpl::CacheKeysFromDB()
1003 : {
1004 : // cache all the keys in the hash. This is used by the Length and Key methods
1005 : // use this cache for better performance. The disadvantage is that the
1006 : // order may break if someone changes the keys in the database directly.
1007 92 : if (gStorageDB->IsScopeDirty(this)) {
1008 38 : nsresult rv = InitDB();
1009 38 : NS_ENSURE_SUCCESS(rv, rv);
1010 :
1011 38 : mItems.Clear();
1012 :
1013 38 : rv = gStorageDB->GetAllKeys(this, &mItems);
1014 38 : NS_ENSURE_SUCCESS(rv, rv);
1015 :
1016 38 : gStorageDB->MarkScopeCached(this);
1017 : }
1018 :
1019 92 : return NS_OK;
1020 : }
1021 :
1022 : struct KeysArrayBuilderStruct
1023 : {
1024 : bool callerIsSecure;
1025 : nsTArray<nsString> *keys;
1026 : };
1027 :
1028 : static PLDHashOperator
1029 0 : KeysArrayBuilder(nsSessionStorageEntry* aEntry, void* userArg)
1030 : {
1031 0 : KeysArrayBuilderStruct *keystruct = (KeysArrayBuilderStruct *)userArg;
1032 :
1033 0 : if (keystruct->callerIsSecure || !aEntry->mItem->IsSecure())
1034 0 : keystruct->keys->AppendElement(aEntry->GetKey());
1035 :
1036 0 : return PL_DHASH_NEXT;
1037 : }
1038 :
1039 : nsTArray<nsString>*
1040 0 : DOMStorageImpl::GetKeys(bool aCallerSecure)
1041 : {
1042 0 : if (UseDB())
1043 0 : CacheKeysFromDB();
1044 :
1045 : KeysArrayBuilderStruct keystruct;
1046 0 : keystruct.callerIsSecure = aCallerSecure;
1047 0 : keystruct.keys = new nsTArray<nsString>();
1048 0 : if (keystruct.keys)
1049 0 : mItems.EnumerateEntries(KeysArrayBuilder, &keystruct);
1050 :
1051 0 : return keystruct.keys;
1052 : }
1053 :
1054 : class ItemCounterState
1055 : {
1056 : public:
1057 44 : ItemCounterState(bool aIsCallerSecure)
1058 44 : : mIsCallerSecure(aIsCallerSecure), mCount(0)
1059 : {
1060 44 : }
1061 :
1062 : bool mIsCallerSecure;
1063 : PRUint32 mCount;
1064 : private:
1065 : ItemCounterState(); // Not to be implemented
1066 : };
1067 :
1068 : static PLDHashOperator
1069 26 : ItemCounter(nsSessionStorageEntry* aEntry, void* userArg)
1070 : {
1071 26 : ItemCounterState *state = (ItemCounterState *)userArg;
1072 :
1073 26 : if (state->mIsCallerSecure || !aEntry->mItem->IsSecure()) {
1074 26 : ++state->mCount;
1075 : }
1076 :
1077 26 : return PL_DHASH_NEXT;
1078 : }
1079 :
1080 : nsresult
1081 44 : DOMStorageImpl::GetLength(bool aCallerSecure, PRUint32* aLength)
1082 : {
1083 : // Force reload of items from database. This ensures sync localStorages for
1084 : // same origins among different windows.
1085 44 : if (UseDB())
1086 44 : CacheKeysFromDB();
1087 :
1088 44 : ItemCounterState state(aCallerSecure);
1089 :
1090 44 : mItems.EnumerateEntries(ItemCounter, &state);
1091 :
1092 44 : *aLength = state.mCount;
1093 44 : return NS_OK;
1094 : }
1095 :
1096 : class IndexFinderData
1097 : {
1098 : public:
1099 24 : IndexFinderData(bool aIsCallerSecure, PRUint32 aWantedIndex)
1100 : : mIsCallerSecure(aIsCallerSecure), mIndex(0), mWantedIndex(aWantedIndex),
1101 24 : mItem(nsnull)
1102 : {
1103 24 : }
1104 :
1105 : bool mIsCallerSecure;
1106 : PRUint32 mIndex;
1107 : PRUint32 mWantedIndex;
1108 : nsSessionStorageEntry *mItem;
1109 :
1110 : private:
1111 : IndexFinderData(); // Not to be implemented
1112 : };
1113 :
1114 : static PLDHashOperator
1115 24 : IndexFinder(nsSessionStorageEntry* aEntry, void* userArg)
1116 : {
1117 24 : IndexFinderData *data = (IndexFinderData *)userArg;
1118 :
1119 48 : if (data->mIndex == data->mWantedIndex &&
1120 24 : (data->mIsCallerSecure || !aEntry->mItem->IsSecure())) {
1121 24 : data->mItem = aEntry;
1122 :
1123 24 : return PL_DHASH_STOP;
1124 : }
1125 :
1126 0 : ++data->mIndex;
1127 :
1128 0 : return PL_DHASH_NEXT;
1129 : }
1130 :
1131 : nsresult
1132 24 : DOMStorageImpl::GetKey(bool aCallerSecure, PRUint32 aIndex, nsAString& aKey)
1133 : {
1134 : // XXXjst: This is as retarded as the DOM spec is, takes an unsigned
1135 : // int, but the spec talks about what to do if a negative value is
1136 : // passed in.
1137 :
1138 : // XXX: This does a linear search for the key at index, which would
1139 : // suck if there's a large numer of indexes. Do we care? If so,
1140 : // maybe we need to have a lazily populated key array here or
1141 : // something?
1142 :
1143 24 : if (UseDB()) {
1144 24 : CacheKeysFromDB();
1145 : }
1146 :
1147 24 : IndexFinderData data(aCallerSecure, aIndex);
1148 24 : mItems.EnumerateEntries(IndexFinder, &data);
1149 :
1150 24 : if (!data.mItem) {
1151 : // aIndex was larger than the number of accessible keys. Throw.
1152 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1153 : }
1154 :
1155 24 : aKey = data.mItem->GetKey();
1156 24 : return NS_OK;
1157 : }
1158 :
1159 : // The behaviour of this function must be kept in sync with StorageChild::GetValue.
1160 : // See the explanatory comment there for more details.
1161 : nsIDOMStorageItem*
1162 44 : DOMStorageImpl::GetValue(bool aCallerSecure, const nsAString& aKey,
1163 : nsresult* aResult)
1164 : {
1165 44 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
1166 44 : nsIDOMStorageItem* item = nsnull;
1167 44 : if (entry) {
1168 30 : if (aCallerSecure || !entry->mItem->IsSecure()) {
1169 30 : item = entry->mItem;
1170 : }
1171 : }
1172 14 : else if (UseDB()) {
1173 : bool secure;
1174 28 : nsAutoString value;
1175 14 : nsresult rv = GetDBValue(aKey, value, &secure);
1176 : // return null if access isn't allowed or the key wasn't found
1177 14 : if (rv == NS_ERROR_DOM_SECURITY_ERR || rv == NS_ERROR_DOM_NOT_FOUND_ERR ||
1178 0 : (!aCallerSecure && secure))
1179 14 : return nsnull;
1180 :
1181 0 : *aResult = rv;
1182 0 : NS_ENSURE_SUCCESS(rv, nsnull);
1183 :
1184 : nsRefPtr<nsDOMStorageItem> newitem =
1185 14 : new nsDOMStorageItem(this, aKey, value, secure);
1186 0 : if (newitem && (entry = mItems.PutEntry(aKey))) {
1187 0 : item = entry->mItem = newitem;
1188 : }
1189 : else {
1190 0 : *aResult = NS_ERROR_OUT_OF_MEMORY;
1191 : }
1192 : }
1193 30 : return item;
1194 : }
1195 :
1196 : nsresult
1197 18 : DOMStorageImpl::SetValue(bool aIsCallerSecure, const nsAString& aKey,
1198 : const nsAString& aData, nsAString& aOldValue)
1199 : {
1200 18 : if (aKey.IsEmpty())
1201 0 : return NS_OK;
1202 :
1203 : nsresult rv;
1204 36 : nsString oldValue;
1205 18 : SetDOMStringToNull(oldValue);
1206 :
1207 : // First store the value to the database, we need to do this before we update
1208 : // the mItems cache. SetDBValue is using the old cached value to decide
1209 : // on quota checking.
1210 18 : if (UseDB()) {
1211 18 : rv = SetDBValue(aKey, aData, aIsCallerSecure);
1212 18 : NS_ENSURE_SUCCESS(rv, rv);
1213 : }
1214 :
1215 18 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
1216 18 : if (entry) {
1217 0 : if (entry->mItem->IsSecure() && !aIsCallerSecure) {
1218 0 : return NS_ERROR_DOM_SECURITY_ERR;
1219 : }
1220 0 : oldValue = entry->mItem->GetValueInternal();
1221 0 : entry->mItem->SetValueInternal(aData);
1222 : }
1223 : else {
1224 : nsRefPtr<nsDOMStorageItem> newitem =
1225 36 : new nsDOMStorageItem(this, aKey, aData, aIsCallerSecure);
1226 18 : if (!newitem)
1227 0 : return NS_ERROR_OUT_OF_MEMORY;
1228 18 : entry = mItems.PutEntry(aKey);
1229 18 : NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
1230 36 : entry->mItem = newitem;
1231 : }
1232 18 : aOldValue = oldValue;
1233 18 : return NS_OK;
1234 : }
1235 :
1236 : nsresult
1237 6 : DOMStorageImpl::RemoveValue(bool aCallerSecure, const nsAString& aKey,
1238 : nsAString& aOldValue)
1239 : {
1240 12 : nsString oldValue;
1241 6 : nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
1242 :
1243 6 : if (entry && entry->mItem->IsSecure() && !aCallerSecure) {
1244 0 : return NS_ERROR_DOM_SECURITY_ERR;
1245 : }
1246 :
1247 6 : if (UseDB()) {
1248 6 : nsresult rv = InitDB();
1249 6 : NS_ENSURE_SUCCESS(rv, rv);
1250 :
1251 12 : nsAutoString value;
1252 : bool secureItem;
1253 6 : rv = GetDBValue(aKey, value, &secureItem);
1254 6 : NS_ENSURE_SUCCESS(rv, rv);
1255 6 : if (!aCallerSecure && secureItem)
1256 0 : return NS_ERROR_DOM_SECURITY_ERR;
1257 :
1258 6 : oldValue = value;
1259 :
1260 6 : rv = gStorageDB->RemoveKey(this, aKey, !IsOfflineAllowed(mDomain),
1261 12 : aKey.Length() + value.Length());
1262 6 : NS_ENSURE_SUCCESS(rv, rv);
1263 : }
1264 0 : else if (entry) {
1265 : // clear string as StorageItems may be referencing this item
1266 0 : oldValue = entry->mItem->GetValueInternal();
1267 0 : entry->mItem->ClearValue();
1268 : }
1269 :
1270 6 : if (entry) {
1271 6 : mItems.RawRemoveEntry(entry);
1272 : }
1273 6 : aOldValue = oldValue;
1274 6 : return NS_OK;
1275 : }
1276 :
1277 : PR_STATIC_CALLBACK(PLDHashOperator)
1278 6 : CheckSecure(nsSessionStorageEntry* aEntry, void* userArg)
1279 : {
1280 6 : bool* secure = (bool*)userArg;
1281 6 : if (aEntry->mItem->IsSecure()) {
1282 0 : *secure = true;
1283 0 : return PL_DHASH_STOP;
1284 : }
1285 :
1286 6 : return PL_DHASH_NEXT;
1287 : }
1288 :
1289 : nsresult
1290 6 : DOMStorageImpl::Clear(bool aCallerSecure, PRInt32* aOldCount)
1291 : {
1292 6 : if (UseDB())
1293 6 : CacheKeysFromDB();
1294 :
1295 6 : PRInt32 oldCount = mItems.Count();
1296 :
1297 6 : bool foundSecureItem = false;
1298 6 : mItems.EnumerateEntries(CheckSecure, &foundSecureItem);
1299 :
1300 6 : if (foundSecureItem && !aCallerSecure) {
1301 0 : return NS_ERROR_DOM_SECURITY_ERR;
1302 : }
1303 :
1304 6 : if (UseDB()) {
1305 6 : nsresult rv = InitDB();
1306 6 : NS_ENSURE_SUCCESS(rv, rv);
1307 :
1308 6 : rv = gStorageDB->ClearStorage(this);
1309 6 : NS_ENSURE_SUCCESS(rv, rv);
1310 : }
1311 :
1312 6 : *aOldCount = oldCount;
1313 6 : mItems.Clear();
1314 6 : return NS_OK;
1315 : }
1316 :
1317 20 : nsDOMStorage::nsDOMStorage()
1318 : : mStorageType(nsPIDOMStorage::Unknown)
1319 20 : , mEventBroadcaster(nsnull)
1320 : {
1321 20 : if (XRE_GetProcessType() != GeckoProcessType_Default)
1322 0 : mStorageImpl = new StorageChild(this);
1323 : else
1324 20 : mStorageImpl = new DOMStorageImpl(this);
1325 20 : }
1326 :
1327 0 : nsDOMStorage::nsDOMStorage(nsDOMStorage& aThat)
1328 : : mStorageType(aThat.mStorageType)
1329 : , mPrincipal(aThat.mPrincipal)
1330 0 : , mEventBroadcaster(nsnull)
1331 : {
1332 0 : if (XRE_GetProcessType() != GeckoProcessType_Default) {
1333 0 : StorageChild* other = static_cast<StorageChild*>(aThat.mStorageImpl.get());
1334 0 : mStorageImpl = new StorageChild(this, *other);
1335 : } else {
1336 0 : DOMStorageImpl* other = static_cast<DOMStorageImpl*>(aThat.mStorageImpl.get());
1337 0 : mStorageImpl = new DOMStorageImpl(this, *other);
1338 : }
1339 0 : }
1340 :
1341 40 : nsDOMStorage::~nsDOMStorage()
1342 : {
1343 80 : }
1344 :
1345 : static
1346 : nsresult
1347 20 : GetDomainURI(nsIPrincipal *aPrincipal, bool aIncludeDomain, nsIURI **_domain)
1348 : {
1349 40 : nsCOMPtr<nsIURI> uri;
1350 :
1351 20 : if (aIncludeDomain) {
1352 0 : nsresult rv = aPrincipal->GetDomain(getter_AddRefs(uri));
1353 0 : NS_ENSURE_SUCCESS(rv, rv);
1354 : }
1355 :
1356 20 : if (!uri) {
1357 20 : nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
1358 20 : NS_ENSURE_SUCCESS(rv, rv);
1359 : }
1360 :
1361 : // Check if we really got any URI. System principal doesn't return a URI
1362 : // instance and we would crash in NS_GetInnermostURI below.
1363 20 : if (!uri)
1364 0 : return NS_ERROR_NOT_AVAILABLE;
1365 :
1366 40 : nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
1367 20 : if (!innerURI)
1368 0 : return NS_ERROR_UNEXPECTED;
1369 20 : innerURI.forget(_domain);
1370 :
1371 20 : return NS_OK;
1372 : }
1373 :
1374 : nsresult
1375 0 : nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
1376 : {
1377 0 : nsCOMPtr<nsIURI> domainURI;
1378 0 : nsresult rv = GetDomainURI(aPrincipal, true, getter_AddRefs(domainURI));
1379 0 : NS_ENSURE_SUCCESS(rv, rv);
1380 :
1381 0 : mDocumentURI = aDocumentURI;
1382 0 : mPrincipal = aPrincipal;
1383 :
1384 0 : mStorageType = SessionStorage;
1385 :
1386 0 : mStorageImpl->InitAsSessionStorage(domainURI);
1387 0 : return NS_OK;
1388 : }
1389 :
1390 : nsresult
1391 20 : nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
1392 : {
1393 40 : nsCOMPtr<nsIURI> domainURI;
1394 20 : nsresult rv = GetDomainURI(aPrincipal, false, getter_AddRefs(domainURI));
1395 20 : NS_ENSURE_SUCCESS(rv, rv);
1396 :
1397 20 : mDocumentURI = aDocumentURI;
1398 20 : mPrincipal = aPrincipal;
1399 :
1400 20 : mStorageType = LocalStorage;
1401 :
1402 20 : bool canUseChromePersist = false;
1403 40 : nsCOMPtr<nsIURI> URI;
1404 20 : if (NS_SUCCEEDED(aPrincipal->GetURI(getter_AddRefs(URI))) && URI) {
1405 20 : canUseChromePersist = URICanUseChromePersist(URI);
1406 : }
1407 :
1408 20 : mStorageImpl->InitAsLocalStorage(domainURI, canUseChromePersist);
1409 20 : return NS_OK;
1410 : }
1411 :
1412 : //static
1413 : bool
1414 172 : nsDOMStorage::CanUseStorage(bool* aSessionOnly)
1415 : {
1416 : // check if the calling domain can use storage. Downgrade to session
1417 : // only if only session storage may be used.
1418 172 : NS_ASSERTION(aSessionOnly, "null session flag");
1419 172 : *aSessionOnly = false;
1420 :
1421 172 : if (!Preferences::GetBool(kStorageEnabled)) {
1422 0 : return false;
1423 : }
1424 :
1425 : // chrome can always use storage regardless of permission preferences
1426 172 : if (nsContentUtils::IsCallerChrome())
1427 172 : return true;
1428 :
1429 0 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
1430 0 : nsresult rv = nsContentUtils::GetSecurityManager()->
1431 0 : GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1432 0 : NS_ENSURE_SUCCESS(rv, false);
1433 :
1434 : // if subjectPrincipal were null we'd have returned after
1435 : // IsCallerChrome().
1436 :
1437 0 : nsCOMPtr<nsIURI> subjectURI;
1438 0 : nsCAutoString unused;
1439 0 : if (NS_FAILED(GetPrincipalURIAndHost(subjectPrincipal,
1440 : getter_AddRefs(subjectURI),
1441 : unused))) {
1442 0 : return false;
1443 : }
1444 :
1445 : nsCOMPtr<nsIPermissionManager> permissionManager =
1446 0 : do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
1447 0 : if (!permissionManager)
1448 0 : return false;
1449 :
1450 : PRUint32 perm;
1451 0 : permissionManager->TestPermission(subjectURI, kPermissionType, &perm);
1452 :
1453 0 : if (perm == nsIPermissionManager::DENY_ACTION)
1454 0 : return false;
1455 :
1456 : // In private browsing mode we ougth to behave as in session-only cookies
1457 : // mode to prevent detection of being in private browsing mode and ensuring
1458 : // that there will be no traces left.
1459 0 : if (perm == nsICookiePermission::ACCESS_SESSION ||
1460 0 : nsDOMStorageManager::gStorageManager->InPrivateBrowsingMode()) {
1461 0 : *aSessionOnly = true;
1462 : }
1463 0 : else if (perm != nsIPermissionManager::ALLOW_ACTION) {
1464 0 : PRUint32 cookieBehavior = Preferences::GetUint(kCookiesBehavior);
1465 0 : PRUint32 lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy);
1466 :
1467 : // Treat "ask every time" as "reject always".
1468 : // Chrome persistent pages can bypass this check.
1469 0 : if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT) &&
1470 0 : !URICanUseChromePersist(subjectURI))
1471 0 : return false;
1472 :
1473 0 : if (lifetimePolicy == ACCEPT_SESSION)
1474 0 : *aSessionOnly = true;
1475 : }
1476 :
1477 0 : return true;
1478 : }
1479 :
1480 : bool
1481 172 : nsDOMStorage::CacheStoragePermissions()
1482 : {
1483 : // Bug 488446, disallowing storage use when in session only mode.
1484 : // This is temporary fix before we find complete solution for storage
1485 : // behavior in private browsing mode or session-only cookies mode.
1486 172 : if (!CanUseStorage(&mStorageImpl->mSessionOnly))
1487 0 : return false;
1488 :
1489 172 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1490 172 : if (!ssm)
1491 0 : return false;
1492 :
1493 344 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
1494 172 : nsresult rv = ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1495 172 : NS_ENSURE_SUCCESS(rv, false);
1496 :
1497 172 : return CanAccess(subjectPrincipal);
1498 : }
1499 :
1500 : // static
1501 : bool
1502 20 : nsDOMStorage::URICanUseChromePersist(nsIURI* aURI) {
1503 : bool isAbout;
1504 : return
1505 20 : (NS_SUCCEEDED(aURI->SchemeIs("moz-safe-about", &isAbout)) && isAbout) ||
1506 20 : (NS_SUCCEEDED(aURI->SchemeIs("about", &isAbout)) && isAbout);
1507 : }
1508 :
1509 : NS_IMETHODIMP
1510 44 : nsDOMStorage::GetLength(PRUint32 *aLength)
1511 : {
1512 44 : if (!CacheStoragePermissions())
1513 0 : return NS_ERROR_DOM_SECURITY_ERR;
1514 :
1515 44 : return mStorageImpl->GetLength(IsCallerSecure(), aLength);
1516 : }
1517 :
1518 : NS_IMETHODIMP
1519 24 : nsDOMStorage::Key(PRUint32 aIndex, nsAString& aKey)
1520 : {
1521 24 : if (!CacheStoragePermissions())
1522 0 : return NS_ERROR_DOM_SECURITY_ERR;
1523 :
1524 24 : return mStorageImpl->GetKey(IsCallerSecure(), aIndex, aKey);
1525 : }
1526 :
1527 : nsIDOMStorageItem*
1528 44 : nsDOMStorage::GetNamedItem(const nsAString& aKey, nsresult* aResult)
1529 : {
1530 44 : if (!CacheStoragePermissions()) {
1531 0 : *aResult = NS_ERROR_DOM_SECURITY_ERR;
1532 0 : return nsnull;
1533 : }
1534 :
1535 44 : *aResult = NS_OK;
1536 44 : if (aKey.IsEmpty())
1537 0 : return nsnull;
1538 :
1539 44 : return mStorageImpl->GetValue(IsCallerSecure(), aKey, aResult);
1540 : }
1541 :
1542 : nsresult
1543 44 : nsDOMStorage::GetItem(const nsAString& aKey, nsAString &aData)
1544 : {
1545 : nsresult rv;
1546 :
1547 : // IMPORTANT:
1548 : // CacheStoragePermissions() is called inside of
1549 : // GetItem(nsAString, nsIDOMStorageItem)
1550 : // To call it particularly in this method would just duplicate
1551 : // the call. If the code changes, make sure that call to
1552 : // CacheStoragePermissions() is put here!
1553 :
1554 88 : nsCOMPtr<nsIDOMStorageItem> item;
1555 44 : rv = GetItem(aKey, getter_AddRefs(item));
1556 44 : if (NS_FAILED(rv))
1557 0 : return rv;
1558 :
1559 44 : if (item) {
1560 30 : rv = item->GetValue(aData);
1561 30 : NS_ENSURE_SUCCESS(rv, rv);
1562 : }
1563 : else
1564 14 : SetDOMStringToNull(aData);
1565 :
1566 44 : return NS_OK;
1567 : }
1568 :
1569 : static Telemetry::ID
1570 18 : TelemetryIDForKey(nsPIDOMStorage::nsDOMStorageType type)
1571 : {
1572 18 : switch (type) {
1573 : default:
1574 0 : MOZ_ASSERT(false);
1575 : // We need to return something to satisfy the compiler.
1576 : // Fallthrough.
1577 : case nsPIDOMStorage::LocalStorage:
1578 18 : return Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES;
1579 : case nsPIDOMStorage::SessionStorage:
1580 0 : return Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES;
1581 : }
1582 : }
1583 :
1584 : static Telemetry::ID
1585 18 : TelemetryIDForValue(nsPIDOMStorage::nsDOMStorageType type)
1586 : {
1587 18 : switch (type) {
1588 : default:
1589 0 : MOZ_ASSERT(false);
1590 : // We need to return something to satisfy the compiler.
1591 : // Fallthrough.
1592 : case nsPIDOMStorage::LocalStorage:
1593 18 : return Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES;
1594 : case nsPIDOMStorage::SessionStorage:
1595 0 : return Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES;
1596 : }
1597 : }
1598 :
1599 : NS_IMETHODIMP
1600 44 : nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem)
1601 : {
1602 : nsresult rv;
1603 :
1604 44 : NS_IF_ADDREF(*aItem = GetNamedItem(aKey, &rv));
1605 :
1606 44 : return rv;
1607 : }
1608 :
1609 : NS_IMETHODIMP
1610 18 : nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
1611 : {
1612 18 : if (!CacheStoragePermissions())
1613 0 : return NS_ERROR_DOM_SECURITY_ERR;
1614 :
1615 18 : Telemetry::Accumulate(TelemetryIDForKey(mStorageType), aKey.Length());
1616 18 : Telemetry::Accumulate(TelemetryIDForValue(mStorageType), aData.Length());
1617 :
1618 36 : nsString oldValue;
1619 18 : nsresult rv = mStorageImpl->SetValue(IsCallerSecure(), aKey, aData, oldValue);
1620 18 : if (NS_FAILED(rv))
1621 0 : return rv;
1622 :
1623 18 : if (oldValue != aData && mEventBroadcaster)
1624 18 : mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, aData);
1625 :
1626 18 : return NS_OK;
1627 : }
1628 :
1629 6 : NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
1630 : {
1631 6 : if (!CacheStoragePermissions())
1632 0 : return NS_ERROR_DOM_SECURITY_ERR;
1633 :
1634 6 : if (aKey.IsEmpty())
1635 0 : return NS_OK;
1636 :
1637 12 : nsString oldValue;
1638 6 : nsresult rv = mStorageImpl->RemoveValue(IsCallerSecure(), aKey, oldValue);
1639 6 : if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
1640 0 : return NS_OK;
1641 6 : if (NS_FAILED(rv))
1642 0 : return rv;
1643 :
1644 6 : if (!oldValue.IsEmpty() && mEventBroadcaster) {
1645 12 : nsAutoString nullString;
1646 6 : SetDOMStringToNull(nullString);
1647 6 : mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, nullString);
1648 : }
1649 :
1650 6 : return NS_OK;
1651 : }
1652 :
1653 : nsresult
1654 6 : nsDOMStorage::Clear()
1655 : {
1656 6 : if (!CacheStoragePermissions())
1657 0 : return NS_ERROR_DOM_SECURITY_ERR;
1658 :
1659 : PRInt32 oldCount;
1660 6 : nsresult rv = mStorageImpl->Clear(IsCallerSecure(), &oldCount);
1661 6 : if (NS_FAILED(rv))
1662 0 : return rv;
1663 :
1664 6 : if (oldCount && mEventBroadcaster) {
1665 12 : nsAutoString nullString;
1666 6 : SetDOMStringToNull(nullString);
1667 6 : mEventBroadcaster->BroadcastChangeNotification(nullString, nullString, nullString);
1668 : }
1669 :
1670 6 : return NS_OK;
1671 : }
1672 :
1673 : already_AddRefed<nsIDOMStorage>
1674 0 : nsDOMStorage::Clone()
1675 : {
1676 0 : NS_ASSERTION(false, "Old DOMStorage doesn't implement cloning");
1677 0 : return nsnull;
1678 : }
1679 :
1680 : already_AddRefed<nsIDOMStorage>
1681 0 : nsDOMStorage::Fork(const nsSubstring &aDocumentURI)
1682 : {
1683 0 : NS_ASSERTION(false, "Old DOMStorage doesn't implement forking");
1684 0 : return nsnull;
1685 : }
1686 :
1687 0 : bool nsDOMStorage::IsForkOf(nsIDOMStorage* aThat)
1688 : {
1689 0 : NS_ASSERTION(false, "Old DOMStorage doesn't implement forking");
1690 0 : return false;
1691 : }
1692 :
1693 : nsresult
1694 0 : nsDOMStorage::CloneFrom(nsDOMStorage* aThat)
1695 : {
1696 0 : return mStorageImpl->CloneFrom(IsCallerSecure(), aThat->mStorageImpl);
1697 : }
1698 :
1699 : nsTArray<nsString> *
1700 0 : nsDOMStorage::GetKeys()
1701 : {
1702 0 : return mStorageImpl->GetKeys(IsCallerSecure());
1703 : }
1704 :
1705 : nsIPrincipal*
1706 0 : nsDOMStorage::Principal()
1707 : {
1708 0 : return nsnull;
1709 : }
1710 :
1711 : bool
1712 0 : nsDOMStorage::CanAccessSystem(nsIPrincipal *aPrincipal)
1713 : {
1714 0 : if (!aPrincipal)
1715 0 : return true;
1716 :
1717 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1718 0 : if (!ssm)
1719 0 : return false;
1720 :
1721 : bool isSystem;
1722 0 : nsresult rv = ssm->IsSystemPrincipal(aPrincipal, &isSystem);
1723 :
1724 0 : return NS_SUCCEEDED(rv) && isSystem;
1725 : }
1726 :
1727 : bool
1728 172 : nsDOMStorage::CanAccess(nsIPrincipal *aPrincipal)
1729 : {
1730 : // Allow C++ callers to access the storage
1731 172 : if (!aPrincipal)
1732 0 : return true;
1733 :
1734 : // Allow more powerful principals (e.g. system) to access the storage
1735 : bool subsumes;
1736 172 : nsresult rv = aPrincipal->SubsumesIgnoringDomain(mPrincipal, &subsumes);
1737 172 : if (NS_FAILED(rv))
1738 0 : return false;
1739 :
1740 172 : return subsumes;
1741 : }
1742 :
1743 : nsPIDOMStorage::nsDOMStorageType
1744 0 : nsDOMStorage::StorageType()
1745 : {
1746 0 : return mStorageType;
1747 : }
1748 :
1749 : void
1750 0 : nsDOMStorage::BroadcastChangeNotification(const nsSubstring &aKey,
1751 : const nsSubstring &aOldValue,
1752 : const nsSubstring &aNewValue)
1753 : {
1754 : nsCOMPtr<nsIObserverService> observerService =
1755 0 : mozilla::services::GetObserverService();
1756 0 : if (!observerService) {
1757 : return;
1758 : }
1759 :
1760 : // Fire off a notification that a storage object changed. If the
1761 : // storage object is a session storage object, we don't pass a
1762 : // domain, but if it's a global storage object we do.
1763 0 : observerService->NotifyObservers((nsIDOMStorageObsolete *)this,
1764 : "dom-storage-changed",
1765 0 : NS_ConvertUTF8toUTF16(mStorageImpl->mDomain).get());
1766 : }
1767 :
1768 : //
1769 : // nsDOMStorage2
1770 : //
1771 :
1772 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorage2)
1773 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStorage2)
1774 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStorage)
1775 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1776 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStorage2)
1777 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mStorage, nsIDOMStorageObsolete)
1778 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1779 :
1780 : DOMCI_DATA(Storage, nsDOMStorage2)
1781 :
1782 262 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStorage2)
1783 282 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStorage2)
1784 596 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage2)
1785 434 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
1786 414 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
1787 222 : NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
1788 222 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
1789 202 : NS_INTERFACE_MAP_END
1790 :
1791 20 : nsDOMStorage2::nsDOMStorage2()
1792 : {
1793 20 : }
1794 :
1795 0 : nsDOMStorage2::nsDOMStorage2(nsDOMStorage2& aThat)
1796 : {
1797 0 : mStorage = new nsDOMStorage(*aThat.mStorage.get());
1798 0 : mPrincipal = aThat.mPrincipal;
1799 0 : }
1800 :
1801 : nsresult
1802 0 : nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
1803 : {
1804 0 : mStorage = new nsDOMStorage();
1805 0 : if (!mStorage)
1806 0 : return NS_ERROR_OUT_OF_MEMORY;
1807 :
1808 0 : mPrincipal = aPrincipal;
1809 0 : mDocumentURI = aDocumentURI;
1810 :
1811 0 : return mStorage->InitAsSessionStorage(aPrincipal, aDocumentURI);
1812 : }
1813 :
1814 : nsresult
1815 20 : nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
1816 : {
1817 20 : mStorage = new nsDOMStorage();
1818 20 : if (!mStorage)
1819 0 : return NS_ERROR_OUT_OF_MEMORY;
1820 :
1821 20 : mPrincipal = aPrincipal;
1822 20 : mDocumentURI = aDocumentURI;
1823 :
1824 20 : return mStorage->InitAsLocalStorage(aPrincipal, aDocumentURI);
1825 : }
1826 :
1827 : already_AddRefed<nsIDOMStorage>
1828 0 : nsDOMStorage2::Clone()
1829 : {
1830 0 : nsDOMStorage2* storage = new nsDOMStorage2(*this);
1831 0 : if (!storage)
1832 0 : return nsnull;
1833 :
1834 0 : storage->mStorage->CloneFrom(mStorage);
1835 0 : NS_ADDREF(storage);
1836 :
1837 0 : return storage;
1838 : }
1839 :
1840 : already_AddRefed<nsIDOMStorage>
1841 0 : nsDOMStorage2::Fork(const nsSubstring &aDocumentURI)
1842 : {
1843 0 : nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
1844 0 : if (!storage)
1845 0 : return nsnull;
1846 :
1847 0 : nsresult rv = storage->InitAsSessionStorageFork(mPrincipal, aDocumentURI, mStorage);
1848 0 : if (NS_FAILED(rv))
1849 0 : return nsnull;
1850 :
1851 0 : nsIDOMStorage* result = static_cast<nsIDOMStorage*>(storage.get());
1852 0 : storage.forget();
1853 0 : return result;
1854 : }
1855 :
1856 0 : bool nsDOMStorage2::IsForkOf(nsIDOMStorage* aThat)
1857 : {
1858 0 : if (!aThat)
1859 0 : return false;
1860 :
1861 0 : nsDOMStorage2* storage = static_cast<nsDOMStorage2*>(aThat);
1862 0 : return mStorage == storage->mStorage;
1863 : }
1864 :
1865 : nsresult
1866 0 : nsDOMStorage2::InitAsSessionStorageFork(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI, nsIDOMStorageObsolete* aStorage)
1867 : {
1868 0 : mPrincipal = aPrincipal;
1869 0 : mDocumentURI = aDocumentURI;
1870 0 : mStorage = static_cast<nsDOMStorage*>(aStorage);
1871 :
1872 0 : return NS_OK;
1873 : }
1874 :
1875 : nsTArray<nsString> *
1876 0 : nsDOMStorage2::GetKeys()
1877 : {
1878 0 : return mStorage->GetKeys();
1879 : }
1880 :
1881 : nsIPrincipal*
1882 0 : nsDOMStorage2::Principal()
1883 : {
1884 0 : return mPrincipal;
1885 : }
1886 :
1887 : bool
1888 0 : nsDOMStorage2::CanAccess(nsIPrincipal *aPrincipal)
1889 : {
1890 0 : return mStorage->CanAccess(aPrincipal);
1891 : }
1892 :
1893 : nsPIDOMStorage::nsDOMStorageType
1894 0 : nsDOMStorage2::StorageType()
1895 : {
1896 0 : if (mStorage)
1897 0 : return mStorage->StorageType();
1898 :
1899 0 : return nsPIDOMStorage::Unknown;
1900 : }
1901 :
1902 : namespace {
1903 :
1904 : class StorageNotifierRunnable : public nsRunnable
1905 120 : {
1906 : public:
1907 30 : StorageNotifierRunnable(nsISupports* aSubject)
1908 30 : : mSubject(aSubject)
1909 30 : { }
1910 :
1911 : NS_DECL_NSIRUNNABLE
1912 :
1913 : private:
1914 : nsCOMPtr<nsISupports> mSubject;
1915 : };
1916 :
1917 : NS_IMETHODIMP
1918 30 : StorageNotifierRunnable::Run()
1919 : {
1920 : nsCOMPtr<nsIObserverService> observerService =
1921 60 : mozilla::services::GetObserverService();
1922 30 : if (observerService) {
1923 30 : observerService->NotifyObservers(mSubject, "dom-storage2-changed", nsnull);
1924 : }
1925 30 : return NS_OK;
1926 : }
1927 :
1928 : } // anonymous namespace
1929 :
1930 : void
1931 30 : nsDOMStorage2::BroadcastChangeNotification(const nsSubstring &aKey,
1932 : const nsSubstring &aOldValue,
1933 : const nsSubstring &aNewValue)
1934 : {
1935 : nsresult rv;
1936 60 : nsCOMPtr<nsIDOMStorageEvent> event = new nsDOMStorageEvent();
1937 60 : rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
1938 : false,
1939 : false,
1940 : aKey,
1941 : aOldValue,
1942 : aNewValue,
1943 : mDocumentURI,
1944 30 : static_cast<nsIDOMStorage*>(this));
1945 30 : if (NS_FAILED(rv)) {
1946 : return;
1947 : }
1948 :
1949 90 : nsRefPtr<StorageNotifierRunnable> r = new StorageNotifierRunnable(event);
1950 30 : NS_DispatchToMainThread(r);
1951 : }
1952 :
1953 : NS_IMETHODIMP
1954 44 : nsDOMStorage2::GetLength(PRUint32 *aLength)
1955 : {
1956 44 : return mStorage->GetLength(aLength);
1957 : }
1958 :
1959 : NS_IMETHODIMP
1960 24 : nsDOMStorage2::Key(PRUint32 aIndex, nsAString& aKey)
1961 : {
1962 24 : return mStorage->Key(aIndex, aKey);
1963 : }
1964 :
1965 : NS_IMETHODIMP
1966 44 : nsDOMStorage2::GetItem(const nsAString& aKey, nsAString &aData)
1967 : {
1968 44 : return mStorage->GetItem(aKey, aData);
1969 : }
1970 :
1971 : NS_IMETHODIMP
1972 18 : nsDOMStorage2::SetItem(const nsAString& aKey, const nsAString& aData)
1973 : {
1974 18 : mStorage->mEventBroadcaster = this;
1975 18 : return mStorage->SetItem(aKey, aData);
1976 : }
1977 :
1978 : NS_IMETHODIMP
1979 6 : nsDOMStorage2::RemoveItem(const nsAString& aKey)
1980 : {
1981 6 : mStorage->mEventBroadcaster = this;
1982 6 : return mStorage->RemoveItem(aKey);
1983 : }
1984 :
1985 : NS_IMETHODIMP
1986 6 : nsDOMStorage2::Clear()
1987 : {
1988 6 : mStorage->mEventBroadcaster = this;
1989 6 : return mStorage->Clear();
1990 : }
1991 :
1992 : //
1993 : // nsDOMStorageList
1994 : //
1995 :
1996 : DOMCI_DATA(StorageList, nsDOMStorageList)
1997 :
1998 0 : NS_INTERFACE_MAP_BEGIN(nsDOMStorageList)
1999 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
2000 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageList)
2001 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageList)
2002 0 : NS_INTERFACE_MAP_END
2003 :
2004 0 : NS_IMPL_ADDREF(nsDOMStorageList)
2005 0 : NS_IMPL_RELEASE(nsDOMStorageList)
2006 :
2007 : nsIDOMStorageObsolete*
2008 0 : nsDOMStorageList::GetNamedItem(const nsAString& aDomain, nsresult* aResult)
2009 : {
2010 0 : nsCAutoString requestedDomain;
2011 :
2012 : // Normalize the requested domain
2013 0 : nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
2014 0 : if (idn) {
2015 0 : *aResult = idn->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aDomain),
2016 0 : requestedDomain);
2017 0 : NS_ENSURE_SUCCESS(*aResult, nsnull);
2018 : } else {
2019 : // Don't have the IDN service, best we can do is URL escape.
2020 0 : NS_EscapeURL(NS_ConvertUTF16toUTF8(aDomain),
2021 : esc_OnlyNonASCII | esc_AlwaysCopy,
2022 0 : requestedDomain);
2023 : }
2024 0 : ToLowerCase(requestedDomain);
2025 :
2026 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
2027 0 : if (!ssm) {
2028 0 : *aResult = NS_ERROR_FAILURE;
2029 0 : return nsnull;
2030 : }
2031 :
2032 0 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
2033 0 : *aResult = ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
2034 0 : NS_ENSURE_SUCCESS(*aResult, nsnull);
2035 :
2036 0 : nsCAutoString currentDomain;
2037 0 : if (subjectPrincipal) {
2038 0 : nsCOMPtr<nsIURI> unused;
2039 0 : *aResult = GetPrincipalURIAndHost(subjectPrincipal, getter_AddRefs(unused),
2040 0 : currentDomain);
2041 0 : NS_ENSURE_SUCCESS(*aResult, nsnull);
2042 :
2043 : bool sessionOnly;
2044 0 : if (!nsDOMStorage::CanUseStorage(&sessionOnly)) {
2045 0 : *aResult = NS_ERROR_DOM_SECURITY_ERR;
2046 0 : return nsnull;
2047 : }
2048 : }
2049 :
2050 0 : bool isSystem = nsContentUtils::IsCallerTrustedForRead();
2051 0 : if (currentDomain.IsEmpty() && !isSystem) {
2052 0 : *aResult = NS_ERROR_DOM_SECURITY_ERR;
2053 0 : return nsnull;
2054 : }
2055 :
2056 : return GetStorageForDomain(requestedDomain,
2057 0 : currentDomain, isSystem, aResult);
2058 : }
2059 :
2060 : NS_IMETHODIMP
2061 0 : nsDOMStorageList::NamedItem(const nsAString& aDomain,
2062 : nsIDOMStorageObsolete** aStorage)
2063 : {
2064 : nsresult rv;
2065 0 : NS_IF_ADDREF(*aStorage = GetNamedItem(aDomain, &rv));
2066 0 : return rv;
2067 : }
2068 :
2069 : // static
2070 : bool
2071 0 : nsDOMStorageList::CanAccessDomain(const nsACString& aRequestedDomain,
2072 : const nsACString& aCurrentDomain)
2073 : {
2074 0 : return aRequestedDomain.Equals(aCurrentDomain);
2075 : }
2076 :
2077 : nsIDOMStorageObsolete*
2078 0 : nsDOMStorageList::GetStorageForDomain(const nsACString& aRequestedDomain,
2079 : const nsACString& aCurrentDomain,
2080 : bool aNoCurrentDomainCheck,
2081 : nsresult* aResult)
2082 : {
2083 0 : nsTArray<nsCString> requestedDomainArray;
2084 0 : if ((!aNoCurrentDomainCheck &&
2085 0 : !CanAccessDomain(aRequestedDomain, aCurrentDomain)) ||
2086 0 : !ConvertDomainToArray(aRequestedDomain, &requestedDomainArray)) {
2087 0 : *aResult = NS_ERROR_DOM_SECURITY_ERR;
2088 :
2089 0 : return nsnull;
2090 : }
2091 :
2092 : // now rebuild a string for the domain.
2093 0 : nsCAutoString usedDomain;
2094 0 : PRUint32 requestedPos = 0;
2095 0 : for (requestedPos = 0; requestedPos < requestedDomainArray.Length();
2096 : requestedPos++) {
2097 0 : if (!usedDomain.IsEmpty())
2098 0 : usedDomain.Append('.');
2099 0 : usedDomain.Append(requestedDomainArray[requestedPos]);
2100 : }
2101 :
2102 0 : *aResult = NS_OK;
2103 :
2104 : // now have a valid domain, so look it up in the storage table
2105 0 : nsIDOMStorageObsolete* storage = mStorages.GetWeak(usedDomain);
2106 0 : if (!storage) {
2107 0 : nsRefPtr<nsDOMStorage> newstorage;
2108 0 : newstorage = new nsDOMStorage();
2109 0 : if (newstorage && mStorages.Put(usedDomain, newstorage)) {
2110 0 : storage = newstorage;
2111 : }
2112 : else {
2113 0 : *aResult = NS_ERROR_OUT_OF_MEMORY;
2114 : }
2115 : }
2116 :
2117 0 : return storage;
2118 : }
2119 :
2120 : // static
2121 : bool
2122 0 : nsDOMStorageList::ConvertDomainToArray(const nsACString& aDomain,
2123 : nsTArray<nsCString> *aArray)
2124 : {
2125 0 : PRInt32 length = aDomain.Length();
2126 0 : PRInt32 n = 0;
2127 0 : while (n < length) {
2128 0 : PRInt32 dotpos = aDomain.FindChar('.', n);
2129 0 : nsCAutoString domain;
2130 :
2131 0 : if (dotpos == -1) // no more dots
2132 0 : domain.Assign(Substring(aDomain, n));
2133 0 : else if (dotpos - n == 0) // no point continuing in this case
2134 0 : return false;
2135 0 : else if (dotpos >= 0)
2136 0 : domain.Assign(Substring(aDomain, n, dotpos - n));
2137 :
2138 0 : ToLowerCase(domain);
2139 0 : aArray->AppendElement(domain);
2140 :
2141 0 : if (dotpos == -1)
2142 : break;
2143 :
2144 0 : n = dotpos + 1;
2145 : }
2146 :
2147 : // if n equals the length, there is a dot at the end, so treat it as invalid
2148 0 : return (n != length);
2149 : }
2150 :
2151 : nsresult
2152 0 : NS_NewDOMStorageList(nsIDOMStorageList** aResult)
2153 : {
2154 0 : *aResult = new nsDOMStorageList();
2155 0 : if (!*aResult)
2156 0 : return NS_ERROR_OUT_OF_MEMORY;
2157 :
2158 0 : NS_ADDREF(*aResult);
2159 0 : return NS_OK;
2160 : }
2161 :
2162 : //
2163 : // nsDOMStorageItem
2164 : //
2165 :
2166 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorageItem)
2167 4 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStorageItem)
2168 : {
2169 4 : tmp->mStorage = nsnull;
2170 : }
2171 4 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2172 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStorageItem)
2173 : {
2174 4 : cb.NoteXPCOMChild((nsISupports*) tmp->mStorage);
2175 : }
2176 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2177 :
2178 116 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStorageItem)
2179 116 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStorageItem)
2180 :
2181 : DOMCI_DATA(StorageItem, nsDOMStorageItem)
2182 :
2183 102 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorageItem)
2184 30 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorageItem)
2185 30 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageItem)
2186 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMToString)
2187 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageItem)
2188 0 : NS_INTERFACE_MAP_END
2189 :
2190 34 : nsDOMStorageItem::nsDOMStorageItem(DOMStorageBase* aStorage,
2191 : const nsAString& aKey,
2192 : const nsAString& aValue,
2193 : bool aSecure)
2194 : : mSecure(aSecure),
2195 : mKey(aKey),
2196 : mValue(aValue),
2197 34 : mStorage(aStorage)
2198 : {
2199 34 : }
2200 :
2201 68 : nsDOMStorageItem::~nsDOMStorageItem()
2202 : {
2203 136 : }
2204 :
2205 : NS_IMETHODIMP
2206 0 : nsDOMStorageItem::GetSecure(bool* aSecure)
2207 : {
2208 0 : if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) {
2209 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2210 : }
2211 :
2212 0 : if (mStorage->UseDB()) {
2213 0 : nsAutoString value;
2214 0 : return mStorage->GetDBValue(mKey, value, aSecure);
2215 : }
2216 :
2217 0 : *aSecure = IsSecure();
2218 0 : return NS_OK;
2219 : }
2220 :
2221 : NS_IMETHODIMP
2222 0 : nsDOMStorageItem::SetSecure(bool aSecure)
2223 : {
2224 0 : if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) {
2225 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2226 : }
2227 :
2228 0 : if (mStorage->UseDB()) {
2229 0 : nsresult rv = mStorage->SetSecure(mKey, aSecure);
2230 0 : NS_ENSURE_SUCCESS(rv, rv);
2231 : }
2232 :
2233 0 : mSecure = aSecure;
2234 0 : return NS_OK;
2235 : }
2236 :
2237 : NS_IMETHODIMP
2238 30 : nsDOMStorageItem::GetValue(nsAString& aValue)
2239 : {
2240 30 : if (!mStorage->CacheStoragePermissions())
2241 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2242 :
2243 30 : if (mStorage->UseDB()) {
2244 : bool secure;
2245 30 : nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure);
2246 30 : if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
2247 4 : return NS_OK;
2248 26 : if (NS_SUCCEEDED(rv) && !IsCallerSecure() && secure)
2249 0 : return NS_ERROR_DOM_SECURITY_ERR;
2250 26 : return rv;
2251 : }
2252 :
2253 0 : if (IsSecure() && !IsCallerSecure()) {
2254 0 : return NS_ERROR_DOM_SECURITY_ERR;
2255 : }
2256 :
2257 0 : aValue = mValue;
2258 0 : return NS_OK;
2259 : }
2260 :
2261 : NS_IMETHODIMP
2262 0 : nsDOMStorageItem::SetValue(const nsAString& aValue)
2263 : {
2264 0 : if (!mStorage->CacheStoragePermissions())
2265 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2266 :
2267 0 : bool secureCaller = IsCallerSecure();
2268 :
2269 0 : if (mStorage->UseDB()) {
2270 : // SetDBValue() does the security checks for us.
2271 0 : return mStorage->SetDBValue(mKey, aValue, secureCaller);
2272 : }
2273 :
2274 0 : bool secureItem = IsSecure();
2275 :
2276 0 : if (!secureCaller && secureItem) {
2277 : // The item is secure, but the caller isn't. Throw.
2278 0 : return NS_ERROR_DOM_SECURITY_ERR;
2279 : }
2280 :
2281 0 : mValue = aValue;
2282 0 : mSecure = secureCaller;
2283 0 : return NS_OK;
2284 : }
2285 :
2286 : NS_IMETHODIMP
2287 0 : nsDOMStorageItem::ToString(nsAString& aStr)
2288 : {
2289 0 : return GetValue(aStr);
2290 : }
2291 :
2292 : // Cycle collection implementation for nsDOMStorageEvent
2293 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorageEvent)
2294 :
2295 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMStorageEvent, nsDOMEvent)
2296 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStorageArea)
2297 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2298 :
2299 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMStorageEvent, nsDOMEvent)
2300 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStorageArea)
2301 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2302 :
2303 90 : NS_IMPL_ADDREF_INHERITED(nsDOMStorageEvent, nsDOMEvent)
2304 90 : NS_IMPL_RELEASE_INHERITED(nsDOMStorageEvent, nsDOMEvent)
2305 :
2306 : DOMCI_DATA(StorageEvent, nsDOMStorageEvent)
2307 :
2308 : // QueryInterface implementation for nsDOMStorageEvent
2309 90 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMStorageEvent)
2310 30 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEvent)
2311 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEvent)
2312 0 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
2313 :
2314 :
2315 : /* readonly attribute DOMString key; */
2316 0 : NS_IMETHODIMP nsDOMStorageEvent::GetKey(nsAString & aKey)
2317 : {
2318 0 : aKey = mKey;
2319 0 : return NS_OK;
2320 : }
2321 :
2322 : /* readonly attribute DOMString oldValue; */
2323 0 : NS_IMETHODIMP nsDOMStorageEvent::GetOldValue(nsAString & aOldValue)
2324 : {
2325 0 : aOldValue = mOldValue;
2326 0 : return NS_OK;
2327 : }
2328 :
2329 : /* readonly attribute DOMString newValue; */
2330 0 : NS_IMETHODIMP nsDOMStorageEvent::GetNewValue(nsAString & aNewValue)
2331 : {
2332 0 : aNewValue = mNewValue;
2333 0 : return NS_OK;
2334 : }
2335 :
2336 : /* readonly attribute DOMString url; */
2337 0 : NS_IMETHODIMP nsDOMStorageEvent::GetUrl(nsAString & aUrl)
2338 : {
2339 0 : aUrl = mUrl;
2340 0 : return NS_OK;
2341 : }
2342 :
2343 : /* readonly attribute nsIDOMStorage storageArea; */
2344 0 : NS_IMETHODIMP nsDOMStorageEvent::GetStorageArea(nsIDOMStorage * *aStorageArea)
2345 : {
2346 0 : NS_ENSURE_ARG_POINTER(aStorageArea);
2347 :
2348 0 : NS_IF_ADDREF(*aStorageArea = mStorageArea);
2349 0 : return NS_OK;
2350 : }
2351 :
2352 : /* 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); */
2353 30 : NS_IMETHODIMP nsDOMStorageEvent::InitStorageEvent(const nsAString & typeArg,
2354 : bool canBubbleArg,
2355 : bool cancelableArg,
2356 : const nsAString & keyArg,
2357 : const nsAString & oldValueArg,
2358 : const nsAString & newValueArg,
2359 : const nsAString & urlArg,
2360 : nsIDOMStorage *storageAreaArg)
2361 : {
2362 : nsresult rv;
2363 :
2364 30 : rv = InitEvent(typeArg, canBubbleArg, cancelableArg);
2365 30 : NS_ENSURE_SUCCESS(rv, rv);
2366 :
2367 30 : mKey = keyArg;
2368 30 : mOldValue = oldValueArg;
2369 30 : mNewValue = newValueArg;
2370 30 : mUrl = urlArg;
2371 30 : mStorageArea = storageAreaArg;
2372 :
2373 30 : return NS_OK;
2374 : }
2375 :
2376 : nsresult
2377 0 : nsDOMStorageEvent::InitFromCtor(const nsAString& aType,
2378 : JSContext* aCx, jsval* aVal)
2379 : {
2380 0 : mozilla::dom::StorageEventInit d;
2381 0 : nsresult rv = d.Init(aCx, aVal);
2382 0 : NS_ENSURE_SUCCESS(rv, rv);
2383 : return InitStorageEvent(aType, d.bubbles, d.cancelable, d.key, d.oldValue,
2384 0 : d.newValue, d.url, d.storageArea);
2385 : }
2386 :
2387 : // Obsolete globalStorage event
2388 :
2389 : DOMCI_DATA(StorageEventObsolete, nsDOMStorageEventObsolete)
2390 :
2391 : // QueryInterface implementation for nsDOMStorageEventObsolete
2392 0 : NS_INTERFACE_MAP_BEGIN(nsDOMStorageEventObsolete)
2393 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEventObsolete)
2394 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEventObsolete)
2395 0 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
2396 :
2397 0 : NS_IMPL_ADDREF_INHERITED(nsDOMStorageEventObsolete, nsDOMEvent)
2398 0 : NS_IMPL_RELEASE_INHERITED(nsDOMStorageEventObsolete, nsDOMEvent)
2399 :
2400 :
2401 : NS_IMETHODIMP
2402 0 : nsDOMStorageEventObsolete::GetDomain(nsAString& aDomain)
2403 : {
2404 : // mDomain will be #session for session storage for events that fire
2405 : // due to a change in a session storage object.
2406 0 : aDomain = mDomain;
2407 :
2408 0 : return NS_OK;
2409 : }
2410 :
2411 : NS_IMETHODIMP
2412 0 : nsDOMStorageEventObsolete::InitStorageEvent(const nsAString& aTypeArg,
2413 : bool aCanBubbleArg,
2414 : bool aCancelableArg,
2415 : const nsAString& aDomainArg)
2416 : {
2417 0 : nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
2418 0 : NS_ENSURE_SUCCESS(rv, rv);
2419 :
2420 0 : mDomain = aDomainArg;
2421 :
2422 0 : return NS_OK;
2423 4392 : }
|