LCOV - code coverage report
Current view: top level - extensions/cookie - nsPermissionManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 393 1449 27.1 %
Date: 2017-07-14 16:53:18 Functions: 41 107 38.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/Attributes.h"
       8             : #include "mozilla/DebugOnly.h"
       9             : 
      10             : #include "mozilla/dom/ContentParent.h"
      11             : #include "mozilla/dom/ContentChild.h"
      12             : #include "mozilla/BasePrincipal.h"
      13             : #include "mozilla/Services.h"
      14             : #include "mozilla/SystemGroup.h"
      15             : #include "mozilla/Unused.h"
      16             : #include "nsPermissionManager.h"
      17             : #include "nsPermission.h"
      18             : #include "nsCRT.h"
      19             : #include "nsNetUtil.h"
      20             : #include "nsCOMArray.h"
      21             : #include "nsArrayEnumerator.h"
      22             : #include "nsTArray.h"
      23             : #include "nsReadableUtils.h"
      24             : #include "nsILineInputStream.h"
      25             : #include "nsAppDirectoryServiceDefs.h"
      26             : #include "nsDirectoryServiceDefs.h"
      27             : #include "mozilla/storage.h"
      28             : #include "mozilla/Attributes.h"
      29             : #include "nsXULAppAPI.h"
      30             : #include "nsIPrincipal.h"
      31             : #include "nsContentUtils.h"
      32             : #include "nsIScriptSecurityManager.h"
      33             : #include "nsIEffectiveTLDService.h"
      34             : #include "nsPIDOMWindow.h"
      35             : #include "nsIDocument.h"
      36             : #include "mozilla/net/NeckoMessageUtils.h"
      37             : #include "mozilla/Preferences.h"
      38             : #include "nsReadLine.h"
      39             : #include "mozilla/Telemetry.h"
      40             : #include "nsIConsoleService.h"
      41             : #include "nsINavHistoryService.h"
      42             : #include "nsToolkitCompsCID.h"
      43             : #include "nsIObserverService.h"
      44             : #include "nsPrintfCString.h"
      45             : #include "mozilla/AbstractThread.h"
      46             : #include "ContentPrincipal.h"
      47             : 
      48             : static nsPermissionManager *gPermissionManager = nullptr;
      49             : 
      50             : using namespace mozilla;
      51             : using namespace mozilla::dom;
      52             : 
      53             : static bool
      54          33 : IsChildProcess()
      55             : {
      56          33 :   return XRE_IsContentProcess();
      57             : }
      58             : 
      59             : static void
      60           0 : LogToConsole(const nsAString& aMsg)
      61             : {
      62           0 :   nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
      63           0 :   if (!console) {
      64           0 :     NS_WARNING("Failed to log message to console.");
      65           0 :     return;
      66             :   }
      67             : 
      68           0 :   nsAutoString msg(aMsg);
      69           0 :   console->LogStringMessage(msg.get());
      70             : }
      71             : 
      72             : #define ENSURE_NOT_CHILD_PROCESS_(onError) \
      73             :   PR_BEGIN_MACRO \
      74             :   if (IsChildProcess()) { \
      75             :     NS_ERROR("Cannot perform action in content process!"); \
      76             :     onError \
      77             :   } \
      78             :   PR_END_MACRO
      79             : 
      80             : #define ENSURE_NOT_CHILD_PROCESS \
      81             :   ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
      82             : 
      83             : #define ENSURE_NOT_CHILD_PROCESS_NORET \
      84             :   ENSURE_NOT_CHILD_PROCESS_(;)
      85             : 
      86             : ////////////////////////////////////////////////////////////////////////////////
      87             : 
      88             : namespace {
      89             : 
      90             : // The number of permissions from the kPreloadPermissions list which are present
      91             : // in the permission manager. Used to determine if the permission manager should
      92             : // be checked for one of these preload permissions in nsContentBlocker.
      93             : static int32_t sPreloadPermissionCount = 0;
      94             : 
      95             : // These permissions are special permissions which must be transmitted to the
      96             : // content process before documents with their principals have loaded within
      97             : // that process. This is because these permissions are used for content
      98             : // blocking in nsContentBlocker.
      99             : //
     100             : // Permissions which are in this list are considered to have a "" permission
     101             : // key, even if their principal would not normally have that key.
     102             : static const char* kPreloadPermissions[] = {
     103             :   // NOTE: These permissions are the different nsContentBlocker permissions for
     104             :   // allowing or denying certain content types from being loaded. Every
     105             :   // permission listed in the `kTypeString` array in nsContentBlocker.cpp should
     106             :   // appear in this list.
     107             :   "other",
     108             :   "script",
     109             :   "image",
     110             :   "stylesheet",
     111             :   "object",
     112             :   "document",
     113             :   "subdocument",
     114             :   "refresh",
     115             :   "xbl",
     116             :   "ping",
     117             :   "xmlhttprequest",
     118             :   "objectsubrequest",
     119             :   "dtd",
     120             :   "font",
     121             :   "media",
     122             :   "websocket",
     123             :   "csp_report",
     124             :   "xslt",
     125             :   "beacon",
     126             :   "fetch",
     127             :   "image",
     128             :   "manifest"
     129             : };
     130             : 
     131             : // NOTE: nullptr can be passed as aType - if it is this function will return
     132             : // "false" unconditionally.
     133             : bool
     134          58 : IsPreloadPermission(const char* aType)
     135             : {
     136          58 :   if (aType) {
     137        1334 :     for (uint32_t i = 0; i < mozilla::ArrayLength(kPreloadPermissions); ++i) {
     138        1276 :       if (!strcmp(aType, kPreloadPermissions[i])) {
     139           0 :         return true;
     140             :       }
     141             :     }
     142             :   }
     143             : 
     144          58 :   return false;
     145             : }
     146             : 
     147             : nsresult
     148          30 : GetOriginFromPrincipal(nsIPrincipal* aPrincipal, nsACString& aOrigin)
     149             : {
     150          30 :   nsresult rv = aPrincipal->GetOriginNoSuffix(aOrigin);
     151             :   // The principal may belong to the about:blank content viewer, so this can be
     152             :   // expected to fail.
     153          30 :   if (NS_FAILED(rv)) {
     154           0 :     return rv;
     155             :   }
     156             : 
     157          60 :   nsAutoCString suffix;
     158          30 :   rv = aPrincipal->GetOriginSuffix(suffix);
     159          30 :   NS_ENSURE_SUCCESS(rv, rv);
     160             : 
     161          60 :   mozilla::OriginAttributes attrs;
     162          30 :   if (!attrs.PopulateFromSuffix(suffix)) {
     163           0 :     return NS_ERROR_FAILURE;
     164             :   }
     165             : 
     166             :   // mPrivateBrowsingId must be set to false because PermissionManager is not supposed to have
     167             :   // any knowledge of private browsing. Allowing it to be true changes the suffix being hashed.
     168          30 :   attrs.mPrivateBrowsingId = 0;
     169             : 
     170             :   // Disable userContext and firstParty isolation for permissions.
     171             :   attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID |
     172          30 :                         mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
     173             : 
     174          30 :   attrs.CreateSuffix(suffix);
     175          30 :   aOrigin.Append(suffix);
     176          30 :   return NS_OK;
     177             : }
     178             : 
     179             : nsresult
     180          54 : GetPrincipalFromOrigin(const nsACString& aOrigin, nsIPrincipal** aPrincipal)
     181             : {
     182         108 :   nsAutoCString originNoSuffix;
     183         108 :   mozilla::OriginAttributes attrs;
     184          54 :   if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
     185           0 :     return NS_ERROR_FAILURE;
     186             :   }
     187             : 
     188             :   // mPrivateBrowsingId must be set to false because PermissionManager is not supposed to have
     189             :   // any knowledge of private browsing. Allowing it to be true changes the suffix being hashed.
     190          54 :   attrs.mPrivateBrowsingId = 0;
     191             : 
     192             :   // Disable userContext and firstParty isolation for permissions.
     193             :   attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID |
     194          54 :                         mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
     195             : 
     196         108 :   nsCOMPtr<nsIURI> uri;
     197          54 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
     198          54 :   NS_ENSURE_SUCCESS(rv, rv);
     199             : 
     200         108 :   nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
     201          54 :   principal.forget(aPrincipal);
     202          54 :   return NS_OK;
     203             : }
     204             : 
     205             : nsresult
     206           0 : GetPrincipal(nsIURI* aURI, uint32_t aAppId, bool aIsInIsolatedMozBrowserElement, nsIPrincipal** aPrincipal)
     207             : {
     208           0 :   mozilla::OriginAttributes attrs(aAppId, aIsInIsolatedMozBrowserElement);
     209           0 :   nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
     210           0 :   NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
     211             : 
     212           0 :   principal.forget(aPrincipal);
     213           0 :   return NS_OK;
     214             : }
     215             : 
     216             : nsresult
     217           2 : GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal)
     218             : {
     219           4 :   mozilla::OriginAttributes attrs;
     220           4 :   nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
     221           2 :   NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
     222             : 
     223           2 :   principal.forget(aPrincipal);
     224           2 :   return NS_OK;
     225             : }
     226             : 
     227             : nsCString
     228           1 : GetNextSubDomainForHost(const nsACString& aHost)
     229             : {
     230             :   nsCOMPtr<nsIEffectiveTLDService> tldService =
     231           2 :     do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
     232           1 :   if (!tldService) {
     233           0 :     NS_ERROR("Should have a tld service!");
     234           0 :     return EmptyCString();
     235             :   }
     236             : 
     237           2 :   nsCString subDomain;
     238           1 :   nsresult rv = tldService->GetNextSubDomain(aHost, subDomain);
     239             :   // We can fail if there is no more subdomain or if the host can't have a
     240             :   // subdomain.
     241           1 :   if (NS_FAILED(rv)) {
     242           1 :     return EmptyCString();
     243             :   }
     244             : 
     245           0 :   return subDomain;
     246             : }
     247             : 
     248             : // This function produces a nsIURI which is identical to the current
     249             : // nsIURI, except that it has one less subdomain segment. It returns
     250             : // `nullptr` if there are no more segments to remove.
     251             : already_AddRefed<nsIURI>
     252           1 : GetNextSubDomainURI(nsIURI* aURI)
     253             : {
     254           2 :   nsAutoCString host;
     255           1 :   nsresult rv = aURI->GetHost(host);
     256           1 :   if (NS_FAILED(rv)) {
     257           0 :     return nullptr;
     258             :   }
     259             : 
     260           2 :   nsCString domain = GetNextSubDomainForHost(host);
     261           1 :   if (domain.IsEmpty()) {
     262           1 :     return nullptr;
     263             :   }
     264             : 
     265           0 :   nsCOMPtr<nsIURI> uri;
     266           0 :   rv = aURI->Clone(getter_AddRefs(uri));
     267           0 :   if (NS_FAILED(rv) || !uri) {
     268           0 :     return nullptr;
     269             :   }
     270             : 
     271           0 :   rv = uri->SetHost(domain);
     272           0 :   if (NS_FAILED(rv)) {
     273           0 :     return nullptr;
     274             :   }
     275             : 
     276           0 :   return uri.forget();
     277             : }
     278             : 
     279             : // This function produces a nsIPrincipal which is identical to the current
     280             : // nsIPrincipal, except that it has one less subdomain segment. It returns
     281             : // `nullptr` if there are no more segments to remove.
     282             : already_AddRefed<nsIPrincipal>
     283           1 : GetNextSubDomainPrincipal(nsIPrincipal* aPrincipal)
     284             : {
     285           2 :   nsCOMPtr<nsIURI> uri;
     286           1 :   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
     287           1 :   if (NS_FAILED(rv) || !uri) {
     288           0 :     return nullptr;
     289             :   }
     290             : 
     291             :   // Create a new principal which is identical to the current one, but with the new host
     292           2 :   nsCOMPtr<nsIURI> newURI = GetNextSubDomainURI(uri);
     293           1 :   if (!newURI) {
     294           1 :     return nullptr;
     295             :   }
     296             : 
     297             :   // Copy the attributes over
     298           0 :   mozilla::OriginAttributes attrs = aPrincipal->OriginAttributesRef();
     299             : 
     300             :   // Disable userContext and firstParty isolation for permissions.
     301             :   attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID |
     302           0 :                         mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
     303             : 
     304             :   nsCOMPtr<nsIPrincipal> principal =
     305           0 :     mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs);
     306             : 
     307           0 :   return principal.forget();
     308             : }
     309             : 
     310           3 : class ClearOriginDataObserver final : public nsIObserver {
     311           0 :   ~ClearOriginDataObserver() {}
     312             : 
     313             : public:
     314             :   NS_DECL_ISUPPORTS
     315             : 
     316             :   // nsIObserver implementation.
     317             :   NS_IMETHOD
     318           0 :   Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override
     319             :   {
     320           0 :     MOZ_ASSERT(!nsCRT::strcmp(aTopic, "clear-origin-attributes-data"));
     321             : 
     322           0 :     nsCOMPtr<nsIPermissionManager> permManager = do_GetService("@mozilla.org/permissionmanager;1");
     323           0 :     return permManager->RemovePermissionsWithAttributes(nsDependentString(aData));
     324             :   }
     325             : };
     326             : 
     327           3 : NS_IMPL_ISUPPORTS(ClearOriginDataObserver, nsIObserver)
     328             : 
     329           0 : class MOZ_STACK_CLASS UpgradeHostToOriginHelper {
     330             : public:
     331             :   virtual nsresult Insert(const nsACString& aOrigin, const nsCString& aType,
     332             :                           uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
     333             :                           int64_t aModificationTime) = 0;
     334             : };
     335             : 
     336           0 : class MOZ_STACK_CLASS UpgradeHostToOriginDBMigration final : public UpgradeHostToOriginHelper {
     337             : public:
     338           0 :   UpgradeHostToOriginDBMigration(mozIStorageConnection* aDBConn, int64_t* aID) : mDBConn(aDBConn)
     339           0 :                                                                                , mID(aID)
     340             :   {
     341           0 :     mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     342             :       "INSERT INTO moz_hosts_new "
     343             :       "(id, origin, type, permission, expireType, expireTime, modificationTime) "
     344           0 :       "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mStmt));
     345           0 :   }
     346             : 
     347             :   nsresult
     348           0 :   Insert(const nsACString& aOrigin, const nsCString& aType,
     349             :          uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
     350             :          int64_t aModificationTime) final
     351             :   {
     352           0 :     nsresult rv = mStmt->BindInt64ByIndex(0, *mID);
     353           0 :     NS_ENSURE_SUCCESS(rv, rv);
     354             : 
     355           0 :     rv = mStmt->BindUTF8StringByIndex(1, aOrigin);
     356           0 :     NS_ENSURE_SUCCESS(rv, rv);
     357             : 
     358           0 :     rv = mStmt->BindUTF8StringByIndex(2, aType);
     359           0 :     NS_ENSURE_SUCCESS(rv, rv);
     360             : 
     361           0 :     rv = mStmt->BindInt32ByIndex(3, aPermission);
     362           0 :     NS_ENSURE_SUCCESS(rv, rv);
     363             : 
     364           0 :     rv = mStmt->BindInt32ByIndex(4, aExpireType);
     365           0 :     NS_ENSURE_SUCCESS(rv, rv);
     366             : 
     367           0 :     rv = mStmt->BindInt64ByIndex(5, aExpireTime);
     368           0 :     NS_ENSURE_SUCCESS(rv, rv);
     369             : 
     370           0 :     rv = mStmt->BindInt64ByIndex(6, aModificationTime);
     371           0 :     NS_ENSURE_SUCCESS(rv, rv);
     372             : 
     373             :     // Increment the working identifier, as we are about to use this one
     374           0 :     (*mID)++;
     375             : 
     376           0 :     rv = mStmt->Execute();
     377           0 :     NS_ENSURE_SUCCESS(rv, rv);
     378             : 
     379           0 :     return NS_OK;
     380             :   }
     381             : 
     382             : private:
     383             :   nsCOMPtr<mozIStorageStatement> mStmt;
     384             :   nsCOMPtr<mozIStorageConnection> mDBConn;
     385             :   int64_t* mID;
     386             : };
     387             : 
     388           0 : class MOZ_STACK_CLASS UpgradeHostToOriginHostfileImport final : public UpgradeHostToOriginHelper {
     389             : public:
     390           0 :   UpgradeHostToOriginHostfileImport(nsPermissionManager* aPm,
     391             :                                     nsPermissionManager::DBOperationType aOperation,
     392           0 :                                     int64_t aID) : mPm(aPm)
     393             :                                                  , mOperation(aOperation)
     394           0 :                                                  , mID(aID)
     395           0 :   {}
     396             : 
     397             :   nsresult
     398           0 :   Insert(const nsACString& aOrigin, const nsCString& aType,
     399             :          uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
     400             :          int64_t aModificationTime) final
     401             :   {
     402           0 :     nsCOMPtr<nsIPrincipal> principal;
     403           0 :     nsresult rv = GetPrincipalFromOrigin(aOrigin, getter_AddRefs(principal));
     404           0 :     NS_ENSURE_SUCCESS(rv, rv);
     405             : 
     406           0 :     return mPm->AddInternal(principal, aType, aPermission, mID,
     407             :                             aExpireType, aExpireTime, aModificationTime,
     408           0 :                             nsPermissionManager::eDontNotify, mOperation);
     409             :   }
     410             : 
     411             : private:
     412             :   RefPtr<nsPermissionManager> mPm;
     413             :   nsPermissionManager::DBOperationType mOperation;
     414             :   int64_t mID;
     415             : };
     416             : 
     417           0 : class MOZ_STACK_CLASS UpgradeIPHostToOriginDB final : public UpgradeHostToOriginHelper {
     418             : public:
     419           0 :   UpgradeIPHostToOriginDB(mozIStorageConnection* aDBConn, int64_t* aID) : mDBConn(aDBConn)
     420           0 :                                                                         , mID(aID)
     421             :   {
     422           0 :     mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     423             :       "INSERT INTO moz_perms"
     424             :       "(id, origin, type, permission, expireType, expireTime, modificationTime) "
     425           0 :       "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mStmt));
     426             : 
     427           0 :     mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     428             :       "SELECT id FROM moz_perms WHERE origin = ?1 AND type = ?2"),
     429           0 :       getter_AddRefs(mLookupStmt));
     430           0 :   }
     431             : 
     432             :   nsresult
     433           0 :   Insert(const nsACString& aOrigin, const nsCString& aType,
     434             :          uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
     435             :          int64_t aModificationTime) final
     436             :   {
     437             :     // Every time the migration code wants to insert an origin into
     438             :     // the database we need to check to see if someone has already
     439             :     // created a permissions entry for that permission. If they have,
     440             :     // we don't want to insert a duplicate row.
     441             :     //
     442             :     // We can afford to do this lookup unconditionally and not perform
     443             :     // caching, as a origin type pair should only be attempted to be
     444             :     // inserted once.
     445             : 
     446           0 :     nsresult rv = mLookupStmt->Reset();
     447           0 :     NS_ENSURE_SUCCESS(rv, rv);
     448             : 
     449           0 :     rv = mLookupStmt->BindUTF8StringByIndex(0, aOrigin);
     450           0 :     NS_ENSURE_SUCCESS(rv, rv);
     451             : 
     452           0 :     rv = mLookupStmt->BindUTF8StringByIndex(1, aType);
     453           0 :     NS_ENSURE_SUCCESS(rv, rv);
     454             : 
     455             :     // Check if we already have the row in the database, if we do, then
     456             :     // we don't want to be inserting it again.
     457           0 :     bool moreStmts = false;
     458           0 :     if (NS_FAILED(mLookupStmt->ExecuteStep(&moreStmts)) || moreStmts) {
     459           0 :       mLookupStmt->Reset();
     460             :       NS_WARNING("A permissions entry was going to be re-migrated, "
     461           0 :                  "but was already found in the permissions database.");
     462           0 :       return NS_OK;
     463             :     }
     464             : 
     465             :     // Actually insert the statement into the database.
     466           0 :     rv = mStmt->BindInt64ByIndex(0, *mID);
     467           0 :     NS_ENSURE_SUCCESS(rv, rv);
     468             : 
     469           0 :     rv = mStmt->BindUTF8StringByIndex(1, aOrigin);
     470           0 :     NS_ENSURE_SUCCESS(rv, rv);
     471             : 
     472           0 :     rv = mStmt->BindUTF8StringByIndex(2, aType);
     473           0 :     NS_ENSURE_SUCCESS(rv, rv);
     474             : 
     475           0 :     rv = mStmt->BindInt32ByIndex(3, aPermission);
     476           0 :     NS_ENSURE_SUCCESS(rv, rv);
     477             : 
     478           0 :     rv = mStmt->BindInt32ByIndex(4, aExpireType);
     479           0 :     NS_ENSURE_SUCCESS(rv, rv);
     480             : 
     481           0 :     rv = mStmt->BindInt64ByIndex(5, aExpireTime);
     482           0 :     NS_ENSURE_SUCCESS(rv, rv);
     483             : 
     484           0 :     rv = mStmt->BindInt64ByIndex(6, aModificationTime);
     485           0 :     NS_ENSURE_SUCCESS(rv, rv);
     486             : 
     487             :     // Increment the working identifier, as we are about to use this one
     488           0 :     (*mID)++;
     489             : 
     490           0 :     rv = mStmt->Execute();
     491           0 :     NS_ENSURE_SUCCESS(rv, rv);
     492             : 
     493           0 :     return NS_OK;
     494             :   }
     495             : 
     496             : private:
     497             :   nsCOMPtr<mozIStorageStatement> mStmt;
     498             :   nsCOMPtr<mozIStorageStatement> mLookupStmt;
     499             :   nsCOMPtr<mozIStorageConnection> mDBConn;
     500             :   int64_t* mID;
     501             : };
     502             : 
     503             : 
     504             : nsresult
     505           0 : UpgradeHostToOriginAndInsert(const nsACString& aHost, const nsCString& aType,
     506             :                              uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
     507             :                              int64_t aModificationTime, uint32_t aAppId, bool aIsInIsolatedMozBrowserElement,
     508             :                              UpgradeHostToOriginHelper* aHelper)
     509             : {
     510           0 :   if (aHost.EqualsLiteral("<file>")) {
     511             :     // We no longer support the magic host <file>
     512             :     NS_WARNING("The magic host <file> is no longer supported. "
     513           0 :                "It is being removed from the permissions database.");
     514           0 :     return NS_OK;
     515             :   }
     516             : 
     517             :   // First, we check to see if the host is a valid URI. If it is, it can be imported directly
     518           0 :   nsCOMPtr<nsIURI> uri;
     519           0 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost);
     520           0 :   if (NS_SUCCEEDED(rv)) {
     521             :     // It was previously possible to insert useless entries to your permissions database
     522             :     // for URIs which have a null principal. This acts as a cleanup, getting rid of
     523             :     // these useless database entries
     524           0 :     bool nullpScheme = false;
     525           0 :     if (NS_SUCCEEDED(uri->SchemeIs("moz-nullprincipal", &nullpScheme)) && nullpScheme) {
     526           0 :       NS_WARNING("A moz-nullprincipal: permission is being discarded.");
     527           0 :       return NS_OK;
     528             :     }
     529             : 
     530           0 :     nsCOMPtr<nsIPrincipal> principal;
     531           0 :     rv = GetPrincipal(uri, aAppId, aIsInIsolatedMozBrowserElement, getter_AddRefs(principal));
     532           0 :     NS_ENSURE_SUCCESS(rv, rv);
     533             : 
     534           0 :     nsAutoCString origin;
     535           0 :     rv = GetOriginFromPrincipal(principal, origin);
     536           0 :     NS_ENSURE_SUCCESS(rv, rv);
     537             : 
     538             :     return aHelper->Insert(origin, aType, aPermission,
     539           0 :                            aExpireType, aExpireTime, aModificationTime);
     540             :     return NS_OK;
     541             :   }
     542             : 
     543             :   // The user may use this host at non-standard ports or protocols, we can use their history
     544             :   // to guess what ports and protocols we want to add permissions for.
     545             :   // We find every URI which they have visited with this host (or a subdomain of this host),
     546             :   // and try to add it as a principal.
     547           0 :   bool foundHistory = false;
     548             : 
     549           0 :   nsCOMPtr<nsINavHistoryService> histSrv = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
     550             : 
     551           0 :   if (histSrv) {
     552           0 :     nsCOMPtr<nsINavHistoryQuery> histQuery;
     553           0 :     rv = histSrv->GetNewQuery(getter_AddRefs(histQuery));
     554           0 :     NS_ENSURE_SUCCESS(rv, rv);
     555             : 
     556             :     // Get the eTLD+1 of the domain
     557           0 :     nsAutoCString eTLD1;
     558             :     nsCOMPtr<nsIEffectiveTLDService> tldService =
     559           0 :       do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
     560           0 :     MOZ_ASSERT(tldService); // We should always have a tldService
     561           0 :     if (tldService) {
     562           0 :       rv = tldService->GetBaseDomainFromHost(aHost, 0, eTLD1);
     563             :     }
     564             : 
     565           0 :     if (!tldService || NS_FAILED(rv)) {
     566             :       // If the lookup on the tldService for the base domain for the host failed,
     567             :       // that means that we just want to directly use the host as the host name
     568             :       // for the lookup.
     569           0 :       eTLD1 = aHost;
     570             :     }
     571             : 
     572             :     // We want to only find history items for this particular eTLD+1, and subdomains
     573           0 :     rv = histQuery->SetDomain(eTLD1);
     574           0 :     NS_ENSURE_SUCCESS(rv, rv);
     575             : 
     576           0 :     rv = histQuery->SetDomainIsHost(false);
     577           0 :     NS_ENSURE_SUCCESS(rv, rv);
     578             : 
     579           0 :     nsCOMPtr<nsINavHistoryQueryOptions> histQueryOpts;
     580           0 :     rv = histSrv->GetNewQueryOptions(getter_AddRefs(histQueryOpts));
     581           0 :     NS_ENSURE_SUCCESS(rv, rv);
     582             : 
     583             :     // We want to get the URIs for every item in the user's history with the given host
     584           0 :     rv = histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI);
     585           0 :     NS_ENSURE_SUCCESS(rv, rv);
     586             : 
     587             :     // We only search history, because searching both bookmarks and history
     588             :     // is not supported, and history tends to be more comprehensive.
     589           0 :     rv = histQueryOpts->SetQueryType(nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY);
     590           0 :     NS_ENSURE_SUCCESS(rv, rv);
     591             : 
     592             :     // We include hidden URIs (such as those visited via iFrames) as they may have permissions too
     593           0 :     rv = histQueryOpts->SetIncludeHidden(true);
     594           0 :     NS_ENSURE_SUCCESS(rv, rv);
     595             : 
     596           0 :     nsCOMPtr<nsINavHistoryResult> histResult;
     597           0 :     rv = histSrv->ExecuteQuery(histQuery, histQueryOpts, getter_AddRefs(histResult));
     598           0 :     NS_ENSURE_SUCCESS(rv, rv);
     599             : 
     600           0 :     nsCOMPtr<nsINavHistoryContainerResultNode> histResultContainer;
     601           0 :     rv = histResult->GetRoot(getter_AddRefs(histResultContainer));
     602           0 :     NS_ENSURE_SUCCESS(rv, rv);
     603             : 
     604           0 :     rv = histResultContainer->SetContainerOpen(true);
     605           0 :     NS_ENSURE_SUCCESS(rv, rv);
     606             : 
     607           0 :     uint32_t childCount = 0;
     608           0 :     rv = histResultContainer->GetChildCount(&childCount);
     609           0 :     NS_ENSURE_SUCCESS(rv, rv);
     610             : 
     611           0 :     nsTHashtable<nsCStringHashKey> insertedOrigins;
     612           0 :     for (uint32_t i = 0; i < childCount; i++) {
     613           0 :       nsCOMPtr<nsINavHistoryResultNode> child;
     614           0 :       histResultContainer->GetChild(i, getter_AddRefs(child));
     615           0 :       if (NS_WARN_IF(NS_FAILED(rv))) continue;
     616             : 
     617             :       uint32_t type;
     618           0 :       rv = child->GetType(&type);
     619           0 :       if (NS_WARN_IF(NS_FAILED(rv)) || type != nsINavHistoryResultNode::RESULT_TYPE_URI) {
     620             :         NS_WARNING("Unexpected non-RESULT_TYPE_URI node in "
     621           0 :                    "UpgradeHostToOriginAndInsert()");
     622           0 :         continue;
     623             :       }
     624             : 
     625           0 :       nsAutoCString uriSpec;
     626           0 :       rv = child->GetUri(uriSpec);
     627           0 :       if (NS_WARN_IF(NS_FAILED(rv))) continue;
     628             : 
     629           0 :       nsCOMPtr<nsIURI> uri;
     630           0 :       rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
     631           0 :       if (NS_WARN_IF(NS_FAILED(rv))) continue;
     632             : 
     633             :       // Use the provided host - this URI may be for a subdomain, rather than the host we care about.
     634           0 :       rv = uri->SetHost(aHost);
     635           0 :       if (NS_WARN_IF(NS_FAILED(rv))) continue;
     636             : 
     637             :       // We now have a URI which we can make a nsIPrincipal out of
     638           0 :       nsCOMPtr<nsIPrincipal> principal;
     639           0 :       rv = GetPrincipal(uri, aAppId, aIsInIsolatedMozBrowserElement, getter_AddRefs(principal));
     640           0 :       if (NS_WARN_IF(NS_FAILED(rv))) continue;
     641             : 
     642           0 :       nsAutoCString origin;
     643           0 :       rv = GetOriginFromPrincipal(principal, origin);
     644           0 :       if (NS_WARN_IF(NS_FAILED(rv))) continue;
     645             : 
     646             :       // Ensure that we don't insert the same origin repeatedly
     647           0 :       if (insertedOrigins.Contains(origin)) {
     648           0 :         continue;
     649             :       }
     650             : 
     651           0 :       foundHistory = true;
     652             :       rv = aHelper->Insert(origin, aType, aPermission,
     653           0 :                            aExpireType, aExpireTime, aModificationTime);
     654           0 :       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Insert failed");
     655           0 :       insertedOrigins.PutEntry(origin);
     656             :     }
     657             : 
     658           0 :     rv = histResultContainer->SetContainerOpen(false);
     659           0 :     NS_ENSURE_SUCCESS(rv, rv);
     660             :   }
     661             : 
     662             :   // If we didn't find any origins for this host in the poermissions database,
     663             :   // we can insert the default http:// and https:// permissions into the database.
     664             :   // This has a relatively high liklihood of applying the permission to the correct
     665             :   // origin.
     666           0 :   if (!foundHistory) {
     667           0 :     nsAutoCString hostSegment;
     668           0 :     nsCOMPtr<nsIPrincipal> principal;
     669           0 :     nsAutoCString origin;
     670             : 
     671             :     // If this is an ipv6 URI, we need to surround it in '[', ']' before trying to
     672             :     // parse it as a URI.
     673           0 :     if (aHost.FindChar(':') != -1) {
     674           0 :       hostSegment.Assign("[");
     675           0 :       hostSegment.Append(aHost);
     676           0 :       hostSegment.Append("]");
     677             :     } else {
     678           0 :       hostSegment.Assign(aHost);
     679             :     }
     680             : 
     681             :     // http:// URI default
     682           0 :     rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + hostSegment);
     683           0 :     NS_ENSURE_SUCCESS(rv, rv);
     684             : 
     685           0 :     rv = GetPrincipal(uri, aAppId, aIsInIsolatedMozBrowserElement, getter_AddRefs(principal));
     686           0 :     NS_ENSURE_SUCCESS(rv, rv);
     687             : 
     688           0 :     rv = GetOriginFromPrincipal(principal, origin);
     689           0 :     NS_ENSURE_SUCCESS(rv, rv);
     690             : 
     691             :     aHelper->Insert(origin, aType, aPermission,
     692           0 :                     aExpireType, aExpireTime, aModificationTime);
     693             : 
     694             :     // https:// URI default
     695           0 :     rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + hostSegment);
     696           0 :     NS_ENSURE_SUCCESS(rv, rv);
     697             : 
     698           0 :     rv = GetPrincipal(uri, aAppId, aIsInIsolatedMozBrowserElement, getter_AddRefs(principal));
     699           0 :     NS_ENSURE_SUCCESS(rv, rv);
     700             : 
     701           0 :     rv = GetOriginFromPrincipal(principal, origin);
     702           0 :     NS_ENSURE_SUCCESS(rv, rv);
     703             : 
     704             :     aHelper->Insert(origin, aType, aPermission,
     705           0 :                     aExpireType, aExpireTime, aModificationTime);
     706             :   }
     707             : 
     708           0 :   return NS_OK;
     709             : }
     710             : 
     711             : static bool
     712           0 : IsExpandedPrincipal(nsIPrincipal* aPrincipal)
     713             : {
     714           0 :   nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
     715           0 :   return !!ep;
     716             : }
     717             : 
     718             : } // namespace
     719             : 
     720             : ////////////////////////////////////////////////////////////////////////////////
     721             : 
     722             : nsPermissionManager::PermissionKey*
     723          16 : nsPermissionManager::PermissionKey::CreateFromPrincipal(nsIPrincipal* aPrincipal,
     724             :                                                         nsresult& aResult)
     725             : {
     726          32 :   nsAutoCString origin;
     727          16 :   aResult = GetOriginFromPrincipal(aPrincipal, origin);
     728          16 :   if (NS_WARN_IF(NS_FAILED(aResult))) {
     729           0 :     return nullptr;
     730             :   }
     731             : 
     732          16 :   return new PermissionKey(origin);
     733             : }
     734             : 
     735             : nsPermissionManager::PermissionKey*
     736           0 : nsPermissionManager::PermissionKey::CreateFromURI(nsIURI* aURI, nsresult& aResult)
     737             : {
     738           0 :   nsAutoCString origin;
     739           0 :   aResult = ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, origin);
     740           0 :   if (NS_WARN_IF(NS_FAILED(aResult))) {
     741           0 :     return nullptr;
     742             :   }
     743             : 
     744           0 :   return new PermissionKey(origin);
     745             : }
     746             : 
     747             : /**
     748             :  * Simple callback used by |AsyncClose| to trigger a treatment once
     749             :  * the database is closed.
     750             :  *
     751             :  * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a
     752             :  * |nsPermissionManager|, this will create a cycle.
     753             :  *
     754             :  * Note: Once the callback has been called this DeleteFromMozHostListener cannot
     755             :  * be reused.
     756             :  */
     757             : class CloseDatabaseListener final : public mozIStorageCompletionCallback
     758             : {
     759           0 :   ~CloseDatabaseListener() {}
     760             : 
     761             : public:
     762             :   NS_DECL_ISUPPORTS
     763             :   NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
     764             :   /**
     765             :    * @param aManager The owning manager.
     766             :    * @param aRebuildOnSuccess If |true|, reinitialize the database once
     767             :    * it has been closed. Otherwise, do nothing such.
     768             :    */
     769             :   CloseDatabaseListener(nsPermissionManager* aManager,
     770             :                         bool aRebuildOnSuccess);
     771             : 
     772             : protected:
     773             :   RefPtr<nsPermissionManager> mManager;
     774             :   bool mRebuildOnSuccess;
     775             : };
     776             : 
     777           0 : NS_IMPL_ISUPPORTS(CloseDatabaseListener, mozIStorageCompletionCallback)
     778             : 
     779           0 : CloseDatabaseListener::CloseDatabaseListener(nsPermissionManager* aManager,
     780           0 :                                              bool aRebuildOnSuccess)
     781             :   : mManager(aManager)
     782           0 :   , mRebuildOnSuccess(aRebuildOnSuccess)
     783             : {
     784           0 : }
     785             : 
     786             : NS_IMETHODIMP
     787           0 : CloseDatabaseListener::Complete(nsresult, nsISupports*)
     788             : {
     789             :   // Help breaking cycles
     790           0 :   RefPtr<nsPermissionManager> manager = mManager.forget();
     791           0 :   if (mRebuildOnSuccess && !manager->mIsShuttingDown) {
     792           0 :     return manager->InitDB(true);
     793             :   }
     794           0 :   return NS_OK;
     795             : }
     796             : 
     797             : 
     798             : /**
     799             :  * Simple callback used by |RemoveAllInternal| to trigger closing
     800             :  * the database and reinitializing it.
     801             :  *
     802             :  * Note: Beware that, if you hold onto a |DeleteFromMozHostListener| from a
     803             :  * |nsPermissionManager|, this will create a cycle.
     804             :  *
     805             :  * Note: Once the callback has been called this DeleteFromMozHostListener cannot
     806             :  * be reused.
     807             :  */
     808             : class DeleteFromMozHostListener final : public mozIStorageStatementCallback
     809             : {
     810           0 :   ~DeleteFromMozHostListener() {}
     811             : 
     812             : public:
     813             :   NS_DECL_ISUPPORTS
     814             :   NS_DECL_MOZISTORAGESTATEMENTCALLBACK
     815             : 
     816             :   /**
     817             :    * @param aManager The owning manager.
     818             :    */
     819             :   explicit DeleteFromMozHostListener(nsPermissionManager* aManager);
     820             : 
     821             : protected:
     822             :   RefPtr<nsPermissionManager> mManager;
     823             : };
     824             : 
     825           0 : NS_IMPL_ISUPPORTS(DeleteFromMozHostListener, mozIStorageStatementCallback)
     826             : 
     827           0 : DeleteFromMozHostListener::
     828           0 : DeleteFromMozHostListener(nsPermissionManager* aManager)
     829           0 :   : mManager(aManager)
     830             : {
     831           0 : }
     832             : 
     833           0 : NS_IMETHODIMP DeleteFromMozHostListener::HandleResult(mozIStorageResultSet *)
     834             : {
     835           0 :   MOZ_CRASH("Should not get any results");
     836             : }
     837             : 
     838           0 : NS_IMETHODIMP DeleteFromMozHostListener::HandleError(mozIStorageError *)
     839             : {
     840             :   // Errors are handled in |HandleCompletion|
     841           0 :   return NS_OK;
     842             : }
     843             : 
     844           0 : NS_IMETHODIMP DeleteFromMozHostListener::HandleCompletion(uint16_t aReason)
     845             : {
     846             :   // Help breaking cycles
     847           0 :   RefPtr<nsPermissionManager> manager = mManager.forget();
     848             : 
     849           0 :   if (aReason == REASON_ERROR) {
     850           0 :     manager->CloseDB(true);
     851             :   }
     852             : 
     853           0 :   return NS_OK;
     854             : }
     855             : 
     856             : /* static */ void
     857           3 : nsPermissionManager::ClearOriginDataObserverInit()
     858             : {
     859             :   nsCOMPtr<nsIObserverService> observerService =
     860           6 :     mozilla::services::GetObserverService();
     861           6 :   observerService->AddObserver(new ClearOriginDataObserver(), "clear-origin-attributes-data", /* ownsWeak= */ false);
     862           3 : }
     863             : 
     864             : ////////////////////////////////////////////////////////////////////////////////
     865             : // nsPermissionManager Implementation
     866             : 
     867             : #define PERMISSIONS_FILE_NAME "permissions.sqlite"
     868             : #define HOSTS_SCHEMA_VERSION 9
     869             : 
     870             : #define HOSTPERM_FILE_NAME "hostperm.1"
     871             : 
     872             : // Default permissions are read from a URL - this is the preference we read
     873             : // to find that URL. If not set, don't use any default permissions.
     874             : static const char kDefaultsUrlPrefName[] = "permissions.manager.defaultsUrl";
     875             : 
     876             : static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
     877             : 
     878         277 : NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
     879             : 
     880           3 : nsPermissionManager::nsPermissionManager()
     881             :  : mMemoryOnlyDB(false)
     882             :  , mLargestID(0)
     883           3 :  , mIsShuttingDown(false)
     884             : {
     885           3 : }
     886             : 
     887           0 : nsPermissionManager::~nsPermissionManager()
     888             : {
     889             :   // NOTE: Make sure to reject each of the promises in mPermissionKeyPromiseMap
     890             :   // before destroying.
     891           0 :   for (auto iter = mPermissionKeyPromiseMap.Iter(); !iter.Done(); iter.Next()) {
     892           0 :     if (iter.Data()) {
     893           0 :       iter.Data()->Reject(NS_ERROR_FAILURE, __func__);
     894             :     }
     895             :   }
     896           0 :   mPermissionKeyPromiseMap.Clear();
     897             : 
     898           0 :   RemoveAllFromMemory();
     899           0 :   gPermissionManager = nullptr;
     900           0 : }
     901             : 
     902             : // static
     903             : nsIPermissionManager*
     904           3 : nsPermissionManager::GetXPCOMSingleton()
     905             : {
     906           3 :   if (gPermissionManager) {
     907           0 :     NS_ADDREF(gPermissionManager);
     908           0 :     return gPermissionManager;
     909             :   }
     910             : 
     911             :   // Create a new singleton nsPermissionManager.
     912             :   // We AddRef only once since XPCOM has rules about the ordering of module
     913             :   // teardowns - by the time our module destructor is called, it's too late to
     914             :   // Release our members, since GC cycles have already been completed and
     915             :   // would result in serious leaks.
     916             :   // See bug 209571.
     917           3 :   gPermissionManager = new nsPermissionManager();
     918           3 :   if (gPermissionManager) {
     919           3 :     NS_ADDREF(gPermissionManager);
     920           3 :     if (NS_FAILED(gPermissionManager->Init())) {
     921           0 :       NS_RELEASE(gPermissionManager);
     922             :     }
     923             :   }
     924             : 
     925           3 :   return gPermissionManager;
     926             : }
     927             : 
     928             : nsresult
     929           3 : nsPermissionManager::Init()
     930             : {
     931             :   // If the 'permissions.memory_only' pref is set to true, then don't write any
     932             :   // permission settings to disk, but keep them in a memory-only database.
     933           3 :   mMemoryOnlyDB = mozilla::Preferences::GetBool("permissions.memory_only", false);
     934             : 
     935           3 :   if (IsChildProcess()) {
     936             :     // Stop here; we don't need the DB in the child process. Instead we will be
     937             :     // sent permissions as we need them by our parent process.
     938           2 :     return NS_OK;
     939             :   }
     940             : 
     941             :   nsCOMPtr<nsIObserverService> observerService =
     942           2 :     mozilla::services::GetObserverService();
     943           1 :   if (observerService) {
     944           1 :     observerService->AddObserver(this, "profile-before-change", true);
     945           1 :     observerService->AddObserver(this, "profile-do-change", true);
     946             :   }
     947             : 
     948             :   // ignore failure here, since it's non-fatal (we can run fine without
     949             :   // persistent storage - e.g. if there's no profile).
     950             :   // XXX should we tell the user about this?
     951           1 :   InitDB(false);
     952             : 
     953           1 :   return NS_OK;
     954             : }
     955             : 
     956             : nsresult
     957           1 : nsPermissionManager::OpenDatabase(nsIFile* aPermissionsFile)
     958             : {
     959             :   nsresult rv;
     960           2 :   nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
     961           1 :   if (!storage) {
     962           0 :     return NS_ERROR_UNEXPECTED;
     963             :   }
     964             :   // cache a connection to the hosts database
     965           1 :   if (mMemoryOnlyDB) {
     966           0 :     rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
     967             :   } else {
     968           1 :     rv = storage->OpenDatabase(aPermissionsFile, getter_AddRefs(mDBConn));
     969             :   }
     970           1 :   return rv;
     971             : }
     972             : 
     973             : nsresult
     974           1 : nsPermissionManager::InitDB(bool aRemoveFile)
     975             : {
     976           2 :   nsCOMPtr<nsIFile> permissionsFile;
     977           1 :   nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR, getter_AddRefs(permissionsFile));
     978           1 :   if (NS_FAILED(rv)) {
     979           1 :     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
     980             :   }
     981           1 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
     982             : 
     983           1 :   rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(PERMISSIONS_FILE_NAME));
     984           1 :   NS_ENSURE_SUCCESS(rv, rv);
     985             : 
     986           1 :   if (aRemoveFile) {
     987           0 :     bool exists = false;
     988           0 :     rv = permissionsFile->Exists(&exists);
     989           0 :     NS_ENSURE_SUCCESS(rv, rv);
     990           0 :     if (exists) {
     991           0 :       rv = permissionsFile->Remove(false);
     992           0 :       NS_ENSURE_SUCCESS(rv, rv);
     993             :     }
     994             :   }
     995             : 
     996           1 :   rv = OpenDatabase(permissionsFile);
     997           1 :   if (rv == NS_ERROR_FILE_CORRUPTED) {
     998           0 :     LogToConsole(NS_LITERAL_STRING("permissions.sqlite is corrupted! Try again!"));
     999             : 
    1000             :     // Add telemetry probe
    1001           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::PERMISSIONS_SQL_CORRUPTED, 1);
    1002             : 
    1003             :     // delete corrupted permissions.sqlite and try again
    1004           0 :     rv = permissionsFile->Remove(false);
    1005           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1006           0 :     LogToConsole(NS_LITERAL_STRING("Corrupted permissions.sqlite has been removed."));
    1007             : 
    1008           0 :     rv = OpenDatabase(permissionsFile);
    1009           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1010           0 :     LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!"));
    1011           1 :   } else if (NS_FAILED(rv)) {
    1012           0 :     return rv;
    1013             :   }
    1014             : 
    1015             :   bool ready;
    1016           1 :   mDBConn->GetConnectionReady(&ready);
    1017           1 :   if (!ready) {
    1018           0 :     LogToConsole(NS_LITERAL_STRING("Fail to get connection to permissions.sqlite! Try again!"));
    1019             : 
    1020             :     // delete and try again
    1021           0 :     rv = permissionsFile->Remove(false);
    1022           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1023           0 :     LogToConsole(NS_LITERAL_STRING("Defective permissions.sqlite has been removed."));
    1024             : 
    1025             :     // Add telemetry probe
    1026           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::DEFECTIVE_PERMISSIONS_SQL_REMOVED, 1);
    1027             : 
    1028           0 :     rv = OpenDatabase(permissionsFile);
    1029           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1030           0 :     LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!"));
    1031             : 
    1032           0 :     mDBConn->GetConnectionReady(&ready);
    1033           0 :     if (!ready)
    1034           0 :       return NS_ERROR_UNEXPECTED;
    1035             :   }
    1036             : 
    1037             : 
    1038           1 :   bool tableExists = false;
    1039           1 :   mDBConn->TableExists(NS_LITERAL_CSTRING("moz_perms"), &tableExists);
    1040           1 :   if (!tableExists) {
    1041           0 :     mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
    1042             :   }
    1043           1 :   if (!tableExists) {
    1044           0 :     rv = CreateTable();
    1045           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1046             :   } else {
    1047             :     // table already exists; check the schema version before reading
    1048             :     int32_t dbSchemaVersion;
    1049           1 :     rv = mDBConn->GetSchemaVersion(&dbSchemaVersion);
    1050           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1051             : 
    1052           1 :     switch (dbSchemaVersion) {
    1053             :     // upgrading.
    1054             :     // every time you increment the database schema, you need to implement
    1055             :     // the upgrading code from the previous version to the new one.
    1056             :     // fall through to current version
    1057             : 
    1058             :     case 1:
    1059             :       {
    1060             :         // previous non-expiry version of database.  Upgrade it by adding the
    1061             :         // expiration columns
    1062           0 :         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1063           0 :               "ALTER TABLE moz_hosts ADD expireType INTEGER"));
    1064           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1065             : 
    1066           0 :         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1067           0 :               "ALTER TABLE moz_hosts ADD expireTime INTEGER"));
    1068           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1069             :       }
    1070             : 
    1071             :       // fall through to the next upgrade
    1072             :       MOZ_FALLTHROUGH;
    1073             : 
    1074             :     // TODO: we want to make default version as version 2 in order to fix bug 784875.
    1075             :     case 0:
    1076             :     case 2:
    1077             :       {
    1078             :         // Add appId/isInBrowserElement fields.
    1079           0 :         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1080           0 :               "ALTER TABLE moz_hosts ADD appId INTEGER"));
    1081           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1082             : 
    1083           0 :         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1084           0 :               "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
    1085           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1086             : 
    1087           0 :         rv = mDBConn->SetSchemaVersion(3);
    1088           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1089             :       }
    1090             : 
    1091             :       // fall through to the next upgrade
    1092             :       MOZ_FALLTHROUGH;
    1093             : 
    1094             :     // Version 3->4 is the creation of the modificationTime field.
    1095             :     case 3:
    1096             :       {
    1097           0 :         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1098           0 :               "ALTER TABLE moz_hosts ADD modificationTime INTEGER"));
    1099           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1100             : 
    1101             :         // We leave the modificationTime at zero for all existing records; using
    1102             :         // now() would mean, eg, that doing "remove all from the last hour"
    1103             :         // within the first hour after migration would remove all permissions.
    1104             : 
    1105           0 :         rv = mDBConn->SetSchemaVersion(4);
    1106           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1107             :       }
    1108             : 
    1109             :       // fall through to the next upgrade
    1110             :       MOZ_FALLTHROUGH;
    1111             : 
    1112             :     // In version 5, host appId, and isInBrowserElement were merged into a
    1113             :     // single origin entry
    1114             :     //
    1115             :     // In version 6, the tables were renamed for backwards compatability reasons
    1116             :     // with version 4 and earlier.
    1117             :     //
    1118             :     // In version 7, a bug in the migration used for version 4->5 was discovered
    1119             :     // which could have triggered data-loss. Because of that, all users with a
    1120             :     // version 4, 5, or 6 database will be re-migrated from the backup database.
    1121             :     // (bug 1186034). This migration bug is not present after bug 1185340, and the
    1122             :     // re-migration ensures that all users have the fix.
    1123             :     case 5:
    1124             :       // This branch could also be reached via dbSchemaVersion == 3, in which case
    1125             :       // we want to fall through to the dbSchemaVersion == 4 case.
    1126             :       // The easiest way to do that is to perform this extra check here to make
    1127             :       // sure that we didn't get here via a fallthrough from v3
    1128           0 :       if (dbSchemaVersion == 5) {
    1129             :         // In version 5, the backup database is named moz_hosts_v4. We perform
    1130             :         // the version 5->6 migration to get the tables to have consistent
    1131             :         // naming conventions.
    1132             : 
    1133             :         // Version 5->6 is the renaming of moz_hosts to moz_perms, and
    1134             :         // moz_hosts_v4 to moz_hosts (bug 1185343)
    1135             :         //
    1136             :         // In version 5, we performed the modifications to the permissions
    1137             :         // database in place, this meant that if you upgraded to a version which
    1138             :         // used V5, and then downgraded to a version which used v4 or earlier,
    1139             :         // the fallback path would drop the table, and your permissions data
    1140             :         // would be lost. This migration undoes that mistake, by restoring the
    1141             :         // old moz_hosts table (if it was present), and instead using the new
    1142             :         // table moz_perms for the new permissions schema.
    1143             :         //
    1144             :         // NOTE: If you downgrade, store new permissions, and then upgrade
    1145             :         // again, these new permissions won't be migrated or reflected in the
    1146             :         // updated database. This migration only occurs once, as if moz_perms
    1147             :         // exists, it will skip creating it. In addition, permissions added
    1148             :         // after the migration will not be visible in previous versions of
    1149             :         // firefox.
    1150             : 
    1151           0 :         bool permsTableExists = false;
    1152           0 :         mDBConn->TableExists(NS_LITERAL_CSTRING("moz_perms"), &permsTableExists);
    1153           0 :         if (!permsTableExists) {
    1154             :           // Move the upgraded database to moz_perms
    1155           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1156           0 :                  "ALTER TABLE moz_hosts RENAME TO moz_perms"));
    1157           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1158             :         } else {
    1159             :           NS_WARNING("moz_hosts was not renamed to moz_perms, "
    1160           0 :                      "as a moz_perms table already exists");
    1161             : 
    1162             :           // In the situation where a moz_perms table already exists, but the
    1163             :           // schema is lower than 6, a migration has already previously occured
    1164             :           // to V6, but a downgrade has caused the moz_hosts table to be
    1165             :           // dropped. This should only occur in the case of a downgrade to a V5
    1166             :           // database, which was only present in a few day's nightlies. As that
    1167             :           // version was likely used only on a temporary basis, we assume that
    1168             :           // the database from the previous V6 has the permissions which the
    1169             :           // user actually wants to use. We have to get rid of moz_hosts such
    1170             :           // that moz_hosts_v4 can be moved into its place if it exists.
    1171           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
    1172           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1173             :         }
    1174             : 
    1175             : #ifdef DEBUG
    1176             :         // The moz_hosts table shouldn't exist anymore
    1177           0 :         bool hostsTableExists = false;
    1178           0 :         mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &hostsTableExists);
    1179           0 :         MOZ_ASSERT(!hostsTableExists);
    1180             : #endif
    1181             : 
    1182             :         // Rename moz_hosts_v4 back to it's original location, if it exists
    1183           0 :         bool v4TableExists = false;
    1184           0 :         mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_v4"), &v4TableExists);
    1185           0 :         if (v4TableExists) {
    1186           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1187           0 :                  "ALTER TABLE moz_hosts_v4 RENAME TO moz_hosts"));
    1188           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1189             :         }
    1190             : 
    1191           0 :         rv = mDBConn->SetSchemaVersion(6);
    1192           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1193             :       }
    1194             : 
    1195             :       // fall through to the next upgrade
    1196             :       MOZ_FALLTHROUGH;
    1197             : 
    1198             :     // At this point, the version 5 table has been migrated to a version 6 table
    1199             :     // We are guaranteed to have at least one of moz_hosts and moz_perms. If
    1200             :     // we have moz_hosts, we will migrate moz_hosts into moz_perms (even if
    1201             :     // we already have a moz_perms, as we need a re-migration due to bug 1186034).
    1202             :     //
    1203             :     // After this migration, we are guaranteed to have both a moz_hosts (for backwards
    1204             :     // compatability), and a moz_perms table. The moz_hosts table will have a v4 schema,
    1205             :     // and the moz_perms table will have a v6 schema.
    1206             :     case 4:
    1207             :     case 6:
    1208             :       {
    1209           0 :         bool hostsTableExists = false;
    1210           0 :         mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &hostsTableExists);
    1211           0 :         if (hostsTableExists) {
    1212           0 :           bool migrationError = false;
    1213             : 
    1214             :           // Both versions 4 and 6 have a version 4 formatted hosts table named
    1215             :           // moz_hosts. We can migrate this table to our version 7 table moz_perms.
    1216             :           // If moz_perms is present, then we can use it as a basis for comparison.
    1217             : 
    1218           0 :           rv = mDBConn->BeginTransaction();
    1219           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1220             : 
    1221           0 :           bool tableExists = false;
    1222           0 :           mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_new"), &tableExists);
    1223           0 :           if (tableExists) {
    1224           0 :             NS_WARNING("The temporary database moz_hosts_new already exists, dropping it.");
    1225           0 :             rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts_new"));
    1226           0 :             NS_ENSURE_SUCCESS(rv, rv);
    1227             :           }
    1228           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1229             :             "CREATE TABLE moz_hosts_new ("
    1230             :               " id INTEGER PRIMARY KEY"
    1231             :               ",origin TEXT"
    1232             :               ",type TEXT"
    1233             :               ",permission INTEGER"
    1234             :               ",expireType INTEGER"
    1235             :               ",expireTime INTEGER"
    1236             :               ",modificationTime INTEGER"
    1237           0 :             ")"));
    1238           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1239             : 
    1240           0 :           nsCOMPtr<mozIStorageStatement> stmt;
    1241           0 :           rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    1242             :             "SELECT host, type, permission, expireType, expireTime, "
    1243             :             "modificationTime, appId, isInBrowserElement FROM moz_hosts"),
    1244           0 :              getter_AddRefs(stmt));
    1245           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1246             : 
    1247           0 :           int64_t id = 0;
    1248           0 :           nsAutoCString host, type;
    1249             :           uint32_t permission;
    1250             :           uint32_t expireType;
    1251             :           int64_t expireTime;
    1252             :           int64_t modificationTime;
    1253             :           uint32_t appId;
    1254             :           bool isInBrowserElement;
    1255             :           bool hasResult;
    1256             : 
    1257           0 :           while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1258             :             // Read in the old row
    1259           0 :             rv = stmt->GetUTF8String(0, host);
    1260           0 :             if (NS_WARN_IF(NS_FAILED(rv))) {
    1261           0 :               migrationError = true;
    1262           0 :               continue;
    1263             :             }
    1264           0 :             rv = stmt->GetUTF8String(1, type);
    1265           0 :             if (NS_WARN_IF(NS_FAILED(rv))) {
    1266           0 :               migrationError = true;
    1267           0 :               continue;
    1268             :             }
    1269           0 :             permission = stmt->AsInt32(2);
    1270           0 :             expireType = stmt->AsInt32(3);
    1271           0 :             expireTime = stmt->AsInt64(4);
    1272           0 :             modificationTime = stmt->AsInt64(5);
    1273           0 :             if (NS_WARN_IF(stmt->AsInt64(6) < 0)) {
    1274           0 :               migrationError = true;
    1275           0 :               continue;
    1276             :             }
    1277           0 :             appId = static_cast<uint32_t>(stmt->AsInt64(6));
    1278           0 :             isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
    1279             : 
    1280             :             // Perform the meat of the migration by deferring to the
    1281             :             // UpgradeHostToOriginAndInsert function.
    1282           0 :             UpgradeHostToOriginDBMigration upHelper(mDBConn, &id);
    1283           0 :             rv = UpgradeHostToOriginAndInsert(host, type, permission,
    1284             :                                               expireType, expireTime,
    1285             :                                               modificationTime, appId,
    1286             :                                               isInBrowserElement,
    1287           0 :                                               &upHelper);
    1288           0 :             if (NS_FAILED(rv)) {
    1289             :               NS_WARNING("Unexpected failure when upgrading migrating permission "
    1290           0 :                          "from host to origin");
    1291           0 :               migrationError = true;
    1292             :             }
    1293             :           }
    1294             : 
    1295             :           // We don't drop the moz_hosts table such that it is avaliable for
    1296             :           // backwards-compatability and for future migrations in case of
    1297             :           // migration errors in the current code.
    1298             :           // Create a marker empty table which will indicate that the moz_hosts
    1299             :           // table is intended to act as a backup. If this table is not present,
    1300             :           // then the moz_hosts table was created as a random empty table.
    1301           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1302           0 :             "CREATE TABLE moz_hosts_is_backup (dummy INTEGER PRIMARY KEY)"));
    1303           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1304             : 
    1305           0 :           bool permsTableExists = false;
    1306           0 :           mDBConn->TableExists(NS_LITERAL_CSTRING("moz_perms"), &permsTableExists);
    1307           0 :           if (permsTableExists) {
    1308             :             // The user already had a moz_perms table, and we are performing a
    1309             :             // re-migration. We count the rows in the old table for telemetry,
    1310             :             // and then back up their old database as moz_perms_v6
    1311             : 
    1312           0 :             nsCOMPtr<mozIStorageStatement> countStmt;
    1313           0 :             rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(*) FROM moz_perms"),
    1314           0 :                                           getter_AddRefs(countStmt));
    1315           0 :             bool hasResult = false;
    1316           0 :             if (NS_SUCCEEDED(rv) &&
    1317           0 :                 NS_SUCCEEDED(countStmt->ExecuteStep(&hasResult)) &&
    1318             :                 hasResult) {
    1319           0 :               int32_t permsCount = countStmt->AsInt32(0);
    1320             : 
    1321             :               // The id variable contains the number of rows inserted into the
    1322             :               // moz_hosts_new table (as one ID was used per entry)
    1323             :               uint32_t telemetryValue;
    1324           0 :               if (permsCount > id) {
    1325           0 :                 telemetryValue = 3; // NEW > OLD
    1326           0 :               } else if (permsCount == id) {
    1327           0 :                 telemetryValue = 2; // NEW == OLD
    1328           0 :               } else if (permsCount == 0) {
    1329           0 :                 telemetryValue = 0; // NEW = 0
    1330             :               } else {
    1331           0 :                 telemetryValue = 1; // NEW < OLD
    1332             :               }
    1333             : 
    1334             :               // Report the telemetry value to telemetry
    1335             :               mozilla::Telemetry::Accumulate(
    1336             :                   mozilla::Telemetry::PERMISSIONS_REMIGRATION_COMPARISON,
    1337           0 :                   telemetryValue);
    1338             :             } else {
    1339           0 :               NS_WARNING("Could not count the rows in moz_perms");
    1340             :             }
    1341             : 
    1342             :             // Back up the old moz_perms database as moz_perms_v6 before we
    1343             :             // move the new table into its position
    1344           0 :             rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1345           0 :               "ALTER TABLE moz_perms RENAME TO moz_perms_v6"));
    1346           0 :             NS_ENSURE_SUCCESS(rv, rv);
    1347             :           }
    1348             : 
    1349           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1350           0 :             "ALTER TABLE moz_hosts_new RENAME TO moz_perms"));
    1351           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1352             : 
    1353           0 :           rv = mDBConn->CommitTransaction();
    1354           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1355             : 
    1356           0 :           mozilla::Telemetry::Accumulate(mozilla::Telemetry::PERMISSIONS_MIGRATION_7_ERROR,
    1357           0 :                                          NS_WARN_IF(migrationError));
    1358             :         } else {
    1359             :           // We don't have a moz_hosts table, so we create one for downgrading purposes.
    1360             :           // This table is empty.
    1361           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1362             :             "CREATE TABLE moz_hosts ("
    1363             :               " id INTEGER PRIMARY KEY"
    1364             :               ",host TEXT"
    1365             :               ",type TEXT"
    1366             :               ",permission INTEGER"
    1367             :               ",expireType INTEGER"
    1368             :               ",expireTime INTEGER"
    1369             :               ",modificationTime INTEGER"
    1370             :               ",appId INTEGER"
    1371             :               ",isInBrowserElement INTEGER"
    1372           0 :             ")"));
    1373           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1374             : 
    1375             :           // We are guaranteed to have a moz_perms table at this point.
    1376             :         }
    1377             : 
    1378             : #ifdef DEBUG
    1379             :         {
    1380             :           // At this point, both the moz_hosts and moz_perms tables should exist
    1381           0 :           bool hostsTableExists = false;
    1382           0 :           bool permsTableExists = false;
    1383           0 :           mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &hostsTableExists);
    1384           0 :           mDBConn->TableExists(NS_LITERAL_CSTRING("moz_perms"), &permsTableExists);
    1385           0 :           MOZ_ASSERT(hostsTableExists && permsTableExists);
    1386             :         }
    1387             : #endif
    1388             : 
    1389           0 :         rv = mDBConn->SetSchemaVersion(7);
    1390           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1391             :       }
    1392             : 
    1393             :       // fall through to the next upgrade
    1394             :       MOZ_FALLTHROUGH;
    1395             : 
    1396             :     // The version 7-8 migration is the re-migration of localhost and ip-address
    1397             :     // entries due to errors in the previous version 7 migration which caused
    1398             :     // localhost and ip-address entries to be incorrectly discarded.
    1399             :     // The version 7 migration logic has been corrected, and thus this logic only
    1400             :     // needs to execute if the user is currently on version 7.
    1401             :     case 7:
    1402             :       {
    1403             :         // This migration will be relatively expensive as we need to perform
    1404             :         // database lookups for each origin which we want to insert. Fortunately,
    1405             :         // it shouldn't be too expensive as we only want to insert a small number
    1406             :         // of entries created for localhost or IP addresses.
    1407             : 
    1408             :         // We only want to perform the re-migration if moz_hosts is a backup
    1409           0 :         bool hostsIsBackupExists = false;
    1410           0 :         mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_is_backup"),
    1411           0 :                              &hostsIsBackupExists);
    1412             : 
    1413             :         // Only perform this migration if the original schema version was 7, and
    1414             :         // the moz_hosts table is a backup.
    1415           0 :         if (dbSchemaVersion == 7 && hostsIsBackupExists) {
    1416             :           nsCOMPtr<nsIEffectiveTLDService> tldService =
    1417           0 :             do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
    1418           0 :           MOZ_ASSERT(tldService); // We should always have a tldService
    1419             : 
    1420           0 :           nsCOMPtr<mozIStorageStatement> stmt;
    1421           0 :           rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    1422             :             "SELECT host, type, permission, expireType, expireTime, "
    1423             :             "modificationTime, appId, isInBrowserElement FROM moz_hosts"),
    1424           0 :              getter_AddRefs(stmt));
    1425           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1426             : 
    1427           0 :           nsCOMPtr<mozIStorageStatement> idStmt;
    1428           0 :           rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    1429           0 :             "SELECT MAX(id) FROM moz_hosts"), getter_AddRefs(idStmt));
    1430           0 :           int64_t id = 0;
    1431           0 :           bool hasResult = false;
    1432           0 :           if (NS_SUCCEEDED(rv) &&
    1433           0 :               NS_SUCCEEDED(idStmt->ExecuteStep(&hasResult)) &&
    1434             :               hasResult) {
    1435           0 :             id = idStmt->AsInt32(0) + 1;
    1436             :           }
    1437             : 
    1438           0 :           nsAutoCString host, type;
    1439             :           uint32_t permission;
    1440             :           uint32_t expireType;
    1441             :           int64_t expireTime;
    1442             :           int64_t modificationTime;
    1443             :           uint32_t appId;
    1444             :           bool isInBrowserElement;
    1445             : 
    1446           0 :           while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1447             :             // Read in the old row
    1448           0 :             rv = stmt->GetUTF8String(0, host);
    1449           0 :             if (NS_WARN_IF(NS_FAILED(rv))) {
    1450           0 :               continue;
    1451             :             }
    1452             : 
    1453           0 :             nsAutoCString eTLD1;
    1454           0 :             rv = tldService->GetBaseDomainFromHost(host, 0, eTLD1);
    1455           0 :             if (NS_SUCCEEDED(rv)) {
    1456             :               // We only care about entries which the tldService can't handle
    1457           0 :               continue;
    1458             :             }
    1459             : 
    1460           0 :             rv = stmt->GetUTF8String(1, type);
    1461           0 :             if (NS_WARN_IF(NS_FAILED(rv))) {
    1462           0 :               continue;
    1463             :             }
    1464           0 :             permission = stmt->AsInt32(2);
    1465           0 :             expireType = stmt->AsInt32(3);
    1466           0 :             expireTime = stmt->AsInt64(4);
    1467           0 :             modificationTime = stmt->AsInt64(5);
    1468           0 :             if (NS_WARN_IF(stmt->AsInt64(6) < 0)) {
    1469           0 :               continue;
    1470             :             }
    1471           0 :             appId = static_cast<uint32_t>(stmt->AsInt64(6));
    1472           0 :             isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
    1473             : 
    1474             :             // Perform the meat of the migration by deferring to the
    1475             :             // UpgradeHostToOriginAndInsert function.
    1476           0 :             UpgradeIPHostToOriginDB upHelper(mDBConn, &id);
    1477           0 :             rv = UpgradeHostToOriginAndInsert(host, type, permission,
    1478             :                                               expireType, expireTime,
    1479             :                                               modificationTime, appId,
    1480             :                                               isInBrowserElement,
    1481           0 :                                               &upHelper);
    1482           0 :             if (NS_FAILED(rv)) {
    1483             :               NS_WARNING("Unexpected failure when upgrading migrating permission "
    1484           0 :                          "from host to origin");
    1485             :             }
    1486             :           }
    1487             :         }
    1488             : 
    1489             :         // Even if we didn't perform the migration, we want to bump the schema
    1490             :         // version to 8.
    1491           0 :         rv = mDBConn->SetSchemaVersion(8);
    1492           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1493             :       }
    1494             : 
    1495             :       // fall through to the next upgrade
    1496             :       MOZ_FALLTHROUGH;
    1497             : 
    1498             :     // The version 8-9 migration removes the unnecessary backup moz-hosts database contents.
    1499             :     // as the data no longer needs to be migrated
    1500             :     case 8:
    1501             :       {
    1502             :         // We only want to clear out the old table if it is a backup. If it isn't a backup,
    1503             :         // we don't need to touch it.
    1504           0 :         bool hostsIsBackupExists = false;
    1505           0 :         mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_is_backup"),
    1506           0 :                              &hostsIsBackupExists);
    1507           0 :         if (hostsIsBackupExists) {
    1508             :           // Delete everything from the backup, we want to keep around the table so that
    1509             :           // you can still downgrade and not break things, but we don't need to keep the
    1510             :           // rows around.
    1511           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_hosts"));
    1512           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1513             : 
    1514             :           // The table is no longer a backup, so get rid of it.
    1515           0 :           rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts_is_backup"));
    1516           0 :           NS_ENSURE_SUCCESS(rv, rv);
    1517             :         }
    1518             : 
    1519           0 :         rv = mDBConn->SetSchemaVersion(9);
    1520           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1521             :       }
    1522             : 
    1523             :       // fall through to the next upgrade
    1524             :       MOZ_FALLTHROUGH;
    1525             : 
    1526             :     // current version.
    1527             :     case HOSTS_SCHEMA_VERSION:
    1528           1 :       break;
    1529             : 
    1530             :     // downgrading.
    1531             :     // if columns have been added to the table, we can still use the ones we
    1532             :     // understand safely. if columns have been deleted or altered, just
    1533             :     // blow away the table and start from scratch! if you change the way
    1534             :     // a column is interpreted, make sure you also change its name so this
    1535             :     // check will catch it.
    1536             :     default:
    1537             :       {
    1538             :         // check if all the expected columns exist
    1539           0 :         nsCOMPtr<mozIStorageStatement> stmt;
    1540           0 :         rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    1541             :           "SELECT origin, type, permission, expireType, expireTime, "
    1542             :           "modificationTime FROM moz_perms"),
    1543           0 :            getter_AddRefs(stmt));
    1544           0 :         if (NS_SUCCEEDED(rv))
    1545           0 :           break;
    1546             : 
    1547             :         // our columns aren't there - drop the table!
    1548           0 :         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_perms"));
    1549           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1550             : 
    1551           0 :         rv = CreateTable();
    1552           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1553             :       }
    1554           0 :       break;
    1555             :     }
    1556             :   }
    1557             : 
    1558             :   // cache frequently used statements (for insertion, deletion, and updating)
    1559           4 :   rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1560             :     "INSERT INTO moz_perms "
    1561             :     "(id, origin, type, permission, expireType, expireTime, modificationTime) "
    1562           4 :     "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mStmtInsert));
    1563           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1564             : 
    1565           4 :   rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1566             :     "DELETE FROM moz_perms "
    1567           4 :     "WHERE id = ?1"), getter_AddRefs(mStmtDelete));
    1568           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1569             : 
    1570           4 :   rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1571             :     "UPDATE moz_perms "
    1572             :     "SET permission = ?2, expireType= ?3, expireTime = ?4, modificationTime = ?5 WHERE id = ?1"),
    1573           4 :     getter_AddRefs(mStmtUpdate));
    1574           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1575             : 
    1576             :   // Always import default permissions.
    1577           1 :   ImportDefaults();
    1578             :   // check whether to import or just read in the db
    1579           1 :   if (tableExists)
    1580           1 :     return Read();
    1581             : 
    1582           0 :   return Import();
    1583             : }
    1584             : 
    1585             : // sets the schema version and creates the moz_perms table.
    1586             : nsresult
    1587           0 : nsPermissionManager::CreateTable()
    1588             : {
    1589             :   // set the schema version, before creating the table
    1590           0 :   nsresult rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
    1591           0 :   if (NS_FAILED(rv)) return rv;
    1592             : 
    1593             :   // create the table
    1594             :   // SQL also lives in automation.py.in. If you change this SQL change that
    1595             :   // one too
    1596           0 :   rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1597             :     "CREATE TABLE moz_perms ("
    1598             :       " id INTEGER PRIMARY KEY"
    1599             :       ",origin TEXT"
    1600             :       ",type TEXT"
    1601             :       ",permission INTEGER"
    1602             :       ",expireType INTEGER"
    1603             :       ",expireTime INTEGER"
    1604             :       ",modificationTime INTEGER"
    1605           0 :     ")"));
    1606           0 :   if (NS_FAILED(rv)) return rv;
    1607             : 
    1608             :   // We also create a legacy V4 table, for backwards compatability,
    1609             :   // and to ensure that downgrades don't trigger a schema version change.
    1610           0 :   return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1611             :     "CREATE TABLE moz_hosts ("
    1612             :       " id INTEGER PRIMARY KEY"
    1613             :       ",host TEXT"
    1614             :       ",type TEXT"
    1615             :       ",permission INTEGER"
    1616             :       ",expireType INTEGER"
    1617             :       ",expireTime INTEGER"
    1618             :       ",modificationTime INTEGER"
    1619             :       ",appId INTEGER"
    1620             :       ",isInBrowserElement INTEGER"
    1621           0 :     ")"));
    1622             : }
    1623             : 
    1624             : NS_IMETHODIMP
    1625           0 : nsPermissionManager::Add(nsIURI     *aURI,
    1626             :                          const char *aType,
    1627             :                          uint32_t    aPermission,
    1628             :                          uint32_t    aExpireType,
    1629             :                          int64_t     aExpireTime)
    1630             : {
    1631           0 :   NS_ENSURE_ARG_POINTER(aURI);
    1632             : 
    1633           0 :   nsCOMPtr<nsIPrincipal> principal;
    1634           0 :   nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
    1635           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1636             : 
    1637           0 :   return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime);
    1638             : }
    1639             : 
    1640             : NS_IMETHODIMP
    1641           0 : nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
    1642             :                                       const char* aType, uint32_t aPermission,
    1643             :                                       uint32_t aExpireType, int64_t aExpireTime)
    1644             : {
    1645           0 :   ENSURE_NOT_CHILD_PROCESS;
    1646           0 :   NS_ENSURE_ARG_POINTER(aPrincipal);
    1647           0 :   NS_ENSURE_ARG_POINTER(aType);
    1648           0 :   NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
    1649             :                  aExpireType == nsIPermissionManager::EXPIRE_TIME ||
    1650             :                  aExpireType == nsIPermissionManager::EXPIRE_SESSION,
    1651             :                  NS_ERROR_INVALID_ARG);
    1652             : 
    1653             :   // Skip addition if the permission is already expired. Note that EXPIRE_SESSION only
    1654             :   // honors expireTime if it is nonzero.
    1655           0 :   if ((aExpireType == nsIPermissionManager::EXPIRE_TIME ||
    1656           0 :        (aExpireType == nsIPermissionManager::EXPIRE_SESSION && aExpireTime != 0)) &&
    1657           0 :       aExpireTime <= (PR_Now() / 1000)) {
    1658           0 :     return NS_OK;
    1659             :   }
    1660             : 
    1661             :   // We don't add the system principal because it actually has no URI and we
    1662             :   // always allow action for them.
    1663           0 :   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
    1664           0 :     return NS_OK;
    1665             :   }
    1666             : 
    1667             :   // Null principals can't meaningfully have persisted permissions attached to
    1668             :   // them, so we don't allow adding permissions for them.
    1669           0 :   if (aPrincipal->GetIsNullPrincipal()) {
    1670           0 :     return NS_OK;
    1671             :   }
    1672             : 
    1673             :   // Permissions may not be added to expanded principals.
    1674           0 :   if (IsExpandedPrincipal(aPrincipal)) {
    1675           0 :     return NS_ERROR_INVALID_ARG;
    1676             :   }
    1677             : 
    1678             :   // A modificationTime of zero will cause AddInternal to use now().
    1679           0 :   int64_t modificationTime = 0;
    1680             : 
    1681           0 :   return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
    1682           0 :                      aExpireType, aExpireTime, modificationTime, eNotify, eWriteToDB);
    1683             : }
    1684             : 
    1685             : nsresult
    1686          14 : nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
    1687             :                                  const nsCString& aType,
    1688             :                                  uint32_t              aPermission,
    1689             :                                  int64_t               aID,
    1690             :                                  uint32_t              aExpireType,
    1691             :                                  int64_t               aExpireTime,
    1692             :                                  int64_t               aModificationTime,
    1693             :                                  NotifyOperationType   aNotifyOperation,
    1694             :                                  DBOperationType       aDBOperation,
    1695             :                                  const bool            aIgnoreSessionPermissions)
    1696             : {
    1697          28 :   nsAutoCString origin;
    1698          14 :   nsresult rv = GetOriginFromPrincipal(aPrincipal, origin);
    1699          14 :   NS_ENSURE_SUCCESS(rv, rv);
    1700             : 
    1701          14 :   if (!IsChildProcess()) {
    1702             :     IPC::Permission permission(origin, aType, aPermission,
    1703          20 :                                aExpireType, aExpireTime);
    1704             : 
    1705          20 :     nsAutoCString permissionKey;
    1706          10 :     GetKeyForPermission(aPrincipal, aType.get(), permissionKey);
    1707             : 
    1708          20 :     nsTArray<ContentParent*> cplist;
    1709          10 :     ContentParent::GetAll(cplist);
    1710          10 :     for (uint32_t i = 0; i < cplist.Length(); ++i) {
    1711           0 :       ContentParent* cp = cplist[i];
    1712           0 :       if (cp->NeedsPermissionsUpdate(permissionKey))
    1713           0 :         Unused << cp->SendAddPermission(permission);
    1714             :     }
    1715             :   }
    1716             : 
    1717          14 :   MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType.get()));
    1718             : 
    1719             :   // look up the type index
    1720          14 :   int32_t typeIndex = GetTypeIndex(aType.get(), true);
    1721          14 :   NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
    1722             : 
    1723             :   // When an entry already exists, PutEntry will return that, instead
    1724             :   // of adding a new one
    1725             :   RefPtr<PermissionKey> key =
    1726          28 :     PermissionKey::CreateFromPrincipal(aPrincipal, rv);
    1727          14 :   if (!key) {
    1728           0 :     MOZ_ASSERT(NS_FAILED(rv));
    1729           0 :     return rv;
    1730             :   }
    1731             : 
    1732          14 :   PermissionHashKey* entry = mPermissionTable.PutEntry(key);
    1733          14 :   if (!entry) return NS_ERROR_FAILURE;
    1734          14 :   if (!entry->GetKey()) {
    1735           0 :     mPermissionTable.RemoveEntry(entry);
    1736           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1737             :   }
    1738             : 
    1739             :   // figure out the transaction type, and get any existing permission value
    1740             :   OperationType op;
    1741          14 :   int32_t index = entry->GetPermissionIndex(typeIndex);
    1742          14 :   if (index == -1) {
    1743          14 :     if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
    1744           0 :       op = eOperationNone;
    1745             :     else
    1746          14 :       op = eOperationAdding;
    1747             : 
    1748             :   } else {
    1749           0 :     PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
    1750             : 
    1751             :     // remove the permission if the permission is UNKNOWN, update the
    1752             :     // permission if its value or expire type have changed OR if the time has
    1753             :     // changed and the expire type is time, otherwise, don't modify.  There's
    1754             :     // no need to modify a permission that doesn't expire with time when the
    1755             :     // only thing changed is the expire time.
    1756           0 :     if (aPermission == oldPermissionEntry.mPermission &&
    1757           0 :         aExpireType == oldPermissionEntry.mExpireType &&
    1758           0 :         (aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
    1759           0 :          aExpireTime == oldPermissionEntry.mExpireTime))
    1760           0 :       op = eOperationNone;
    1761           0 :     else if (oldPermissionEntry.mID == cIDPermissionIsDefault)
    1762             :       // The existing permission is one added as a default and the new permission
    1763             :       // doesn't exactly match so we are replacing the default.  This is true
    1764             :       // even if the new permission is UNKNOWN_ACTION (which means a "logical
    1765             :       // remove" of the default)
    1766           0 :       op = eOperationReplacingDefault;
    1767           0 :     else if (aID == cIDPermissionIsDefault)
    1768             :       // We are adding a default permission but a "real" permission already
    1769             :       // exists.  This almost-certainly means we just did a removeAllSince and
    1770             :       // are re-importing defaults - so we can ignore this.
    1771           0 :       op = eOperationNone;
    1772           0 :     else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
    1773           0 :       op = eOperationRemoving;
    1774             :     else
    1775           0 :       op = eOperationChanging;
    1776             :   }
    1777             : 
    1778             :   // child processes should *always* be passed a modificationTime of zero.
    1779          14 :   MOZ_ASSERT(!IsChildProcess() || aModificationTime == 0);
    1780             : 
    1781             :   // do the work for adding, deleting, or changing a permission:
    1782             :   // update the in-memory list, write to the db, and notify consumers.
    1783             :   int64_t id;
    1784          14 :   if (aModificationTime == 0) {
    1785          14 :     aModificationTime = PR_Now() / 1000;
    1786             :   }
    1787             : 
    1788          14 :   switch (op) {
    1789             :   case eOperationNone:
    1790             :     {
    1791             :       // nothing to do
    1792           0 :       return NS_OK;
    1793             :     }
    1794             : 
    1795             :   case eOperationAdding:
    1796             :     {
    1797          14 :       if (aDBOperation == eWriteToDB) {
    1798             :         // we'll be writing to the database - generate a known unique id
    1799           0 :         id = ++mLargestID;
    1800             :       } else {
    1801             :         // we're reading from the database - use the id already assigned
    1802          14 :         id = aID;
    1803             :       }
    1804             : 
    1805             : #ifdef MOZ_B2G
    1806             :       // When we do the initial addition of the permissions we don't want to
    1807             :       // inherit session specific permissions from other tabs or apps
    1808             :       // so we ignore them and set the permission to PROMPT_ACTION if it was
    1809             :       // previously allowed or denied by the user.
    1810             :       if (aIgnoreSessionPermissions &&
    1811             :           aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
    1812             :         aPermission = nsIPermissionManager::PROMPT_ACTION;
    1813             :         aExpireType = nsIPermissionManager::EXPIRE_NEVER;
    1814             :       }
    1815             : #endif // MOZ_B2G
    1816             : 
    1817          28 :       entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission,
    1818             :                                                             aExpireType, aExpireTime,
    1819          14 :                                                             aModificationTime));
    1820             : 
    1821             :       // Record a count of the number of preload permissions present in the
    1822             :       // content process.
    1823          14 :       if (IsPreloadPermission(mTypeArray[typeIndex].get())) {
    1824           0 :         sPreloadPermissionCount++;
    1825             :       }
    1826             : 
    1827          14 :       if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
    1828           0 :         UpdateDB(op, mStmtInsert, id, origin, aType, aPermission, aExpireType, aExpireTime, aModificationTime);
    1829             :       }
    1830             : 
    1831          14 :       if (aNotifyOperation == eNotify) {
    1832             :         NotifyObserversWithPermission(aPrincipal,
    1833           4 :                                       mTypeArray[typeIndex],
    1834             :                                       aPermission,
    1835             :                                       aExpireType,
    1836             :                                       aExpireTime,
    1837           4 :                                       u"added");
    1838             :       }
    1839             : 
    1840          14 :       break;
    1841             :     }
    1842             : 
    1843             :   case eOperationRemoving:
    1844             :     {
    1845           0 :       PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
    1846           0 :       id = oldPermissionEntry.mID;
    1847           0 :       entry->GetPermissions().RemoveElementAt(index);
    1848             : 
    1849             :       // Record a count of the number of preload permissions present in the
    1850             :       // content process.
    1851           0 :       if (IsPreloadPermission(mTypeArray[typeIndex].get())) {
    1852           0 :         sPreloadPermissionCount--;
    1853             :       }
    1854             : 
    1855           0 :       if (aDBOperation == eWriteToDB)
    1856             :         // We care only about the id here so we pass dummy values for all other
    1857             :         // parameters.
    1858           0 :         UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
    1859           0 :                  nsIPermissionManager::EXPIRE_NEVER, 0, 0);
    1860             : 
    1861           0 :       if (aNotifyOperation == eNotify) {
    1862           0 :         NotifyObserversWithPermission(aPrincipal,
    1863           0 :                                       mTypeArray[typeIndex],
    1864             :                                       oldPermissionEntry.mPermission,
    1865             :                                       oldPermissionEntry.mExpireType,
    1866             :                                       oldPermissionEntry.mExpireTime,
    1867           0 :                                       u"deleted");
    1868             :       }
    1869             : 
    1870             :       // If there are no more permissions stored for that entry, clear it.
    1871           0 :       if (entry->GetPermissions().IsEmpty()) {
    1872           0 :         mPermissionTable.RemoveEntry(entry);
    1873             :       }
    1874             : 
    1875           0 :       break;
    1876             :     }
    1877             : 
    1878             :   case eOperationChanging:
    1879             :     {
    1880           0 :       id = entry->GetPermissions()[index].mID;
    1881             : 
    1882             :       // If the new expireType is EXPIRE_SESSION, then we have to keep a
    1883             :       // copy of the previous permission/expireType values. This cached value will be
    1884             :       // used when restoring the permissions of an app.
    1885           0 :       if (entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION &&
    1886             :           aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
    1887           0 :         entry->GetPermissions()[index].mNonSessionPermission = entry->GetPermissions()[index].mPermission;
    1888           0 :         entry->GetPermissions()[index].mNonSessionExpireType = entry->GetPermissions()[index].mExpireType;
    1889           0 :         entry->GetPermissions()[index].mNonSessionExpireTime = entry->GetPermissions()[index].mExpireTime;
    1890           0 :       } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
    1891           0 :         entry->GetPermissions()[index].mNonSessionPermission = aPermission;
    1892           0 :         entry->GetPermissions()[index].mNonSessionExpireType = aExpireType;
    1893           0 :         entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime;
    1894             :       }
    1895             : 
    1896           0 :       entry->GetPermissions()[index].mPermission = aPermission;
    1897           0 :       entry->GetPermissions()[index].mExpireType = aExpireType;
    1898           0 :       entry->GetPermissions()[index].mExpireTime = aExpireTime;
    1899           0 :       entry->GetPermissions()[index].mModificationTime = aModificationTime;
    1900             : 
    1901           0 :       if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
    1902             :         // We care only about the id, the permission and expireType/expireTime/modificationTime here.
    1903             :         // We pass dummy values for all other parameters.
    1904           0 :         UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
    1905           0 :                  aPermission, aExpireType, aExpireTime, aModificationTime);
    1906             : 
    1907           0 :       if (aNotifyOperation == eNotify) {
    1908             :         NotifyObserversWithPermission(aPrincipal,
    1909           0 :                                       mTypeArray[typeIndex],
    1910             :                                       aPermission,
    1911             :                                       aExpireType,
    1912             :                                       aExpireTime,
    1913           0 :                                       u"changed");
    1914             :       }
    1915             : 
    1916           0 :       break;
    1917             :     }
    1918             :   case eOperationReplacingDefault:
    1919             :     {
    1920             :       // this is handling the case when we have an existing permission
    1921             :       // entry that was created as a "default" (and thus isn't in the DB) with
    1922             :       // an explicit permission (that may include UNKNOWN_ACTION.)
    1923             :       // Note we will *not* get here if we are replacing an already replaced
    1924             :       // default value - that is handled as eOperationChanging.
    1925             : 
    1926             :       // So this is a hybrid of eOperationAdding (as we are writing a new entry
    1927             :       // to the DB) and eOperationChanging (as we are replacing the in-memory
    1928             :       // repr and sending a "changed" notification).
    1929             : 
    1930             :       // We want a new ID even if not writing to the DB, so the modified entry
    1931             :       // in memory doesn't have the magic cIDPermissionIsDefault value.
    1932           0 :       id = ++mLargestID;
    1933             : 
    1934             :       // The default permission being replaced can't have session expiry.
    1935           0 :       NS_ENSURE_TRUE(entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION,
    1936             :                      NS_ERROR_UNEXPECTED);
    1937             :       // We don't support the new entry having any expiry - supporting that would
    1938             :       // make things far more complex and none of the permissions we set as a
    1939             :       // default support that.
    1940           0 :       NS_ENSURE_TRUE(aExpireType == EXPIRE_NEVER, NS_ERROR_UNEXPECTED);
    1941             : 
    1942             :       // update the existing entry in memory.
    1943           0 :       entry->GetPermissions()[index].mID = id;
    1944           0 :       entry->GetPermissions()[index].mPermission = aPermission;
    1945           0 :       entry->GetPermissions()[index].mExpireType = aExpireType;
    1946           0 :       entry->GetPermissions()[index].mExpireTime = aExpireTime;
    1947           0 :       entry->GetPermissions()[index].mModificationTime = aModificationTime;
    1948             : 
    1949             :       // If requested, create the entry in the DB.
    1950           0 :       if (aDBOperation == eWriteToDB) {
    1951           0 :         UpdateDB(eOperationAdding, mStmtInsert, id, origin, aType, aPermission,
    1952           0 :                  aExpireType, aExpireTime, aModificationTime);
    1953             :       }
    1954             : 
    1955           0 :       if (aNotifyOperation == eNotify) {
    1956             :         NotifyObserversWithPermission(aPrincipal,
    1957           0 :                                       mTypeArray[typeIndex],
    1958             :                                       aPermission,
    1959             :                                       aExpireType,
    1960             :                                       aExpireTime,
    1961           0 :                                       u"changed");
    1962             :       }
    1963             : 
    1964             :     }
    1965           0 :     break;
    1966             :   }
    1967             : 
    1968          14 :   return NS_OK;
    1969             : }
    1970             : 
    1971             : NS_IMETHODIMP
    1972           0 : nsPermissionManager::Remove(nsIURI*     aURI,
    1973             :                             const char* aType)
    1974             : {
    1975           0 :   NS_ENSURE_ARG_POINTER(aURI);
    1976             : 
    1977           0 :   nsCOMPtr<nsIPrincipal> principal;
    1978           0 :   nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
    1979           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1980             : 
    1981           0 :   return RemoveFromPrincipal(principal, aType);
    1982             : }
    1983             : 
    1984             : NS_IMETHODIMP
    1985           0 : nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
    1986             :                                          const char* aType)
    1987             : {
    1988           0 :   ENSURE_NOT_CHILD_PROCESS;
    1989           0 :   NS_ENSURE_ARG_POINTER(aPrincipal);
    1990           0 :   NS_ENSURE_ARG_POINTER(aType);
    1991             : 
    1992             :   // System principals are never added to the database, no need to remove them.
    1993           0 :   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
    1994           0 :     return NS_OK;
    1995             :   }
    1996             : 
    1997             :   // Permissions may not be added to expanded principals.
    1998           0 :   if (IsExpandedPrincipal(aPrincipal)) {
    1999           0 :     return NS_ERROR_INVALID_ARG;
    2000             :   }
    2001             : 
    2002             :   // AddInternal() handles removal, just let it do the work
    2003             :   return AddInternal(aPrincipal,
    2004           0 :                      nsDependentCString(aType),
    2005             :                      nsIPermissionManager::UNKNOWN_ACTION,
    2006             :                      0,
    2007             :                      nsIPermissionManager::EXPIRE_NEVER,
    2008             :                      0,
    2009             :                      0,
    2010             :                      eNotify,
    2011           0 :                      eWriteToDB);
    2012             : }
    2013             : 
    2014             : NS_IMETHODIMP
    2015           0 : nsPermissionManager::RemovePermission(nsIPermission* aPerm)
    2016             : {
    2017           0 :   if (!aPerm) {
    2018           0 :     return NS_OK;
    2019             :   }
    2020           0 :   nsCOMPtr<nsIPrincipal> principal;
    2021           0 :   nsresult rv = aPerm->GetPrincipal(getter_AddRefs(principal));
    2022           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2023             : 
    2024           0 :   nsAutoCString type;
    2025           0 :   rv = aPerm->GetType(type);
    2026           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2027             : 
    2028             :   // Permissions are uniquely identified by their principal and type.
    2029             :   // We remove the permission using these two pieces of data.
    2030           0 :   return RemoveFromPrincipal(principal, type.get());
    2031             : }
    2032             : 
    2033             : NS_IMETHODIMP
    2034           0 : nsPermissionManager::RemoveAll()
    2035             : {
    2036           0 :   ENSURE_NOT_CHILD_PROCESS;
    2037           0 :   return RemoveAllInternal(true);
    2038             : }
    2039             : 
    2040             : NS_IMETHODIMP
    2041           0 : nsPermissionManager::RemoveAllSince(int64_t aSince)
    2042             : {
    2043           0 :   ENSURE_NOT_CHILD_PROCESS;
    2044           0 :   return RemoveAllModifiedSince(aSince);
    2045             : }
    2046             : 
    2047             : void
    2048           0 : nsPermissionManager::CloseDB(bool aRebuildOnSuccess)
    2049             : {
    2050             :   // Null the statements, this will finalize them.
    2051           0 :   mStmtInsert = nullptr;
    2052           0 :   mStmtDelete = nullptr;
    2053           0 :   mStmtUpdate = nullptr;
    2054           0 :   if (mDBConn) {
    2055             :     mozIStorageCompletionCallback* cb = new CloseDatabaseListener(this,
    2056           0 :            aRebuildOnSuccess);
    2057           0 :     mozilla::DebugOnly<nsresult> rv = mDBConn->AsyncClose(cb);
    2058           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2059           0 :     mDBConn = nullptr; // Avoid race conditions
    2060             :   }
    2061           0 : }
    2062             : 
    2063             : nsresult
    2064           0 : nsPermissionManager::RemoveAllInternal(bool aNotifyObservers)
    2065             : {
    2066             :   // Remove from memory and notify immediately. Since the in-memory
    2067             :   // database is authoritative, we do not need confirmation from the
    2068             :   // on-disk database to notify observers.
    2069           0 :   RemoveAllFromMemory();
    2070             : 
    2071             :   // Re-import the defaults
    2072           0 :   ImportDefaults();
    2073             : 
    2074           0 :   if (aNotifyObservers) {
    2075           0 :     NotifyObservers(nullptr, u"cleared");
    2076             :   }
    2077             : 
    2078             :   // clear the db
    2079           0 :   if (mDBConn) {
    2080           0 :     nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
    2081           0 :     nsresult rv = mDBConn->
    2082           0 :       CreateAsyncStatement(NS_LITERAL_CSTRING(
    2083             :          "DELETE FROM moz_perms"
    2084           0 :       ), getter_AddRefs(removeStmt));
    2085           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2086           0 :     if (!removeStmt) {
    2087           0 :       return NS_ERROR_UNEXPECTED;
    2088             :     }
    2089           0 :     nsCOMPtr<mozIStoragePendingStatement> pending;
    2090           0 :     mozIStorageStatementCallback* cb = new DeleteFromMozHostListener(this);
    2091           0 :     rv = removeStmt->ExecuteAsync(cb, getter_AddRefs(pending));
    2092           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2093             : 
    2094           0 :     return rv;
    2095             :   }
    2096             : 
    2097           0 :   return NS_OK;
    2098             : }
    2099             : 
    2100             : NS_IMETHODIMP
    2101           0 : nsPermissionManager::TestExactPermission(nsIURI     *aURI,
    2102             :                                          const char *aType,
    2103             :                                          uint32_t   *aPermission)
    2104             : {
    2105           0 :   return CommonTestPermission(aURI, aType, aPermission, true, true);
    2106             : }
    2107             : 
    2108             : NS_IMETHODIMP
    2109           1 : nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
    2110             :                                                       const char* aType,
    2111             :                                                       uint32_t* aPermission)
    2112             : {
    2113           1 :   return CommonTestPermission(aPrincipal, aType, aPermission, true, true);
    2114             : }
    2115             : 
    2116             : NS_IMETHODIMP
    2117           0 : nsPermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal,
    2118             :                                                   const char* aType,
    2119             :                                                   uint32_t* aPermission)
    2120             : {
    2121           0 :   return CommonTestPermission(aPrincipal, aType, aPermission, true, false);
    2122             : }
    2123             : 
    2124             : NS_IMETHODIMP
    2125           6 : nsPermissionManager::TestPermission(nsIURI     *aURI,
    2126             :                                     const char *aType,
    2127             :                                     uint32_t   *aPermission)
    2128             : {
    2129           6 :   return CommonTestPermission(aURI, aType, aPermission, false, true);
    2130             : }
    2131             : 
    2132             : NS_IMETHODIMP
    2133           0 : nsPermissionManager::TestPermissionFromWindow(mozIDOMWindow* aWindow,
    2134             :                                               const char* aType,
    2135             :                                               uint32_t* aPermission)
    2136             : {
    2137           0 :   NS_ENSURE_ARG(aWindow);
    2138           0 :   nsCOMPtr<nsPIDOMWindowInner> window = nsPIDOMWindowInner::From(aWindow);
    2139             : 
    2140             :   // Get the document for security check
    2141           0 :   nsCOMPtr<nsIDocument> document = window->GetExtantDoc();
    2142           0 :   NS_ENSURE_TRUE(document, NS_NOINTERFACE);
    2143             : 
    2144           0 :   nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
    2145           0 :   return TestPermissionFromPrincipal(principal, aType, aPermission);
    2146             : }
    2147             : 
    2148             : NS_IMETHODIMP
    2149          13 : nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
    2150             :                                                  const char* aType,
    2151             :                                                  uint32_t* aPermission)
    2152             : {
    2153          13 :   return CommonTestPermission(aPrincipal, aType, aPermission, false, true);
    2154             : }
    2155             : 
    2156             : NS_IMETHODIMP
    2157           0 : nsPermissionManager::GetPermissionObjectForURI(nsIURI* aURI,
    2158             :                                                const char* aType,
    2159             :                                                bool aExactHostMatch,
    2160             :                                                nsIPermission** aResult)
    2161             : {
    2162           0 :   nsCOMPtr<nsIPrincipal> principal;
    2163           0 :   nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
    2164           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2165             : 
    2166           0 :   return GetPermissionObject(principal, aType, aExactHostMatch, aResult);
    2167             : }
    2168             : 
    2169             : NS_IMETHODIMP
    2170           0 : nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
    2171             :                                          const char* aType,
    2172             :                                          bool aExactHostMatch,
    2173             :                                          nsIPermission** aResult)
    2174             : {
    2175           0 :   NS_ENSURE_ARG_POINTER(aPrincipal);
    2176           0 :   NS_ENSURE_ARG_POINTER(aType);
    2177             : 
    2178           0 :   *aResult = nullptr;
    2179             : 
    2180           0 :   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
    2181           0 :     return NS_OK;
    2182             :   }
    2183             : 
    2184             :   // Querying the permission object of an nsEP is non-sensical.
    2185           0 :   if (IsExpandedPrincipal(aPrincipal)) {
    2186           0 :     return NS_ERROR_INVALID_ARG;
    2187             :   }
    2188             : 
    2189           0 :   MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType));
    2190             : 
    2191           0 :   int32_t typeIndex = GetTypeIndex(aType, false);
    2192             :   // If type == -1, the type isn't known,
    2193             :   // so just return NS_OK
    2194           0 :   if (typeIndex == -1) return NS_OK;
    2195             : 
    2196           0 :   PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
    2197           0 :   if (!entry) {
    2198           0 :     return NS_OK;
    2199             :   }
    2200             : 
    2201             :   // We don't call GetPermission(typeIndex) because that returns a fake
    2202             :   // UNKNOWN_ACTION entry if there is no match.
    2203           0 :   int32_t idx = entry->GetPermissionIndex(typeIndex);
    2204           0 :   if (-1 == idx) {
    2205           0 :     return NS_OK;
    2206             :   }
    2207             : 
    2208           0 :   nsCOMPtr<nsIPrincipal> principal;
    2209           0 :   nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
    2210           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2211             : 
    2212           0 :   PermissionEntry& perm = entry->GetPermissions()[idx];
    2213           0 :   nsCOMPtr<nsIPermission> r = nsPermission::Create(principal,
    2214           0 :                                                    mTypeArray.ElementAt(perm.mType),
    2215             :                                                    perm.mPermission,
    2216             :                                                    perm.mExpireType,
    2217           0 :                                                    perm.mExpireTime);
    2218           0 :   if (NS_WARN_IF(!r)) {
    2219           0 :     return NS_ERROR_FAILURE;
    2220             :   }
    2221           0 :   r.forget(aResult);
    2222           0 :   return NS_OK;
    2223             : }
    2224             : 
    2225             : nsresult
    2226          20 : nsPermissionManager::CommonTestPermissionInternal(nsIPrincipal* aPrincipal,
    2227             :                                                   nsIURI      * aURI,
    2228             :                                                   const char  * aType,
    2229             :                                                   uint32_t    * aPermission,
    2230             :                                                   bool          aExactHostMatch,
    2231             :                                                   bool          aIncludingSession)
    2232             : {
    2233          20 :   MOZ_ASSERT(aPrincipal || aURI);
    2234          20 :   MOZ_ASSERT_IF(aPrincipal, !aURI);
    2235          20 :   NS_ENSURE_ARG_POINTER(aPrincipal || aURI);
    2236          20 :   NS_ENSURE_ARG_POINTER(aType);
    2237             : 
    2238          20 :   if (aPrincipal && nsContentUtils::IsSystemPrincipal(aPrincipal)) {
    2239           0 :     *aPermission = nsIPermissionManager::ALLOW_ACTION;
    2240           0 :     return NS_OK;
    2241             :   }
    2242             : 
    2243             :   // Set the default.
    2244          20 :   *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
    2245             : 
    2246             :   // For expanded principals, we want to iterate over the whitelist and see
    2247             :   // if the permission is granted for any of them.
    2248          40 :   nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
    2249          20 :   if (ep) {
    2250             :     nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist;
    2251           0 :     nsresult rv = ep->GetWhiteList(&whitelist);
    2252           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2253             : 
    2254           0 :     for (size_t i = 0; i < whitelist->Length(); ++i) {
    2255             :       uint32_t perm;
    2256           0 :       rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm,
    2257           0 :                                 aExactHostMatch, aIncludingSession);
    2258           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2259           0 :       if (perm == nsIPermissionManager::ALLOW_ACTION) {
    2260           0 :         *aPermission = perm;
    2261           0 :         return NS_OK;
    2262           0 :       } else if (perm == nsIPermissionManager::PROMPT_ACTION) {
    2263             :         // Store it, but keep going to see if we can do better.
    2264           0 :         *aPermission = perm;
    2265             :       }
    2266             :     }
    2267             : 
    2268           0 :     return NS_OK;
    2269             :   }
    2270             : 
    2271             : #ifdef DEBUG
    2272             :   {
    2273          40 :     nsCOMPtr<nsIPrincipal> prin = aPrincipal;
    2274          20 :     if (!prin) {
    2275           6 :       prin = mozilla::BasePrincipal::CreateCodebasePrincipal(aURI, OriginAttributes());
    2276             :     }
    2277          20 :     MOZ_ASSERT(PermissionAvaliable(prin, aType));
    2278             :   }
    2279             : #endif
    2280             : 
    2281          20 :   int32_t typeIndex = GetTypeIndex(aType, false);
    2282             :   // If type == -1, the type isn't known,
    2283             :   // so just return NS_OK
    2284          20 :   if (typeIndex == -1) return NS_OK;
    2285             : 
    2286           0 :   PermissionHashKey* entry = aPrincipal ?
    2287           0 :     GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch) :
    2288           0 :     GetPermissionHashKey(aURI, typeIndex, aExactHostMatch);
    2289           0 :   if (!entry ||
    2290           0 :       (!aIncludingSession &&
    2291           0 :        entry->GetPermission(typeIndex).mNonSessionExpireType ==
    2292             :          nsIPermissionManager::EXPIRE_SESSION)) {
    2293           0 :     return NS_OK;
    2294             :   }
    2295             : 
    2296           0 :   *aPermission = aIncludingSession
    2297           0 :                    ? entry->GetPermission(typeIndex).mPermission
    2298           0 :                    : entry->GetPermission(typeIndex).mNonSessionPermission;
    2299             : 
    2300           0 :   return NS_OK;
    2301             : }
    2302             : 
    2303             : // Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
    2304             : // This is not simply using PermissionKey because we will walk-up domains in
    2305             : // case of |host| contains sub-domains.
    2306             : // Returns null if nothing found.
    2307             : // Also accepts host on the format "<foo>". This will perform an exact match
    2308             : // lookup as the string doesn't contain any dots.
    2309             : nsPermissionManager::PermissionHashKey*
    2310           0 : nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal,
    2311             :                                           uint32_t aType,
    2312             :                                           bool aExactHostMatch)
    2313             : {
    2314           0 :   MOZ_ASSERT(PermissionAvaliable(aPrincipal, mTypeArray[aType].get()));
    2315             : 
    2316             :   nsresult rv;
    2317             :   RefPtr<PermissionKey> key =
    2318           0 :     PermissionKey::CreateFromPrincipal(aPrincipal, rv);
    2319           0 :   if (!key) {
    2320           0 :     return nullptr;
    2321             :   }
    2322             : 
    2323           0 :   PermissionHashKey* entry = mPermissionTable.GetEntry(key);
    2324             : 
    2325           0 :   if (entry) {
    2326           0 :     PermissionEntry permEntry = entry->GetPermission(aType);
    2327             : 
    2328             :     // if the entry is expired, remove and keep looking for others.
    2329             :     // Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
    2330           0 :     if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME ||
    2331           0 :          (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION &&
    2332           0 :           permEntry.mExpireTime != 0)) &&
    2333           0 :         permEntry.mExpireTime <= (PR_Now() / 1000)) {
    2334           0 :       entry = nullptr;
    2335           0 :       RemoveFromPrincipal(aPrincipal, mTypeArray[aType].get());
    2336           0 :     } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
    2337           0 :       entry = nullptr;
    2338             :     }
    2339             :   }
    2340             : 
    2341           0 :   if (entry) {
    2342           0 :     return entry;
    2343             :   }
    2344             : 
    2345             :   // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
    2346           0 :   if (!aExactHostMatch) {
    2347             :     nsCOMPtr<nsIPrincipal> principal =
    2348           0 :       GetNextSubDomainPrincipal(aPrincipal);
    2349           0 :     if (principal) {
    2350           0 :       return GetPermissionHashKey(principal, aType, aExactHostMatch);
    2351             :     }
    2352             :   }
    2353             : 
    2354             :   // No entry, really...
    2355           0 :   return nullptr;
    2356             : }
    2357             : 
    2358             : // Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
    2359             : // This is not simply using PermissionKey because we will walk-up domains in
    2360             : // case of |host| contains sub-domains.
    2361             : // Returns null if nothing found.
    2362             : // Also accepts host on the format "<foo>". This will perform an exact match
    2363             : // lookup as the string doesn't contain any dots.
    2364             : nsPermissionManager::PermissionHashKey*
    2365           0 : nsPermissionManager::GetPermissionHashKey(nsIURI* aURI,
    2366             :                                           uint32_t aType,
    2367             :                                           bool aExactHostMatch)
    2368             : {
    2369             : #ifdef DEBUG
    2370             :   {
    2371           0 :     nsCOMPtr<nsIPrincipal> principal;
    2372           0 :     nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
    2373           0 :     MOZ_ASSERT_IF(NS_SUCCEEDED(rv),
    2374             :                   PermissionAvaliable(principal, mTypeArray[aType].get()));
    2375             :   }
    2376             : #endif
    2377             : 
    2378             :   nsresult rv;
    2379             :   RefPtr<PermissionKey> key =
    2380           0 :     PermissionKey::CreateFromURI(aURI, rv);
    2381           0 :   if (!key) {
    2382           0 :     return nullptr;
    2383             :   }
    2384             : 
    2385           0 :   PermissionHashKey* entry = mPermissionTable.GetEntry(key);
    2386             : 
    2387           0 :   if (entry) {
    2388           0 :     PermissionEntry permEntry = entry->GetPermission(aType);
    2389             : 
    2390             :     // if the entry is expired, remove and keep looking for others.
    2391             :     // Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
    2392           0 :     if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME ||
    2393           0 :          (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION &&
    2394           0 :           permEntry.mExpireTime != 0)) &&
    2395           0 :         permEntry.mExpireTime <= (PR_Now() / 1000)) {
    2396           0 :       entry = nullptr;
    2397             :       // If we need to remove a permission we mint a principal.  This is a bit
    2398             :       // inefficient, but hopefully this code path isn't super common.
    2399           0 :       nsCOMPtr<nsIPrincipal> principal;
    2400           0 :       nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
    2401           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2402           0 :         return nullptr;
    2403             :       }
    2404           0 :       RemoveFromPrincipal(principal, mTypeArray[aType].get());
    2405           0 :     } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
    2406           0 :       entry = nullptr;
    2407             :     }
    2408             :   }
    2409             : 
    2410           0 :   if (entry) {
    2411           0 :     return entry;
    2412             :   }
    2413             : 
    2414             :   // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
    2415           0 :   if (!aExactHostMatch) {
    2416           0 :     nsCOMPtr<nsIURI> uri = GetNextSubDomainURI(aURI);
    2417           0 :     if (uri) {
    2418           0 :       return GetPermissionHashKey(uri, aType, aExactHostMatch);
    2419             :     }
    2420             :   }
    2421             : 
    2422             :   // No entry, really...
    2423           0 :   return nullptr;
    2424             : }
    2425             : 
    2426           1 : NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
    2427             : {
    2428           1 :   if (XRE_IsContentProcess()) {
    2429             :     NS_WARNING("nsPermissionManager's enumerator is not avaliable in the "
    2430           0 :                "content process, as not all permissions may be avaliable.");
    2431           0 :     *aEnum = nullptr;
    2432           0 :     return NS_ERROR_NOT_AVAILABLE;
    2433             :   }
    2434             : 
    2435             :   // roll an nsCOMArray of all our permissions, then hand out an enumerator
    2436           2 :   nsCOMArray<nsIPermission> array;
    2437             : 
    2438           9 :   for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
    2439           8 :     PermissionHashKey* entry = iter.Get();
    2440          18 :     for (const auto& permEntry : entry->GetPermissions()) {
    2441             :       // Given how "default" permissions work and the possibility of them being
    2442             :       // overridden with UNKNOWN_ACTION, we might see this value here - but we
    2443             :       // do *not* want to return them via the enumerator.
    2444          10 :       if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
    2445           0 :         continue;
    2446             :       }
    2447             : 
    2448          20 :       nsCOMPtr<nsIPrincipal> principal;
    2449          10 :       nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
    2450          20 :                                            getter_AddRefs(principal));
    2451          10 :       if (NS_FAILED(rv)) {
    2452           0 :         continue;
    2453             :       }
    2454             : 
    2455             :       nsCOMPtr<nsIPermission> permission =
    2456          20 :         nsPermission::Create(principal,
    2457          10 :                              mTypeArray.ElementAt(permEntry.mType),
    2458          10 :                              permEntry.mPermission,
    2459          10 :                              permEntry.mExpireType,
    2460          30 :                              permEntry.mExpireTime);
    2461          10 :       if (NS_WARN_IF(!permission)) {
    2462           0 :         continue;
    2463             :       }
    2464          10 :       array.AppendObject(permission);
    2465             :     }
    2466             :   }
    2467             : 
    2468           1 :   return NS_NewArrayEnumerator(aEnum, array);
    2469             : }
    2470             : 
    2471           2 : NS_IMETHODIMP nsPermissionManager::GetAllForURI(nsIURI* aURI, nsISimpleEnumerator **aEnum)
    2472             : {
    2473           4 :   nsCOMArray<nsIPermission> array;
    2474             : 
    2475           4 :   nsCOMPtr<nsIPrincipal> principal;
    2476           2 :   nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
    2477           2 :   NS_ENSURE_SUCCESS(rv, rv);
    2478             : 
    2479           2 :   MOZ_ASSERT(PermissionAvaliable(principal, nullptr));
    2480             : 
    2481           4 :   RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal(principal, rv);
    2482           2 :   if (!key) {
    2483           0 :     MOZ_ASSERT(NS_FAILED(rv));
    2484           0 :     return rv;
    2485             :   }
    2486             : 
    2487           2 :   PermissionHashKey* entry = mPermissionTable.GetEntry(key);
    2488             : 
    2489           2 :   if (entry) {
    2490           0 :     for (const auto& permEntry : entry->GetPermissions()) {
    2491             :       // Only return custom permissions
    2492           0 :       if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
    2493           0 :         continue;
    2494             :       }
    2495             : 
    2496             :       nsCOMPtr<nsIPermission> permission =
    2497           0 :         nsPermission::Create(principal,
    2498           0 :                              mTypeArray.ElementAt(permEntry.mType),
    2499           0 :                              permEntry.mPermission,
    2500           0 :                              permEntry.mExpireType,
    2501           0 :                              permEntry.mExpireTime);
    2502           0 :       if (NS_WARN_IF(!permission)) {
    2503           0 :         continue;
    2504             :       }
    2505           0 :       array.AppendObject(permission);
    2506             :     }
    2507             :   }
    2508             : 
    2509           2 :   return NS_NewArrayEnumerator(aEnum, array);
    2510             : }
    2511             : 
    2512           0 : NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
    2513             : {
    2514           0 :   ENSURE_NOT_CHILD_PROCESS;
    2515             : 
    2516           0 :   if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
    2517             :     // The profile is about to change,
    2518             :     // or is going away because the application is shutting down.
    2519           0 :     mIsShuttingDown = true;
    2520           0 :     RemoveAllFromMemory();
    2521           0 :     CloseDB(false);
    2522           0 :   } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
    2523             :     // the profile has already changed; init the db from the new location
    2524           0 :     InitDB(false);
    2525             :   }
    2526             : 
    2527           0 :   return NS_OK;
    2528             : }
    2529             : 
    2530             : nsresult
    2531           0 : nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime)
    2532             : {
    2533           0 :   ENSURE_NOT_CHILD_PROCESS;
    2534             : 
    2535           0 :   nsCOMArray<nsIPermission> array;
    2536           0 :   for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
    2537           0 :     PermissionHashKey* entry = iter.Get();
    2538           0 :     for (const auto& permEntry : entry->GetPermissions()) {
    2539           0 :       if (aModificationTime > permEntry.mModificationTime) {
    2540           0 :         continue;
    2541             :       }
    2542             : 
    2543           0 :       nsCOMPtr<nsIPrincipal> principal;
    2544           0 :       nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
    2545           0 :                                            getter_AddRefs(principal));
    2546           0 :       if (NS_FAILED(rv)) {
    2547           0 :         continue;
    2548             :       }
    2549             : 
    2550             :       nsCOMPtr<nsIPermission> permission =
    2551           0 :         nsPermission::Create(principal,
    2552           0 :                              mTypeArray.ElementAt(permEntry.mType),
    2553           0 :                              permEntry.mPermission,
    2554           0 :                              permEntry.mExpireType,
    2555           0 :                              permEntry.mExpireTime);
    2556           0 :       if (NS_WARN_IF(!permission)) {
    2557           0 :         continue;
    2558             :       }
    2559           0 :       array.AppendObject(permission);
    2560             :     }
    2561             :   }
    2562             : 
    2563           0 :   for (int32_t i = 0; i<array.Count(); ++i) {
    2564           0 :     nsCOMPtr<nsIPrincipal> principal;
    2565           0 :     nsAutoCString type;
    2566             : 
    2567           0 :     nsresult rv = array[i]->GetPrincipal(getter_AddRefs(principal));
    2568           0 :     if (NS_FAILED(rv)) {
    2569           0 :       NS_ERROR("GetPrincipal() failed!");
    2570           0 :       continue;
    2571             :     }
    2572             : 
    2573           0 :     rv = array[i]->GetType(type);
    2574           0 :     if (NS_FAILED(rv)) {
    2575           0 :       NS_ERROR("GetType() failed!");
    2576           0 :       continue;
    2577             :     }
    2578             : 
    2579             :     // AddInternal handles removal, so let it do the work...
    2580           0 :     AddInternal(
    2581             :       principal,
    2582             :       type,
    2583             :       nsIPermissionManager::UNKNOWN_ACTION,
    2584             :       0,
    2585             :       nsIPermissionManager::EXPIRE_NEVER, 0, 0,
    2586             :       nsPermissionManager::eNotify,
    2587           0 :       nsPermissionManager::eWriteToDB);
    2588             :   }
    2589             :   // now re-import any defaults as they may now be required if we just deleted
    2590             :   // an override.
    2591           0 :   ImportDefaults();
    2592           0 :   return NS_OK;
    2593             : }
    2594             : 
    2595             : NS_IMETHODIMP
    2596           0 : nsPermissionManager::RemovePermissionsWithAttributes(const nsAString& aPattern)
    2597             : {
    2598           0 :   ENSURE_NOT_CHILD_PROCESS;
    2599           0 :   mozilla::OriginAttributesPattern pattern;
    2600           0 :   if (!pattern.Init(aPattern)) {
    2601           0 :     return NS_ERROR_INVALID_ARG;
    2602             :   }
    2603             : 
    2604           0 :   return RemovePermissionsWithAttributes(pattern);
    2605             : }
    2606             : 
    2607             : nsresult
    2608           0 : nsPermissionManager::RemovePermissionsWithAttributes(mozilla::OriginAttributesPattern& aPattern)
    2609             : {
    2610           0 :   nsCOMArray<nsIPermission> permissions;
    2611           0 :   for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
    2612           0 :     PermissionHashKey* entry = iter.Get();
    2613             : 
    2614           0 :     nsCOMPtr<nsIPrincipal> principal;
    2615           0 :     nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
    2616           0 :                                          getter_AddRefs(principal));
    2617           0 :     if (NS_FAILED(rv)) {
    2618           0 :       continue;
    2619             :     }
    2620             : 
    2621           0 :     if (!aPattern.Matches(principal->OriginAttributesRef())) {
    2622           0 :       continue;
    2623             :     }
    2624             : 
    2625           0 :     for (const auto& permEntry : entry->GetPermissions()) {
    2626             :       nsCOMPtr<nsIPermission> permission =
    2627           0 :         nsPermission::Create(principal,
    2628           0 :                              mTypeArray.ElementAt(permEntry.mType),
    2629           0 :                              permEntry.mPermission,
    2630           0 :                              permEntry.mExpireType,
    2631           0 :                              permEntry.mExpireTime);
    2632           0 :       if (NS_WARN_IF(!permission)) {
    2633           0 :         continue;
    2634             :       }
    2635           0 :       permissions.AppendObject(permission);
    2636             :     }
    2637             :   }
    2638             : 
    2639           0 :   for (int32_t i = 0; i < permissions.Count(); ++i) {
    2640           0 :     nsCOMPtr<nsIPrincipal> principal;
    2641           0 :     nsAutoCString type;
    2642             : 
    2643           0 :     permissions[i]->GetPrincipal(getter_AddRefs(principal));
    2644           0 :     permissions[i]->GetType(type);
    2645             : 
    2646           0 :     AddInternal(principal,
    2647             :                 type,
    2648             :                 nsIPermissionManager::UNKNOWN_ACTION,
    2649             :                 0,
    2650             :                 nsIPermissionManager::EXPIRE_NEVER,
    2651             :                 0,
    2652             :                 0,
    2653             :                 nsPermissionManager::eNotify,
    2654           0 :                 nsPermissionManager::eWriteToDB);
    2655             :   }
    2656             : 
    2657           0 :   return NS_OK;
    2658             : }
    2659             : 
    2660             : //*****************************************************************************
    2661             : //*** nsPermissionManager private methods
    2662             : //*****************************************************************************
    2663             : 
    2664             : nsresult
    2665           0 : nsPermissionManager::RemoveAllFromMemory()
    2666             : {
    2667           0 :   mLargestID = 0;
    2668           0 :   mTypeArray.Clear();
    2669           0 :   mPermissionTable.Clear();
    2670             : 
    2671           0 :   return NS_OK;
    2672             : }
    2673             : 
    2674             : // Returns -1 on failure
    2675             : int32_t
    2676          34 : nsPermissionManager::GetTypeIndex(const char *aType,
    2677             :                                   bool        aAdd)
    2678             : {
    2679          88 :   for (uint32_t i = 0; i < mTypeArray.Length(); ++i)
    2680          63 :     if (mTypeArray[i].Equals(aType))
    2681           9 :       return i;
    2682             : 
    2683          25 :   if (!aAdd) {
    2684             :     // Not found, but that is ok - we were just looking.
    2685          20 :     return -1;
    2686             :   }
    2687             : 
    2688             :   // This type was not registered before.
    2689             :   // append it to the array, without copy-constructing the string
    2690           5 :   nsCString *elem = mTypeArray.AppendElement();
    2691           5 :   if (!elem)
    2692           0 :     return -1;
    2693             : 
    2694           5 :   elem->Assign(aType);
    2695           5 :   return mTypeArray.Length() - 1;
    2696             : }
    2697             : 
    2698             : // wrapper function for mangling (host,type,perm,expireType,expireTime)
    2699             : // set into an nsIPermission.
    2700             : void
    2701           4 : nsPermissionManager::NotifyObserversWithPermission(nsIPrincipal*     aPrincipal,
    2702             :                                                    const nsCString  &aType,
    2703             :                                                    uint32_t          aPermission,
    2704             :                                                    uint32_t          aExpireType,
    2705             :                                                    int64_t           aExpireTime,
    2706             :                                                    const char16_t  *aData)
    2707             : {
    2708             :   nsCOMPtr<nsIPermission> permission =
    2709           8 :     nsPermission::Create(aPrincipal, aType, aPermission,
    2710           8 :                          aExpireType, aExpireTime);
    2711           4 :   if (permission)
    2712           4 :     NotifyObservers(permission, aData);
    2713           4 : }
    2714             : 
    2715             : // notify observers that the permission list changed. there are four possible
    2716             : // values for aData:
    2717             : // "deleted" means a permission was deleted. aPermission is the deleted permission.
    2718             : // "added"   means a permission was added. aPermission is the added permission.
    2719             : // "changed" means a permission was altered. aPermission is the new permission.
    2720             : // "cleared" means the entire permission list was cleared. aPermission is null.
    2721             : void
    2722           4 : nsPermissionManager::NotifyObservers(nsIPermission   *aPermission,
    2723             :                                      const char16_t *aData)
    2724             : {
    2725             :   nsCOMPtr<nsIObserverService> observerService =
    2726           8 :     mozilla::services::GetObserverService();
    2727           4 :   if (observerService)
    2728           4 :     observerService->NotifyObservers(aPermission,
    2729             :                                      kPermissionChangeNotification,
    2730           4 :                                      aData);
    2731           4 : }
    2732             : 
    2733             : nsresult
    2734           1 : nsPermissionManager::Read()
    2735             : {
    2736           1 :   ENSURE_NOT_CHILD_PROCESS;
    2737             : 
    2738             :   nsresult rv;
    2739             : 
    2740             :   // delete expired permissions before we read in the db
    2741             :   {
    2742             :     // this deletion has its own scope so the write lock is released when done.
    2743           2 :     nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
    2744           4 :     rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    2745             :           "DELETE FROM moz_perms WHERE expireType = ?1 AND expireTime <= ?2"),
    2746           4 :           getter_AddRefs(stmtDeleteExpired));
    2747           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2748             : 
    2749           1 :     rv = stmtDeleteExpired->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME);
    2750           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2751             : 
    2752           1 :     rv = stmtDeleteExpired->BindInt64ByIndex(1, PR_Now() / 1000);
    2753           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2754             : 
    2755             :     bool hasResult;
    2756           1 :     rv = stmtDeleteExpired->ExecuteStep(&hasResult);
    2757           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2758             :   }
    2759             : 
    2760           2 :   nsCOMPtr<mozIStorageStatement> stmt;
    2761           4 :   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    2762             :     "SELECT id, origin, type, permission, expireType, expireTime, modificationTime "
    2763           4 :     "FROM moz_perms"), getter_AddRefs(stmt));
    2764           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2765             : 
    2766             :   int64_t id;
    2767           2 :   nsAutoCString origin, type;
    2768             :   uint32_t permission;
    2769             :   uint32_t expireType;
    2770             :   int64_t expireTime;
    2771             :   int64_t modificationTime;
    2772             :   bool hasResult;
    2773           1 :   bool readError = false;
    2774             : 
    2775           1 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    2776             :     // explicitly set our entry id counter for use in AddInternal(),
    2777             :     // and keep track of the largest id so we know where to pick up.
    2778           0 :     id = stmt->AsInt64(0);
    2779           0 :     if (id > mLargestID)
    2780           0 :       mLargestID = id;
    2781             : 
    2782           0 :     rv = stmt->GetUTF8String(1, origin);
    2783           0 :     if (NS_FAILED(rv)) {
    2784           0 :       readError = true;
    2785           0 :       continue;
    2786             :     }
    2787             : 
    2788           0 :     rv = stmt->GetUTF8String(2, type);
    2789           0 :     if (NS_FAILED(rv)) {
    2790           0 :       readError = true;
    2791           0 :       continue;
    2792             :     }
    2793             : 
    2794           0 :     permission = stmt->AsInt32(3);
    2795           0 :     expireType = stmt->AsInt32(4);
    2796             : 
    2797             :     // convert into int64_t values (milliseconds)
    2798           0 :     expireTime = stmt->AsInt64(5);
    2799           0 :     modificationTime = stmt->AsInt64(6);
    2800             : 
    2801           0 :     nsCOMPtr<nsIPrincipal> principal;
    2802           0 :     nsresult rv = GetPrincipalFromOrigin(origin, getter_AddRefs(principal));
    2803           0 :     if (NS_FAILED(rv)) {
    2804           0 :       readError = true;
    2805           0 :       continue;
    2806             :     }
    2807             : 
    2808           0 :     rv = AddInternal(principal, type, permission, id, expireType, expireTime,
    2809           0 :                      modificationTime, eDontNotify, eNoDBOperation);
    2810           0 :     if (NS_FAILED(rv)) {
    2811           0 :       readError = true;
    2812           0 :       continue;
    2813             :     }
    2814             :   }
    2815             : 
    2816           1 :   if (readError) {
    2817           0 :     NS_ERROR("Error occured while reading the permissions database!");
    2818           0 :     return NS_ERROR_FAILURE;
    2819             :   }
    2820             : 
    2821           1 :   return NS_OK;
    2822             : }
    2823             : 
    2824             : static const char kMatchTypeHost[] = "host";
    2825             : static const char kMatchTypeOrigin[] = "origin";
    2826             : 
    2827             : // Import() will read a file from the profile directory and add them to the
    2828             : // database before deleting the file - ie, this is a one-shot operation that
    2829             : // will not succeed on subsequent runs as the file imported from is removed.
    2830             : nsresult
    2831           0 : nsPermissionManager::Import()
    2832             : {
    2833             :   nsresult rv;
    2834             : 
    2835           0 :   nsCOMPtr<nsIFile> permissionsFile;
    2836           0 :   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
    2837           0 :   if (NS_FAILED(rv)) return rv;
    2838             : 
    2839           0 :   rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(HOSTPERM_FILE_NAME));
    2840           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2841             : 
    2842           0 :   nsCOMPtr<nsIInputStream> fileInputStream;
    2843           0 :   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
    2844           0 :                                   permissionsFile);
    2845           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2846             : 
    2847           0 :   rv = _DoImport(fileInputStream, mDBConn);
    2848           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2849             : 
    2850             :   // we successfully imported and wrote to the DB - delete the old file.
    2851           0 :   permissionsFile->Remove(false);
    2852           0 :   return NS_OK;
    2853             : }
    2854             : 
    2855             : // ImportDefaults will read a URL with default permissions and add them to the
    2856             : // in-memory copy of permissions.  The database is *not* written to.
    2857             : nsresult
    2858           1 : nsPermissionManager::ImportDefaults()
    2859             : {
    2860           2 :   nsCString defaultsURL = mozilla::Preferences::GetCString(kDefaultsUrlPrefName);
    2861           1 :   if (defaultsURL.IsEmpty()) { // == Don't use built-in permissions.
    2862           0 :     return NS_OK;
    2863             :   }
    2864             : 
    2865           2 :   nsCOMPtr<nsIURI> defaultsURI;
    2866           1 :   nsresult rv = NS_NewURI(getter_AddRefs(defaultsURI), defaultsURL);
    2867           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2868             : 
    2869           2 :   nsCOMPtr<nsIChannel> channel;
    2870           2 :   rv = NS_NewChannel(getter_AddRefs(channel),
    2871             :                      defaultsURI,
    2872             :                      nsContentUtils::GetSystemPrincipal(),
    2873             :                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
    2874           1 :                      nsIContentPolicy::TYPE_OTHER);
    2875           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2876             : 
    2877           2 :   nsCOMPtr<nsIInputStream> inputStream;
    2878           1 :   rv = channel->Open2(getter_AddRefs(inputStream));
    2879           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2880             : 
    2881           1 :   rv = _DoImport(inputStream, nullptr);
    2882           1 :   inputStream->Close();
    2883           1 :   return rv;
    2884             : }
    2885             : 
    2886             : // _DoImport reads the specified stream and adds the parsed elements.  If
    2887             : // |conn| is passed, the imported data will be written to the database, but if
    2888             : // |conn| is null the data will be added only to the in-memory copy of the
    2889             : // database.
    2890             : nsresult
    2891           1 : nsPermissionManager::_DoImport(nsIInputStream *inputStream, mozIStorageConnection *conn)
    2892             : {
    2893           1 :   ENSURE_NOT_CHILD_PROCESS;
    2894             : 
    2895             :   nsresult rv;
    2896             :   // start a transaction on the storage db, to optimize insertions.
    2897             :   // transaction will automically commit on completion
    2898             :   // (note the transaction is a no-op if a null connection is passed)
    2899           2 :   mozStorageTransaction transaction(conn, true);
    2900             : 
    2901             :   // The DB operation - we only try and write if a connection was passed.
    2902           1 :   DBOperationType operation = conn ? eWriteToDB : eNoDBOperation;
    2903             :   // and if no DB connection was passed we assume this is a "default" permission,
    2904             :   // so use the special ID which indicates this.
    2905           1 :   int64_t id = conn ? 0 : cIDPermissionIsDefault;
    2906             : 
    2907             :   /* format is:
    2908             :    * matchtype \t type \t permission \t host
    2909             :    * Only "host" is supported for matchtype
    2910             :    * type is a string that identifies the type of permission (e.g. "cookie")
    2911             :    * permission is an integer between 1 and 15
    2912             :    */
    2913             : 
    2914             :   // Ideally we'd do this with nsILineInputString, but this is called with an
    2915             :   // nsIInputStream that comes from a resource:// URI, which doesn't support
    2916             :   // that interface.  So NS_ReadLine to the rescue...
    2917           1 :   nsLineBuffer<char> lineBuffer;
    2918           2 :   nsCString line;
    2919           1 :   bool isMore = true;
    2920          23 :   do {
    2921          23 :     rv = NS_ReadLine(inputStream, &lineBuffer, line, &isMore);
    2922          23 :     NS_ENSURE_SUCCESS(rv, rv);
    2923             : 
    2924          23 :     if (line.IsEmpty() || line.First() == '#') {
    2925          26 :       continue;
    2926             :     }
    2927             : 
    2928          20 :     nsTArray<nsCString> lineArray;
    2929             : 
    2930             :     // Split the line at tabs
    2931          10 :     ParseString(line, '\t', lineArray);
    2932             : 
    2933          10 :     if (lineArray[0].EqualsLiteral(kMatchTypeHost) &&
    2934           0 :         lineArray.Length() == 4) {
    2935           0 :       nsresult error = NS_OK;
    2936           0 :       uint32_t permission = lineArray[2].ToInteger(&error);
    2937           0 :       if (NS_FAILED(error))
    2938           0 :         continue;
    2939             : 
    2940             :       // the import file format doesn't handle modification times, so we use
    2941             :       // 0, which AddInternal will convert to now()
    2942           0 :       int64_t modificationTime = 0;
    2943             : 
    2944           0 :       UpgradeHostToOriginHostfileImport upHelper(this, operation, id);
    2945           0 :       error = UpgradeHostToOriginAndInsert(lineArray[3], lineArray[1], permission,
    2946             :                                            nsIPermissionManager::EXPIRE_NEVER, 0,
    2947             :                                            modificationTime, nsIScriptSecurityManager::NO_APP_ID,
    2948             :                                            false, &upHelper);
    2949           0 :       if (NS_FAILED(error)) {
    2950           0 :         NS_WARNING("There was a problem importing a host permission");
    2951             :       }
    2952          20 :     } else if (lineArray[0].EqualsLiteral(kMatchTypeOrigin) &&
    2953          10 :                lineArray.Length() == 4) {
    2954          10 :       nsresult error = NS_OK;
    2955          10 :       uint32_t permission = lineArray[2].ToInteger(&error);
    2956          10 :       if (NS_FAILED(error))
    2957           0 :         continue;
    2958             : 
    2959          20 :       nsCOMPtr<nsIPrincipal> principal;
    2960          10 :       error = GetPrincipalFromOrigin(lineArray[3], getter_AddRefs(principal));
    2961          10 :       if (NS_FAILED(error)) {
    2962           0 :         NS_WARNING("Couldn't import an origin permission - malformed origin");
    2963           0 :         continue;
    2964             :       }
    2965             : 
    2966             :       // the import file format doesn't handle modification times, so we use
    2967             :       // 0, which AddInternal will convert to now()
    2968          10 :       int64_t modificationTime = 0;
    2969             : 
    2970          10 :       error = AddInternal(principal, lineArray[1], permission, id,
    2971             :                           nsIPermissionManager::EXPIRE_NEVER, 0,
    2972             :                           modificationTime,
    2973             :                           eDontNotify, operation);
    2974          10 :       if (NS_FAILED(error)) {
    2975           0 :         NS_WARNING("There was a problem importing an origin permission");
    2976             :       }
    2977             :     }
    2978             : 
    2979             :   } while (isMore);
    2980             : 
    2981           1 :   return NS_OK;
    2982             : }
    2983             : 
    2984             : void
    2985           0 : nsPermissionManager::UpdateDB(OperationType aOp,
    2986             :                               mozIStorageAsyncStatement* aStmt,
    2987             :                               int64_t aID,
    2988             :                               const nsACString &aOrigin,
    2989             :                               const nsACString &aType,
    2990             :                               uint32_t aPermission,
    2991             :                               uint32_t aExpireType,
    2992             :                               int64_t aExpireTime,
    2993             :                               int64_t aModificationTime)
    2994             : {
    2995           0 :   ENSURE_NOT_CHILD_PROCESS_NORET;
    2996             : 
    2997             :   nsresult rv;
    2998             : 
    2999             :   // no statement is ok - just means we don't have a profile
    3000           0 :   if (!aStmt)
    3001           0 :     return;
    3002             : 
    3003           0 :   switch (aOp) {
    3004             :   case eOperationAdding:
    3005             :     {
    3006           0 :       rv = aStmt->BindInt64ByIndex(0, aID);
    3007           0 :       if (NS_FAILED(rv)) break;
    3008             : 
    3009           0 :       rv = aStmt->BindUTF8StringByIndex(1, aOrigin);
    3010           0 :       if (NS_FAILED(rv)) break;
    3011             : 
    3012           0 :       rv = aStmt->BindUTF8StringByIndex(2, aType);
    3013           0 :       if (NS_FAILED(rv)) break;
    3014             : 
    3015           0 :       rv = aStmt->BindInt32ByIndex(3, aPermission);
    3016           0 :       if (NS_FAILED(rv)) break;
    3017             : 
    3018           0 :       rv = aStmt->BindInt32ByIndex(4, aExpireType);
    3019           0 :       if (NS_FAILED(rv)) break;
    3020             : 
    3021           0 :       rv = aStmt->BindInt64ByIndex(5, aExpireTime);
    3022           0 :       if (NS_FAILED(rv)) break;
    3023             : 
    3024           0 :       rv = aStmt->BindInt64ByIndex(6, aModificationTime);
    3025           0 :       break;
    3026             :     }
    3027             : 
    3028             :   case eOperationRemoving:
    3029             :     {
    3030           0 :       rv = aStmt->BindInt64ByIndex(0, aID);
    3031           0 :       break;
    3032             :     }
    3033             : 
    3034             :   case eOperationChanging:
    3035             :     {
    3036           0 :       rv = aStmt->BindInt64ByIndex(0, aID);
    3037           0 :       if (NS_FAILED(rv)) break;
    3038             : 
    3039           0 :       rv = aStmt->BindInt32ByIndex(1, aPermission);
    3040           0 :       if (NS_FAILED(rv)) break;
    3041             : 
    3042           0 :       rv = aStmt->BindInt32ByIndex(2, aExpireType);
    3043           0 :       if (NS_FAILED(rv)) break;
    3044             : 
    3045           0 :       rv = aStmt->BindInt64ByIndex(3, aExpireTime);
    3046           0 :       if (NS_FAILED(rv)) break;
    3047             : 
    3048           0 :       rv = aStmt->BindInt64ByIndex(4, aModificationTime);
    3049           0 :       break;
    3050             :     }
    3051             : 
    3052             :   default:
    3053             :     {
    3054           0 :       NS_NOTREACHED("need a valid operation in UpdateDB()!");
    3055           0 :       rv = NS_ERROR_UNEXPECTED;
    3056           0 :       break;
    3057             :     }
    3058             :   }
    3059             : 
    3060           0 :   if (NS_FAILED(rv)) {
    3061           0 :     NS_WARNING("db change failed!");
    3062           0 :     return;
    3063             :   }
    3064             : 
    3065           0 :   nsCOMPtr<mozIStoragePendingStatement> pending;
    3066           0 :   rv = aStmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
    3067           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    3068             : }
    3069             : 
    3070             : NS_IMETHODIMP
    3071           0 : nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
    3072             :                                      const char* aType,
    3073             :                                      bool aExactHostMatch,
    3074             :                                      uint64_t aSessionExpireTime,
    3075             :                                      uint64_t aPersistentExpireTime)
    3076             : {
    3077           0 :   NS_ENSURE_ARG_POINTER(aPrincipal);
    3078           0 :   NS_ENSURE_ARG_POINTER(aType);
    3079             : 
    3080           0 :   uint64_t nowms = PR_Now() / 1000;
    3081           0 :   if (aSessionExpireTime < nowms || aPersistentExpireTime < nowms) {
    3082           0 :     return NS_ERROR_INVALID_ARG;
    3083             :   }
    3084             : 
    3085           0 :   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
    3086           0 :     return NS_OK;
    3087             :   }
    3088             : 
    3089             :   // Setting the expire time of an nsEP is non-sensical.
    3090           0 :   if (IsExpandedPrincipal(aPrincipal)) {
    3091           0 :     return NS_ERROR_INVALID_ARG;
    3092             :   }
    3093             : 
    3094           0 :   MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType));
    3095             : 
    3096           0 :   int32_t typeIndex = GetTypeIndex(aType, false);
    3097             :   // If type == -1, the type isn't known,
    3098             :   // so just return NS_OK
    3099           0 :   if (typeIndex == -1) return NS_OK;
    3100             : 
    3101           0 :   PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
    3102           0 :   if (!entry) {
    3103           0 :     return NS_OK;
    3104             :   }
    3105             : 
    3106           0 :   int32_t idx = entry->GetPermissionIndex(typeIndex);
    3107           0 :   if (-1 == idx) {
    3108           0 :     return NS_OK;
    3109             :   }
    3110             : 
    3111           0 :   PermissionEntry& perm = entry->GetPermissions()[idx];
    3112           0 :   if (perm.mExpireType == EXPIRE_TIME) {
    3113           0 :     perm.mExpireTime = aPersistentExpireTime;
    3114           0 :   } else if (perm.mExpireType == EXPIRE_SESSION && perm.mExpireTime != 0) {
    3115           0 :     perm.mExpireTime = aSessionExpireTime;
    3116             :   }
    3117           0 :   return NS_OK;
    3118             : }
    3119             : 
    3120             : NS_IMETHODIMP
    3121           3 : nsPermissionManager::GetPermissionsWithKey(const nsACString& aPermissionKey,
    3122             :                                            nsTArray<IPC::Permission>& aPerms)
    3123             : {
    3124           3 :   aPerms.Clear();
    3125           3 :   if (NS_WARN_IF(XRE_IsContentProcess())) {
    3126           0 :     return NS_ERROR_NOT_AVAILABLE;
    3127             :   }
    3128             : 
    3129          27 :   for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
    3130          24 :     PermissionHashKey* entry = iter.Get();
    3131             : 
    3132          40 :     nsAutoCString permissionKey;
    3133          24 :     GetKeyForOrigin(entry->GetKey()->mOrigin, permissionKey);
    3134             : 
    3135             :     // If the keys don't match, and we aren't getting the default "" key, then
    3136             :     // we can exit early. We have to keep looking if we're getting the default
    3137             :     // key, as we may see a preload permission which should be transmitted.
    3138          24 :     if (aPermissionKey != permissionKey && !aPermissionKey.IsEmpty()) {
    3139           8 :       continue;
    3140             :     }
    3141             : 
    3142          36 :     for (const auto& permEntry : entry->GetPermissions()) {
    3143             :       // Given how "default" permissions work and the possibility of them being
    3144             :       // overridden with UNKNOWN_ACTION, we might see this value here - but we
    3145             :       // do not want to send it to the content process.
    3146          20 :       if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
    3147           0 :         continue;
    3148             :       }
    3149             : 
    3150          20 :       bool isPreload = IsPreloadPermission(mTypeArray[permEntry.mType].get());
    3151          20 :       if ((isPreload && aPermissionKey.IsEmpty()) || (!isPreload && aPermissionKey == permissionKey)) {
    3152           8 :         aPerms.AppendElement(IPC::Permission(entry->GetKey()->mOrigin,
    3153           4 :                                              mTypeArray.ElementAt(permEntry.mType),
    3154           4 :                                              permEntry.mPermission,
    3155           4 :                                              permEntry.mExpireType,
    3156           8 :                                              permEntry.mExpireTime));
    3157             :       }
    3158             :     }
    3159             :   }
    3160             : 
    3161           3 :   return NS_OK;
    3162             : }
    3163             : 
    3164             : NS_IMETHODIMP
    3165           3 : nsPermissionManager::SetPermissionsWithKey(const nsACString& aPermissionKey,
    3166             :                                            nsTArray<IPC::Permission>& aPerms)
    3167             : {
    3168           3 :   if (NS_WARN_IF(XRE_IsParentProcess())) {
    3169           0 :     return NS_ERROR_NOT_AVAILABLE;
    3170             :   }
    3171             : 
    3172           6 :   RefPtr<GenericPromise::Private> promise;
    3173           3 :   bool foundKey = mPermissionKeyPromiseMap.Get(aPermissionKey, getter_AddRefs(promise));
    3174           3 :   if (promise) {
    3175           0 :     MOZ_ASSERT(foundKey);
    3176             :     // NOTE: This will resolve asynchronously, so we can mark it as resolved
    3177             :     // now, and be confident that we will have filled in the database before any
    3178             :     // callbacks run.
    3179           0 :     promise->Resolve(true, __func__);
    3180           3 :   } else if (foundKey) {
    3181             :     // NOTE: We shouldn't be sent two InitializePermissionsWithKey for the same
    3182             :     // key, but it's possible.
    3183           0 :     return NS_OK;
    3184             :   }
    3185           3 :   mPermissionKeyPromiseMap.Put(aPermissionKey, nullptr);
    3186             : 
    3187             :   // Add the permissions locally to our process
    3188           7 :   for (IPC::Permission& perm : aPerms) {
    3189           8 :     nsCOMPtr<nsIPrincipal> principal;
    3190           4 :     nsresult rv = GetPrincipalFromOrigin(perm.origin, getter_AddRefs(principal));
    3191           4 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3192           0 :       continue;
    3193             :     }
    3194             : 
    3195             : #ifdef DEBUG
    3196           8 :     nsAutoCString permissionKey;
    3197           4 :     GetKeyForPermission(principal, perm.type.get(), permissionKey);
    3198           4 :     MOZ_ASSERT(permissionKey == aPermissionKey,
    3199             :                "The permission keys which were sent over should match!");
    3200             : #endif
    3201             : 
    3202             :     // The child process doesn't care about modification times - it neither
    3203             :     // reads nor writes, nor removes them based on the date - so 0 (which
    3204             :     // will end up as now()) is fine.
    3205           4 :     uint64_t modificationTime = 0;
    3206           4 :     AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
    3207             :                 perm.expireTime, modificationTime, eNotify, eNoDBOperation,
    3208           4 :                 true /* ignoreSessionPermissions */);
    3209             :   }
    3210           3 :   return NS_OK;
    3211             : }
    3212             : 
    3213             : /* static */ void
    3214          49 : nsPermissionManager::GetKeyForOrigin(const nsACString& aOrigin, nsACString& aKey)
    3215             : {
    3216          49 :   aKey.Truncate();
    3217             : 
    3218             :   // We only key origins for http, https, and ftp URIs. All origins begin with
    3219             :   // the URL which they apply to, which means that they should begin with their
    3220             :   // scheme in the case where they are one of these interesting URIs. We don't
    3221             :   // want to actually parse the URL here however, because this can be called on
    3222             :   // hot paths.
    3223         286 :   if (!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("http:")) &&
    3224         279 :       !StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("https:")) &&
    3225          87 :       !StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("ftp:"))) {
    3226          38 :     return;
    3227             :   }
    3228             : 
    3229             :   // We need to look at the originAttributes if they are present, to make sure
    3230             :   // to remove any which we don't want. We put the rest of the origin, not
    3231             :   // including the attributes, into the key.
    3232          60 :   OriginAttributes attrs;
    3233          30 :   if (!attrs.PopulateFromOrigin(aOrigin, aKey)) {
    3234           0 :     aKey.Truncate();
    3235           0 :     return;
    3236             :   }
    3237             : 
    3238             :   // mPrivateBrowsingId must be set to false because PermissionManager is not supposed to have
    3239             :   // any knowledge of private browsing. Allowing it to be true changes the suffix being hashed.
    3240          30 :   attrs.mPrivateBrowsingId = 0;
    3241             : 
    3242             :   // Disable userContext and firstParty isolation for permissions.
    3243             :   attrs.StripAttributes(OriginAttributes::STRIP_USER_CONTEXT_ID |
    3244          30 :                         OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
    3245             : 
    3246             : #ifdef DEBUG
    3247             :   // Parse the origin string into a principal, and extract some useful
    3248             :   // information from it for assertions.
    3249          60 :   nsCOMPtr<nsIPrincipal> dbgPrincipal;
    3250          30 :   MOZ_ALWAYS_SUCCEEDS(GetPrincipalFromOrigin(aOrigin, getter_AddRefs(dbgPrincipal)));
    3251          60 :   nsCOMPtr<nsIURI> dbgUri;
    3252          30 :   MOZ_ALWAYS_SUCCEEDS(dbgPrincipal->GetURI(getter_AddRefs(dbgUri)));
    3253          60 :   nsAutoCString dbgScheme;
    3254          30 :   MOZ_ALWAYS_SUCCEEDS(dbgUri->GetScheme(dbgScheme));
    3255          30 :   MOZ_ASSERT(dbgScheme.EqualsLiteral("http") ||
    3256             :              dbgScheme.EqualsLiteral("https") ||
    3257             :              dbgScheme.EqualsLiteral("ftp"));
    3258          30 :   MOZ_ASSERT(dbgPrincipal->OriginAttributesRef() == attrs);
    3259             : #endif
    3260             : 
    3261             :   // Append the stripped suffix to the output origin key.
    3262          60 :   nsAutoCString suffix;
    3263          30 :   attrs.CreateSuffix(suffix);
    3264          30 :   aKey.Append(suffix);
    3265             : }
    3266             : 
    3267             : /* static */ void
    3268          25 : nsPermissionManager::GetKeyForPrincipal(nsIPrincipal* aPrincipal, nsACString& aKey)
    3269             : {
    3270          50 :   nsAutoCString origin;
    3271          25 :   nsresult rv = aPrincipal->GetOrigin(origin);
    3272          25 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3273           0 :     aKey.Truncate();
    3274           0 :     return;
    3275             :   }
    3276          25 :   GetKeyForOrigin(origin, aKey);
    3277             : }
    3278             : 
    3279             : /* static */ void
    3280          24 : nsPermissionManager::GetKeyForPermission(nsIPrincipal* aPrincipal, const char* aType, nsACString& aKey)
    3281             : {
    3282             :   // Preload permissions have the "" key.
    3283          24 :   if (IsPreloadPermission(aType)) {
    3284           0 :     aKey.Truncate();
    3285           0 :     return;
    3286             :   }
    3287             : 
    3288          24 :   GetKeyForPrincipal(aPrincipal, aKey);
    3289             : }
    3290             : 
    3291             : /* static */ nsTArray<nsCString>
    3292           1 : nsPermissionManager::GetAllKeysForPrincipal(nsIPrincipal* aPrincipal)
    3293             : {
    3294           1 :   MOZ_ASSERT(aPrincipal);
    3295             : 
    3296           1 :   nsTArray<nsCString> keys;
    3297           2 :   nsCOMPtr<nsIPrincipal> prin = aPrincipal;
    3298           3 :   while (prin) {
    3299             :     // Add the key to the list
    3300           1 :     nsCString* key = keys.AppendElement();
    3301           1 :     GetKeyForPrincipal(prin, *key);
    3302             : 
    3303             :     // Get the next subdomain principal and loop back around.
    3304           1 :     prin = GetNextSubDomainPrincipal(prin);
    3305             :   }
    3306             : 
    3307           1 :   MOZ_ASSERT(keys.Length() >= 1,
    3308             :              "Every principal should have at least one key.");
    3309           2 :   return keys;
    3310             : }
    3311             : 
    3312             : NS_IMETHODIMP
    3313           0 : nsPermissionManager::BroadcastPermissionsForPrincipalToAllContentProcesses(nsIPrincipal* aPrincipal)
    3314             : {
    3315           0 :   nsTArray<ContentParent*> cps;
    3316           0 :   ContentParent::GetAll(cps);
    3317           0 :   for (ContentParent* cp : cps) {
    3318           0 :     nsresult rv = cp->TransmitPermissionsForPrincipal(aPrincipal);
    3319           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3320             :   }
    3321             : 
    3322           0 :   return NS_OK;
    3323             : }
    3324             : 
    3325             : bool
    3326          36 : nsPermissionManager::PermissionAvaliable(nsIPrincipal* aPrincipal, const char* aType)
    3327             : {
    3328          36 :   if (XRE_IsContentProcess()) {
    3329          20 :     nsAutoCString permissionKey;
    3330             :     // NOTE: GetKeyForPermission accepts a null aType.
    3331          10 :     GetKeyForPermission(aPrincipal, aType, permissionKey);
    3332             : 
    3333             :     // If we have a pending promise for the permission key in question, we don't
    3334             :     // have the permission avaliable, so report a warning and return false.
    3335          20 :     RefPtr<GenericPromise::Private> promise;
    3336          10 :     if (!mPermissionKeyPromiseMap.Get(permissionKey, getter_AddRefs(promise)) || promise) {
    3337             :       // Emit a useful diagnostic warning with the permissionKey for the process
    3338             :       // which hasn't received permissions yet.
    3339           0 :       NS_WARNING(nsPrintfCString("This content process hasn't received the "
    3340           0 :                                  "permissions for %s yet", permissionKey.get()).get());
    3341           0 :       return false;
    3342             :     }
    3343             :   }
    3344          36 :   return true;
    3345             : }
    3346             : 
    3347             : NS_IMETHODIMP
    3348           0 : nsPermissionManager::WhenPermissionsAvailable(nsIPrincipal* aPrincipal,
    3349             :                                               nsIRunnable* aRunnable)
    3350             : {
    3351           0 :   MOZ_ASSERT(aRunnable);
    3352             : 
    3353           0 :   if (!XRE_IsContentProcess()) {
    3354           0 :     aRunnable->Run();
    3355           0 :     return NS_OK;
    3356             :   }
    3357             : 
    3358           0 :   nsTArray<RefPtr<GenericPromise>> promises;
    3359           0 :   for (auto& key : GetAllKeysForPrincipal(aPrincipal)) {
    3360           0 :     RefPtr<GenericPromise::Private> promise;
    3361           0 :     if (!mPermissionKeyPromiseMap.Get(key, getter_AddRefs(promise))) {
    3362             :       // In this case we have found a permission which isn't avaliable in the
    3363             :       // content process and hasn't been requested yet. We need to create a new
    3364             :       // promise, and send the request to the parent (if we have not already
    3365             :       // done so).
    3366           0 :       promise = new GenericPromise::Private(__func__);
    3367           0 :       mPermissionKeyPromiseMap.Put(key, RefPtr<GenericPromise::Private>(promise).forget());
    3368             :     }
    3369             : 
    3370           0 :     if (promise) {
    3371           0 :       promises.AppendElement(Move(promise));
    3372             :     }
    3373             :   }
    3374             : 
    3375             :   // If all of our permissions are avaliable, immediately run the runnable. This
    3376             :   // avoids any extra overhead during fetch interception which is performance
    3377             :   // sensitive.
    3378           0 :   if (promises.IsEmpty()) {
    3379           0 :     aRunnable->Run();
    3380           0 :     return NS_OK;
    3381             :   }
    3382             : 
    3383           0 :   auto* thread = SystemGroup::AbstractMainThreadFor(TaskCategory::Other);
    3384             : 
    3385           0 :   RefPtr<nsIRunnable> runnable = aRunnable;
    3386           0 :   GenericPromise::All(thread, promises)->Then(
    3387             :     thread, __func__,
    3388           0 :     [runnable] () { runnable->Run(); },
    3389           0 :     [] () {
    3390           0 :       NS_WARNING("nsPermissionManager permission promise rejected. We're probably shutting down.");
    3391           0 :     });
    3392           0 :   return NS_OK;
    3393             : }
    3394             : 
    3395             : NS_IMETHODIMP
    3396           7 : nsPermissionManager::GetHasPreloadPermissions(bool* aResult)
    3397             : {
    3398           7 :   *aResult = sPreloadPermissionCount > 0;
    3399           7 :   return NS_OK;
    3400             : }

Generated by: LCOV version 1.13