dom/src/storage/DOMStorage.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "DOMStorage.h"
     7 #include "DOMStorageCache.h"
     8 #include "DOMStorageManager.h"
    10 #include "nsIDOMStorageEvent.h"
    11 #include "nsIObserverService.h"
    12 #include "nsIScriptSecurityManager.h"
    13 #include "nsIPermissionManager.h"
    14 #include "nsIPrincipal.h"
    15 #include "nsICookiePermission.h"
    17 #include "nsDOMClassInfoID.h"
    18 #include "mozilla/Services.h"
    19 #include "mozilla/Preferences.h"
    20 #include "GeneratedEvents.h"
    21 #include "nsThreadUtils.h"
    22 #include "nsContentUtils.h"
    23 #include "nsServiceManagerUtils.h"
    25 DOMCI_DATA(Storage, mozilla::dom::DOMStorage)
    27 namespace mozilla {
    28 namespace dom {
    30 NS_IMPL_ADDREF(DOMStorage)
    31 NS_IMPL_RELEASE(DOMStorage)
    33 NS_INTERFACE_MAP_BEGIN(DOMStorage)
    34   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
    35   NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
    36   NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
    37   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    38   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
    39 NS_INTERFACE_MAP_END
    41 DOMStorage::DOMStorage(DOMStorageManager* aManager,
    42                        DOMStorageCache* aCache,
    43                        const nsAString& aDocumentURI,
    44                        nsIPrincipal* aPrincipal,
    45                        bool aIsPrivate)
    46 : mManager(aManager)
    47 , mCache(aCache)
    48 , mDocumentURI(aDocumentURI)
    49 , mPrincipal(aPrincipal)
    50 , mIsPrivate(aIsPrivate)
    51 , mIsSessionOnly(false)
    52 {
    53   mCache->Preload();
    54 }
    56 DOMStorage::~DOMStorage()
    57 {
    58   mCache->KeepAlive();
    59 }
    61 // nsIDOMStorage (web content public API implementation)
    63 NS_IMETHODIMP
    64 DOMStorage::GetLength(uint32_t* aLength)
    65 {
    66   if (!CanUseStorage(this)) {
    67     return NS_ERROR_DOM_SECURITY_ERR;
    68   }
    70   return mCache->GetLength(this, aLength);
    71 }
    73 NS_IMETHODIMP
    74 DOMStorage::Key(uint32_t aIndex, nsAString& aRetval)
    75 {
    76   if (!CanUseStorage(this)) {
    77     return NS_ERROR_DOM_SECURITY_ERR;
    78   }
    80   return mCache->GetKey(this, aIndex, aRetval);
    81 }
    83 NS_IMETHODIMP
    84 DOMStorage::GetItem(const nsAString& aKey, nsAString& aRetval)
    85 {
    86   if (!CanUseStorage(this)) {
    87     return NS_ERROR_DOM_SECURITY_ERR;
    88   }
    90   return mCache->GetItem(this, aKey, aRetval);
    91 }
    93 NS_IMETHODIMP
    94 DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
    95 {
    96   if (!CanUseStorage(this)) {
    97     return NS_ERROR_DOM_SECURITY_ERR;
    98   }
   100   Telemetry::Accumulate(GetType() == LocalStorage
   101       ? Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES
   102       : Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES, aKey.Length());
   103   Telemetry::Accumulate(GetType() == LocalStorage
   104       ? Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES
   105       : Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES, aData.Length());
   107   nsString old;
   108   nsresult rv = mCache->SetItem(this, aKey, nsString(aData), old);
   109   if (NS_FAILED(rv)) {
   110     return rv;
   111   }
   113   if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
   114     BroadcastChangeNotification(aKey, old, aData);
   115   }
   117   return NS_OK;
   118 }
   120 NS_IMETHODIMP
   121 DOMStorage::RemoveItem(const nsAString& aKey)
   122 {
   123   if (!CanUseStorage(this)) {
   124     return NS_ERROR_DOM_SECURITY_ERR;
   125   }
   127   nsAutoString old;
   128   nsresult rv = mCache->RemoveItem(this, aKey, old);
   129   if (NS_FAILED(rv)) {
   130     return rv;
   131   }
   133   if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
   134     BroadcastChangeNotification(aKey, old, NullString());
   135   }
   137   return NS_OK;
   138 }
   140 NS_IMETHODIMP
   141 DOMStorage::Clear()
   142 {
   143   if (!CanUseStorage(this)) {
   144     return NS_ERROR_DOM_SECURITY_ERR;
   145   }
   147   nsresult rv = mCache->Clear(this);
   148   if (NS_FAILED(rv)) {
   149     return rv;
   150   }
   152   if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
   153     BroadcastChangeNotification(NullString(), NullString(), NullString());
   154   }
   156   return NS_OK;
   157 }
   159 namespace {
   161 class StorageNotifierRunnable : public nsRunnable
   162 {
   163 public:
   164   StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aType)
   165     : mSubject(aSubject), mType(aType)
   166   { }
   168   NS_DECL_NSIRUNNABLE
   170 private:
   171   nsCOMPtr<nsISupports> mSubject;
   172   const char16_t* mType;
   173 };
   175 NS_IMETHODIMP
   176 StorageNotifierRunnable::Run()
   177 {
   178   nsCOMPtr<nsIObserverService> observerService =
   179     mozilla::services::GetObserverService();
   180   if (observerService) {
   181     observerService->NotifyObservers(mSubject, "dom-storage2-changed", mType);
   182   }
   183   return NS_OK;
   184 }
   186 } // anonymous namespace
   188 void
   189 DOMStorage::BroadcastChangeNotification(const nsSubstring& aKey,
   190                                         const nsSubstring& aOldValue,
   191                                         const nsSubstring& aNewValue)
   192 {
   193   nsCOMPtr<nsIDOMEvent> domEvent;
   194   // Note, this DOM event should never reach JS. It is cloned later in
   195   // nsGlobalWindow.
   196   NS_NewDOMStorageEvent(getter_AddRefs(domEvent), nullptr, nullptr, nullptr);
   198   nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(domEvent);
   199   nsresult rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
   200                                         false,
   201                                         false,
   202                                         aKey,
   203                                         aOldValue,
   204                                         aNewValue,
   205                                         mDocumentURI,
   206                                         static_cast<nsIDOMStorage*>(this));
   207   if (NS_FAILED(rv)) {
   208     return;
   209   }
   211   nsRefPtr<StorageNotifierRunnable> r =
   212     new StorageNotifierRunnable(event,
   213                                 GetType() == LocalStorage
   214                                   ? MOZ_UTF16("localStorage")
   215                                   : MOZ_UTF16("sessionStorage"));
   216   NS_DispatchToMainThread(r);
   217 }
   219 static const uint32_t ASK_BEFORE_ACCEPT = 1;
   220 static const uint32_t ACCEPT_SESSION = 2;
   221 static const uint32_t BEHAVIOR_REJECT = 2;
   223 static const char kPermissionType[] = "cookie";
   224 static const char kStorageEnabled[] = "dom.storage.enabled";
   225 static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
   226 static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
   228 // static, public
   229 bool
   230 DOMStorage::CanUseStorage(DOMStorage* aStorage)
   231 {
   232   // This method is responsible for correct setting of mIsSessionOnly.
   233   // It doesn't work with mIsPrivate flag at all, since it is checked
   234   // regardless mIsSessionOnly flag in DOMStorageCache code.
   235   if (aStorage) {
   236     aStorage->mIsSessionOnly = false;
   237   }
   239   if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
   240     return false;
   241   }
   243   // chrome can always use aStorage regardless of permission preferences
   244   if (nsContentUtils::IsCallerChrome()) {
   245     return true;
   246   }
   248   nsCOMPtr<nsIPrincipal> subjectPrincipal;
   249   nsresult rv = nsContentUtils::GetSecurityManager()->
   250                   GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
   251   NS_ENSURE_SUCCESS(rv, false);
   253   // if subjectPrincipal were null we'd have returned after
   254   // IsCallerChrome().
   256   nsCOMPtr<nsIPermissionManager> permissionManager =
   257     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   258   if (!permissionManager) {
   259     return false;
   260   }
   262   uint32_t perm;
   263   permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
   264                                                  kPermissionType, &perm);
   266   if (perm == nsIPermissionManager::DENY_ACTION) {
   267     return false;
   268   }
   270   if (perm == nsICookiePermission::ACCESS_SESSION) {
   271     if (aStorage) {
   272       aStorage->mIsSessionOnly = true;
   273     }
   274   } else if (perm != nsIPermissionManager::ALLOW_ACTION) {
   275     uint32_t cookieBehavior = Preferences::GetUint(kCookiesBehavior);
   276     uint32_t lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy);
   278     // Treat "ask every time" as "reject always".
   279     if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT)) {
   280       return false;
   281     }
   283     if (lifetimePolicy == ACCEPT_SESSION && aStorage) {
   284       aStorage->mIsSessionOnly = true;
   285     }
   286   }
   288   if (aStorage) {
   289     return aStorage->CanAccess(subjectPrincipal);
   290   }
   292   return true;
   293 }
   295 // nsPIDOMStorage
   297 nsPIDOMStorage::StorageType
   298 DOMStorage::GetType() const
   299 {
   300   return mManager->Type();
   301 }
   303 nsIPrincipal*
   304 DOMStorage::GetPrincipal()
   305 {
   306   return mPrincipal;
   307 }
   309 // Defined in DOMStorageManager.cpp
   310 extern bool
   311 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
   313 bool
   314 DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
   315 {
   316   return PrincipalsEqual(mPrincipal, aPrincipal);
   317 }
   319 bool
   320 DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
   321 {
   322   return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
   323 }
   325 nsTArray<nsString>*
   326 DOMStorage::GetKeys()
   327 {
   328   if (!CanUseStorage(this)) {
   329     return new nsTArray<nsString>(); // return just an empty array
   330   }
   332   return mCache->GetKeys(this);
   333 }
   335 } // ::dom
   336 } // ::mozilla

mercurial