nsresult nsDOMStorage::Clear() { if (!CacheStoragePermissions()) return NS_ERROR_DOM_SECURITY_ERR; if (UseDB()) CacheKeysFromDB(); PRBool foundSecureItem = PR_FALSE; mItems.EnumerateEntries(CheckSecure, &foundSecureItem); if (foundSecureItem && !IsCallerSecure()) { return NS_ERROR_DOM_SECURITY_ERR; } #ifdef MOZ_STORAGE if (UseDB()) { nsresult rv = InitDB(); NS_ENSURE_SUCCESS(rv, rv); rv = gStorageDB->ClearStorage(this); NS_ENSURE_SUCCESS(rv, rv); } #endif mItems.Clear(); BroadcastChangeNotification(); return NS_OK; }
NS_IMETHODIMP nsDOMStorage::Key(PRUint32 aIndex, nsAString& aKey) { // XXXjst: This is as retarded as the DOM spec is, takes an unsigned // int, but the spec talks about what to do if a negative value is // passed in. // XXX: This does a linear search for the key at index, which would // suck if there's a large numer of indexes. Do we care? If so, // maybe we need to have a lazily populated key array here or // something? if (!CacheStoragePermissions()) return NS_ERROR_DOM_SECURITY_ERR; if (UseDB()) CacheKeysFromDB(); IndexFinderData data(IsCallerSecure(), aIndex); mItems.EnumerateEntries(IndexFinder, &data); if (!data.mItem) { // aIndex was larger than the number of accessible keys. Throw. return NS_ERROR_DOM_INDEX_SIZE_ERR; } aKey = data.mItem->GetKey(); return NS_OK; }
nsresult nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue, PRBool* aSecure) { aValue.Truncate(); #ifdef MOZ_STORAGE if (!UseDB()) return NS_OK; nsresult rv = InitDB(); NS_ENSURE_SUCCESS(rv, rv); nsAutoString value; rv = gStorageDB->GetKeyValue(this, aKey, value, aSecure); if (rv == NS_ERROR_DOM_NOT_FOUND_ERR && mLocalStorage) { SetDOMStringToNull(aValue); } if (NS_FAILED(rv)) return rv; if (!IsCallerSecure() && *aSecure) { return NS_ERROR_DOM_SECURITY_ERR; } aValue.Assign(value); #endif return NS_OK; }
nsresult nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue, PRBool* aSecure, nsAString& aOwner) { aValue.Truncate(); #ifdef MOZ_STORAGE if (!UseDB()) return NS_OK; nsresult rv = InitDB(); NS_ENSURE_SUCCESS(rv, rv); nsAutoString value; rv = gStorageDB->GetKeyValue(mDomain, aKey, value, aSecure, aOwner); if (NS_FAILED(rv)) return rv; if (!IsCallerSecure() && *aSecure) { return NS_ERROR_DOM_SECURITY_ERR; } aValue.Assign(value); #endif return NS_OK; }
NS_IMETHODIMP nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData) { if (!CacheStoragePermissions()) return NS_ERROR_DOM_SECURITY_ERR; if (aKey.IsEmpty()) return NS_OK; nsresult rv; nsRefPtr<nsDOMStorageItem> newitem = nsnull; nsSessionStorageEntry *entry = mItems.GetEntry(aKey); if (entry) { if (entry->mItem->IsSecure() && !IsCallerSecure()) { return NS_ERROR_DOM_SECURITY_ERR; } if (!UseDB()) { entry->mItem->SetValueInternal(aData); } } else { if (UseDB()) newitem = new nsDOMStorageItem(this, aKey, aData, PR_FALSE); else newitem = new nsDOMStorageItem(this, aKey, aData, PR_FALSE); if (!newitem) return NS_ERROR_OUT_OF_MEMORY; } if (UseDB()) { rv = SetDBValue(aKey, aData, IsCallerSecure()); NS_ENSURE_SUCCESS(rv, rv); } if (newitem) { entry = mItems.PutEntry(aKey); NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); entry->mItem = newitem; } // SetDBValue already calls BroadcastChangeNotification so don't do it again if (!UseDB()) BroadcastChangeNotification(); return NS_OK; }
nsTArray<nsString> * nsDOMStorage::GetKeys() { if (UseDB()) CacheKeysFromDB(); KeysArrayBuilderStruct keystruct; keystruct.callerIsSecure = IsCallerSecure(); keystruct.keys = new nsTArray<nsString>(); if (keystruct.keys) mItems.EnumerateEntries(KeysArrayBuilder, &keystruct); return keystruct.keys; }
NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey) { if (!CacheStoragePermissions()) return NS_ERROR_DOM_SECURITY_ERR; if (aKey.IsEmpty()) return NS_OK; nsSessionStorageEntry *entry = mItems.GetEntry(aKey); if (entry && entry->mItem->IsSecure() && !IsCallerSecure()) { return NS_ERROR_DOM_SECURITY_ERR; } if (UseDB()) { #ifdef MOZ_STORAGE nsresult rv = InitDB(); NS_ENSURE_SUCCESS(rv, rv); nsAutoString value; PRBool secureItem; nsAutoString owner; rv = GetDBValue(aKey, value, &secureItem, owner); if (rv == NS_ERROR_DOM_NOT_FOUND_ERR) return NS_OK; NS_ENSURE_SUCCESS(rv, rv); rv = gStorageDB->RemoveKey(mDomain, aKey, owner, aKey.Length() + value.Length()); NS_ENSURE_SUCCESS(rv, rv); mItemsCached = PR_FALSE; BroadcastChangeNotification(); #endif } else if (entry) { // clear string as StorageItems may be referencing this item entry->mItem->ClearValue(); BroadcastChangeNotification(); } if (entry) { mItems.RawRemoveEntry(entry); } return NS_OK; }
NS_IMETHODIMP nsDOMStorageItem::GetSecure(PRBool* aSecure) { if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } if (mStorage->UseDB()) { nsAutoString value; return mStorage->GetDBValue(mKey, value, aSecure); } *aSecure = IsSecure(); return NS_OK; }
NS_IMETHODIMP nsDOMStorageItem::SetSecure(PRBool aSecure) { if (!mStorage->CacheStoragePermissions() || !IsCallerSecure()) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } if (mStorage->UseDB()) { nsresult rv = mStorage->SetSecure(mKey, aSecure); NS_ENSURE_SUCCESS(rv, rv); } mSecure = aSecure; return NS_OK; }
nsresult nsDOMStorage::SetDBValue(const nsAString& aKey, const nsAString& aValue, PRBool aSecure) { #ifdef MOZ_STORAGE if (!UseDB()) return NS_OK; nsresult rv = InitDB(); NS_ENSURE_SUCCESS(rv, rv); // Get the current domain for quota enforcement nsCOMPtr<nsIPrincipal> subjectPrincipal; nsContentUtils::GetSecurityManager()-> GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); nsAutoString currentDomain; if (subjectPrincipal) { nsCOMPtr<nsIURI> uri; rv = subjectPrincipal->GetURI(getter_AddRefs(uri)); if (NS_SUCCEEDED(rv) && uri) { nsCAutoString currentDomainAscii; uri->GetAsciiHost(currentDomainAscii); currentDomain = NS_ConvertUTF8toUTF16(currentDomainAscii); } if (currentDomain.IsEmpty()) { return NS_ERROR_DOM_SECURITY_ERR; } } else { currentDomain = mDomain; } rv = gStorageDB->SetKey(mDomain, aKey, aValue, aSecure, currentDomain, GetQuota(currentDomain)); NS_ENSURE_SUCCESS(rv, rv); mItemsCached = PR_FALSE; BroadcastChangeNotification(); #endif return NS_OK; }
void nsDOMStorage::BroadcastChangeNotification() { nsresult rv; nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv); if (NS_FAILED(rv)) { return; } // Fire off a notification that a storage object changed. If the // storage object is a session storage object, we don't pass a // domain, but if it's a global storage object we do. observerService->NotifyObservers((nsIDOMStorageObsolete *)this, "dom-storage-changed", UseDB() ? NS_ConvertUTF8toUTF16(mDomain).get() : nsnull); }
NS_IMETHODIMP nsDOMStorage::GetLength(PRUint32 *aLength) { if (!CacheStoragePermissions()) return NS_ERROR_DOM_SECURITY_ERR; if (UseDB()) CacheKeysFromDB(); ItemCounterState state(IsCallerSecure()); mItems.EnumerateEntries(ItemCounter, &state); *aLength = state.mCount; return NS_OK; }
NS_IMETHODIMP nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem) { *aItem = nsnull; if (!CacheStoragePermissions()) return NS_ERROR_DOM_SECURITY_ERR; if (aKey.IsEmpty()) return NS_OK; nsSessionStorageEntry *entry = mItems.GetEntry(aKey); if (entry) { if (!IsCallerSecure() && entry->mItem->IsSecure()) { return NS_OK; } NS_ADDREF(*aItem = entry->mItem); } else if (UseDB()) { PRBool secure; nsAutoString value; nsAutoString unused; nsresult rv = GetDBValue(aKey, value, &secure, unused); // return null if access isn't allowed or the key wasn't found if (rv == NS_ERROR_DOM_SECURITY_ERR || rv == NS_ERROR_DOM_NOT_FOUND_ERR) return NS_OK; NS_ENSURE_SUCCESS(rv, rv); nsRefPtr<nsDOMStorageItem> newitem = new nsDOMStorageItem(this, aKey, value, secure); if (!newitem) return NS_ERROR_OUT_OF_MEMORY; entry = mItems.PutEntry(aKey); NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); entry->mItem = newitem; NS_ADDREF(*aItem = newitem); } return NS_OK; }
nsIDOMStorageItem* nsDOMStorage::GetNamedItem(const nsAString& aKey, nsresult* aResult) { if (!CacheStoragePermissions()) { *aResult = NS_ERROR_DOM_SECURITY_ERR; return nsnull; } *aResult = NS_OK; if (aKey.IsEmpty()) return nsnull; nsSessionStorageEntry *entry = mItems.GetEntry(aKey); nsIDOMStorageItem* item = nsnull; if (entry) { if (IsCallerSecure() || !entry->mItem->IsSecure()) { item = entry->mItem; } } else if (UseDB()) { PRBool secure; nsAutoString value; nsresult rv = GetDBValue(aKey, value, &secure); // return null if access isn't allowed or the key wasn't found if (rv == NS_ERROR_DOM_SECURITY_ERR || rv == NS_ERROR_DOM_NOT_FOUND_ERR) return nsnull; *aResult = rv; NS_ENSURE_SUCCESS(rv, nsnull); nsRefPtr<nsDOMStorageItem> newitem = new nsDOMStorageItem(this, aKey, value, secure); if (newitem && (entry = mItems.PutEntry(aKey))) { item = entry->mItem = newitem; } else { *aResult = NS_ERROR_OUT_OF_MEMORY; } } return item; }
NS_IMETHODIMP nsDOMStorage::GetLength(PRUint32 *aLength) { if (!CacheStoragePermissions()) return NS_ERROR_DOM_SECURITY_ERR; // Force reload of items from database. This ensures sync localStorages for // same origins among different windows. mItemsCached = PR_FALSE; if (UseDB()) CacheKeysFromDB(); ItemCounterState state(IsCallerSecure()); mItems.EnumerateEntries(ItemCounter, &state); *aLength = state.mCount; return NS_OK; }
NS_IMETHODIMP nsDOMStorageItem::GetValue(nsAString& aValue) { if (!mStorage->CacheStoragePermissions()) return NS_ERROR_DOM_INVALID_ACCESS_ERR; if (mStorage->UseDB()) { // GetDBValue checks the secure state so no need to do it here PRBool secure; nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure); if (rv == NS_ERROR_DOM_NOT_FOUND_ERR) return NS_OK; return rv; } if (IsSecure() && !IsCallerSecure()) { return NS_ERROR_DOM_SECURITY_ERR; } aValue = mValue; return NS_OK; }
nsresult nsDOMStorage::SetSecure(const nsAString& aKey, PRBool aSecure) { #ifdef MOZ_STORAGE if (UseDB()) { nsresult rv = InitDB(); NS_ENSURE_SUCCESS(rv, rv); return gStorageDB->SetSecure(this, aKey, aSecure); } #else return NS_ERROR_NOT_IMPLEMENTED; #endif nsSessionStorageEntry *entry = mItems.GetEntry(aKey); NS_ASSERTION(entry, "Don't use SetSecure() with non-existing keys!"); if (entry) { entry->mItem->SetSecureInternal(aSecure); } return NS_OK; }
NS_IMETHODIMP nsDOMStorageItem::SetValue(const nsAString& aValue) { if (!mStorage->CacheStoragePermissions()) return NS_ERROR_DOM_INVALID_ACCESS_ERR; PRBool secureCaller = IsCallerSecure(); if (mStorage->UseDB()) { // SetDBValue() does the security checks for us. return mStorage->SetDBValue(mKey, aValue, secureCaller); } PRBool secureItem = IsSecure(); if (!secureCaller && secureItem) { // The item is secure, but the caller isn't. Throw. return NS_ERROR_DOM_SECURITY_ERR; } mValue = aValue; mSecure = secureCaller; return NS_OK; }
nsresult nsDOMStorage::SetDBValue(const nsAString& aKey, const nsAString& aValue, PRBool aSecure) { #ifdef MOZ_STORAGE if (!UseDB()) return NS_OK; nsresult rv = InitDB(); NS_ENSURE_SUCCESS(rv, rv); // Get the current domain for quota enforcement nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); if (!ssm) return NS_ERROR_FAILURE; nsCOMPtr<nsIPrincipal> subjectPrincipal; ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); nsCAutoString currentDomain; if (subjectPrincipal) { nsCOMPtr<nsIURI> unused; rv = GetPrincipalURIAndHost(subjectPrincipal, getter_AddRefs(unused), currentDomain); // Don't bail out on NS_ERROR_DOM_SECURITY_ERR, since we want to allow // trusted file:// URIs below. if (NS_FAILED(rv) && rv != NS_ERROR_DOM_SECURITY_ERR) { return rv; } if (currentDomain.IsEmpty()) { // allow chrome urls and trusted file urls to write using // the storage's domain if (nsContentUtils::IsCallerTrustedForWrite()) currentDomain = mDomain; else return NS_ERROR_DOM_SECURITY_ERR; } } else { currentDomain = mDomain; } PRInt32 quota; PRInt32 warnQuota; GetQuota(currentDomain, "a, &warnQuota); PRInt32 usage; rv = gStorageDB->SetKey(this, aKey, aValue, aSecure, quota, &usage); NS_ENSURE_SUCCESS(rv, rv); mItemsCached = PR_FALSE; if (warnQuota >= 0 && usage > warnQuota) { // try to include the window that exceeded the warn quota nsCOMPtr<nsIDOMWindow> window; JSContext *cx; nsCOMPtr<nsIJSContextStack> stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1"); if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) { nsCOMPtr<nsIScriptContext> scriptContext; scriptContext = GetScriptContextFromJSContext(cx); if (scriptContext) { window = do_QueryInterface(scriptContext->GetGlobalObject()); } } nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1"); os->NotifyObservers(window, "dom-storage-warn-quota-exceeded", NS_ConvertUTF8toUTF16(currentDomain).get()); } BroadcastChangeNotification(); #endif return NS_OK; }