Back to index

lightning-sunbird  0.9+nobinonly
nsDOMStorage.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Mozilla Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2006
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Neil Deakin <enndeakin@sympatico.ca>
00024  *   Johnny Stenback <jst@mozilla.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "prnetdb.h"
00041 #include "nsCOMPtr.h"
00042 #include "nsDOMError.h"
00043 #include "nsDOMClassInfo.h"
00044 #include "nsUnicharUtils.h"
00045 #include "nsIDocument.h"
00046 #include "nsDOMStorage.h"
00047 #include "nsContentUtils.h"
00048 #include "nsIScriptSecurityManager.h"
00049 #include "nsIPrincipal.h"
00050 #include "nsIURI.h"
00051 #include "nsReadableUtils.h"
00052 #include "nsIObserverService.h"
00053 #include "nsIJARURI.h"
00054 #include "nsIPrefBranch.h"
00055 #include "nsICookiePermission.h"
00056 #include "nsIPermissionManager.h"
00057 
00058 static const PRUint32 ASK_BEFORE_ACCEPT = 1;
00059 static const PRUint32 ACCEPT_SESSION = 2;
00060 static const PRUint32 BEHAVIOR_REJECT = 2;
00061 
00062 static const PRUint32 DEFAULT_QUOTA = 5 * 1024;
00063 
00064 static const char kPermissionType[] = "cookie";
00065 static const char kStorageEnabled[] = "dom.storage.enabled";
00066 static const char kDefaultQuota[] = "dom.storage.default_quota";
00067 static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
00068 static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
00069 
00070 //
00071 // Helper that tells us whether the caller is secure or not.
00072 //
00073 
00074 static PRBool
00075 IsCallerSecure()
00076 {
00077   nsCOMPtr<nsIPrincipal> subjectPrincipal;
00078   nsContentUtils::GetSecurityManager()->
00079     GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
00080 
00081   if (!subjectPrincipal) {
00082     // No subject principal means no code is running. Default to not
00083     // being secure in that case.
00084 
00085     return PR_FALSE;
00086   }
00087 
00088   nsCOMPtr<nsIURI> codebase;
00089   subjectPrincipal->GetURI(getter_AddRefs(codebase));
00090 
00091   if (!codebase) {
00092     return PR_FALSE;
00093   }
00094 
00095   nsCOMPtr<nsIJARURI> jarURI;
00096   nsCOMPtr<nsIURI> innerUri(codebase);
00097   while((jarURI = do_QueryInterface(innerUri))) {
00098     jarURI->GetJARFile(getter_AddRefs(innerUri));
00099   }
00100 
00101   if (!innerUri) {
00102     return PR_FALSE;
00103   }
00104 
00105   PRBool isHttps = PR_FALSE;
00106   nsresult rv = innerUri->SchemeIs("https", &isHttps);
00107 
00108   return NS_SUCCEEDED(rv) && isHttps;
00109 }
00110 
00111 static PRInt32
00112 GetQuota(const nsAString &domain)
00113 {
00114   // FIXME: per-domain quotas?
00115   return ((PRInt32)nsContentUtils::GetIntPref(kDefaultQuota, DEFAULT_QUOTA) * 1024);
00116 }
00117 
00118 nsSessionStorageEntry::nsSessionStorageEntry(KeyTypePointer aStr)
00119   : nsStringHashKey(aStr), mItem(nsnull)
00120 {
00121 }
00122 
00123 nsSessionStorageEntry::nsSessionStorageEntry(const nsSessionStorageEntry& aToCopy)
00124   : nsStringHashKey(aToCopy), mItem(nsnull)
00125 {
00126   NS_ERROR("We're horked.");
00127 }
00128 
00129 nsSessionStorageEntry::~nsSessionStorageEntry()
00130 {
00131 }
00132 
00133 //
00134 // nsDOMStorageManager
00135 //
00136 
00137 nsDOMStorageManager* nsDOMStorageManager::gStorageManager;
00138 
00139 NS_IMPL_ISUPPORTS1(nsDOMStorageManager, nsIObserver)
00140 
00141 //static
00142 nsresult
00143 nsDOMStorageManager::Initialize()
00144 {
00145   gStorageManager = new nsDOMStorageManager();
00146   if (!gStorageManager)
00147     return NS_ERROR_OUT_OF_MEMORY;
00148 
00149   if (!gStorageManager->mStorages.Init()) {
00150     delete gStorageManager;
00151     gStorageManager = nsnull;
00152     return NS_ERROR_OUT_OF_MEMORY;
00153   }
00154 
00155   NS_ADDREF(gStorageManager);
00156 
00157   nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
00158   if (os)
00159     os->AddObserver(gStorageManager, "cookie-changed", PR_FALSE);
00160 
00161   return NS_OK;
00162 }
00163 
00164 //static
00165 void
00166 nsDOMStorageManager::Shutdown()
00167 {
00168   NS_IF_RELEASE(gStorageManager);
00169   gStorageManager = nsnull;
00170 }
00171 
00172 PR_STATIC_CALLBACK(PLDHashOperator)
00173 ClearStorage(nsDOMStorageEntry* aEntry, void* userArg)
00174 {
00175   aEntry->mStorage->ClearAll();
00176   return PL_DHASH_REMOVE;
00177 }
00178 
00179 nsresult
00180 nsDOMStorageManager::Observe(nsISupports *aSubject,
00181                              const char *aTopic,
00182                              const PRUnichar *aData)
00183 {
00184   if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("cleared").get())) {
00185     mStorages.EnumerateEntries(ClearStorage, nsnull);
00186 
00187 #ifdef MOZ_STORAGE
00188     nsresult rv = nsDOMStorage::InitDB();
00189     NS_ENSURE_SUCCESS(rv, rv);
00190     return nsDOMStorage::gStorageDB->RemoveAll();
00191 #endif
00192   }
00193 
00194   return NS_OK;
00195 }
00196 
00197 void
00198 nsDOMStorageManager::AddToStoragesHash(nsDOMStorage* aStorage)
00199 {
00200   nsDOMStorageEntry* entry = mStorages.PutEntry(aStorage);
00201   if (entry)
00202     entry->mStorage = aStorage;
00203 }
00204 
00205 void
00206 nsDOMStorageManager::RemoveFromStoragesHash(nsDOMStorage* aStorage)
00207 {
00208  nsDOMStorageEntry* entry = mStorages.GetEntry(aStorage);
00209   if (entry)
00210     mStorages.RemoveEntry(aStorage);
00211 }
00212 
00213 //
00214 // nsDOMStorage
00215 //
00216 
00217 #ifdef MOZ_STORAGE
00218 nsDOMStorageDB* nsDOMStorage::gStorageDB = nsnull;
00219 #endif
00220 
00221 nsDOMStorageEntry::nsDOMStorageEntry(KeyTypePointer aStr)
00222   : nsVoidPtrHashKey(aStr), mStorage(nsnull)
00223 {
00224 }
00225 
00226 nsDOMStorageEntry::nsDOMStorageEntry(const nsDOMStorageEntry& aToCopy)
00227   : nsVoidPtrHashKey(aToCopy), mStorage(nsnull)
00228 {
00229   NS_ERROR("DOMStorage horked.");
00230 }
00231 
00232 nsDOMStorageEntry::~nsDOMStorageEntry()
00233 {
00234 }
00235 
00236 NS_INTERFACE_MAP_BEGIN(nsDOMStorage)
00237   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
00238   NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
00239   NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
00240   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
00241 NS_INTERFACE_MAP_END
00242 
00243 NS_IMPL_ADDREF(nsDOMStorage)
00244 NS_IMPL_RELEASE(nsDOMStorage)
00245 
00246 NS_IMETHODIMP
00247 NS_NewDOMStorage(nsISupports* aOuter, REFNSIID aIID, void** aResult)
00248 {
00249   nsDOMStorage* storage = new nsDOMStorage();
00250   if (!storage)
00251     return NS_ERROR_OUT_OF_MEMORY;
00252 
00253   NS_ADDREF(storage);
00254   *aResult = storage;
00255 
00256   return NS_OK;
00257 }
00258 
00259 nsDOMStorage::nsDOMStorage()
00260   : mUseDB(PR_FALSE), mSessionOnly(PR_TRUE), mItemsCached(PR_FALSE)
00261 {
00262   mItems.Init(8);
00263   if (nsDOMStorageManager::gStorageManager)
00264     nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
00265 }
00266 
00267 nsDOMStorage::nsDOMStorage(nsIURI* aURI, const nsAString& aDomain, PRBool aUseDB)
00268   : mUseDB(aUseDB),
00269     mSessionOnly(PR_TRUE),
00270     mItemsCached(PR_FALSE),
00271     mURI(aURI),
00272     mDomain(aDomain)
00273 {
00274 #ifndef MOZ_STORAGE
00275   mUseDB = PR_FALSE;
00276 #endif
00277 
00278   mItems.Init(8);
00279   if (nsDOMStorageManager::gStorageManager)
00280     nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
00281 }
00282 
00283 nsDOMStorage::~nsDOMStorage()
00284 {
00285   if (nsDOMStorageManager::gStorageManager)
00286     nsDOMStorageManager::gStorageManager->RemoveFromStoragesHash(this);
00287 }
00288 
00289 void
00290 nsDOMStorage::Init(nsIURI* aURI, const nsAString& aDomain, PRBool aUseDB)
00291 {
00292   mURI = aURI;
00293   mDomain.Assign(aDomain);
00294 #ifdef MOZ_STORAGE
00295   mUseDB = aUseDB;
00296 #else
00297   mUseDB = PR_FALSE;
00298 #endif
00299 }
00300 
00301 //static
00302 PRBool
00303 nsDOMStorage::CanUseStorage(nsIURI* aURI, PRPackedBool* aSessionOnly)
00304 {
00305   // check if the domain can use storage. Downgrade to session only if only
00306   // session storage may be used.
00307   NS_ASSERTION(aURI && aSessionOnly, "null URI or session flag");
00308 
00309   if (!nsContentUtils::GetBoolPref(kStorageEnabled))
00310     return PR_FALSE;
00311 
00312   nsCOMPtr<nsIPermissionManager> permissionManager =
00313     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
00314   if (!permissionManager)
00315     return PR_FALSE;
00316 
00317   *aSessionOnly = PR_FALSE;
00318 
00319   PRUint32 perm;
00320   permissionManager->TestPermission(aURI, kPermissionType, &perm);
00321 
00322   if (perm == nsIPermissionManager::DENY_ACTION)
00323     return PR_FALSE;
00324 
00325   if (perm == nsICookiePermission::ACCESS_SESSION) {
00326     *aSessionOnly = PR_TRUE;
00327   }
00328   else if (perm != nsIPermissionManager::ALLOW_ACTION) {
00329     PRUint32 cookieBehavior = nsContentUtils::GetIntPref(kCookiesBehavior);
00330     PRUint32 lifetimePolicy = nsContentUtils::GetIntPref(kCookiesLifetimePolicy);
00331 
00332     // treat ask as reject always
00333     if (cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT)
00334       return PR_FALSE;
00335 
00336     if (lifetimePolicy == ACCEPT_SESSION)
00337       *aSessionOnly = PR_TRUE;
00338   }
00339 
00340   return PR_TRUE;
00341 }
00342 
00343 class ItemCounterState
00344 {
00345 public:
00346   ItemCounterState(PRBool aIsCallerSecure)
00347     : mIsCallerSecure(aIsCallerSecure), mCount(0)
00348   {
00349   }
00350 
00351   PRBool mIsCallerSecure;
00352   PRBool mCount;
00353 private:
00354   ItemCounterState(); // Not to be implemented
00355 };
00356 
00357 PR_STATIC_CALLBACK(PLDHashOperator)
00358 ItemCounter(nsSessionStorageEntry* aEntry, void* userArg)
00359 {
00360   ItemCounterState *state = (ItemCounterState *)userArg;
00361 
00362   if (state->mIsCallerSecure || !aEntry->mItem->IsSecure()) {
00363     ++state->mCount;
00364   }
00365 
00366   return PL_DHASH_NEXT;
00367 }
00368 
00369 NS_IMETHODIMP
00370 nsDOMStorage::GetLength(PRUint32 *aLength)
00371 {
00372   if (!CacheStoragePermissions())
00373     return NS_ERROR_DOM_SECURITY_ERR;
00374 
00375   if (UseDB())
00376     CacheKeysFromDB();
00377 
00378   ItemCounterState state(IsCallerSecure());
00379 
00380   mItems.EnumerateEntries(ItemCounter, &state);
00381 
00382   *aLength = state.mCount;
00383 
00384   return NS_OK;
00385 }
00386 
00387 class IndexFinderData
00388 {
00389 public:
00390   IndexFinderData(PRBool aIsCallerSecure, PRUint32 aWantedIndex)
00391     : mIsCallerSecure(aIsCallerSecure), mIndex(0), mWantedIndex(aWantedIndex),
00392       mItem(nsnull)
00393   {
00394   }
00395 
00396   PRBool mIsCallerSecure;
00397   PRUint32 mIndex;
00398   PRUint32 mWantedIndex;
00399   nsSessionStorageEntry *mItem;
00400 
00401 private:
00402   IndexFinderData(); // Not to be implemented
00403 };
00404 
00405 PR_STATIC_CALLBACK(PLDHashOperator)
00406 IndexFinder(nsSessionStorageEntry* aEntry, void* userArg)
00407 {
00408   IndexFinderData *data = (IndexFinderData *)userArg;
00409 
00410   if (data->mIndex == data->mWantedIndex &&
00411       (data->mIsCallerSecure || !aEntry->mItem->IsSecure())) {
00412     data->mItem = aEntry;
00413 
00414     return PL_DHASH_STOP;
00415   }
00416 
00417   ++data->mIndex;
00418 
00419   return PL_DHASH_NEXT;
00420 }
00421 
00422 NS_IMETHODIMP
00423 nsDOMStorage::Key(PRUint32 aIndex, nsAString& aKey)
00424 {
00425   // XXXjst: This is as retarded as the DOM spec is, takes an unsigned
00426   // int, but the spec talks about what to do if a negative value is
00427   // passed in.
00428 
00429   // XXX: This does a linear search for the key at index, which would
00430   // suck if there's a large numer of indexes. Do we care? If so,
00431   // maybe we need to have a lazily populated key array here or
00432   // something?
00433 
00434   if (!CacheStoragePermissions())
00435     return NS_ERROR_DOM_SECURITY_ERR;
00436 
00437   if (UseDB())
00438     CacheKeysFromDB();
00439 
00440   IndexFinderData data(IsCallerSecure(), aIndex);
00441   mItems.EnumerateEntries(IndexFinder, &data);
00442 
00443   if (!data.mItem) {
00444     // aIndex was larger than the number of accessible keys. Throw.
00445     return NS_ERROR_DOM_INDEX_SIZE_ERR;
00446   }
00447 
00448   aKey = data.mItem->GetKey();
00449 
00450   return NS_OK;
00451 }
00452 
00453 NS_IMETHODIMP
00454 nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem)
00455 {
00456   *aItem = nsnull;
00457 
00458   if (!CacheStoragePermissions())
00459     return NS_ERROR_DOM_SECURITY_ERR;
00460 
00461   if (aKey.IsEmpty())
00462     return NS_OK;
00463 
00464   nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
00465  
00466   if (entry) {
00467     if (!IsCallerSecure() && entry->mItem->IsSecure()) {
00468       return NS_OK;
00469     }
00470     NS_ADDREF(*aItem = entry->mItem);
00471   }
00472   else if (UseDB()) {
00473     PRBool secure;
00474     nsAutoString value;
00475     nsAutoString unused;
00476     nsresult rv = GetDBValue(aKey, value, &secure, unused);
00477     // return null if access isn't allowed or the key wasn't found
00478     if (rv == NS_ERROR_DOM_SECURITY_ERR || rv == NS_ERROR_DOM_NOT_FOUND_ERR)
00479       return NS_OK;
00480     NS_ENSURE_SUCCESS(rv, rv);
00481 
00482     nsRefPtr<nsDOMStorageItem> newitem =
00483       new nsDOMStorageItem(this, aKey, value, secure);
00484     if (!newitem)
00485       return NS_ERROR_OUT_OF_MEMORY;
00486 
00487     entry = mItems.PutEntry(aKey);
00488     NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
00489 
00490     entry->mItem = newitem;
00491     NS_ADDREF(*aItem = newitem);
00492   }
00493 
00494   return NS_OK;
00495 }
00496 
00497 NS_IMETHODIMP
00498 nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
00499 {
00500   if (!CacheStoragePermissions())
00501     return NS_ERROR_DOM_SECURITY_ERR;
00502 
00503   if (aKey.IsEmpty())
00504     return NS_OK;
00505 
00506   nsresult rv;
00507   nsRefPtr<nsDOMStorageItem> newitem = nsnull;
00508   nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
00509   if (entry) {
00510     if (entry->mItem->IsSecure() && !IsCallerSecure()) {
00511       return NS_ERROR_DOM_SECURITY_ERR;
00512     }
00513     if (!UseDB()) {
00514       entry->mItem->SetValueInternal(aData);
00515     }
00516   }
00517   else {
00518     if (UseDB())
00519       newitem = new nsDOMStorageItem(this, aKey, aData, PR_FALSE);
00520     else 
00521       newitem = new nsDOMStorageItem(this, aKey, aData, PR_FALSE);
00522     if (!newitem)
00523       return NS_ERROR_OUT_OF_MEMORY;
00524   }
00525 
00526   if (UseDB()) {
00527     rv = SetDBValue(aKey, aData, IsCallerSecure());
00528     NS_ENSURE_SUCCESS(rv, rv);
00529   }
00530 
00531   if (newitem) {
00532     entry = mItems.PutEntry(aKey);
00533     NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
00534     entry->mItem = newitem;
00535   }
00536 
00537   // SetDBValue already calls BroadcastChangeNotification so don't do it again
00538   if (!UseDB())
00539     BroadcastChangeNotification();
00540 
00541   return NS_OK;
00542 }
00543 
00544 NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
00545 {
00546   if (!CacheStoragePermissions())
00547     return NS_ERROR_DOM_SECURITY_ERR;
00548 
00549   if (aKey.IsEmpty())
00550     return NS_OK;
00551 
00552   nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
00553 
00554   if (entry && entry->mItem->IsSecure() && !IsCallerSecure()) {
00555     return NS_ERROR_DOM_SECURITY_ERR;
00556   }
00557 
00558   if (UseDB()) {
00559 #ifdef MOZ_STORAGE
00560     nsresult rv = InitDB();
00561     NS_ENSURE_SUCCESS(rv, rv);
00562 
00563     nsAutoString value;
00564     PRBool secureItem;
00565     nsAutoString owner;
00566     rv = GetDBValue(aKey, value, &secureItem, owner);
00567     if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
00568       return NS_OK;
00569     NS_ENSURE_SUCCESS(rv, rv);
00570 
00571     rv = gStorageDB->RemoveKey(mDomain, aKey, owner,
00572                                aKey.Length() + value.Length());
00573     NS_ENSURE_SUCCESS(rv, rv);
00574 
00575     mItemsCached = PR_FALSE;
00576 
00577     BroadcastChangeNotification();
00578 #endif
00579   }
00580   else if (entry) {
00581     // clear string as StorageItems may be referencing this item
00582     entry->mItem->ClearValue();
00583 
00584     BroadcastChangeNotification();
00585   }
00586 
00587   if (entry) {
00588     mItems.RawRemoveEntry(entry);
00589   }
00590 
00591   return NS_OK;
00592 }
00593 
00594 nsresult
00595 nsDOMStorage::InitDB()
00596 {
00597 #ifdef MOZ_STORAGE
00598   if (!gStorageDB) {
00599     gStorageDB = new nsDOMStorageDB();
00600     if (!gStorageDB)
00601       return NS_ERROR_OUT_OF_MEMORY;
00602 
00603     nsresult rv = gStorageDB->Init();
00604     if (NS_FAILED(rv)) {
00605       // Failed to initialize the DB, delete it and null out the
00606       // pointer so we don't end up attempting to use an
00607       // un-initialized DB later on.
00608 
00609       delete gStorageDB;
00610       gStorageDB = nsnull;
00611 
00612       return rv;
00613     }
00614   }
00615 #endif
00616 
00617   return NS_OK;
00618 }
00619 
00620 nsresult
00621 nsDOMStorage::CacheKeysFromDB()
00622 {
00623 #ifdef MOZ_STORAGE
00624   // cache all the keys in the hash. This is used by the Length and Key methods
00625   // use this cache for better performance. The disadvantage is that the
00626   // order may break if someone changes the keys in the database directly.
00627   if (!mItemsCached) {
00628     nsresult rv = InitDB();
00629     NS_ENSURE_SUCCESS(rv, rv);
00630 
00631     rv = gStorageDB->GetAllKeys(mDomain, this, &mItems);
00632     NS_ENSURE_SUCCESS(rv, rv);
00633 
00634     mItemsCached = PR_TRUE;
00635   }
00636 #endif
00637 
00638   return NS_OK;
00639 }
00640 
00641 nsresult
00642 nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue,
00643                          PRBool* aSecure, nsAString& aOwner)
00644 {
00645   aValue.Truncate();
00646 
00647 #ifdef MOZ_STORAGE
00648   if (!UseDB())
00649     return NS_OK;
00650 
00651   nsresult rv = InitDB();
00652   NS_ENSURE_SUCCESS(rv, rv);
00653 
00654   nsAutoString value;
00655   rv = gStorageDB->GetKeyValue(mDomain, aKey, value, aSecure, aOwner);
00656   if (NS_FAILED(rv))
00657     return rv;
00658 
00659   if (!IsCallerSecure() && *aSecure) {
00660     return NS_ERROR_DOM_SECURITY_ERR;
00661   }
00662 
00663   aValue.Assign(value);
00664 #endif
00665 
00666   return NS_OK;
00667 }
00668 
00669 nsresult
00670 nsDOMStorage::SetDBValue(const nsAString& aKey,
00671                          const nsAString& aValue,
00672                          PRBool aSecure)
00673 {
00674 #ifdef MOZ_STORAGE
00675   if (!UseDB())
00676     return NS_OK;
00677 
00678   nsresult rv = InitDB();
00679   NS_ENSURE_SUCCESS(rv, rv);
00680 
00681   // Get the current domain for quota enforcement
00682   nsCOMPtr<nsIPrincipal> subjectPrincipal;
00683   nsContentUtils::GetSecurityManager()->
00684     GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
00685 
00686   nsAutoString currentDomain;
00687 
00688   if (subjectPrincipal) {
00689     nsCOMPtr<nsIURI> uri;
00690     rv = subjectPrincipal->GetURI(getter_AddRefs(uri));
00691 
00692     if (NS_SUCCEEDED(rv) && uri) {
00693       nsCAutoString currentDomainAscii;
00694       uri->GetAsciiHost(currentDomainAscii);
00695       currentDomain = NS_ConvertUTF8toUTF16(currentDomainAscii);
00696     }
00697     
00698     if (currentDomain.IsEmpty()) {
00699       return NS_ERROR_DOM_SECURITY_ERR;
00700     }
00701   } else {
00702     currentDomain = mDomain;
00703   }
00704   
00705   rv = gStorageDB->SetKey(mDomain, aKey, aValue, aSecure,
00706                           currentDomain, GetQuota(currentDomain));
00707   NS_ENSURE_SUCCESS(rv, rv);
00708 
00709   mItemsCached = PR_FALSE;
00710 
00711   BroadcastChangeNotification();
00712 #endif
00713 
00714   return NS_OK;
00715 }
00716 
00717 nsresult
00718 nsDOMStorage::SetSecure(const nsAString& aKey, PRBool aSecure)
00719 {
00720 #ifdef MOZ_STORAGE
00721   if (UseDB()) {
00722     nsresult rv = InitDB();
00723     NS_ENSURE_SUCCESS(rv, rv);
00724 
00725     return gStorageDB->SetSecure(mDomain, aKey, aSecure);
00726   }
00727 #else
00728   return NS_ERROR_NOT_IMPLEMENTED;
00729 #endif
00730 
00731   nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
00732   NS_ASSERTION(entry, "Don't use SetSecure() with non-existing keys!");
00733 
00734   if (entry) {
00735     entry->mItem->SetSecureInternal(aSecure);
00736   }  
00737 
00738   return NS_OK;
00739 }
00740 
00741 PR_STATIC_CALLBACK(PLDHashOperator)
00742 ClearStorageItem(nsSessionStorageEntry* aEntry, void* userArg)
00743 {
00744   aEntry->mItem->SetValueInternal(EmptyString());
00745   return PL_DHASH_NEXT;
00746 }
00747 
00748 void
00749 nsDOMStorage::ClearAll()
00750 {
00751   mItems.EnumerateEntries(ClearStorageItem, nsnull);
00752 }
00753 
00754 PR_STATIC_CALLBACK(PLDHashOperator)
00755 CopyStorageItems(nsSessionStorageEntry* aEntry, void* userArg)
00756 {
00757   nsDOMStorage* newstorage = NS_STATIC_CAST(nsDOMStorage*, userArg);
00758 
00759   newstorage->SetItem(aEntry->GetKey(), aEntry->mItem->GetValueInternal());
00760 
00761   if (aEntry->mItem->IsSecure()) {
00762     newstorage->SetSecure(aEntry->GetKey(), PR_TRUE);
00763   }
00764 
00765   return PL_DHASH_NEXT;
00766 }
00767 
00768 already_AddRefed<nsIDOMStorage>
00769 nsDOMStorage::Clone(nsIURI* aURI)
00770 {
00771   if (UseDB()) {
00772     NS_ERROR("Uh, don't clone a global storage object.");
00773 
00774     return nsnull;
00775   }
00776 
00777   nsDOMStorage* storage = new nsDOMStorage(aURI, mDomain, PR_FALSE);
00778   if (!storage)
00779     return nsnull;
00780 
00781   mItems.EnumerateEntries(CopyStorageItems, storage);
00782 
00783   NS_ADDREF(storage);
00784 
00785   return storage;
00786 }
00787 
00788 struct KeysArrayBuilderStruct
00789 {
00790   PRBool callerIsSecure;
00791   nsTArray<nsString> *keys;
00792 };
00793 
00794 PR_STATIC_CALLBACK(PLDHashOperator)
00795 KeysArrayBuilder(nsSessionStorageEntry* aEntry, void* userArg)
00796 {
00797   KeysArrayBuilderStruct *keystruct = (KeysArrayBuilderStruct *)userArg;
00798   
00799   if (keystruct->callerIsSecure || !aEntry->mItem->IsSecure())
00800     keystruct->keys->AppendElement(aEntry->GetKey());
00801 
00802   return PL_DHASH_NEXT;
00803 }
00804 
00805 nsTArray<nsString> *
00806 nsDOMStorage::GetKeys()
00807 {
00808   if (UseDB())
00809     CacheKeysFromDB();
00810 
00811   KeysArrayBuilderStruct keystruct;
00812   keystruct.callerIsSecure = IsCallerSecure();
00813   keystruct.keys = new nsTArray<nsString>();
00814   if (keystruct.keys)
00815     mItems.EnumerateEntries(KeysArrayBuilder, &keystruct);
00816  
00817   return keystruct.keys;
00818 }
00819 
00820 void
00821 nsDOMStorage::BroadcastChangeNotification()
00822 {
00823   nsresult rv;
00824   nsCOMPtr<nsIObserverService> observerService =
00825     do_GetService("@mozilla.org/observer-service;1", &rv);
00826   if (NS_FAILED(rv)) {
00827     return;
00828   }
00829 
00830   // Fire off a notification that a storage object changed. If the
00831   // storage object is a session storage object, we don't pass a
00832   // domain, but if it's a global storage object we do.
00833   observerService->NotifyObservers((nsIDOMStorage *)this,
00834                                    "dom-storage-changed",
00835                                    UseDB() ? mDomain.get() : nsnull);
00836 }
00837 
00838 //
00839 // nsDOMStorageList
00840 //
00841 
00842 NS_INTERFACE_MAP_BEGIN(nsDOMStorageList)
00843   NS_INTERFACE_MAP_ENTRY(nsISupports)
00844   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageList)
00845   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageList)
00846 NS_INTERFACE_MAP_END
00847 
00848 NS_IMPL_ADDREF(nsDOMStorageList)
00849 NS_IMPL_RELEASE(nsDOMStorageList)
00850 
00851 nsresult
00852 nsDOMStorageList::NamedItem(const nsAString& aDomain,
00853                             nsIDOMStorage** aStorage)
00854 {
00855   *aStorage = nsnull;
00856 
00857   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
00858   if (!ssm)
00859     return NS_ERROR_FAILURE;
00860 
00861   nsCOMPtr<nsIPrincipal> subjectPrincipal;
00862   nsresult rv = ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
00863   NS_ENSURE_SUCCESS(rv, rv);
00864 
00865   nsCOMPtr<nsIURI> uri;
00866   nsCAutoString currentDomain;
00867   if (subjectPrincipal) {
00868     rv = subjectPrincipal->GetURI(getter_AddRefs(uri));
00869     if (NS_SUCCEEDED(rv) && uri) {
00870       PRPackedBool sessionOnly;
00871       if (!nsDOMStorage::CanUseStorage(uri, &sessionOnly))
00872         return NS_ERROR_DOM_SECURITY_ERR;
00873 
00874       rv = uri->GetAsciiHost(currentDomain);
00875       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
00876     }
00877   }
00878 
00879   PRBool isSystem;
00880   rv = ssm->SubjectPrincipalIsSystem(&isSystem);
00881   NS_ENSURE_SUCCESS(rv, rv);
00882 
00883   if (isSystem || !currentDomain.IsEmpty()) {
00884     return GetStorageForDomain(uri, aDomain, NS_ConvertUTF8toUTF16(currentDomain),
00885                                isSystem, aStorage);
00886   }
00887 
00888   return NS_ERROR_DOM_SECURITY_ERR;
00889 }
00890 
00891 // static
00892 PRBool
00893 nsDOMStorageList::CanAccessDomain(const nsAString& aRequestedDomain,
00894                                   const nsAString& aCurrentDomain)
00895 {
00896   PRNetAddr address;
00897   PRStatus status = PR_StringToNetAddr(NS_ConvertUTF16toUTF8(aCurrentDomain).get(), &address);
00898 
00899   if (status == PR_SUCCESS) {
00900     // An IP address must match exactly. IPv6: when location is e.g. "::1" and we require
00901     // "0:0:0:0:0:1" then access will be denied. 
00902     return aRequestedDomain == aCurrentDomain;
00903   }
00904 
00905   nsStringArray requestedDomainArray, currentDomainArray;
00906   PRBool ok = ConvertDomainToArray(aRequestedDomain, &requestedDomainArray);
00907   if (!ok)
00908     return PR_FALSE;
00909 
00910   ok = ConvertDomainToArray(aCurrentDomain, &currentDomainArray);
00911   if (!ok)
00912     return PR_FALSE;
00913 
00914   if (currentDomainArray.Count() == 1)
00915     currentDomainArray.AppendString(NS_LITERAL_STRING("localdomain"));
00916 
00917   // need to use the shorter of the two arrays
00918   PRInt32 currentPos = 0;
00919   PRInt32 requestedPos = 0;
00920   PRInt32 length = requestedDomainArray.Count();
00921   if (currentDomainArray.Count() > length)
00922     currentPos = currentDomainArray.Count() - length;
00923   else if (currentDomainArray.Count() < length)
00924     requestedPos = length - currentDomainArray.Count();
00925 
00926   // If the current domain is different in any of the parts from the
00927   // requested domain, a security exception is raised
00928   for (; requestedPos < length; requestedPos++, currentPos++) {
00929     if (*requestedDomainArray[requestedPos] != *currentDomainArray[currentPos])
00930       return PR_FALSE;
00931   }
00932 
00933   return PR_TRUE;
00934 }
00935 
00936 nsresult
00937 nsDOMStorageList::GetStorageForDomain(nsIURI* aURI,
00938                                       const nsAString& aRequestedDomain,
00939                                       const nsAString& aCurrentDomain,
00940                                       PRBool aNoCurrentDomainCheck,
00941                                       nsIDOMStorage** aStorage)
00942 {
00943   // fail if the domain contains no periods.
00944   // XXXndeakin update this when bug 342314 is fixed so that we can check
00945   // for top-level domain names properly
00946   nsAutoString trimmedDomain(aRequestedDomain);
00947   trimmedDomain.Trim(".");
00948   if (trimmedDomain.FindChar('.') == kNotFound)
00949     return NS_ERROR_DOM_SECURITY_ERR;
00950 
00951   if (!aNoCurrentDomainCheck && !CanAccessDomain(aRequestedDomain,
00952                                                  aCurrentDomain)) {
00953     return NS_ERROR_DOM_SECURITY_ERR;
00954   }
00955 
00956   nsStringArray requestedDomainArray;
00957   PRBool ok = ConvertDomainToArray(aRequestedDomain, &requestedDomainArray);
00958   if (!ok)
00959     return NS_ERROR_DOM_SECURITY_ERR;
00960   
00961   // now rebuild a string for the domain.
00962   nsAutoString usedDomain;
00963   PRInt32 requestedPos = 0;
00964   for (requestedPos = 0; requestedPos < requestedDomainArray.Count();
00965        requestedPos++) {
00966     if (!usedDomain.IsEmpty())
00967       usedDomain.AppendLiteral(".");
00968     usedDomain.Append(*requestedDomainArray[requestedPos]);
00969   }
00970 
00971   // now have a valid domain, so look it up in the storage table
00972   if (!mStorages.Get(usedDomain, aStorage)) {
00973     nsCOMPtr<nsIDOMStorage> newstorage = new nsDOMStorage(aURI, usedDomain, PR_TRUE);
00974     if (!newstorage)
00975       return NS_ERROR_OUT_OF_MEMORY;
00976 
00977     if (!mStorages.Put(usedDomain, newstorage))
00978       return NS_ERROR_OUT_OF_MEMORY;
00979 
00980     newstorage.swap(*aStorage);
00981   }
00982 
00983   return NS_OK;
00984 }
00985 
00986 // static
00987 PRBool
00988 nsDOMStorageList::ConvertDomainToArray(const nsAString& aDomain,
00989                                        nsStringArray* aArray)
00990 {
00991   PRInt32 length = aDomain.Length();
00992   PRInt32 n = 0;
00993   while (n < length) {
00994     PRInt32 dotpos = aDomain.FindChar('.', n);
00995     nsAutoString domain;
00996 
00997     if (dotpos == -1) // no more dots
00998       domain.Assign(Substring(aDomain, n));
00999     else if (dotpos - n == 0) // no point continuing in this case
01000       return false;
01001     else if (dotpos >= 0)
01002       domain.Assign(Substring(aDomain, n, dotpos - n));
01003 
01004     ToLowerCase(domain);
01005     aArray->AppendString(domain);
01006 
01007     if (dotpos == -1)
01008       break;
01009 
01010     n = dotpos + 1;
01011   }
01012 
01013   // if n equals the length, there is a dot at the end, so treat it as invalid
01014   return (n != length);
01015 }
01016 
01017 nsresult
01018 NS_NewDOMStorageList(nsIDOMStorageList** aResult)
01019 {
01020   *aResult = new nsDOMStorageList();
01021   if (!*aResult)
01022     return NS_ERROR_OUT_OF_MEMORY;
01023 
01024   NS_ADDREF(*aResult);
01025   return NS_OK;
01026 }
01027 
01028 //
01029 // nsDOMStorageItem
01030 //
01031 
01032 NS_INTERFACE_MAP_BEGIN(nsDOMStorageItem)
01033   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorageItem)
01034   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageItem)
01035   NS_INTERFACE_MAP_ENTRY(nsIDOMToString)
01036   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageItem)
01037 NS_INTERFACE_MAP_END
01038 
01039 NS_IMPL_ADDREF(nsDOMStorageItem)
01040 NS_IMPL_RELEASE(nsDOMStorageItem)
01041 
01042 nsDOMStorageItem::nsDOMStorageItem(nsDOMStorage* aStorage,
01043                                    const nsAString& aKey,
01044                                    const nsAString& aValue,
01045                                    PRBool aSecure)
01046   : mSecure(aSecure),
01047     mKey(aKey),
01048     mValue(aValue),
01049     mStorage(aStorage)
01050 {
01051 }
01052 
01053 nsDOMStorageItem::~nsDOMStorageItem()
01054 {
01055 }
01056 
01057 NS_IMETHODIMP
01058 nsDOMStorageItem::GetSecure(PRBool* aSecure)
01059 {
01060   if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) {
01061     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
01062   }
01063 
01064   if (mStorage->UseDB()) {
01065     nsAutoString value;
01066     nsAutoString owner;
01067     return mStorage->GetDBValue(mKey, value, aSecure, owner);
01068   }
01069 
01070   *aSecure = IsSecure();
01071   return NS_OK;
01072 }
01073 
01074 NS_IMETHODIMP
01075 nsDOMStorageItem::SetSecure(PRBool aSecure)
01076 {
01077   if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) {
01078     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
01079   }
01080 
01081   if (mStorage->UseDB()) {
01082     nsresult rv = mStorage->SetSecure(mKey, aSecure);
01083     NS_ENSURE_SUCCESS(rv, rv);
01084   }
01085 
01086   mSecure = aSecure;
01087   return NS_OK;
01088 }
01089 
01090 NS_IMETHODIMP
01091 nsDOMStorageItem::GetValue(nsAString& aValue)
01092 {
01093   if (!mStorage->CacheStoragePermissions())
01094     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
01095 
01096   if (mStorage->UseDB()) {
01097     // GetDBValue checks the secure state so no need to do it here
01098     PRBool secure;
01099     nsAutoString unused;
01100     nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure, unused);
01101     return (rv == NS_ERROR_DOM_NOT_FOUND_ERR) ? NS_OK : rv;
01102   }
01103 
01104   if (IsSecure() && !IsCallerSecure()) {
01105     return NS_ERROR_DOM_SECURITY_ERR;
01106   }
01107 
01108   aValue = mValue;
01109   return NS_OK;
01110 }
01111 
01112 NS_IMETHODIMP
01113 nsDOMStorageItem::SetValue(const nsAString& aValue)
01114 {
01115   if (!mStorage->CacheStoragePermissions())
01116     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
01117 
01118   PRBool secureCaller = IsCallerSecure();
01119 
01120   if (mStorage->UseDB()) {
01121     // SetDBValue() does the security checks for us.
01122     return mStorage->SetDBValue(mKey, aValue, secureCaller);
01123   }
01124 
01125   PRBool secureItem = IsSecure();
01126 
01127   if (!secureCaller && secureItem) {
01128     // The item is secure, but the caller isn't. Throw.
01129     return NS_ERROR_DOM_SECURITY_ERR;
01130   }
01131 
01132   mValue = aValue;
01133   mSecure = secureCaller;
01134   return NS_OK;
01135 }
01136 
01137 NS_IMETHODIMP
01138 nsDOMStorageItem::ToString(nsAString& aStr)
01139 {
01140   return GetValue(aStr);
01141 }
01142 
01143 // QueryInterface implementation for nsDOMStorageEvent
01144 NS_INTERFACE_MAP_BEGIN(nsDOMStorageEvent)
01145   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEvent)
01146   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEvent)
01147 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
01148 
01149 NS_IMPL_ADDREF_INHERITED(nsDOMStorageEvent, nsDOMEvent)
01150 NS_IMPL_RELEASE_INHERITED(nsDOMStorageEvent, nsDOMEvent)
01151 
01152 
01153 NS_IMETHODIMP
01154 nsDOMStorageEvent::GetDomain(nsAString& aDomain)
01155 {
01156   // mDomain will be #session for session storage for events that fire
01157   // due to a change in a session storage object.
01158   aDomain = mDomain;
01159 
01160   return NS_OK;
01161 }
01162 
01163 nsresult
01164 nsDOMStorageEvent::Init()
01165 {
01166   nsresult rv = InitEvent(NS_LITERAL_STRING("storage"), PR_TRUE, PR_FALSE);
01167   NS_ENSURE_SUCCESS(rv, rv);
01168 
01169   // This init method is only called by native code, so set the
01170   // trusted flag here.
01171   SetTrusted(PR_TRUE);
01172 
01173   return NS_OK;
01174 }
01175 
01176 NS_IMETHODIMP
01177 nsDOMStorageEvent::InitStorageEvent(const nsAString& aTypeArg,
01178                                     PRBool aCanBubbleArg,
01179                                     PRBool aCancelableArg,
01180                                     const nsAString& aDomainArg)
01181 {
01182   nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
01183   NS_ENSURE_SUCCESS(rv, rv);
01184 
01185   mDomain = aDomainArg;
01186 
01187   return NS_OK;
01188 }
01189 
01190 NS_IMETHODIMP
01191 nsDOMStorageEvent::InitStorageEventNS(const nsAString& aNamespaceURIArg,
01192                                       const nsAString& aTypeArg,
01193                                       PRBool aCanBubbleArg,
01194                                       PRBool aCancelableArg,
01195                                       const nsAString& aDomainArg)
01196 {
01197   // XXXjst: Figure out what to do with aNamespaceURIArg here!
01198   nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
01199   NS_ENSURE_SUCCESS(rv, rv);
01200 
01201   mDomain = aDomainArg;
01202 
01203   return NS_OK;
01204 }