tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PermissionManager.cpp (148709B)


      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/AbstractThread.h"
      8 #include "mozilla/AppShutdown.h"
      9 #ifdef MOZ_BACKGROUNDTASKS
     10 #  include "mozilla/BackgroundTasks.h"
     11 #endif
     12 #include "mozilla/BasePrincipal.h"
     13 #include "mozilla/ClearOnShutdown.h"
     14 #include "mozilla/Components.h"
     15 #include "mozilla/ContentPrincipal.h"
     16 #include "mozilla/DebugOnly.h"
     17 #include "mozilla/dom/CanonicalBrowsingContext.h"
     18 #include "mozilla/dom/ContentParent.h"
     19 #include "mozilla/dom/Document.h"
     20 #include "mozilla/dom/WindowGlobalParent.h"
     21 #include "mozilla/ExpandedPrincipal.h"
     22 #include "mozilla/net/NeckoMessageUtils.h"
     23 #include "mozilla/Permission.h"
     24 #include "mozilla/PermissionManager.h"
     25 #include "mozilla/Preferences.h"
     26 #include "mozilla/ScopeExit.h"
     27 #include "mozilla/StaticPrefs_permissions.h"
     28 #include "mozilla/glean/ExtensionsPermissionsMetrics.h"
     29 
     30 #include "mozIStorageService.h"
     31 #include "mozIStorageConnection.h"
     32 #include "mozIStorageStatement.h"
     33 #include "mozStorageCID.h"
     34 
     35 #include "nsAppDirectoryServiceDefs.h"
     36 #include "nsComponentManagerUtils.h"
     37 #include "nsContentUtils.h"
     38 #include "nsCRT.h"
     39 #include "nsDebug.h"
     40 #include "nsIConsoleService.h"
     41 #include "nsIEffectiveTLDService.h"
     42 #include "nsIUserIdleService.h"
     43 #include "nsIInputStream.h"
     44 #include "nsINavHistoryService.h"
     45 #include "nsIObserverService.h"
     46 #include "nsIPrefBranch.h"
     47 #include "nsIPrincipal.h"
     48 #include "nsIURIMutator.h"
     49 #include "nsIWritablePropertyBag2.h"
     50 #include "nsReadLine.h"
     51 #include "nsStringFwd.h"
     52 #include "nsTHashSet.h"
     53 #include "nsToolkitCompsCID.h"
     54 
     55 using namespace mozilla::dom;
     56 
     57 namespace mozilla {
     58 
     59 #define PERMISSIONS_FILE_NAME "permissions.sqlite"
     60 #define HOSTS_SCHEMA_VERSION 12
     61 
     62 // Default permissions are read from a URL - this is the preference we read
     63 // to find that URL. If not set, don't use any default permissions.
     64 constexpr char kDefaultsUrlPrefName[] = "permissions.manager.defaultsUrl";
     65 
     66 constexpr char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
     67 
     68 // A special value for a permission ID that indicates the ID was loaded as
     69 // a default value.  These will never be written to the database, but may
     70 // be overridden with an explicit permission (including UNKNOWN_ACTION)
     71 constexpr int64_t cIDPermissionIsDefault = -1;
     72 
     73 #define ENSURE_NOT_CHILD_PROCESS_(onError)                 \
     74  PR_BEGIN_MACRO                                           \
     75  if (IsChildProcess()) {                                  \
     76    NS_ERROR("Cannot perform action in content process!"); \
     77    onError                                                \
     78  }                                                        \
     79  PR_END_MACRO
     80 
     81 #define ENSURE_NOT_CHILD_PROCESS \
     82  ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
     83 
     84 #define ENSURE_NOT_CHILD_PROCESS_NORET ENSURE_NOT_CHILD_PROCESS_(;)
     85 
     86 #define EXPIRY_NOW PR_Now() / 1000
     87 
     88 ////////////////////////////////////////////////////////////////////////////////
     89 
     90 namespace {
     91 
     92 inline bool IsChildProcess() { return XRE_IsContentProcess(); }
     93 
     94 void LogToConsole(const nsAString& aMsg) {
     95  nsCOMPtr<nsIConsoleService> console(
     96      do_GetService("@mozilla.org/consoleservice;1"));
     97  if (!console) {
     98    NS_WARNING("Failed to log message to console.");
     99    return;
    100  }
    101 
    102  nsAutoString msg(aMsg);
    103  console->LogStringMessage(msg.get());
    104 }
    105 
    106 // NOTE: an empty string can be passed as aType - if it is this function will
    107 // return "false" unconditionally.
    108 bool HasDefaultPref(const nsACString& aType) {
    109  // A list of permissions that can have a fallback default permission
    110  // set under the permissions.default.* pref.
    111  static const nsLiteralCString kPermissionsWithDefaults[] = {
    112      "camera"_ns,    "microphone"_ns,      "geo"_ns, "desktop-notification"_ns,
    113      "shortcuts"_ns, "screen-wake-lock"_ns};
    114 
    115  if (!aType.IsEmpty()) {
    116    for (const auto& perm : kPermissionsWithDefaults) {
    117      if (perm.Equals(aType)) {
    118        return true;
    119      }
    120    }
    121  }
    122 
    123  return false;
    124 }
    125 
    126 // These permissions are special permissions which must be transmitted to the
    127 // content process before documents with their principals have loaded within
    128 // that process.
    129 //
    130 // Permissions which are in this list are considered to have a "" permission
    131 // key, even if their principal would not normally have that key.
    132 static const nsLiteralCString kPreloadPermissions[] = {
    133    // This permission is preloaded to support properly blocking service worker
    134    // interception when a user has disabled storage for a specific site.  Once
    135    // service worker interception moves to the parent process this should be
    136    // removed.  See bug 1428130.
    137    "cookie"_ns, "https-only-load-insecure"_ns};
    138 
    139 // NOTE: nullptr can be passed as aType - if it is this function will return
    140 // "false" unconditionally.
    141 bool IsPreloadPermission(const nsACString& aType) {
    142  if (!aType.IsEmpty()) {
    143    for (const auto& perm : kPreloadPermissions) {
    144      if (perm.Equals(aType)) {
    145        return true;
    146      }
    147    }
    148  }
    149 
    150  return false;
    151 }
    152 
    153 // Array of permission types which should not be isolated by origin attributes,
    154 // for user context and private browsing.
    155 // Keep this array in sync with 'STRIPPED_PERMS' in
    156 // 'test_permmanager_oa_strip.js'
    157 // Currently only preloaded permissions are supported.
    158 // This is because perms are sent to the content process in bulk by perm key.
    159 // Non-preloaded, but OA stripped permissions would not be accessible by sites
    160 // in private browsing / non-default user context.
    161 static constexpr std::array<nsLiteralCString, 3> kStripOAPermissions = {
    162    {"cookie"_ns, "https-only-load-insecure"_ns, "ipp-vpn"_ns}};
    163 
    164 bool IsOAForceStripPermission(const nsACString& aType) {
    165  if (aType.IsEmpty()) {
    166    return false;
    167  }
    168  for (const auto& perm : kStripOAPermissions) {
    169    if (perm.Equals(aType)) {
    170      return true;
    171    }
    172  }
    173  return false;
    174 }
    175 
    176 // Array of permission prefixes which should be isolated only by site.
    177 // These site-scoped permissions are stored under their site's principal.
    178 // GetAllForPrincipal also needs to look for these especially.
    179 static constexpr std::array<nsLiteralCString, 3> kSiteScopedPermissions = {
    180    {"3rdPartyStorage^"_ns, "AllowStorageAccessRequest^"_ns,
    181     "3rdPartyFrameStorage^"_ns}};
    182 
    183 bool IsSiteScopedPermission(const nsACString& aType) {
    184  if (aType.IsEmpty()) {
    185    return false;
    186  }
    187  for (const auto& perm : kSiteScopedPermissions) {
    188    if (aType.Length() >= perm.Length() &&
    189        Substring(aType, 0, perm.Length()) == perm) {
    190      return true;
    191    }
    192  }
    193  return false;
    194 }
    195 
    196 // Array of permission type prefixes which have a secondary key encoded in the
    197 // permission type. These permissions will not be stored in-process with the
    198 // secondary key, but updates to them will cause "perm-changed" notifications on
    199 // processes for that key.
    200 static constexpr std::array<nsLiteralCString, 3> kSecondaryKeyedPermissions = {
    201    {"3rdPartyStorage^"_ns, "AllowStorageAccessRequest^"_ns,
    202     "3rdPartyFrameStorage^"_ns}};
    203 
    204 bool GetSecondaryKey(const nsACString& aType, nsACString& aSecondaryKey) {
    205  aSecondaryKey.Truncate();
    206  if (aType.IsEmpty()) {
    207    return false;
    208  }
    209  for (const auto& perm : kSecondaryKeyedPermissions) {
    210    if (aType.Length() > perm.Length() &&
    211        Substring(aType, 0, perm.Length()) == perm) {
    212      aSecondaryKey = Substring(aType, perm.Length());
    213      return true;
    214    }
    215  }
    216  return false;
    217 }
    218 
    219 void OriginAppendOASuffix(OriginAttributes aOriginAttributes,
    220                          bool aForceStripOA, nsACString& aOrigin) {
    221  PermissionManager::MaybeStripOriginAttributes(aForceStripOA,
    222                                                aOriginAttributes);
    223 
    224  nsAutoCString oaSuffix;
    225  aOriginAttributes.CreateSuffix(oaSuffix);
    226  aOrigin.Append(oaSuffix);
    227 }
    228 
    229 nsresult GetOriginFromPrincipal(nsIPrincipal* aPrincipal, bool aForceStripOA,
    230                                nsACString& aOrigin) {
    231  nsresult rv = aPrincipal->GetOriginNoSuffix(aOrigin);
    232  // The principal may belong to the about:blank content viewer, so this can be
    233  // expected to fail.
    234  if (NS_FAILED(rv)) {
    235    return rv;
    236  }
    237 
    238  nsAutoCString suffix;
    239  rv = aPrincipal->GetOriginSuffix(suffix);
    240  NS_ENSURE_SUCCESS(rv, rv);
    241 
    242  OriginAttributes attrs;
    243  NS_ENSURE_TRUE(attrs.PopulateFromSuffix(suffix), NS_ERROR_FAILURE);
    244 
    245  OriginAppendOASuffix(attrs, aForceStripOA, aOrigin);
    246 
    247  return NS_OK;
    248 }
    249 
    250 // Returns the site of the principal, including OA, given a principal.
    251 nsresult GetSiteFromPrincipal(nsIPrincipal* aPrincipal, bool aForceStripOA,
    252                              nsACString& aSite) {
    253  nsCOMPtr<nsIURI> uri = aPrincipal->GetURI();
    254  nsCOMPtr<nsIEffectiveTLDService> etld =
    255      mozilla::components::EffectiveTLD::Service();
    256  NS_ENSURE_TRUE(etld, NS_ERROR_FAILURE);
    257  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
    258  nsresult rv = etld->GetSite(uri, aSite);
    259 
    260  // The principal may belong to the about:blank content viewer, so this can be
    261  // expected to fail.
    262  if (NS_FAILED(rv)) {
    263    rv = aPrincipal->GetOrigin(aSite);
    264    NS_ENSURE_SUCCESS(rv, rv);
    265    return NS_OK;
    266  }
    267 
    268  nsAutoCString suffix;
    269  rv = aPrincipal->GetOriginSuffix(suffix);
    270  NS_ENSURE_SUCCESS(rv, rv);
    271 
    272  OriginAttributes attrs;
    273  NS_ENSURE_TRUE(attrs.PopulateFromSuffix(suffix), NS_ERROR_FAILURE);
    274 
    275  OriginAppendOASuffix(attrs, aForceStripOA, aSite);
    276 
    277  return NS_OK;
    278 }
    279 
    280 nsresult GetOriginFromURIAndOA(nsIURI* aURI,
    281                               const OriginAttributes* aOriginAttributes,
    282                               bool aForceStripOA, nsACString& aOrigin) {
    283  nsAutoCString origin(aOrigin);
    284  nsresult rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, origin);
    285  NS_ENSURE_SUCCESS(rv, rv);
    286 
    287  OriginAppendOASuffix(*aOriginAttributes, aForceStripOA, origin);
    288 
    289  aOrigin = origin;
    290 
    291  return NS_OK;
    292 }
    293 
    294 nsresult GetPrincipalFromOrigin(const nsACString& aOrigin, bool aForceStripOA,
    295                                nsIPrincipal** aPrincipal) {
    296  nsAutoCString originNoSuffix;
    297  OriginAttributes attrs;
    298  if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
    299    return NS_ERROR_FAILURE;
    300  }
    301 
    302  PermissionManager::MaybeStripOriginAttributes(aForceStripOA, attrs);
    303 
    304  nsCOMPtr<nsIURI> uri;
    305  nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
    306  NS_ENSURE_SUCCESS(rv, rv);
    307 
    308  nsCOMPtr<nsIPrincipal> principal =
    309      BasePrincipal::CreateContentPrincipal(uri, attrs);
    310  principal.forget(aPrincipal);
    311  return NS_OK;
    312 }
    313 
    314 nsresult GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal) {
    315  OriginAttributes attrs;
    316  nsCOMPtr<nsIPrincipal> principal =
    317      BasePrincipal::CreateContentPrincipal(aURI, attrs);
    318  NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
    319 
    320  principal.forget(aPrincipal);
    321  return NS_OK;
    322 }
    323 
    324 nsCString GetNextSubDomainForHost(const nsACString& aHost) {
    325  nsCString subDomain;
    326  nsCOMPtr<nsIEffectiveTLDService> etld =
    327      mozilla::components::EffectiveTLD::Service();
    328  nsresult rv = etld->GetNextSubDomain(aHost, subDomain);
    329  // We can fail if there is no more subdomain or if the host can't have a
    330  // subdomain.
    331  if (NS_FAILED(rv)) {
    332    return ""_ns;
    333  }
    334 
    335  return subDomain;
    336 }
    337 
    338 // This function produces a nsIURI which is identical to the current
    339 // nsIURI, except that it has one less subdomain segment. It returns
    340 // `nullptr` if there are no more segments to remove.
    341 already_AddRefed<nsIURI> GetNextSubDomainURI(nsIURI* aURI) {
    342  nsAutoCString host;
    343  nsresult rv = aURI->GetHost(host);
    344  if (NS_FAILED(rv)) {
    345    return nullptr;
    346  }
    347 
    348  nsCString domain = GetNextSubDomainForHost(host);
    349  if (domain.IsEmpty()) {
    350    return nullptr;
    351  }
    352 
    353  nsCOMPtr<nsIURI> uri;
    354  rv = NS_MutateURI(aURI).SetHost(domain).Finalize(uri);
    355  if (NS_FAILED(rv) || !uri) {
    356    return nullptr;
    357  }
    358 
    359  return uri.forget();
    360 }
    361 
    362 nsresult UpgradeHostToOriginAndInsert(
    363    const nsACString& aHost, const nsCString& aType, uint32_t aPermission,
    364    uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime,
    365    std::function<nsresult(const nsACString& aOrigin, const nsCString& aType,
    366                           uint32_t aPermission, uint32_t aExpireType,
    367                           int64_t aExpireTime, int64_t aModificationTime)>&&
    368        aCallback) {
    369  if (aHost.EqualsLiteral("<file>")) {
    370    // We no longer support the magic host <file>
    371    NS_WARNING(
    372        "The magic host <file> is no longer supported. "
    373        "It is being removed from the permissions database.");
    374    return NS_OK;
    375  }
    376 
    377  // First, we check to see if the host is a valid URI. If it is, it can be
    378  // imported directly
    379  nsCOMPtr<nsIURI> uri;
    380  nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost);
    381  if (NS_SUCCEEDED(rv)) {
    382    // It was previously possible to insert useless entries to your permissions
    383    // database for URIs which have a null principal. This acts as a cleanup,
    384    // getting rid of these useless database entries
    385    if (uri->SchemeIs("moz-nullprincipal")) {
    386      NS_WARNING("A moz-nullprincipal: permission is being discarded.");
    387      return NS_OK;
    388    }
    389 
    390    nsCOMPtr<nsIPrincipal> principal;
    391    rv = GetPrincipal(uri, getter_AddRefs(principal));
    392    NS_ENSURE_SUCCESS(rv, rv);
    393 
    394    nsAutoCString origin;
    395    rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType),
    396                                origin);
    397    NS_ENSURE_SUCCESS(rv, rv);
    398 
    399    aCallback(origin, aType, aPermission, aExpireType, aExpireTime,
    400              aModificationTime);
    401    return NS_OK;
    402  }
    403 
    404  // The user may use this host at non-standard ports or protocols, we can use
    405  // their history to guess what ports and protocols we want to add permissions
    406  // for. We find every URI which they have visited with this host (or a
    407  // subdomain of this host), and try to add it as a principal.
    408  bool foundHistory = false;
    409 
    410  nsCOMPtr<nsINavHistoryService> histSrv = nullptr;
    411  if (NS_IsMainThread()) {
    412    histSrv = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
    413  }
    414 
    415  if (histSrv) {
    416    nsCOMPtr<nsINavHistoryQuery> histQuery;
    417    rv = histSrv->GetNewQuery(getter_AddRefs(histQuery));
    418    NS_ENSURE_SUCCESS(rv, rv);
    419 
    420    // Get the eTLD+1 of the domain
    421    nsAutoCString eTLD1;
    422    nsCOMPtr<nsIEffectiveTLDService> etld =
    423        mozilla::components::EffectiveTLD::Service();
    424    rv = etld->GetBaseDomainFromHost(aHost, 0, eTLD1);
    425 
    426    if (NS_FAILED(rv)) {
    427      // If the lookup on the tldService for the base domain for the host
    428      // failed, that means that we just want to directly use the host as the
    429      // host name for the lookup.
    430      eTLD1 = aHost;
    431    }
    432 
    433    // We want to only find history items for this particular eTLD+1, and
    434    // subdomains
    435    rv = histQuery->SetDomain(eTLD1);
    436    NS_ENSURE_SUCCESS(rv, rv);
    437 
    438    rv = histQuery->SetDomainIsHost(false);
    439    NS_ENSURE_SUCCESS(rv, rv);
    440 
    441    nsCOMPtr<nsINavHistoryQueryOptions> histQueryOpts;
    442    rv = histSrv->GetNewQueryOptions(getter_AddRefs(histQueryOpts));
    443    NS_ENSURE_SUCCESS(rv, rv);
    444 
    445    // We want to get the URIs for every item in the user's history with the
    446    // given host
    447    rv =
    448        histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI);
    449    NS_ENSURE_SUCCESS(rv, rv);
    450 
    451    // We only search history, because searching both bookmarks and history
    452    // is not supported, and history tends to be more comprehensive.
    453    rv = histQueryOpts->SetQueryType(
    454        nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY);
    455    NS_ENSURE_SUCCESS(rv, rv);
    456 
    457    // We include hidden URIs (such as those visited via iFrames) as they may
    458    // have permissions too
    459    rv = histQueryOpts->SetIncludeHidden(true);
    460    NS_ENSURE_SUCCESS(rv, rv);
    461 
    462    nsCOMPtr<nsINavHistoryResult> histResult;
    463    rv = histSrv->ExecuteQuery(histQuery, histQueryOpts,
    464                               getter_AddRefs(histResult));
    465    NS_ENSURE_SUCCESS(rv, rv);
    466 
    467    nsCOMPtr<nsINavHistoryContainerResultNode> histResultContainer;
    468    rv = histResult->GetRoot(getter_AddRefs(histResultContainer));
    469    NS_ENSURE_SUCCESS(rv, rv);
    470 
    471    rv = histResultContainer->SetContainerOpen(true);
    472    NS_ENSURE_SUCCESS(rv, rv);
    473 
    474    uint32_t childCount = 0;
    475    rv = histResultContainer->GetChildCount(&childCount);
    476    NS_ENSURE_SUCCESS(rv, rv);
    477 
    478    nsTHashSet<nsCString> insertedOrigins;
    479    for (uint32_t i = 0; i < childCount; i++) {
    480      nsCOMPtr<nsINavHistoryResultNode> child;
    481      histResultContainer->GetChild(i, getter_AddRefs(child));
    482      if (NS_WARN_IF(NS_FAILED(rv))) continue;
    483 
    484      uint32_t type;
    485      rv = child->GetType(&type);
    486      if (NS_WARN_IF(NS_FAILED(rv)) ||
    487          type != nsINavHistoryResultNode::RESULT_TYPE_URI) {
    488        NS_WARNING(
    489            "Unexpected non-RESULT_TYPE_URI node in "
    490            "UpgradeHostToOriginAndInsert()");
    491        continue;
    492      }
    493 
    494      nsAutoCString uriSpec;
    495      rv = child->GetUri(uriSpec);
    496      if (NS_WARN_IF(NS_FAILED(rv))) continue;
    497 
    498      nsCOMPtr<nsIURI> uri;
    499      rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
    500      if (NS_WARN_IF(NS_FAILED(rv))) continue;
    501 
    502      // Use the provided host - this URI may be for a subdomain, rather than
    503      // the host we care about.
    504      rv = NS_MutateURI(uri).SetHost(aHost).Finalize(uri);
    505      if (NS_WARN_IF(NS_FAILED(rv))) continue;
    506 
    507      // We now have a URI which we can make a nsIPrincipal out of
    508      nsCOMPtr<nsIPrincipal> principal;
    509      rv = GetPrincipal(uri, getter_AddRefs(principal));
    510      if (NS_WARN_IF(NS_FAILED(rv))) continue;
    511 
    512      nsAutoCString origin;
    513      rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType),
    514                                  origin);
    515      if (NS_WARN_IF(NS_FAILED(rv))) continue;
    516 
    517      // Ensure that we don't insert the same origin repeatedly
    518      if (insertedOrigins.Contains(origin)) {
    519        continue;
    520      }
    521 
    522      foundHistory = true;
    523      rv = aCallback(origin, aType, aPermission, aExpireType, aExpireTime,
    524                     aModificationTime);
    525      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Insert failed");
    526      insertedOrigins.Insert(origin);
    527    }
    528 
    529    rv = histResultContainer->SetContainerOpen(false);
    530    NS_ENSURE_SUCCESS(rv, rv);
    531  }
    532 
    533  // If we didn't find any origins for this host in the poermissions database,
    534  // we can insert the default http:// and https:// permissions into the
    535  // database. This has a relatively high likelihood of applying the permission
    536  // to the correct origin.
    537  if (!foundHistory) {
    538    nsAutoCString hostSegment;
    539    nsCOMPtr<nsIPrincipal> principal;
    540    nsAutoCString origin;
    541 
    542    // If this is an ipv6 URI, we need to surround it in '[', ']' before trying
    543    // to parse it as a URI.
    544    if (aHost.FindChar(':') != -1) {
    545      hostSegment.AssignLiteral("[");
    546      hostSegment.Append(aHost);
    547      hostSegment.AppendLiteral("]");
    548    } else {
    549      hostSegment.Assign(aHost);
    550    }
    551 
    552    // http:// URI default
    553    rv = NS_NewURI(getter_AddRefs(uri), "http://"_ns + hostSegment);
    554    NS_ENSURE_SUCCESS(rv, rv);
    555 
    556    rv = GetPrincipal(uri, getter_AddRefs(principal));
    557    NS_ENSURE_SUCCESS(rv, rv);
    558 
    559    rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType),
    560                                origin);
    561    NS_ENSURE_SUCCESS(rv, rv);
    562 
    563    aCallback(origin, aType, aPermission, aExpireType, aExpireTime,
    564              aModificationTime);
    565 
    566    // https:// URI default
    567    rv = NS_NewURI(getter_AddRefs(uri), "https://"_ns + hostSegment);
    568    NS_ENSURE_SUCCESS(rv, rv);
    569 
    570    rv = GetPrincipal(uri, getter_AddRefs(principal));
    571    NS_ENSURE_SUCCESS(rv, rv);
    572 
    573    rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType),
    574                                origin);
    575    NS_ENSURE_SUCCESS(rv, rv);
    576 
    577    aCallback(origin, aType, aPermission, aExpireType, aExpireTime,
    578              aModificationTime);
    579  }
    580 
    581  return NS_OK;
    582 }
    583 
    584 bool IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
    585  nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
    586  return !!ep;
    587 }
    588 
    589 // We only want to persist permissions which don't have session or policy
    590 // expiration.
    591 bool IsPersistentExpire(uint32_t aExpire, const nsACString& aType) {
    592  bool res = (aExpire != nsIPermissionManager::EXPIRE_SESSION &&
    593              aExpire != nsIPermissionManager::EXPIRE_POLICY);
    594  return res;
    595 }
    596 
    597 nsresult NotifySecondaryKeyPermissionUpdateInContentProcess(
    598    const nsACString& aType, uint32_t aPermission,
    599    const nsACString& aSecondaryKey, nsIPrincipal* aTopPrincipal) {
    600  NS_ENSURE_ARG_POINTER(aTopPrincipal);
    601  MOZ_ASSERT(XRE_IsParentProcess());
    602  AutoTArray<RefPtr<BrowsingContextGroup>, 5> bcGroups;
    603  BrowsingContextGroup::GetAllGroups(bcGroups);
    604  for (const auto& bcGroup : bcGroups) {
    605    for (const auto& topBC : bcGroup->Toplevels()) {
    606      CanonicalBrowsingContext* topCBC = topBC->Canonical();
    607      RefPtr<nsIURI> topURI = topCBC->GetCurrentURI();
    608      if (!topURI) {
    609        continue;
    610      }
    611      bool thirdParty;
    612      nsresult rv = aTopPrincipal->IsThirdPartyURI(topURI, &thirdParty);
    613      if (NS_FAILED(rv)) {
    614        continue;
    615      }
    616      if (!thirdParty) {
    617        AutoTArray<RefPtr<BrowsingContext>, 5> bcs;
    618        topBC->GetAllBrowsingContextsInSubtree(bcs);
    619        for (const auto& bc : bcs) {
    620          CanonicalBrowsingContext* cbc = bc->Canonical();
    621          ContentParent* cp = cbc->GetContentParent();
    622          if (!cp) {
    623            continue;
    624          }
    625          if (cp->NeedsSecondaryKeyPermissionsUpdate(aSecondaryKey)) {
    626            WindowGlobalParent* wgp = cbc->GetCurrentWindowGlobal();
    627            if (!wgp) {
    628              continue;
    629            }
    630            bool success = wgp->SendNotifyPermissionChange(aType, aPermission);
    631            (void)NS_WARN_IF(!success);
    632          }
    633        }
    634      }
    635    }
    636  }
    637  return NS_OK;
    638 }
    639 
    640 }  // namespace
    641 
    642 ////////////////////////////////////////////////////////////////////////////////
    643 
    644 PermissionManager::PermissionKey*
    645 PermissionManager::PermissionKey::CreateFromPrincipal(nsIPrincipal* aPrincipal,
    646                                                      bool aForceStripOA,
    647                                                      bool aScopeToSite,
    648                                                      nsresult& aResult) {
    649  nsAutoCString keyString;
    650  if (aScopeToSite) {
    651    aResult = GetSiteFromPrincipal(aPrincipal, aForceStripOA, keyString);
    652  } else {
    653    aResult = GetOriginFromPrincipal(aPrincipal, aForceStripOA, keyString);
    654  }
    655  if (NS_WARN_IF(NS_FAILED(aResult))) {
    656    return nullptr;
    657  }
    658  return new PermissionKey(keyString);
    659 }
    660 
    661 PermissionManager::PermissionKey*
    662 PermissionManager::PermissionKey::CreateFromURIAndOriginAttributes(
    663    nsIURI* aURI, const OriginAttributes* aOriginAttributes, bool aForceStripOA,
    664    nsresult& aResult) {
    665  nsAutoCString origin;
    666  aResult =
    667      GetOriginFromURIAndOA(aURI, aOriginAttributes, aForceStripOA, origin);
    668  if (NS_WARN_IF(NS_FAILED(aResult))) {
    669    return nullptr;
    670  }
    671 
    672  return new PermissionKey(origin);
    673 }
    674 
    675 PermissionManager::PermissionKey*
    676 PermissionManager::PermissionKey::CreateFromURI(nsIURI* aURI,
    677                                                nsresult& aResult) {
    678  nsAutoCString origin;
    679  aResult = ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, origin);
    680  if (NS_WARN_IF(NS_FAILED(aResult))) {
    681    return nullptr;
    682  }
    683 
    684  return new PermissionKey(origin);
    685 }
    686 
    687 ////////////////////////////////////////////////////////////////////////////////
    688 // PermissionManager Implementation
    689 
    690 NS_IMPL_ISUPPORTS(PermissionManager, nsIPermissionManager, nsIObserver,
    691                  nsISupportsWeakReference, nsIAsyncShutdownBlocker)
    692 
    693 PermissionManager::PermissionManager()
    694    : mMonitor("PermissionManager::mMonitor"),
    695      mState(eInitializing),
    696      mMemoryOnlyDB(false),
    697      mLargestID(0) {}
    698 
    699 PermissionManager::~PermissionManager() {
    700  MonitorAutoLock lock{mMonitor};
    701 
    702  // NOTE: Make sure to reject each of the promises in mPermissionKeyPromiseMap
    703  // before destroying.
    704  for (const auto& promise : mPermissionKeyPromiseMap.Values()) {
    705    if (promise) {
    706      promise->Reject(NS_ERROR_FAILURE, __func__);
    707    }
    708  }
    709  mPermissionKeyPromiseMap.Clear();
    710 
    711  if (mThread) {
    712    mThread->Shutdown();
    713    mThread = nullptr;
    714  }
    715 }
    716 
    717 /* static */
    718 StaticMutex PermissionManager::sCreationMutex;
    719 StaticRefPtr<PermissionManager> PermissionManager::sInstanceHolder;
    720 bool PermissionManager::sInstanceDead(false);
    721 
    722 // static
    723 already_AddRefed<nsIPermissionManager> PermissionManager::GetXPCOMSingleton() {
    724  return GetInstance();
    725 }
    726 
    727 // static
    728 already_AddRefed<PermissionManager> PermissionManager::GetInstance() {
    729  // The lazy initialization could race.
    730  StaticMutexAutoLock lock(sCreationMutex);
    731 
    732  if (sInstanceDead) {
    733    return nullptr;
    734  }
    735 
    736  if (sInstanceHolder) {
    737    RefPtr<PermissionManager> ret(sInstanceHolder);
    738    return ret.forget();
    739  }
    740 
    741  auto permManager = MakeRefPtr<PermissionManager>();
    742  if (NS_SUCCEEDED(permManager->Init())) {
    743    // Note that this does an extra AddRef on purpose to keep us alive
    744    // until shutdown.
    745    sInstanceHolder = permManager.get();
    746    return permManager.forget();
    747  }
    748 
    749  sInstanceDead = true;
    750  return nullptr;
    751 }
    752 
    753 nsresult PermissionManager::Init() {
    754  // If we are already shutting down, do not permit a creation.
    755  // This must match the phase in GetAsyncShutdownBarrier.
    756  if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) {
    757    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
    758  }
    759 
    760  MOZ_ASSERT(NS_IsMainThread());
    761 
    762  MonitorAutoLock lock{mMonitor};
    763  MOZ_ASSERT(mState == eInitializing);
    764 
    765  // If the 'permissions.memory_only' pref is set to true, then don't write any
    766  // permission settings to disk, but keep them in a memory-only database.
    767  mMemoryOnlyDB = Preferences::GetBool("permissions.memory_only", false);
    768 
    769  nsresult rv;
    770  nsCOMPtr<nsIPrefService> prefService =
    771      do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
    772  NS_ENSURE_SUCCESS(rv, rv);
    773 
    774  rv = prefService->GetBranch("permissions.default.",
    775                              getter_AddRefs(mDefaultPrefBranch));
    776  NS_ENSURE_SUCCESS(rv, rv);
    777 
    778  if (IsChildProcess()) {
    779    // Stop here; we don't need the DB in the child process. Instead we will be
    780    // sent permissions as we need them by our parent process.
    781    mState = eReady;
    782 
    783    // We use ClearOnShutdown on the content process only because on the parent
    784    // process we need to block the shutdown for the final closeDB() call.
    785    ClearOnShutdown(&sInstanceHolder);
    786    return NS_OK;
    787  }
    788 
    789  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
    790  if (observerService) {
    791    observerService->AddObserver(this, "profile-do-change", true);
    792    observerService->AddObserver(this, "testonly-reload-permissions-from-disk",
    793                                 true);
    794    observerService->AddObserver(this, "last-pb-context-exited", true);
    795  }
    796 
    797  if (XRE_IsParentProcess()) {
    798    nsCOMPtr<nsIAsyncShutdownClient> asc = GetAsyncShutdownBarrier();
    799    if (!asc) {
    800      return NS_ERROR_NOT_AVAILABLE;
    801    }
    802    nsAutoString blockerName;
    803    MOZ_ALWAYS_SUCCEEDS(GetName(blockerName));
    804 
    805    nsresult rv = asc->AddBlocker(
    806        this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, blockerName);
    807    NS_ENSURE_SUCCESS(rv, rv);
    808  }
    809 
    810  AddIdleDailyMaintenanceJob();
    811 
    812  MOZ_ASSERT(!mThread);
    813  NS_ENSURE_SUCCESS(NS_NewNamedThread("Permission", getter_AddRefs(mThread)),
    814                    NS_ERROR_FAILURE);
    815 
    816  PRThread* prThread;
    817  MOZ_ALWAYS_SUCCEEDS(mThread->GetPRThread(&prThread));
    818  MOZ_ASSERT(prThread);
    819 
    820  mThreadBoundData.Transfer(prThread);
    821 
    822  InitDB(false);
    823 
    824  return NS_OK;
    825 }
    826 
    827 nsresult PermissionManager::OpenDatabase(nsIFile* aPermissionsFile) {
    828  MOZ_ASSERT(!NS_IsMainThread());
    829  auto data = mThreadBoundData.Access();
    830 
    831  nsresult rv;
    832  nsCOMPtr<mozIStorageService> storage =
    833      do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
    834  if (!storage) {
    835    return NS_ERROR_UNEXPECTED;
    836  }
    837  // cache a connection to the hosts database
    838  if (mMemoryOnlyDB) {
    839    rv = storage->OpenSpecialDatabase(
    840        kMozStorageMemoryStorageKey, VoidCString(),
    841        mozIStorageService::CONNECTION_DEFAULT, getter_AddRefs(data->mDBConn));
    842  } else {
    843    rv = storage->OpenDatabase(aPermissionsFile,
    844                               mozIStorageService::CONNECTION_DEFAULT,
    845                               getter_AddRefs(data->mDBConn));
    846  }
    847  return rv;
    848 }
    849 
    850 void PermissionManager::InitDB(bool aRemoveFile) {
    851  mState = eInitializing;
    852  MOZ_ASSERT(NS_IsMainThread());
    853 
    854  mReadEntries.Clear();
    855 
    856  auto readyIfFailed = MakeScopeExit([&]() {
    857    // ignore failure here, since it's non-fatal (we can run fine without
    858    // persistent storage - e.g. if there's no profile).
    859    // XXX should we tell the user about this?
    860    mState = eReady;
    861  });
    862 
    863  if (!mPermissionsFile) {
    864    nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR,
    865                                         getter_AddRefs(mPermissionsFile));
    866    if (NS_FAILED(rv)) {
    867      rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    868                                  getter_AddRefs(mPermissionsFile));
    869      if (NS_FAILED(rv)) {
    870        return;
    871      }
    872    }
    873 
    874    rv =
    875        mPermissionsFile->AppendNative(nsLiteralCString(PERMISSIONS_FILE_NAME));
    876    NS_ENSURE_SUCCESS_VOID(rv);
    877  }
    878 
    879  nsCOMPtr<nsIInputStream> defaultsInputStream = GetDefaultsInputStream();
    880 
    881  RefPtr<PermissionManager> self = this;
    882  mThread->Dispatch(NS_NewRunnableFunction(
    883      "PermissionManager::InitDB", [self, aRemoveFile, defaultsInputStream] {
    884        MonitorAutoLock lock(self->mMonitor);
    885 
    886        nsresult rv = self->TryInitDB(aRemoveFile, defaultsInputStream, lock);
    887        (void)NS_WARN_IF(NS_FAILED(rv));
    888 
    889        // This extra runnable calls EnsureReadCompleted to finialize the
    890        // initialization. If there is something blocked by the monitor, it will
    891        // be NOP.
    892        NS_DispatchToMainThread(NS_NewRunnableFunction(
    893            "PermissionManager::InitDB-MainThread", [self] {
    894              MonitorAutoLock lock{self->mMonitor};
    895              self->EnsureReadCompleted();
    896            }));
    897 
    898        self->mMonitor.Notify();
    899      }));
    900 
    901  readyIfFailed.release();
    902 }
    903 
    904 nsresult PermissionManager::TryInitDB(bool aRemoveFile,
    905                                      nsIInputStream* aDefaultsInputStream,
    906                                      const MonitorAutoLock& aProofOfLock) {
    907  MOZ_ASSERT(!NS_IsMainThread());
    908 
    909  auto raii = MakeScopeExit([&]() {
    910    if (aDefaultsInputStream) {
    911      aDefaultsInputStream->Close();
    912    }
    913 
    914    mState = eDBInitialized;
    915  });
    916 
    917  auto data = mThreadBoundData.Access();
    918 
    919  auto raiiFailure = MakeScopeExit([&]() {
    920    if (data->mDBConn) {
    921      DebugOnly<nsresult> rv = data->mDBConn->Close();
    922      MOZ_ASSERT(NS_SUCCEEDED(rv));
    923      data->mDBConn = nullptr;
    924    }
    925  });
    926 
    927  nsresult rv;
    928 
    929  if (aRemoveFile) {
    930    bool exists = false;
    931    rv = mPermissionsFile->Exists(&exists);
    932    NS_ENSURE_SUCCESS(rv, rv);
    933    if (exists) {
    934      rv = mPermissionsFile->Remove(false);
    935      NS_ENSURE_SUCCESS(rv, rv);
    936    }
    937  }
    938 
    939  rv = OpenDatabase(mPermissionsFile);
    940  if (rv == NS_ERROR_FILE_CORRUPTED) {
    941    LogToConsole(u"permissions.sqlite is corrupted! Try again!"_ns);
    942 
    943    // Add telemetry probe
    944    glean::permissions::sql_corrupted.Add(1);
    945 
    946    // delete corrupted permissions.sqlite and try again
    947    rv = mPermissionsFile->Remove(false);
    948    NS_ENSURE_SUCCESS(rv, rv);
    949    LogToConsole(u"Corrupted permissions.sqlite has been removed."_ns);
    950 
    951    rv = OpenDatabase(mPermissionsFile);
    952    NS_ENSURE_SUCCESS(rv, rv);
    953    LogToConsole(u"OpenDatabase to permissions.sqlite is successful!"_ns);
    954  }
    955 
    956  if (NS_WARN_IF(NS_FAILED(rv))) {
    957    return rv;
    958  }
    959 
    960  bool ready;
    961  data->mDBConn->GetConnectionReady(&ready);
    962  if (!ready) {
    963    LogToConsole(nsLiteralString(
    964        u"Fail to get connection to permissions.sqlite! Try again!"));
    965 
    966    // delete and try again
    967    rv = mPermissionsFile->Remove(false);
    968    NS_ENSURE_SUCCESS(rv, rv);
    969    LogToConsole(u"Defective permissions.sqlite has been removed."_ns);
    970 
    971    // Add telemetry probe
    972    glean::permissions::defective_sql_removed.Add(1);
    973 
    974    rv = OpenDatabase(mPermissionsFile);
    975    NS_ENSURE_SUCCESS(rv, rv);
    976    LogToConsole(u"OpenDatabase to permissions.sqlite is successful!"_ns);
    977 
    978    data->mDBConn->GetConnectionReady(&ready);
    979    if (!ready) return NS_ERROR_UNEXPECTED;
    980  }
    981 
    982  bool tableExists = false;
    983  data->mDBConn->TableExists("moz_perms"_ns, &tableExists);
    984  if (!tableExists) {
    985    data->mDBConn->TableExists("moz_hosts"_ns, &tableExists);
    986  }
    987  if (!tableExists) {
    988    rv = CreateTable();
    989    NS_ENSURE_SUCCESS(rv, rv);
    990  } else {
    991    // table already exists; check the schema version before reading
    992    int32_t dbSchemaVersion;
    993    rv = data->mDBConn->GetSchemaVersion(&dbSchemaVersion);
    994    NS_ENSURE_SUCCESS(rv, rv);
    995 
    996    switch (dbSchemaVersion) {
    997        // upgrading.
    998        // every time you increment the database schema, you need to
    999        // implement the upgrading code from the previous version to the
   1000        // new one. fall through to current version
   1001 
   1002      case 1: {
   1003        // previous non-expiry version of database.  Upgrade it by adding
   1004        // the expiration columns
   1005        rv = data->mDBConn->ExecuteSimpleSQL(
   1006            "ALTER TABLE moz_hosts ADD expireType INTEGER"_ns);
   1007        NS_ENSURE_SUCCESS(rv, rv);
   1008 
   1009        rv = data->mDBConn->ExecuteSimpleSQL(
   1010            "ALTER TABLE moz_hosts ADD expireTime INTEGER"_ns);
   1011        NS_ENSURE_SUCCESS(rv, rv);
   1012      }
   1013 
   1014        // fall through to the next upgrade
   1015        [[fallthrough]];
   1016 
   1017      // TODO: we want to make default version as version 2 in order to
   1018      // fix bug 784875.
   1019      case 0:
   1020      case 2: {
   1021        // Add appId/isInBrowserElement fields.
   1022        rv = data->mDBConn->ExecuteSimpleSQL(
   1023            "ALTER TABLE moz_hosts ADD appId INTEGER"_ns);
   1024        NS_ENSURE_SUCCESS(rv, rv);
   1025 
   1026        rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString(
   1027            "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
   1028        NS_ENSURE_SUCCESS(rv, rv);
   1029 
   1030        rv = data->mDBConn->SetSchemaVersion(3);
   1031        NS_ENSURE_SUCCESS(rv, rv);
   1032      }
   1033 
   1034        // fall through to the next upgrade
   1035        [[fallthrough]];
   1036 
   1037      // Version 3->4 is the creation of the modificationTime field.
   1038      case 3: {
   1039        rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString(
   1040            "ALTER TABLE moz_hosts ADD modificationTime INTEGER"));
   1041        NS_ENSURE_SUCCESS(rv, rv);
   1042 
   1043        // We leave the modificationTime at zero for all existing records;
   1044        // using now() would mean, eg, that doing "remove all from the
   1045        // last hour" within the first hour after migration would remove
   1046        // all permissions.
   1047 
   1048        rv = data->mDBConn->SetSchemaVersion(4);
   1049        NS_ENSURE_SUCCESS(rv, rv);
   1050      }
   1051 
   1052        // fall through to the next upgrade
   1053        [[fallthrough]];
   1054 
   1055      // In version 5, host appId, and isInBrowserElement were merged into
   1056      // a single origin entry
   1057      //
   1058      // In version 6, the tables were renamed for backwards compatability
   1059      // reasons with version 4 and earlier.
   1060      //
   1061      // In version 7, a bug in the migration used for version 4->5 was
   1062      // discovered which could have triggered data-loss. Because of that,
   1063      // all users with a version 4, 5, or 6 database will be re-migrated
   1064      // from the backup database. (bug 1186034). This migration bug is
   1065      // not present after bug 1185340, and the re-migration ensures that
   1066      // all users have the fix.
   1067      case 5:
   1068        // This branch could also be reached via dbSchemaVersion == 3, in
   1069        // which case we want to fall through to the dbSchemaVersion == 4
   1070        // case. The easiest way to do that is to perform this extra check
   1071        // here to make sure that we didn't get here via a fallthrough
   1072        // from v3
   1073        if (dbSchemaVersion == 5) {
   1074          // In version 5, the backup database is named moz_hosts_v4. We
   1075          // perform the version 5->6 migration to get the tables to have
   1076          // consistent naming conventions.
   1077 
   1078          // Version 5->6 is the renaming of moz_hosts to moz_perms, and
   1079          // moz_hosts_v4 to moz_hosts (bug 1185343)
   1080          //
   1081          // In version 5, we performed the modifications to the
   1082          // permissions database in place, this meant that if you
   1083          // upgraded to a version which used V5, and then downgraded to a
   1084          // version which used v4 or earlier, the fallback path would
   1085          // drop the table, and your permissions data would be lost. This
   1086          // migration undoes that mistake, by restoring the old moz_hosts
   1087          // table (if it was present), and instead using the new table
   1088          // moz_perms for the new permissions schema.
   1089          //
   1090          // NOTE: If you downgrade, store new permissions, and then
   1091          // upgrade again, these new permissions won't be migrated or
   1092          // reflected in the updated database. This migration only occurs
   1093          // once, as if moz_perms exists, it will skip creating it. In
   1094          // addition, permissions added after the migration will not be
   1095          // visible in previous versions of firefox.
   1096 
   1097          bool permsTableExists = false;
   1098          data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists);
   1099          if (!permsTableExists) {
   1100            // Move the upgraded database to moz_perms
   1101            rv = data->mDBConn->ExecuteSimpleSQL(
   1102                "ALTER TABLE moz_hosts RENAME TO moz_perms"_ns);
   1103            NS_ENSURE_SUCCESS(rv, rv);
   1104          } else {
   1105            NS_WARNING(
   1106                "moz_hosts was not renamed to moz_perms, "
   1107                "as a moz_perms table already exists");
   1108 
   1109            // In the situation where a moz_perms table already exists,
   1110            // but the schema is lower than 6, a migration has already
   1111            // previously occured to V6, but a downgrade has caused the
   1112            // moz_hosts table to be dropped. This should only occur in
   1113            // the case of a downgrade to a V5 database, which was only
   1114            // present in a few day's nightlies. As that version was
   1115            // likely used only on a temporary basis, we assume that the
   1116            // database from the previous V6 has the permissions which the
   1117            // user actually wants to use. We have to get rid of moz_hosts
   1118            // such that moz_hosts_v4 can be moved into its place if it
   1119            // exists.
   1120            rv = data->mDBConn->ExecuteSimpleSQL("DROP TABLE moz_hosts"_ns);
   1121            NS_ENSURE_SUCCESS(rv, rv);
   1122          }
   1123 
   1124 #ifdef DEBUG
   1125          // The moz_hosts table shouldn't exist anymore
   1126          bool hostsTableExists = false;
   1127          data->mDBConn->TableExists("moz_hosts"_ns, &hostsTableExists);
   1128          MOZ_ASSERT(!hostsTableExists);
   1129 #endif
   1130 
   1131          // Rename moz_hosts_v4 back to it's original location, if it
   1132          // exists
   1133          bool v4TableExists = false;
   1134          data->mDBConn->TableExists("moz_hosts_v4"_ns, &v4TableExists);
   1135          if (v4TableExists) {
   1136            rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString(
   1137                "ALTER TABLE moz_hosts_v4 RENAME TO moz_hosts"));
   1138            NS_ENSURE_SUCCESS(rv, rv);
   1139          }
   1140 
   1141          rv = data->mDBConn->SetSchemaVersion(6);
   1142          NS_ENSURE_SUCCESS(rv, rv);
   1143        }
   1144 
   1145        // fall through to the next upgrade
   1146        [[fallthrough]];
   1147 
   1148      // At this point, the version 5 table has been migrated to a version
   1149      // 6 table We are guaranteed to have at least one of moz_hosts and
   1150      // moz_perms. If we have moz_hosts, we will migrate moz_hosts into
   1151      // moz_perms (even if we already have a moz_perms, as we need a
   1152      // re-migration due to bug 1186034).
   1153      //
   1154      // After this migration, we are guaranteed to have both a moz_hosts
   1155      // (for backwards compatability), and a moz_perms table. The
   1156      // moz_hosts table will have a v4 schema, and the moz_perms table
   1157      // will have a v6 schema.
   1158      case 4:
   1159      case 6: {
   1160        bool hostsTableExists = false;
   1161        data->mDBConn->TableExists("moz_hosts"_ns, &hostsTableExists);
   1162        if (hostsTableExists) {
   1163          // Both versions 4 and 6 have a version 4 formatted hosts table
   1164          // named moz_hosts. We can migrate this table to our version 7
   1165          // table moz_perms. If moz_perms is present, then we can use it
   1166          // as a basis for comparison.
   1167 
   1168          rv = data->mDBConn->BeginTransaction();
   1169          NS_ENSURE_SUCCESS(rv, rv);
   1170 
   1171          bool tableExists = false;
   1172          data->mDBConn->TableExists("moz_hosts_new"_ns, &tableExists);
   1173          if (tableExists) {
   1174            NS_WARNING(
   1175                "The temporary database moz_hosts_new already exists, "
   1176                "dropping "
   1177                "it.");
   1178            rv = data->mDBConn->ExecuteSimpleSQL("DROP TABLE moz_hosts_new"_ns);
   1179            NS_ENSURE_SUCCESS(rv, rv);
   1180          }
   1181          rv = data->mDBConn->ExecuteSimpleSQL(
   1182              nsLiteralCString("CREATE TABLE moz_hosts_new ("
   1183                               " id INTEGER PRIMARY KEY"
   1184                               ",origin TEXT"
   1185                               ",type TEXT"
   1186                               ",permission INTEGER"
   1187                               ",expireType INTEGER"
   1188                               ",expireTime INTEGER"
   1189                               ",modificationTime INTEGER"
   1190                               ")"));
   1191          NS_ENSURE_SUCCESS(rv, rv);
   1192 
   1193          nsCOMPtr<mozIStorageStatement> stmt;
   1194          rv = data->mDBConn->CreateStatement(
   1195              nsLiteralCString(
   1196                  "SELECT host, type, permission, expireType, "
   1197                  "expireTime, "
   1198                  "modificationTime, isInBrowserElement FROM moz_hosts"),
   1199              getter_AddRefs(stmt));
   1200          NS_ENSURE_SUCCESS(rv, rv);
   1201 
   1202          int64_t id = 0;
   1203          bool hasResult;
   1204 
   1205          while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   1206            MigrationEntry entry;
   1207 
   1208            // Read in the old row
   1209            rv = stmt->GetUTF8String(0, entry.mHost);
   1210            if (NS_WARN_IF(NS_FAILED(rv))) {
   1211              continue;
   1212            }
   1213            rv = stmt->GetUTF8String(1, entry.mType);
   1214            if (NS_WARN_IF(NS_FAILED(rv))) {
   1215              continue;
   1216            }
   1217 
   1218            entry.mId = id++;
   1219            entry.mPermission = stmt->AsInt32(2);
   1220            entry.mExpireType = stmt->AsInt32(3);
   1221            entry.mExpireTime = stmt->AsInt64(4);
   1222            entry.mModificationTime = stmt->AsInt64(5);
   1223 
   1224            mMigrationEntries.AppendElement(entry);
   1225          }
   1226 
   1227          // We don't drop the moz_hosts table such that it is available
   1228          // for backwards-compatability and for future migrations in case
   1229          // of migration errors in the current code. Create a marker
   1230          // empty table which will indicate that the moz_hosts table is
   1231          // intended to act as a backup. If this table is not present,
   1232          // then the moz_hosts table was created as a random empty table.
   1233          rv = data->mDBConn->ExecuteSimpleSQL(
   1234              nsLiteralCString("CREATE TABLE moz_hosts_is_backup (dummy "
   1235                               "INTEGER PRIMARY KEY)"));
   1236          NS_ENSURE_SUCCESS(rv, rv);
   1237 
   1238          bool permsTableExists = false;
   1239          data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists);
   1240          if (permsTableExists) {
   1241            // The user already had a moz_perms table, and we are
   1242            // performing a re-migration. We count the rows in the old
   1243            // table for telemetry, and then back up their old database as
   1244            // moz_perms_v6
   1245 
   1246            nsCOMPtr<mozIStorageStatement> countStmt;
   1247            rv = data->mDBConn->CreateStatement(
   1248                "SELECT COUNT(*) FROM moz_perms"_ns, getter_AddRefs(countStmt));
   1249            bool hasResult = false;
   1250            if (NS_FAILED(rv) ||
   1251                NS_FAILED(countStmt->ExecuteStep(&hasResult)) || !hasResult) {
   1252              NS_WARNING("Could not count the rows in moz_perms");
   1253            }
   1254 
   1255            // Back up the old moz_perms database as moz_perms_v6 before
   1256            // we move the new table into its position
   1257            rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString(
   1258                "ALTER TABLE moz_perms RENAME TO moz_perms_v6"));
   1259            NS_ENSURE_SUCCESS(rv, rv);
   1260          }
   1261 
   1262          rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString(
   1263              "ALTER TABLE moz_hosts_new RENAME TO moz_perms"));
   1264          NS_ENSURE_SUCCESS(rv, rv);
   1265 
   1266          rv = data->mDBConn->CommitTransaction();
   1267          NS_ENSURE_SUCCESS(rv, rv);
   1268        } else {
   1269          // We don't have a moz_hosts table, so we create one for
   1270          // downgrading purposes. This table is empty.
   1271          rv = data->mDBConn->ExecuteSimpleSQL(
   1272              nsLiteralCString("CREATE TABLE moz_hosts ("
   1273                               " id INTEGER PRIMARY KEY"
   1274                               ",host TEXT"
   1275                               ",type TEXT"
   1276                               ",permission INTEGER"
   1277                               ",expireType INTEGER"
   1278                               ",expireTime INTEGER"
   1279                               ",modificationTime INTEGER"
   1280                               ",appId INTEGER"
   1281                               ",isInBrowserElement INTEGER"
   1282                               ")"));
   1283          NS_ENSURE_SUCCESS(rv, rv);
   1284 
   1285          // We are guaranteed to have a moz_perms table at this point.
   1286        }
   1287 
   1288 #ifdef DEBUG
   1289        {
   1290          // At this point, both the moz_hosts and moz_perms tables should
   1291          // exist
   1292          bool hostsTableExists = false;
   1293          bool permsTableExists = false;
   1294          data->mDBConn->TableExists("moz_hosts"_ns, &hostsTableExists);
   1295          data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists);
   1296          MOZ_ASSERT(hostsTableExists && permsTableExists);
   1297        }
   1298 #endif
   1299 
   1300        rv = data->mDBConn->SetSchemaVersion(7);
   1301        NS_ENSURE_SUCCESS(rv, rv);
   1302      }
   1303 
   1304        // fall through to the next upgrade
   1305        [[fallthrough]];
   1306 
   1307      // The version 7-8 migration is the re-migration of localhost and
   1308      // ip-address entries due to errors in the previous version 7
   1309      // migration which caused localhost and ip-address entries to be
   1310      // incorrectly discarded. The version 7 migration logic has been
   1311      // corrected, and thus this logic only needs to execute if the user
   1312      // is currently on version 7.
   1313      case 7: {
   1314        // This migration will be relatively expensive as we need to
   1315        // perform database lookups for each origin which we want to
   1316        // insert. Fortunately, it shouldn't be too expensive as we only
   1317        // want to insert a small number of entries created for localhost
   1318        // or IP addresses.
   1319 
   1320        // We only want to perform the re-migration if moz_hosts is a
   1321        // backup
   1322        bool hostsIsBackupExists = false;
   1323        data->mDBConn->TableExists("moz_hosts_is_backup"_ns,
   1324                                   &hostsIsBackupExists);
   1325 
   1326        // Only perform this migration if the original schema version was
   1327        // 7, and the moz_hosts table is a backup.
   1328        if (dbSchemaVersion == 7 && hostsIsBackupExists) {
   1329          nsCOMPtr<mozIStorageStatement> stmt;
   1330          rv = data->mDBConn->CreateStatement(
   1331              nsLiteralCString(
   1332                  "SELECT host, type, permission, expireType, "
   1333                  "expireTime, "
   1334                  "modificationTime, isInBrowserElement FROM moz_hosts"),
   1335              getter_AddRefs(stmt));
   1336          NS_ENSURE_SUCCESS(rv, rv);
   1337 
   1338          nsCOMPtr<mozIStorageStatement> idStmt;
   1339          rv = data->mDBConn->CreateStatement(
   1340              "SELECT MAX(id) FROM moz_hosts"_ns, getter_AddRefs(idStmt));
   1341 
   1342          int64_t id = 0;
   1343          bool hasResult = false;
   1344          if (NS_SUCCEEDED(rv) &&
   1345              NS_SUCCEEDED(idStmt->ExecuteStep(&hasResult)) && hasResult) {
   1346            id = idStmt->AsInt32(0) + 1;
   1347          }
   1348 
   1349          nsCOMPtr<nsIEffectiveTLDService> etld =
   1350              mozilla::components::EffectiveTLD::Service();
   1351          while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   1352            MigrationEntry entry;
   1353 
   1354            // Read in the old row
   1355            rv = stmt->GetUTF8String(0, entry.mHost);
   1356            if (NS_WARN_IF(NS_FAILED(rv))) {
   1357              continue;
   1358            }
   1359 
   1360            nsAutoCString eTLD1;
   1361            rv = etld->GetBaseDomainFromHost(entry.mHost, 0, eTLD1);
   1362            if (NS_SUCCEEDED(rv)) {
   1363              // We only care about entries which the tldService can't
   1364              // handle
   1365              continue;
   1366            }
   1367 
   1368            rv = stmt->GetUTF8String(1, entry.mType);
   1369            if (NS_WARN_IF(NS_FAILED(rv))) {
   1370              continue;
   1371            }
   1372 
   1373            entry.mId = id++;
   1374            entry.mPermission = stmt->AsInt32(2);
   1375            entry.mExpireType = stmt->AsInt32(3);
   1376            entry.mExpireTime = stmt->AsInt64(4);
   1377            entry.mModificationTime = stmt->AsInt64(5);
   1378 
   1379            mMigrationEntries.AppendElement(entry);
   1380          }
   1381        }
   1382 
   1383        // Even if we didn't perform the migration, we want to bump the
   1384        // schema version to 8.
   1385        rv = data->mDBConn->SetSchemaVersion(8);
   1386        NS_ENSURE_SUCCESS(rv, rv);
   1387      }
   1388 
   1389        // fall through to the next upgrade
   1390        [[fallthrough]];
   1391 
   1392      // The version 8-9 migration removes the unnecessary backup
   1393      // moz-hosts database contents. as the data no longer needs to be
   1394      // migrated
   1395      case 8: {
   1396        // We only want to clear out the old table if it is a backup. If
   1397        // it isn't a backup, we don't need to touch it.
   1398        bool hostsIsBackupExists = false;
   1399        data->mDBConn->TableExists("moz_hosts_is_backup"_ns,
   1400                                   &hostsIsBackupExists);
   1401        if (hostsIsBackupExists) {
   1402          // Delete everything from the backup, we want to keep around the
   1403          // table so that you can still downgrade and not break things,
   1404          // but we don't need to keep the rows around.
   1405          rv = data->mDBConn->ExecuteSimpleSQL("DELETE FROM moz_hosts"_ns);
   1406          NS_ENSURE_SUCCESS(rv, rv);
   1407 
   1408          // The table is no longer a backup, so get rid of it.
   1409          rv = data->mDBConn->ExecuteSimpleSQL(
   1410              "DROP TABLE moz_hosts_is_backup"_ns);
   1411          NS_ENSURE_SUCCESS(rv, rv);
   1412        }
   1413 
   1414        rv = data->mDBConn->SetSchemaVersion(9);
   1415        NS_ENSURE_SUCCESS(rv, rv);
   1416      }
   1417 
   1418        // fall through to the next upgrade
   1419        [[fallthrough]];
   1420 
   1421      case 9: {
   1422        rv = data->mDBConn->SetSchemaVersion(10);
   1423        NS_ENSURE_SUCCESS(rv, rv);
   1424      }
   1425 
   1426        // fall through to the next upgrade
   1427        [[fallthrough]];
   1428 
   1429      case 10: {
   1430        // Filter out the rows with storage access API permissions with a
   1431        // granted origin, and remove the granted origin part from the
   1432        // permission type.
   1433        rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString(
   1434            "UPDATE moz_perms "
   1435            "SET type=SUBSTR(type, 0, INSTR(SUBSTR(type, INSTR(type, "
   1436            "'^') + "
   1437            "1), '^') + INSTR(type, '^')) "
   1438            "WHERE INSTR(SUBSTR(type, INSTR(type, '^') + 1), '^') AND "
   1439            "SUBSTR(type, 0, 18) == \"storageAccessAPI^\";"));
   1440        NS_ENSURE_SUCCESS(rv, rv);
   1441 
   1442        rv = data->mDBConn->SetSchemaVersion(11);
   1443        NS_ENSURE_SUCCESS(rv, rv);
   1444      }
   1445 
   1446        // fall through to the next upgrade
   1447        [[fallthrough]];
   1448 
   1449      case 11: {
   1450        // Migrate 3rdPartyStorage keys to a site scope
   1451        rv = data->mDBConn->BeginTransaction();
   1452        NS_ENSURE_SUCCESS(rv, rv);
   1453        nsCOMPtr<mozIStorageStatement> updateStmt;
   1454        rv = data->mDBConn->CreateStatement(
   1455            nsLiteralCString("UPDATE moz_perms SET origin = ?2 WHERE id = ?1"),
   1456            getter_AddRefs(updateStmt));
   1457        NS_ENSURE_SUCCESS(rv, rv);
   1458 
   1459        nsCOMPtr<mozIStorageStatement> deleteStmt;
   1460        rv = data->mDBConn->CreateStatement(
   1461            nsLiteralCString("DELETE FROM moz_perms WHERE id = ?1"),
   1462            getter_AddRefs(deleteStmt));
   1463        NS_ENSURE_SUCCESS(rv, rv);
   1464 
   1465        nsCOMPtr<mozIStorageStatement> selectStmt;
   1466        rv = data->mDBConn->CreateStatement(
   1467            nsLiteralCString("SELECT id, origin, type FROM moz_perms WHERE "
   1468                             " SUBSTR(type, 0, 17) == \"3rdPartyStorage^\""),
   1469            getter_AddRefs(selectStmt));
   1470        NS_ENSURE_SUCCESS(rv, rv);
   1471 
   1472        nsTHashSet<nsCStringHashKey> deduplicationSet;
   1473        bool hasResult;
   1474        nsCOMPtr<nsIEffectiveTLDService> etld =
   1475            mozilla::components::EffectiveTLD::Service();
   1476        while (NS_SUCCEEDED(selectStmt->ExecuteStep(&hasResult)) && hasResult) {
   1477          int64_t id;
   1478          rv = selectStmt->GetInt64(0, &id);
   1479          NS_ENSURE_SUCCESS(rv, rv);
   1480 
   1481          nsCString origin;
   1482          rv = selectStmt->GetUTF8String(1, origin);
   1483          NS_ENSURE_SUCCESS(rv, rv);
   1484 
   1485          nsCString type;
   1486          rv = selectStmt->GetUTF8String(2, type);
   1487          NS_ENSURE_SUCCESS(rv, rv);
   1488 
   1489          nsCOMPtr<nsIURI> uri;
   1490          rv = NS_NewURI(getter_AddRefs(uri), origin);
   1491          if (NS_FAILED(rv)) {
   1492            continue;
   1493          }
   1494          nsCString site;
   1495          rv = etld->GetSite(uri, site);
   1496          if (NS_WARN_IF(NS_FAILED(rv))) {
   1497            continue;
   1498          }
   1499 
   1500          nsCString deduplicationKey =
   1501              nsPrintfCString("%s,%s", site.get(), type.get());
   1502          if (deduplicationSet.Contains(deduplicationKey)) {
   1503            rv = deleteStmt->BindInt64ByIndex(0, id);
   1504            NS_ENSURE_SUCCESS(rv, rv);
   1505 
   1506            rv = deleteStmt->Execute();
   1507            NS_ENSURE_SUCCESS(rv, rv);
   1508          } else {
   1509            deduplicationSet.Insert(deduplicationKey);
   1510            rv = updateStmt->BindInt64ByIndex(0, id);
   1511            NS_ENSURE_SUCCESS(rv, rv);
   1512            rv = updateStmt->BindUTF8StringByIndex(1, site);
   1513            NS_ENSURE_SUCCESS(rv, rv);
   1514 
   1515            rv = updateStmt->Execute();
   1516            NS_ENSURE_SUCCESS(rv, rv);
   1517          }
   1518        }
   1519        rv = data->mDBConn->CommitTransaction();
   1520        NS_ENSURE_SUCCESS(rv, rv);
   1521 
   1522        rv = data->mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
   1523        NS_ENSURE_SUCCESS(rv, rv);
   1524      }
   1525 
   1526        // fall through to the next upgrade
   1527        [[fallthrough]];
   1528 
   1529      // current version.
   1530      case HOSTS_SCHEMA_VERSION:
   1531        break;
   1532 
   1533      // downgrading.
   1534      // if columns have been added to the table, we can still use the
   1535      // ones we understand safely. if columns have been deleted or
   1536      // altered, just blow away the table and start from scratch! if you
   1537      // change the way a column is interpreted, make sure you also change
   1538      // its name so this check will catch it.
   1539      default: {
   1540        // check if all the expected columns exist
   1541        nsCOMPtr<mozIStorageStatement> stmt;
   1542        rv = data->mDBConn->CreateStatement(
   1543            nsLiteralCString("SELECT origin, type, permission, "
   1544                             "expireType, expireTime, "
   1545                             "modificationTime FROM moz_perms"),
   1546            getter_AddRefs(stmt));
   1547        if (NS_SUCCEEDED(rv)) break;
   1548 
   1549        // our columns aren't there - drop the table!
   1550        rv = data->mDBConn->ExecuteSimpleSQL("DROP TABLE moz_perms"_ns);
   1551        NS_ENSURE_SUCCESS(rv, rv);
   1552 
   1553        rv = CreateTable();
   1554        NS_ENSURE_SUCCESS(rv, rv);
   1555      } break;
   1556    }
   1557  }
   1558 
   1559  // cache frequently used statements (for insertion, deletion, and
   1560  // updating)
   1561  rv = data->mDBConn->CreateStatement(
   1562      nsLiteralCString("INSERT INTO moz_perms "
   1563                       "(id, origin, type, permission, expireType, "
   1564                       "expireTime, modificationTime) "
   1565                       "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"),
   1566      getter_AddRefs(data->mStmtInsert));
   1567  NS_ENSURE_SUCCESS(rv, rv);
   1568 
   1569  rv = data->mDBConn->CreateStatement(nsLiteralCString("DELETE FROM moz_perms "
   1570                                                       "WHERE id = ?1"),
   1571                                      getter_AddRefs(data->mStmtDelete));
   1572  NS_ENSURE_SUCCESS(rv, rv);
   1573 
   1574  rv = data->mDBConn->CreateStatement(
   1575      nsLiteralCString("UPDATE moz_perms "
   1576                       "SET permission = ?2, expireType= ?3, expireTime = "
   1577                       "?4, modificationTime = ?5 WHERE id = ?1"),
   1578      getter_AddRefs(data->mStmtUpdate));
   1579  NS_ENSURE_SUCCESS(rv, rv);
   1580 
   1581  // Always import default permissions.
   1582  ConsumeDefaultsInputStream(aDefaultsInputStream, aProofOfLock);
   1583 
   1584  // check whether to import or just read in the db
   1585  if (tableExists) {
   1586    rv = Read(aProofOfLock);
   1587    NS_ENSURE_SUCCESS(rv, rv);
   1588  }
   1589 
   1590  raiiFailure.release();
   1591 
   1592  return NS_OK;
   1593 }
   1594 
   1595 void PermissionManager::AddIdleDailyMaintenanceJob() {
   1596  MOZ_ASSERT(NS_IsMainThread());
   1597 
   1598  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   1599  NS_ENSURE_TRUE_VOID(observerService);
   1600 
   1601  nsresult rv =
   1602      observerService->AddObserver(this, OBSERVER_TOPIC_IDLE_DAILY, false);
   1603  NS_ENSURE_SUCCESS_VOID(rv);
   1604 }
   1605 
   1606 void PermissionManager::RemoveIdleDailyMaintenanceJob() {
   1607  MOZ_ASSERT(NS_IsMainThread());
   1608 
   1609  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   1610  NS_ENSURE_TRUE_VOID(observerService);
   1611 
   1612  nsresult rv =
   1613      observerService->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY);
   1614  NS_ENSURE_SUCCESS_VOID(rv);
   1615 }
   1616 
   1617 void PermissionManager::PerformIdleDailyMaintenance() {
   1618  MOZ_ASSERT(NS_IsMainThread());
   1619 
   1620  RefPtr<PermissionManager> self = this;
   1621  mThread->Dispatch(NS_NewRunnableFunction(
   1622      "PermissionManager::PerformIdleDailyMaintenance", [self] {
   1623        auto data = self->mThreadBoundData.Access();
   1624 
   1625        if (self->mState == eClosed || !data->mDBConn) {
   1626          return;
   1627        }
   1628 
   1629        nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
   1630        nsresult rv = data->mDBConn->CreateStatement(
   1631            nsLiteralCString("DELETE FROM moz_perms WHERE expireType = "
   1632                             "?1 AND expireTime <= ?2"),
   1633            getter_AddRefs(stmtDeleteExpired));
   1634        NS_ENSURE_SUCCESS_VOID(rv);
   1635 
   1636        rv = stmtDeleteExpired->BindInt32ByIndex(
   1637            0, nsIPermissionManager::EXPIRE_TIME);
   1638        NS_ENSURE_SUCCESS_VOID(rv);
   1639 
   1640        rv = stmtDeleteExpired->BindInt64ByIndex(1, EXPIRY_NOW);
   1641        NS_ENSURE_SUCCESS_VOID(rv);
   1642 
   1643        rv = stmtDeleteExpired->Execute();
   1644        NS_ENSURE_SUCCESS_VOID(rv);
   1645      }));
   1646 }
   1647 
   1648 // sets the schema version and creates the moz_perms table.
   1649 nsresult PermissionManager::CreateTable() {
   1650  MOZ_ASSERT(!NS_IsMainThread());
   1651  auto data = mThreadBoundData.Access();
   1652 
   1653  // set the schema version, before creating the table
   1654  nsresult rv = data->mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
   1655  if (NS_FAILED(rv)) return rv;
   1656 
   1657  // create the table
   1658  // SQL also lives in automation.py.in. If you change this SQL change that
   1659  // one too
   1660  rv = data->mDBConn->ExecuteSimpleSQL(
   1661      nsLiteralCString("CREATE TABLE moz_perms ("
   1662                       " id INTEGER PRIMARY KEY"
   1663                       ",origin TEXT"
   1664                       ",type TEXT"
   1665                       ",permission INTEGER"
   1666                       ",expireType INTEGER"
   1667                       ",expireTime INTEGER"
   1668                       ",modificationTime INTEGER"
   1669                       ")"));
   1670  if (NS_FAILED(rv)) return rv;
   1671 
   1672  // We also create a legacy V4 table, for backwards compatability,
   1673  // and to ensure that downgrades don't trigger a schema version change.
   1674  return data->mDBConn->ExecuteSimpleSQL(
   1675      nsLiteralCString("CREATE TABLE moz_hosts ("
   1676                       " id INTEGER PRIMARY KEY"
   1677                       ",host TEXT"
   1678                       ",type TEXT"
   1679                       ",permission INTEGER"
   1680                       ",expireType INTEGER"
   1681                       ",expireTime INTEGER"
   1682                       ",modificationTime INTEGER"
   1683                       ",isInBrowserElement INTEGER"
   1684                       ")"));
   1685 }
   1686 
   1687 // Returns whether the given combination of expire type and expire time are
   1688 // expired. Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
   1689 bool PermissionManager::HasExpired(uint32_t aExpireType, int64_t aExpireTime) {
   1690  return (aExpireType == nsIPermissionManager::EXPIRE_TIME ||
   1691          (aExpireType == nsIPermissionManager::EXPIRE_SESSION &&
   1692           aExpireTime != 0)) &&
   1693         aExpireTime <= EXPIRY_NOW;
   1694 }
   1695 
   1696 NS_IMETHODIMP
   1697 PermissionManager::AddFromPrincipalAndPersistInPrivateBrowsing(
   1698    nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aPermission) {
   1699  ENSURE_NOT_CHILD_PROCESS;
   1700 
   1701  bool isValidPermissionPrincipal = false;
   1702  nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal,
   1703                                                   isValidPermissionPrincipal);
   1704 
   1705  NS_ENSURE_SUCCESS(rv, rv);
   1706  if (!isValidPermissionPrincipal) {
   1707    // return early if the principal is invalid for permissions
   1708    return rv;
   1709  }
   1710 
   1711  // A modificationTime of zero will cause AddInternal to use now().
   1712  int64_t modificationTime = 0;
   1713 
   1714  MonitorAutoLock lock{mMonitor};
   1715 
   1716  return AddInternal(aPrincipal, aType, aPermission, 0,
   1717                     nsIPermissionManager::EXPIRE_NEVER,
   1718                     /* aExpireTime */ 0, modificationTime, eNotify, eWriteToDB,
   1719                     /* aOriginString*/ nullptr,
   1720                     /* aAllowPersistInPrivateBrowsing */ true);
   1721 }
   1722 
   1723 NS_IMETHODIMP
   1724 PermissionManager::AddDefaultFromPrincipal(nsIPrincipal* aPrincipal,
   1725                                           const nsACString& aType,
   1726                                           uint32_t aPermission) {
   1727  ENSURE_NOT_CHILD_PROCESS;
   1728 
   1729  bool isValidPermissionPrincipal = false;
   1730  nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal,
   1731                                                   isValidPermissionPrincipal);
   1732  NS_ENSURE_SUCCESS(rv, rv);
   1733  if (!isValidPermissionPrincipal) {
   1734    // return early if the principal is invalid for permissions
   1735    return rv;
   1736  }
   1737 
   1738  nsCString origin;
   1739  rv = GetOriginFromPrincipal(aPrincipal, IsOAForceStripPermission(aType),
   1740                              origin);
   1741  NS_ENSURE_SUCCESS(rv, rv);
   1742 
   1743  MonitorAutoLock lock(mMonitor);
   1744 
   1745  DefaultEntry entry;
   1746  {
   1747    // Try to update existing entry in mDefaultEntriesForImport, which will
   1748    // later be used to restore the default permissions when permissions are
   1749    // cleared
   1750    bool updatedExistingEntry = false;
   1751    nsTArray<DefaultEntry>::iterator defaultEntry =
   1752        mDefaultEntriesForImport.begin();
   1753    while (defaultEntry != mDefaultEntriesForImport.end()) {
   1754      if (defaultEntry->mType == aType && defaultEntry->mOrigin == origin) {
   1755        defaultEntry->mPermission = aPermission;
   1756        entry = *defaultEntry;
   1757        if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   1758          mDefaultEntriesForImport.RemoveElementAt(defaultEntry);
   1759        }
   1760        updatedExistingEntry = true;
   1761        break;
   1762      }
   1763      ++defaultEntry;
   1764    }
   1765 
   1766    // Or add a new entry if there wasn't already one and we aren't deleting the
   1767    // default permission
   1768    if (!updatedExistingEntry) {
   1769      entry.mOrigin = origin;
   1770      entry.mPermission = aPermission;
   1771      entry.mType = aType;
   1772      if (aPermission != nsIPermissionManager::UNKNOWN_ACTION) {
   1773        mDefaultEntriesForImport.AppendElement(entry);
   1774      }
   1775    }
   1776  }
   1777 
   1778  // So far, we have only updated mDefaultEntriesForImport for later recovery.
   1779  // Now, we actually need to import this change into the permission manager.
   1780  return ImportDefaultEntry(entry);
   1781 }
   1782 
   1783 NS_IMETHODIMP
   1784 PermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
   1785                                    const nsACString& aType,
   1786                                    uint32_t aPermission, uint32_t aExpireType,
   1787                                    int64_t aExpireTime) {
   1788  ENSURE_NOT_CHILD_PROCESS;
   1789  NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
   1790                     aExpireType == nsIPermissionManager::EXPIRE_TIME ||
   1791                     aExpireType == nsIPermissionManager::EXPIRE_SESSION ||
   1792                     aExpireType == nsIPermissionManager::EXPIRE_POLICY,
   1793                 NS_ERROR_INVALID_ARG);
   1794 
   1795  // Skip addition if the permission is already expired.
   1796  if (HasExpired(aExpireType, aExpireTime)) {
   1797    return NS_OK;
   1798  }
   1799 
   1800  bool isValidPermissionPrincipal = false;
   1801  nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal,
   1802                                                   isValidPermissionPrincipal);
   1803 
   1804  NS_ENSURE_SUCCESS(rv, rv);
   1805  if (!isValidPermissionPrincipal) {
   1806    // return early if the principal is invalid for permissions
   1807    return rv;
   1808  }
   1809 
   1810  // A modificationTime of zero will cause AddInternal to use now().
   1811  int64_t modificationTime = 0;
   1812  MonitorAutoLock lock{mMonitor};
   1813  return AddInternal(aPrincipal, aType, aPermission, 0, aExpireType,
   1814                     aExpireTime, modificationTime, eNotify, eWriteToDB);
   1815 }
   1816 
   1817 NS_IMETHODIMP
   1818 PermissionManager::TestAddFromPrincipalByTime(nsIPrincipal* aPrincipal,
   1819                                              const nsACString& aType,
   1820                                              uint32_t aPermission,
   1821                                              int64_t aModificationTime) {
   1822  ENSURE_NOT_CHILD_PROCESS;
   1823 
   1824  bool isValidPermissionPrincipal = false;
   1825  nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal,
   1826                                                   isValidPermissionPrincipal);
   1827 
   1828  NS_ENSURE_SUCCESS(rv, rv);
   1829  if (!isValidPermissionPrincipal) {
   1830    // return early if the principal is invalid for permissions
   1831    return rv;
   1832  }
   1833 
   1834  MonitorAutoLock lock{mMonitor};
   1835  return AddInternal(aPrincipal, aType, aPermission, 0,
   1836                     nsIPermissionManager::EXPIRE_NEVER, 0, aModificationTime,
   1837                     eNotify, eWriteToDB);
   1838 }
   1839 
   1840 nsresult PermissionManager::Add(nsIPrincipal* aPrincipal,
   1841                                const nsACString& aType, uint32_t aPermission,
   1842                                int64_t aID, uint32_t aExpireType,
   1843                                int64_t aExpireTime, int64_t aModificationTime,
   1844                                NotifyOperationType aNotifyOperation,
   1845                                DBOperationType aDBOperation,
   1846                                const nsACString* aOriginString,
   1847                                const bool aAllowPersistInPrivateBrowsing) {
   1848  MOZ_ASSERT(IsChildProcess());
   1849 
   1850  MonitorAutoLock lock{mMonitor};
   1851  return AddInternal(aPrincipal, aType, aPermission, aID, aExpireType,
   1852                     aExpireTime, aModificationTime, aNotifyOperation,
   1853                     aDBOperation, aOriginString,
   1854                     aAllowPersistInPrivateBrowsing);
   1855 }
   1856 
   1857 nsresult PermissionManager::AddInternal(
   1858    nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aPermission,
   1859    int64_t aID, uint32_t aExpireType, int64_t aExpireTime,
   1860    int64_t aModificationTime, NotifyOperationType aNotifyOperation,
   1861    DBOperationType aDBOperation, const nsACString* aOriginString,
   1862    const bool aAllowPersistInPrivateBrowsing) {
   1863  MOZ_ASSERT_IF(!IsChildProcess(), NS_IsMainThread());
   1864 
   1865  // If this is a default permission, no changes should not be written to disk.
   1866  MOZ_ASSERT((aID != cIDPermissionIsDefault) || (aDBOperation != eWriteToDB));
   1867 
   1868  EnsureReadCompleted();
   1869 
   1870  nsresult rv = NS_OK;
   1871  nsAutoCString origin;
   1872  // Only attempt to compute the origin string when it is going to be needed
   1873  // later on in the function.
   1874  if (!IsChildProcess() ||
   1875      (aDBOperation == eWriteToDB && IsPersistentExpire(aExpireType, aType))) {
   1876    if (aOriginString) {
   1877      // Use the origin string provided by the caller.
   1878      origin = *aOriginString;
   1879    } else {
   1880      if (IsSiteScopedPermission(aType)) {
   1881        rv = GetSiteFromPrincipal(aPrincipal, IsOAForceStripPermission(aType),
   1882                                  origin);
   1883      } else {
   1884        // Compute it from the principal provided.
   1885        rv = GetOriginFromPrincipal(aPrincipal, IsOAForceStripPermission(aType),
   1886                                    origin);
   1887      }
   1888      NS_ENSURE_SUCCESS(rv, rv);
   1889    }
   1890  }
   1891 
   1892  // Unless the caller sets aAllowPersistInPrivateBrowsing, only store
   1893  // permissions for the session in Private Browsing. Except for default
   1894  // permissions which are stored in-memory only and imported each startup. We
   1895  // also allow setting persistent UKNOWN_ACTION, to support removing default
   1896  // private browsing permissions.
   1897  if (!aAllowPersistInPrivateBrowsing && aID != cIDPermissionIsDefault &&
   1898      aPermission != UNKNOWN_ACTION && aExpireType != EXPIRE_SESSION) {
   1899    uint32_t privateBrowsingId =
   1900        nsScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
   1901    nsresult rv = aPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
   1902    if (NS_SUCCEEDED(rv) &&
   1903        privateBrowsingId !=
   1904            nsScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID) {
   1905      aExpireType = EXPIRE_SESSION;
   1906    }
   1907  }
   1908 
   1909  // Let's send the new permission to the content process only if it has to be
   1910  // notified.
   1911  if (!IsChildProcess() && aNotifyOperation == eNotify) {
   1912    IPC::Permission permission(origin, aType, aPermission, aExpireType,
   1913                               aExpireTime);
   1914 
   1915    nsAutoCString permissionKey;
   1916    GetKeyForPermission(aPrincipal, aType, permissionKey);
   1917    bool isSecondaryKeyed;
   1918    nsAutoCString secondaryKey;
   1919    isSecondaryKeyed = GetSecondaryKey(aType, secondaryKey);
   1920    if (isSecondaryKeyed) {
   1921      NotifySecondaryKeyPermissionUpdateInContentProcess(
   1922          aType, aPermission, secondaryKey, aPrincipal);
   1923    }
   1924 
   1925    nsTArray<ContentParent*> cplist;
   1926    ContentParent::GetAll(cplist);
   1927    for (uint32_t i = 0; i < cplist.Length(); ++i) {
   1928      ContentParent* cp = cplist[i];
   1929      if (cp->NeedsPermissionsUpdate(permissionKey)) {
   1930        (void)cp->SendAddPermission(permission);
   1931      }
   1932    }
   1933  }
   1934 
   1935  MOZ_ASSERT(PermissionAvailableInternal(aPrincipal, aType));
   1936 
   1937  // look up the type index
   1938  int32_t typeIndex = GetTypeIndex(aType, true);
   1939  NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
   1940 
   1941  // When an entry already exists, PutEntry will return that, instead
   1942  // of adding a new one
   1943  RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal(
   1944      aPrincipal, IsOAForceStripPermission(aType),
   1945      IsSiteScopedPermission(aType), rv);
   1946  if (!key) {
   1947    MOZ_ASSERT(NS_FAILED(rv));
   1948    return rv;
   1949  }
   1950 
   1951  PermissionHashKey* entry = mPermissionTable.PutEntry(key);
   1952  if (!entry) return NS_ERROR_FAILURE;
   1953  if (!entry->GetKey()) {
   1954    mPermissionTable.RemoveEntry(entry);
   1955    return NS_ERROR_OUT_OF_MEMORY;
   1956  }
   1957 
   1958  // figure out the transaction type, and get any existing permission value
   1959  OperationType op;
   1960  int32_t index = entry->GetPermissionIndex(typeIndex);
   1961  if (index == -1) {
   1962    if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   1963      op = eOperationNone;
   1964    } else {
   1965      op = eOperationAdding;
   1966    }
   1967 
   1968  } else {
   1969    PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
   1970 
   1971    // remove the permission if the permission is UNKNOWN, update the
   1972    // permission if its value or expire type have changed OR if the time has
   1973    // changed and the expire type is time, otherwise, don't modify.  There's
   1974    // no need to modify a permission that doesn't expire with time when the
   1975    // only thing changed is the expire time.
   1976    if (aPermission == oldPermissionEntry.mPermission &&
   1977        aExpireType == oldPermissionEntry.mExpireType &&
   1978        (aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
   1979         aExpireTime == oldPermissionEntry.mExpireTime)) {
   1980      op = eOperationNone;
   1981    } else if (oldPermissionEntry.mID == cIDPermissionIsDefault &&
   1982               aID != cIDPermissionIsDefault) {
   1983      // An existing default permission already exists, but the new permission
   1984      // isn't a default permission. This case requires some special handing.
   1985      op = eOperationReplacingDefault;
   1986    } else if (oldPermissionEntry.mID != cIDPermissionIsDefault &&
   1987               aID == cIDPermissionIsDefault) {
   1988      // We are adding a default permission but a "real" permission already
   1989      // exists. This means we don't have to do anything here.
   1990      op = eOperationNone;
   1991    } else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   1992      // At this point, both the old and new permission are either both default
   1993      // permissions, or both not default permissions. Now we only need to check
   1994      // wether to change or remove the old permission.
   1995      op = eOperationRemoving;
   1996    } else {
   1997      op = eOperationChanging;
   1998    }
   1999  }
   2000 
   2001  // child processes should *always* be passed a modificationTime of zero.
   2002  MOZ_ASSERT(!IsChildProcess() || aModificationTime == 0);
   2003 
   2004  // do the work for adding, deleting, or changing a permission:
   2005  // update the in-memory list, write to the db, and notify consumers.
   2006  int64_t id;
   2007  if (aModificationTime == 0) {
   2008    aModificationTime = EXPIRY_NOW;
   2009  }
   2010 
   2011  switch (op) {
   2012    case eOperationNone: {
   2013      // nothing to do
   2014      return NS_OK;
   2015    }
   2016 
   2017    case eOperationAdding: {
   2018      if (aDBOperation == eWriteToDB) {
   2019        // we'll be writing to the database - generate a known unique id
   2020        id = ++mLargestID;
   2021      } else {
   2022        // we're reading from the database - use the id already assigned
   2023        id = aID;
   2024      }
   2025 
   2026      entry->GetPermissions().AppendElement(
   2027          PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime,
   2028                          aModificationTime));
   2029 
   2030      if (aDBOperation == eWriteToDB &&
   2031          IsPersistentExpire(aExpireType, aType)) {
   2032        UpdateDB(op, id, origin, aType, aPermission, aExpireType, aExpireTime,
   2033                 aModificationTime);
   2034      }
   2035 
   2036      if (aNotifyOperation == eNotify) {
   2037        NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex],
   2038                                      aPermission, aExpireType, aExpireTime,
   2039                                      aModificationTime, u"added"_ns);
   2040      }
   2041 
   2042      break;
   2043    }
   2044 
   2045    case eOperationRemoving: {
   2046      PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
   2047      id = oldPermissionEntry.mID;
   2048 
   2049      // If the type we want to remove is EXPIRE_POLICY, we need to reject
   2050      // attempts to change the permission.
   2051      if (entry->GetPermissions()[index].mExpireType == EXPIRE_POLICY) {
   2052        NS_WARNING("Attempting to remove EXPIRE_POLICY permission");
   2053        break;
   2054      }
   2055 
   2056      entry->GetPermissions().RemoveElementAt(index);
   2057 
   2058      if (aDBOperation == eWriteToDB) {
   2059        // We care only about the id here so we pass dummy values for all other
   2060        // parameters.
   2061        UpdateDB(op, id, ""_ns, ""_ns, 0, nsIPermissionManager::EXPIRE_NEVER, 0,
   2062                 0);
   2063      }
   2064 
   2065      if (aNotifyOperation == eNotify) {
   2066        NotifyObserversWithPermission(
   2067            aPrincipal, mTypeArray[typeIndex], oldPermissionEntry.mPermission,
   2068            oldPermissionEntry.mExpireType, oldPermissionEntry.mExpireTime,
   2069            oldPermissionEntry.mModificationTime, u"deleted"_ns);
   2070      }
   2071 
   2072      // If there are no more permissions stored for that entry, clear it.
   2073      if (entry->GetPermissions().IsEmpty()) {
   2074        mPermissionTable.RemoveEntry(entry);
   2075      }
   2076 
   2077      // If the entry we are removing is not a default, restore the potential
   2078      // default entry in-memory
   2079      if (oldPermissionEntry.mID != cIDPermissionIsDefault) {
   2080        for (const DefaultEntry& defaultEntry : mDefaultEntriesForImport) {
   2081          if (defaultEntry.mType == aType && defaultEntry.mOrigin == origin &&
   2082              defaultEntry.mPermission !=
   2083                  nsIPermissionManager::UNKNOWN_ACTION) {
   2084            rv = ImportDefaultEntry(defaultEntry);
   2085            NS_ENSURE_SUCCESS(rv, rv);
   2086            break;
   2087          }
   2088        }
   2089      }
   2090 
   2091      break;
   2092    }
   2093 
   2094    case eOperationChanging: {
   2095      id = entry->GetPermissions()[index].mID;
   2096 
   2097      // If the existing type is EXPIRE_POLICY, we need to reject attempts to
   2098      // change the permission.
   2099      if (entry->GetPermissions()[index].mExpireType == EXPIRE_POLICY) {
   2100        NS_WARNING("Attempting to modify EXPIRE_POLICY permission");
   2101        break;
   2102      }
   2103 
   2104      PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
   2105 
   2106      // If the new expireType is EXPIRE_SESSION, then we have to keep a
   2107      // copy of the previous permission/expireType values. This cached value
   2108      // will be used when restoring the permissions of an app.
   2109      if (entry->GetPermissions()[index].mExpireType !=
   2110              nsIPermissionManager::EXPIRE_SESSION &&
   2111          aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
   2112        entry->GetPermissions()[index].mNonSessionPermission =
   2113            entry->GetPermissions()[index].mPermission;
   2114        entry->GetPermissions()[index].mNonSessionExpireType =
   2115            entry->GetPermissions()[index].mExpireType;
   2116        entry->GetPermissions()[index].mNonSessionExpireTime =
   2117            entry->GetPermissions()[index].mExpireTime;
   2118      } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
   2119        entry->GetPermissions()[index].mNonSessionPermission = aPermission;
   2120        entry->GetPermissions()[index].mNonSessionExpireType = aExpireType;
   2121        entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime;
   2122      }
   2123 
   2124      entry->GetPermissions()[index].mPermission = aPermission;
   2125      entry->GetPermissions()[index].mExpireType = aExpireType;
   2126      entry->GetPermissions()[index].mExpireTime = aExpireTime;
   2127      entry->GetPermissions()[index].mModificationTime = aModificationTime;
   2128 
   2129      if (aDBOperation == eWriteToDB) {
   2130        bool newIsPersistentExpire = IsPersistentExpire(aExpireType, aType);
   2131        bool oldIsPersistentExpire =
   2132            IsPersistentExpire(oldPermissionEntry.mExpireType, aType);
   2133 
   2134        if (!newIsPersistentExpire && oldIsPersistentExpire) {
   2135          // Maybe we have to remove the previous permission if that was
   2136          // persistent.
   2137          UpdateDB(eOperationRemoving, id, ""_ns, ""_ns, 0,
   2138                   nsIPermissionManager::EXPIRE_NEVER, 0, 0);
   2139        } else if (newIsPersistentExpire && !oldIsPersistentExpire) {
   2140          // It could also be that the previous permission was session-only but
   2141          // this needs to be written into the DB. In this case, we have to run
   2142          // an Adding operation.
   2143          UpdateDB(eOperationAdding, id, origin, aType, aPermission,
   2144                   aExpireType, aExpireTime, aModificationTime);
   2145        } else if (newIsPersistentExpire) {
   2146          // This is the a simple update.  We care only about the id, the
   2147          // permission and expireType/expireTime/modificationTime here. We pass
   2148          // dummy values for all other parameters.
   2149          UpdateDB(op, id, ""_ns, ""_ns, aPermission, aExpireType, aExpireTime,
   2150                   aModificationTime);
   2151        }
   2152      }
   2153 
   2154      if (aNotifyOperation == eNotify) {
   2155        NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex],
   2156                                      aPermission, aExpireType, aExpireTime,
   2157                                      aModificationTime, u"changed"_ns);
   2158      }
   2159 
   2160      break;
   2161    }
   2162    case eOperationReplacingDefault: {
   2163      // this is handling the case when we have an existing permission
   2164      // entry that was created as a "default" (and thus isn't in the DB) with
   2165      // an explicit permission (that may include UNKNOWN_ACTION.)
   2166      // Note we will *not* get here if we are replacing an already replaced
   2167      // default value - that is handled as eOperationChanging.
   2168 
   2169      // So this is a hybrid of eOperationAdding (as we are writing a new entry
   2170      // to the DB) and eOperationChanging (as we are replacing the in-memory
   2171      // repr and sending a "changed" notification).
   2172 
   2173      // We want a new ID even if not writing to the DB, so the modified entry
   2174      // in memory doesn't have the magic cIDPermissionIsDefault value.
   2175      id = ++mLargestID;
   2176 
   2177      // The default permission being replaced can't have session expiry or
   2178      // policy expiry.
   2179      NS_ENSURE_TRUE(entry->GetPermissions()[index].mExpireType !=
   2180                         nsIPermissionManager::EXPIRE_SESSION,
   2181                     NS_ERROR_UNEXPECTED);
   2182      NS_ENSURE_TRUE(entry->GetPermissions()[index].mExpireType !=
   2183                         nsIPermissionManager::EXPIRE_POLICY,
   2184                     NS_ERROR_UNEXPECTED);
   2185      // We don't support the new entry having any expiry - supporting that
   2186      // would make things far more complex and none of the permissions we set
   2187      // as a default support that.
   2188      NS_ENSURE_TRUE(aExpireType == EXPIRE_NEVER, NS_ERROR_UNEXPECTED);
   2189 
   2190      // update the existing entry in memory.
   2191      entry->GetPermissions()[index].mID = id;
   2192      entry->GetPermissions()[index].mPermission = aPermission;
   2193      entry->GetPermissions()[index].mExpireType = aExpireType;
   2194      entry->GetPermissions()[index].mExpireTime = aExpireTime;
   2195      entry->GetPermissions()[index].mModificationTime = aModificationTime;
   2196 
   2197      // If requested, create the entry in the DB.
   2198      if (aDBOperation == eWriteToDB &&
   2199          IsPersistentExpire(aExpireType, aType)) {
   2200        UpdateDB(eOperationAdding, id, origin, aType, aPermission, aExpireType,
   2201                 aExpireTime, aModificationTime);
   2202      }
   2203 
   2204      if (aNotifyOperation == eNotify) {
   2205        NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex],
   2206                                      aPermission, aExpireType, aExpireTime,
   2207                                      aModificationTime, u"changed"_ns);
   2208      }
   2209 
   2210    } break;
   2211  }
   2212 
   2213  return NS_OK;
   2214 }
   2215 
   2216 NS_IMETHODIMP
   2217 PermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
   2218                                       const nsACString& aType) {
   2219  ENSURE_NOT_CHILD_PROCESS;
   2220 
   2221  MonitorAutoLock lock{mMonitor};
   2222  return RemoveFromPrincipalInternal(aPrincipal, aType);
   2223 }
   2224 
   2225 nsresult PermissionManager::RemoveFromPrincipalInternal(
   2226    nsIPrincipal* aPrincipal, const nsACString& aType) {
   2227  ENSURE_NOT_CHILD_PROCESS;
   2228  NS_ENSURE_ARG_POINTER(aPrincipal);
   2229 
   2230  // System principals are never added to the database, no need to remove them.
   2231  if (aPrincipal->IsSystemPrincipal()) {
   2232    return NS_OK;
   2233  }
   2234 
   2235  // Permissions may not be added to expanded principals.
   2236  if (IsExpandedPrincipal(aPrincipal)) {
   2237    return NS_ERROR_INVALID_ARG;
   2238  }
   2239 
   2240  // AddInternal() handles removal, just let it do the work
   2241  return AddInternal(aPrincipal, aType, nsIPermissionManager::UNKNOWN_ACTION, 0,
   2242                     nsIPermissionManager::EXPIRE_NEVER, 0, 0, eNotify,
   2243                     eWriteToDB);
   2244 }
   2245 
   2246 NS_IMETHODIMP
   2247 PermissionManager::RemovePermission(nsIPermission* aPerm) {
   2248  if (!aPerm) {
   2249    return NS_OK;
   2250  }
   2251  nsCOMPtr<nsIPrincipal> principal;
   2252  nsresult rv = aPerm->GetPrincipal(getter_AddRefs(principal));
   2253  NS_ENSURE_SUCCESS(rv, rv);
   2254 
   2255  nsAutoCString type;
   2256  rv = aPerm->GetType(type);
   2257  NS_ENSURE_SUCCESS(rv, rv);
   2258 
   2259  MonitorAutoLock lock{mMonitor};
   2260 
   2261  // Permissions are uniquely identified by their principal and type.
   2262  // We remove the permission using these two pieces of data.
   2263  return RemoveFromPrincipalInternal(principal, type);
   2264 }
   2265 
   2266 NS_IMETHODIMP
   2267 PermissionManager::RemoveAll() {
   2268  ENSURE_NOT_CHILD_PROCESS;
   2269 
   2270  MonitorAutoLock lock{mMonitor};
   2271  return RemoveAllInternal(true);
   2272 }
   2273 
   2274 NS_IMETHODIMP
   2275 PermissionManager::RemoveAllSince(int64_t aSince) {
   2276  ENSURE_NOT_CHILD_PROCESS;
   2277  MonitorAutoLock lock{mMonitor};
   2278  return RemoveAllModifiedSince(aSince);
   2279 }
   2280 
   2281 NS_IMETHODIMP
   2282 PermissionManager::RemoveAllExceptTypes(
   2283    const nsTArray<nsCString>& aTypeExceptions) {
   2284  ENSURE_NOT_CHILD_PROCESS;
   2285 
   2286  MonitorAutoLock lock{mMonitor};
   2287  // Need to make sure read is done before we get the type index. Type indexes
   2288  // are populated from DB.
   2289  EnsureReadCompleted();
   2290 
   2291  if (aTypeExceptions.IsEmpty()) {
   2292    return RemoveAllInternal(true);
   2293  }
   2294 
   2295  return RemovePermissionEntries(
   2296      [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) {
   2297        return !aTypeExceptions.Contains(mTypeArray[aPermEntry.mType]);
   2298      });
   2299 }
   2300 
   2301 nsresult PermissionManager::RemovePermissionEntries(
   2302    const std::function<bool(const PermissionEntry& aPermEntry,
   2303                             const nsCOMPtr<nsIPrincipal>& aPrincipal)>&
   2304        aCondition,
   2305    bool aComputePrincipalForCondition) {
   2306  EnsureReadCompleted();
   2307 
   2308  Vector<std::tuple<nsCOMPtr<nsIPrincipal>, nsCString, nsCString>, 10> array;
   2309  for (const PermissionHashKey& entry : mPermissionTable) {
   2310    for (const auto& permEntry : entry.GetPermissions()) {
   2311      // Depending on whether the principal is needed in the condition check, we
   2312      // may already check the condition here, to avoid needing to compute the
   2313      // principal if the condition is true.
   2314      if (!aComputePrincipalForCondition && !aCondition(permEntry, nullptr)) {
   2315        continue;
   2316      }
   2317 
   2318      nsCOMPtr<nsIPrincipal> principal;
   2319      nsresult rv = GetPrincipalFromOrigin(
   2320          entry.GetKey()->mOrigin,
   2321          IsOAForceStripPermission(mTypeArray[permEntry.mType]),
   2322          getter_AddRefs(principal));
   2323      if (NS_FAILED(rv)) {
   2324        continue;
   2325      }
   2326 
   2327      if (aComputePrincipalForCondition && !aCondition(permEntry, principal)) {
   2328        continue;
   2329      }
   2330 
   2331      if (!array.emplaceBack(principal, mTypeArray[permEntry.mType],
   2332                             entry.GetKey()->mOrigin)) {
   2333        continue;
   2334      }
   2335    }
   2336  }
   2337 
   2338  for (auto& i : array) {
   2339    // AddInternal handles removal, so let it do the work...
   2340    AddInternal(
   2341        std::get<0>(i), std::get<1>(i), nsIPermissionManager::UNKNOWN_ACTION, 0,
   2342        nsIPermissionManager::EXPIRE_NEVER, 0, 0, PermissionManager::eNotify,
   2343        PermissionManager::eWriteToDB, &std::get<2>(i));
   2344  }
   2345 
   2346  return NS_OK;
   2347 }
   2348 
   2349 nsresult PermissionManager::RemovePermissionEntries(
   2350    const std::function<bool(const PermissionEntry& aPermEntry)>& aCondition) {
   2351  return RemovePermissionEntries(
   2352      [&](const PermissionEntry& aPermEntry,
   2353          const nsCOMPtr<nsIPrincipal>& aPrincipal) {
   2354        return aCondition(aPermEntry);
   2355      },
   2356      false);
   2357 }
   2358 
   2359 NS_IMETHODIMP
   2360 PermissionManager::RemoveByType(const nsACString& aType) {
   2361  ENSURE_NOT_CHILD_PROCESS;
   2362 
   2363  MonitorAutoLock lock{mMonitor};
   2364 
   2365  // Need to make sure read is done before we get the type index. Type indexes
   2366  // are populated from DB.
   2367  EnsureReadCompleted();
   2368 
   2369  int32_t typeIndex = GetTypeIndex(aType, false);
   2370  // If type == -1, the type isn't known,
   2371  // so just return NS_OK
   2372  if (typeIndex == -1) {
   2373    return NS_OK;
   2374  }
   2375 
   2376  return RemovePermissionEntries(
   2377      [typeIndex](const PermissionEntry& aPermEntry) {
   2378        return static_cast<uint32_t>(typeIndex) == aPermEntry.mType;
   2379      });
   2380 }
   2381 
   2382 NS_IMETHODIMP
   2383 PermissionManager::RemoveByTypeSince(const nsACString& aType,
   2384                                     int64_t aModificationTime) {
   2385  ENSURE_NOT_CHILD_PROCESS;
   2386 
   2387  MonitorAutoLock lock{mMonitor};
   2388 
   2389  // Need to make sure read is done before we get the type index. Type indexes
   2390  // are populated from DB.
   2391  EnsureReadCompleted();
   2392 
   2393  int32_t typeIndex = GetTypeIndex(aType, false);
   2394  // If type == -1, the type isn't known,
   2395  // so just return NS_OK
   2396  if (typeIndex == -1) {
   2397    return NS_OK;
   2398  }
   2399 
   2400  return RemovePermissionEntries(
   2401      [typeIndex, aModificationTime](const PermissionEntry& aPermEntry) {
   2402        return uint32_t(typeIndex) == aPermEntry.mType &&
   2403               aModificationTime <= aPermEntry.mModificationTime;
   2404      });
   2405 }
   2406 
   2407 NS_IMETHODIMP
   2408 PermissionManager::RemoveAllSinceWithTypeExceptions(
   2409    int64_t aModificationTime, const nsTArray<nsCString>& aTypeExceptions) {
   2410  ENSURE_NOT_CHILD_PROCESS;
   2411 
   2412  MonitorAutoLock lock{mMonitor};
   2413 
   2414  // Need to make sure read is done before we get the type index. Type indexes
   2415  // are populated from DB.
   2416  EnsureReadCompleted();
   2417 
   2418  return RemovePermissionEntries(
   2419      [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) {
   2420        return !aTypeExceptions.Contains(mTypeArray[aPermEntry.mType]) &&
   2421               aModificationTime <= aPermEntry.mModificationTime;
   2422      });
   2423 }
   2424 
   2425 void PermissionManager::CloseDB(CloseDBNextOp aNextOp) {
   2426  EnsureReadCompleted();
   2427 
   2428  mState = eClosed;
   2429 
   2430  nsCOMPtr<nsIInputStream> defaultsInputStream;
   2431  if (aNextOp == eRebuldOnSuccess) {
   2432    defaultsInputStream = GetDefaultsInputStream();
   2433  }
   2434 
   2435  RefPtr<PermissionManager> self = this;
   2436  mThread->Dispatch(NS_NewRunnableFunction(
   2437      "PermissionManager::CloseDB", [self, aNextOp, defaultsInputStream] {
   2438        auto data = self->mThreadBoundData.Access();
   2439        // Null the statements, this will finalize them.
   2440        data->mStmtInsert = nullptr;
   2441        data->mStmtDelete = nullptr;
   2442        data->mStmtUpdate = nullptr;
   2443        if (data->mDBConn) {
   2444          DebugOnly<nsresult> rv = data->mDBConn->Close();
   2445          MOZ_ASSERT(NS_SUCCEEDED(rv));
   2446          data->mDBConn = nullptr;
   2447 
   2448          if (aNextOp == eRebuldOnSuccess) {
   2449            MonitorAutoLock lock{self->mMonitor};
   2450            self->TryInitDB(true, defaultsInputStream, lock);
   2451          }
   2452        }
   2453 
   2454        if (aNextOp == eShutdown) {
   2455          NS_DispatchToMainThread(
   2456              NS_NewRunnableFunction("PermissionManager::FinishAsyncShutdown",
   2457                                     [self] { self->FinishAsyncShutdown(); }));
   2458        }
   2459      }));
   2460 }
   2461 
   2462 nsresult PermissionManager::RemoveAllFromIPC() {
   2463  MOZ_ASSERT(IsChildProcess());
   2464 
   2465  MonitorAutoLock lock{mMonitor};
   2466 
   2467  // Remove from memory and notify immediately. Since the in-memory
   2468  // database is authoritative, we do not need confirmation from the
   2469  // on-disk database to notify observers.
   2470  RemoveAllFromMemory();
   2471 
   2472  return NS_OK;
   2473 }
   2474 
   2475 nsresult PermissionManager::RemoveAllInternal(bool aNotifyObservers) {
   2476  ENSURE_NOT_CHILD_PROCESS;
   2477 
   2478  EnsureReadCompleted();
   2479 
   2480  // Let's broadcast the removeAll() to any content process.
   2481  nsTArray<ContentParent*> parents;
   2482  ContentParent::GetAll(parents);
   2483  for (ContentParent* parent : parents) {
   2484    (void)parent->SendRemoveAllPermissions();
   2485  }
   2486 
   2487  // Remove from memory and notify immediately. Since the in-memory
   2488  // database is authoritative, we do not need confirmation from the
   2489  // on-disk database to notify observers.
   2490  RemoveAllFromMemory();
   2491 
   2492  // Re-import the defaults
   2493  ImportLatestDefaults();
   2494 
   2495  if (aNotifyObservers) {
   2496    NotifyObservers(nullptr, u"cleared"_ns);
   2497  }
   2498 
   2499  RefPtr<PermissionManager> self = this;
   2500  mThread->Dispatch(
   2501      NS_NewRunnableFunction("PermissionManager::RemoveAllInternal", [self] {
   2502        auto data = self->mThreadBoundData.Access();
   2503 
   2504        if (self->mState == eClosed || !data->mDBConn) {
   2505          return;
   2506        }
   2507 
   2508        // clear the db
   2509        nsresult rv =
   2510            data->mDBConn->ExecuteSimpleSQL("DELETE FROM moz_perms"_ns);
   2511        if (NS_WARN_IF(NS_FAILED(rv))) {
   2512          NS_DispatchToMainThread(NS_NewRunnableFunction(
   2513              "PermissionManager::RemoveAllInternal-Failure", [self] {
   2514                MonitorAutoLock lock{self->mMonitor};
   2515                self->CloseDB(eRebuldOnSuccess);
   2516              }));
   2517        }
   2518      }));
   2519 
   2520  return NS_OK;
   2521 }
   2522 
   2523 NS_IMETHODIMP
   2524 PermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
   2525                                                    const nsACString& aType,
   2526                                                    uint32_t* aPermission) {
   2527  MonitorAutoLock lock{mMonitor};
   2528  return CommonTestPermission(aPrincipal, -1, aType, aPermission,
   2529                              nsIPermissionManager::UNKNOWN_ACTION, false, true,
   2530                              true);
   2531 }
   2532 
   2533 NS_IMETHODIMP
   2534 PermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal,
   2535                                                const nsACString& aType,
   2536                                                uint32_t* aPermission) {
   2537  MonitorAutoLock lock{mMonitor};
   2538  return CommonTestPermission(aPrincipal, -1, aType, aPermission,
   2539                              nsIPermissionManager::UNKNOWN_ACTION, false, true,
   2540                              false);
   2541 }
   2542 
   2543 NS_IMETHODIMP
   2544 PermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
   2545                                               const nsACString& aType,
   2546                                               uint32_t* aPermission) {
   2547  MonitorAutoLock lock{mMonitor};
   2548  return CommonTestPermission(aPrincipal, -1, aType, aPermission,
   2549                              nsIPermissionManager::UNKNOWN_ACTION, false,
   2550                              false, true);
   2551 }
   2552 
   2553 NS_IMETHODIMP
   2554 PermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
   2555                                       const nsACString& aType,
   2556                                       bool aExactHostMatch,
   2557                                       nsIPermission** aResult) {
   2558  NS_ENSURE_ARG_POINTER(aPrincipal);
   2559  *aResult = nullptr;
   2560 
   2561  MonitorAutoLock lock{mMonitor};
   2562 
   2563  EnsureReadCompleted();
   2564 
   2565  if (aPrincipal->IsSystemPrincipal()) {
   2566    return NS_OK;
   2567  }
   2568 
   2569  // Querying the permission object of an nsEP is non-sensical.
   2570  if (IsExpandedPrincipal(aPrincipal)) {
   2571    return NS_ERROR_INVALID_ARG;
   2572  }
   2573 
   2574  MOZ_ASSERT(PermissionAvailableInternal(aPrincipal, aType));
   2575 
   2576  int32_t typeIndex = GetTypeIndex(aType, false);
   2577  // If type == -1, the type isn't known,
   2578  // so just return NS_OK
   2579  if (typeIndex == -1) return NS_OK;
   2580 
   2581  PermissionHashKey* entry =
   2582      GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
   2583  if (!entry) {
   2584    return NS_OK;
   2585  }
   2586 
   2587  // We don't call GetPermission(typeIndex) because that returns a fake
   2588  // UNKNOWN_ACTION entry if there is no match.
   2589  int32_t idx = entry->GetPermissionIndex(typeIndex);
   2590  if (-1 == idx) {
   2591    return NS_OK;
   2592  }
   2593 
   2594  nsCOMPtr<nsIPrincipal> principal;
   2595  nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
   2596                                       IsOAForceStripPermission(aType),
   2597                                       getter_AddRefs(principal));
   2598  NS_ENSURE_SUCCESS(rv, rv);
   2599 
   2600  PermissionEntry& perm = entry->GetPermissions()[idx];
   2601  nsCOMPtr<nsIPermission> r = Permission::Create(
   2602      principal, mTypeArray[perm.mType], perm.mPermission, perm.mExpireType,
   2603      perm.mExpireTime, perm.mModificationTime);
   2604  if (NS_WARN_IF(!r)) {
   2605    return NS_ERROR_FAILURE;
   2606  }
   2607  r.forget(aResult);
   2608  return NS_OK;
   2609 }
   2610 
   2611 nsresult PermissionManager::CommonTestPermissionInternal(
   2612    nsIPrincipal* aPrincipal, nsIURI* aURI,
   2613    const OriginAttributes* aOriginAttributes, int32_t aTypeIndex,
   2614    const nsACString& aType, uint32_t* aPermission, bool aExactHostMatch,
   2615    bool aIncludingSession) {
   2616  MOZ_ASSERT(aPrincipal || aURI);
   2617  NS_ENSURE_ARG_POINTER(aPrincipal || aURI);
   2618  MOZ_ASSERT_IF(aPrincipal, !aURI && !aOriginAttributes);
   2619  MOZ_ASSERT_IF(aURI || aOriginAttributes, !aPrincipal);
   2620 
   2621  EnsureReadCompleted();
   2622 
   2623 #ifdef DEBUG
   2624  {
   2625    nsCOMPtr<nsIPrincipal> prin = aPrincipal;
   2626    if (!prin) {
   2627      if (aURI) {
   2628        prin = BasePrincipal::CreateContentPrincipal(aURI, OriginAttributes());
   2629      }
   2630    }
   2631    MOZ_ASSERT(prin);
   2632    MOZ_ASSERT(PermissionAvailableInternal(prin, aType));
   2633  }
   2634 #endif
   2635 
   2636  PermissionHashKey* entry =
   2637      aPrincipal ? GetPermissionHashKey(aPrincipal, aTypeIndex, aExactHostMatch)
   2638                 : GetPermissionHashKey(aURI, aOriginAttributes, aTypeIndex,
   2639                                        aExactHostMatch);
   2640  if (!entry || (!aIncludingSession &&
   2641                 entry->GetPermission(aTypeIndex).mNonSessionExpireType ==
   2642                     nsIPermissionManager::EXPIRE_SESSION)) {
   2643    return NS_OK;
   2644  }
   2645 
   2646  *aPermission = aIncludingSession
   2647                     ? entry->GetPermission(aTypeIndex).mPermission
   2648                     : entry->GetPermission(aTypeIndex).mNonSessionPermission;
   2649  return NS_OK;
   2650 }
   2651 
   2652 nsresult PermissionManager::GetPermissionEntries(
   2653    const std::function<bool(const PermissionEntry& aPermEntry)>& aCondition,
   2654    nsTArray<RefPtr<nsIPermission>>& aResult) {
   2655  aResult.Clear();
   2656  if (XRE_IsContentProcess()) {
   2657    NS_WARNING(
   2658        "Iterating over all permissions is not available in the "
   2659        "content process, as not all permissions may be available.");
   2660    return NS_ERROR_NOT_AVAILABLE;
   2661  }
   2662 
   2663  EnsureReadCompleted();
   2664 
   2665  for (const PermissionHashKey& entry : mPermissionTable) {
   2666    for (const auto& permEntry : entry.GetPermissions()) {
   2667      // Given how "default" permissions work and the possibility of them being
   2668      // overridden with UNKNOWN_ACTION, we might see this value here - but we
   2669      // do *not* want to return them via the enumerator.
   2670      if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   2671        continue;
   2672      }
   2673 
   2674      // If the permission is expired, skip it. We're not deleting it here
   2675      // because we're iterating over a lot of permissions.
   2676      // It will be removed as part of the daily maintenance later.
   2677      if (HasExpired(permEntry.mExpireType, permEntry.mExpireTime)) {
   2678        continue;
   2679      }
   2680 
   2681      if (!aCondition(permEntry)) {
   2682        continue;
   2683      }
   2684 
   2685      nsCOMPtr<nsIPrincipal> principal;
   2686      nsresult rv = GetPrincipalFromOrigin(
   2687          entry.GetKey()->mOrigin,
   2688          IsOAForceStripPermission(mTypeArray[permEntry.mType]),
   2689          getter_AddRefs(principal));
   2690      if (NS_FAILED(rv)) {
   2691        continue;
   2692      }
   2693 
   2694      RefPtr<nsIPermission> permission = Permission::Create(
   2695          principal, mTypeArray[permEntry.mType], permEntry.mPermission,
   2696          permEntry.mExpireType, permEntry.mExpireTime,
   2697          permEntry.mModificationTime);
   2698      if (NS_WARN_IF(!permission)) {
   2699        continue;
   2700      }
   2701      aResult.AppendElement(std::move(permission));
   2702    }
   2703  }
   2704 
   2705  return NS_OK;
   2706 }
   2707 
   2708 NS_IMETHODIMP PermissionManager::GetAll(
   2709    nsTArray<RefPtr<nsIPermission>>& aResult) {
   2710  MonitorAutoLock lock{mMonitor};
   2711  return GetPermissionEntries(
   2712      [](const PermissionEntry& aPermEntry) { return true; }, aResult);
   2713 }
   2714 
   2715 NS_IMETHODIMP PermissionManager::GetAllByTypeSince(
   2716    const nsACString& aPrefix, int64_t aSince,
   2717    nsTArray<RefPtr<nsIPermission>>& aResult) {
   2718  // Check that aSince is a reasonable point in time, not in the future
   2719  if (aSince > (PR_Now() / PR_USEC_PER_MSEC)) {
   2720    return NS_ERROR_INVALID_ARG;
   2721  }
   2722 
   2723  MonitorAutoLock lock{mMonitor};
   2724  return GetPermissionEntries(
   2725      [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) {
   2726        return mTypeArray[aPermEntry.mType].Equals(aPrefix) &&
   2727               aSince <= aPermEntry.mModificationTime;
   2728      },
   2729      aResult);
   2730 }
   2731 
   2732 NS_IMETHODIMP PermissionManager::GetAllWithTypePrefix(
   2733    const nsACString& aPrefix, nsTArray<RefPtr<nsIPermission>>& aResult) {
   2734  MonitorAutoLock lock{mMonitor};
   2735  return GetPermissionEntries(
   2736      [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) {
   2737        return StringBeginsWith(mTypeArray[aPermEntry.mType], aPrefix);
   2738      },
   2739      aResult);
   2740 }
   2741 
   2742 NS_IMETHODIMP PermissionManager::GetAllByTypes(
   2743    const nsTArray<nsCString>& aTypes,
   2744    nsTArray<RefPtr<nsIPermission>>& aResult) {
   2745  if (aTypes.IsEmpty()) {
   2746    return NS_OK;
   2747  }
   2748 
   2749  MonitorAutoLock lock{mMonitor};
   2750  return GetPermissionEntries(
   2751      [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) {
   2752        return aTypes.Contains(mTypeArray[aPermEntry.mType]);
   2753      },
   2754      aResult);
   2755 }
   2756 
   2757 nsresult PermissionManager::ShouldHandlePrincipalForPermission(
   2758    nsIPrincipal* aPrincipal, bool& aIsPermissionPrincipalValid) {
   2759  NS_ENSURE_ARG_POINTER(aPrincipal);
   2760  // We don't add the system principal because it actually has no URI and we
   2761  // always allow action for them.
   2762  if (aPrincipal->IsSystemPrincipal()) {
   2763    aIsPermissionPrincipalValid = false;
   2764    return NS_OK;
   2765  }
   2766 
   2767  // Null principals can't meaningfully have persisted permissions attached to
   2768  // them, so we don't allow adding permissions for them.
   2769  if (aPrincipal->GetIsNullPrincipal()) {
   2770    aIsPermissionPrincipalValid = false;
   2771    return NS_OK;
   2772  }
   2773 
   2774  // Permissions may not be added to expanded principals.
   2775  if (IsExpandedPrincipal(aPrincipal)) {
   2776    aIsPermissionPrincipalValid = false;
   2777    return NS_ERROR_INVALID_ARG;
   2778  }
   2779 
   2780  // Permission principal is valid
   2781  aIsPermissionPrincipalValid = true;
   2782  return NS_OK;
   2783 }
   2784 
   2785 nsresult PermissionManager::GetAllForPrincipalHelper(
   2786    nsIPrincipal* aPrincipal, bool aSiteScopePermissions,
   2787    nsTArray<RefPtr<nsIPermission>>& aResult) {
   2788  nsresult rv;
   2789  RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal(
   2790      aPrincipal, false, aSiteScopePermissions, rv);
   2791  if (!key) {
   2792    MOZ_ASSERT(NS_FAILED(rv));
   2793    return rv;
   2794  }
   2795  PermissionHashKey* entry = mPermissionTable.GetEntry(key);
   2796 
   2797  nsTArray<PermissionEntry> strippedPerms;
   2798  rv = GetStripPermsForPrincipal(aPrincipal, aSiteScopePermissions,
   2799                                 strippedPerms);
   2800  if (NS_FAILED(rv)) {
   2801    return rv;
   2802  }
   2803 
   2804  if (entry) {
   2805    for (const auto& permEntry : entry->GetPermissions()) {
   2806      // Only return custom permissions
   2807      if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   2808        continue;
   2809      }
   2810 
   2811      // If the permission is expired, skip it. We're not deleting it here
   2812      // because we're iterating over a lot of permissions.
   2813      // It will be removed as part of the daily maintenance later.
   2814      if (HasExpired(permEntry.mExpireType, permEntry.mExpireTime)) {
   2815        continue;
   2816      }
   2817 
   2818      // Make sure that we only get site scoped permissions if this
   2819      // helper is being invoked for that purpose.
   2820      if (aSiteScopePermissions !=
   2821          IsSiteScopedPermission(mTypeArray[permEntry.mType])) {
   2822        continue;
   2823      }
   2824 
   2825      // Stripped principal permissions overwrite regular ones
   2826      // For each permission check if there is a stripped permission we should
   2827      // use instead
   2828      PermissionEntry perm = permEntry;
   2829      nsTArray<PermissionEntry>::index_type index = 0;
   2830      for (const auto& strippedPerm : strippedPerms) {
   2831        if (strippedPerm.mType == permEntry.mType) {
   2832          perm = strippedPerm;
   2833          strippedPerms.RemoveElementAt(index);
   2834          break;
   2835        }
   2836        index++;
   2837      }
   2838 
   2839      RefPtr<nsIPermission> permission = Permission::Create(
   2840          aPrincipal, mTypeArray[perm.mType], perm.mPermission,
   2841          perm.mExpireType, perm.mExpireTime, perm.mModificationTime);
   2842      if (NS_WARN_IF(!permission)) {
   2843        continue;
   2844      }
   2845      aResult.AppendElement(permission);
   2846    }
   2847  }
   2848 
   2849  for (const auto& perm : strippedPerms) {
   2850    RefPtr<nsIPermission> permission = Permission::Create(
   2851        aPrincipal, mTypeArray[perm.mType], perm.mPermission, perm.mExpireType,
   2852        perm.mExpireTime, perm.mModificationTime);
   2853    if (NS_WARN_IF(!permission)) {
   2854      continue;
   2855    }
   2856    aResult.AppendElement(permission);
   2857  }
   2858 
   2859  return NS_OK;
   2860 }
   2861 
   2862 NS_IMETHODIMP
   2863 PermissionManager::GetAllForPrincipal(
   2864    nsIPrincipal* aPrincipal, nsTArray<RefPtr<nsIPermission>>& aResult) {
   2865  nsresult rv;
   2866  aResult.Clear();
   2867 
   2868  MonitorAutoLock lock{mMonitor};
   2869  EnsureReadCompleted();
   2870 
   2871  MOZ_ASSERT(PermissionAvailableInternal(aPrincipal, ""_ns));
   2872 
   2873  // First, append the non-site-scoped permissions.
   2874  rv = GetAllForPrincipalHelper(aPrincipal, false, aResult);
   2875  NS_ENSURE_SUCCESS(rv, rv);
   2876 
   2877  // Second, append the site-scoped permissions.
   2878  return GetAllForPrincipalHelper(aPrincipal, true, aResult);
   2879 }
   2880 
   2881 NS_IMETHODIMP PermissionManager::Observe(nsISupports* aSubject,
   2882                                         const char* aTopic,
   2883                                         const char16_t* someData) {
   2884  ENSURE_NOT_CHILD_PROCESS;
   2885 
   2886  MonitorAutoLock lock{mMonitor};
   2887 
   2888  if (!nsCRT::strcmp(aTopic, "profile-do-change") && !mPermissionsFile) {
   2889    // profile startup is complete, and we didn't have the permissions file
   2890    // before; init the db from the new location
   2891    InitDB(false);
   2892  } else if (!nsCRT::strcmp(aTopic, "testonly-reload-permissions-from-disk")) {
   2893    // Testing mechanism to reload all permissions from disk. Because the
   2894    // permission manager automatically initializes itself at startup, tests
   2895    // that directly manipulate the permissions database need some way to reload
   2896    // the database for their changes to have any effect. This mechanism was
   2897    // introduced when moving the permissions manager from on-demand startup to
   2898    // always being initialized. This is not guarded by a pref because it's not
   2899    // dangerous to reload permissions from disk, just bad for performance.
   2900    RemoveAllFromMemory();
   2901    CloseDB(eNone);
   2902    InitDB(false);
   2903  } else if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) {
   2904    PerformIdleDailyMaintenance();
   2905  } else if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) {
   2906    DebugOnly<nsresult> rv = RemoveAllForPrivateBrowsing();
   2907    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   2908                         "Failed to clear private browsing permissions");
   2909  }
   2910 
   2911  return NS_OK;
   2912 }
   2913 
   2914 nsresult PermissionManager::RemoveAllModifiedSince(int64_t aModificationTime) {
   2915  ENSURE_NOT_CHILD_PROCESS;
   2916  // Skip remove calls for default permissions to avoid
   2917  // creating UNKNOWN_ACTION overrides in AddInternal
   2918  return RemovePermissionEntries(
   2919      [aModificationTime](const PermissionEntry& aPermEntry) {
   2920        return aModificationTime <= aPermEntry.mModificationTime &&
   2921               aPermEntry.mID != cIDPermissionIsDefault;
   2922      });
   2923 }
   2924 
   2925 nsresult PermissionManager::RemoveAllForPrivateBrowsing() {
   2926  ENSURE_NOT_CHILD_PROCESS;
   2927  return RemovePermissionEntries([](const PermissionEntry& aPermEntry,
   2928                                    const nsCOMPtr<nsIPrincipal>& aPrincipal) {
   2929    return aPrincipal->GetIsInPrivateBrowsing() &&
   2930           aPermEntry.mID != cIDPermissionIsDefault;
   2931  });
   2932 }
   2933 
   2934 NS_IMETHODIMP
   2935 PermissionManager::RemovePermissionsWithAttributes(
   2936    const nsAString& aPattern, const nsTArray<nsCString>& aTypeInclusions,
   2937    const nsTArray<nsCString>& aTypeExceptions) {
   2938  ENSURE_NOT_CHILD_PROCESS;
   2939 
   2940  OriginAttributesPattern pattern;
   2941  if (!pattern.Init(aPattern)) {
   2942    return NS_ERROR_INVALID_ARG;
   2943  }
   2944 
   2945  MonitorAutoLock lock{mMonitor};
   2946  return RemovePermissionsWithAttributes(pattern, aTypeInclusions,
   2947                                         aTypeExceptions);
   2948 }
   2949 
   2950 nsresult PermissionManager::RemovePermissionsWithAttributes(
   2951    OriginAttributesPattern& aPattern,
   2952    const nsTArray<nsCString>& aTypeInclusions,
   2953    const nsTArray<nsCString>& aTypeExceptions) {
   2954  EnsureReadCompleted();
   2955 
   2956  Vector<std::tuple<nsCOMPtr<nsIPrincipal>, nsCString, nsCString>, 10>
   2957      permissions;
   2958  for (const PermissionHashKey& entry : mPermissionTable) {
   2959    nsCOMPtr<nsIPrincipal> principal;
   2960    nsresult rv = GetPrincipalFromOrigin(entry.GetKey()->mOrigin, false,
   2961                                         getter_AddRefs(principal));
   2962    if (NS_FAILED(rv)) {
   2963      continue;
   2964    }
   2965 
   2966    if (!aPattern.Matches(principal->OriginAttributesRef())) {
   2967      continue;
   2968    }
   2969 
   2970    for (const auto& permEntry : entry.GetPermissions()) {
   2971      if (aTypeExceptions.Contains(mTypeArray[permEntry.mType])) {
   2972        continue;
   2973      }
   2974      if (!aTypeInclusions.IsEmpty() &&
   2975          !aTypeInclusions.Contains(mTypeArray[permEntry.mType])) {
   2976        continue;
   2977      }
   2978      if (!permissions.emplaceBack(principal, mTypeArray[permEntry.mType],
   2979                                   entry.GetKey()->mOrigin)) {
   2980        continue;
   2981      }
   2982    }
   2983  }
   2984 
   2985  for (auto& i : permissions) {
   2986    AddInternal(
   2987        std::get<0>(i), std::get<1>(i), nsIPermissionManager::UNKNOWN_ACTION, 0,
   2988        nsIPermissionManager::EXPIRE_NEVER, 0, 0, PermissionManager::eNotify,
   2989        PermissionManager::eWriteToDB, &std::get<2>(i));
   2990  }
   2991 
   2992  return NS_OK;
   2993 }
   2994 
   2995 nsresult PermissionManager::GetStripPermsForPrincipal(
   2996    nsIPrincipal* aPrincipal, bool aSiteScopePermissions,
   2997    nsTArray<PermissionEntry>& aResult) {
   2998  aResult.Clear();
   2999  aResult.SetCapacity(kStripOAPermissions.size());
   3000 
   3001 #ifdef __clang__
   3002 #  pragma clang diagnostic push
   3003 #  pragma clang diagnostic ignored "-Wunreachable-code-return"
   3004 #endif
   3005  // No special strip permissions
   3006  if (kStripOAPermissions.empty()) {
   3007    return NS_OK;
   3008  }
   3009 #ifdef __clang__
   3010 #  pragma clang diagnostic pop
   3011 #endif
   3012 
   3013  nsresult rv;
   3014  // Create a key for the principal, but strip any origin attributes.
   3015  // The key must be created aware of whether or not we are scoping to site.
   3016  RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal(
   3017      aPrincipal, true, aSiteScopePermissions, rv);
   3018  if (!key) {
   3019    MOZ_ASSERT(NS_FAILED(rv));
   3020    return rv;
   3021  }
   3022 
   3023  PermissionHashKey* hashKey = mPermissionTable.GetEntry(key);
   3024  if (!hashKey) {
   3025    return NS_OK;
   3026  }
   3027 
   3028  for (const auto& permType : kStripOAPermissions) {
   3029    // if the permission type's site scoping does not match this function call,
   3030    // we don't care about it, so continue.
   3031    // As of time of writing, this never happens when aSiteScopePermissions
   3032    // is true because there is no common permission between kStripOAPermissions
   3033    // and kSiteScopedPermissions
   3034    if (aSiteScopePermissions != IsSiteScopedPermission(permType)) {
   3035      continue;
   3036    }
   3037    int32_t index = GetTypeIndex(permType, false);
   3038    if (index == -1) {
   3039      continue;
   3040    }
   3041    PermissionEntry perm = hashKey->GetPermission(index);
   3042    if (perm.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   3043      continue;
   3044    }
   3045    aResult.AppendElement(perm);
   3046  }
   3047 
   3048  return NS_OK;
   3049 }
   3050 
   3051 int32_t PermissionManager::GetTypeIndex(const nsACString& aType, bool aAdd) {
   3052  for (uint32_t i = 0; i < mTypeArray.length(); ++i) {
   3053    if (mTypeArray[i].Equals(aType)) {
   3054      return i;
   3055    }
   3056  }
   3057 
   3058  if (!aAdd) {
   3059    // Not found, but that is ok - we were just looking.
   3060    return -1;
   3061  }
   3062 
   3063  // This type was not registered before.
   3064  // append it to the array, without copy-constructing the string
   3065  if (!mTypeArray.emplaceBack(aType)) {
   3066    return -1;
   3067  }
   3068 
   3069  return mTypeArray.length() - 1;
   3070 }
   3071 
   3072 PermissionManager::PermissionHashKey* PermissionManager::GetPermissionHashKey(
   3073    nsIPrincipal* aPrincipal, uint32_t aType, bool aExactHostMatch) {
   3074  EnsureReadCompleted();
   3075 
   3076  MOZ_ASSERT(PermissionAvailableInternal(aPrincipal, mTypeArray[aType]));
   3077 
   3078  nsresult rv;
   3079  RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal(
   3080      aPrincipal, IsOAForceStripPermission(mTypeArray[aType]),
   3081      IsSiteScopedPermission(mTypeArray[aType]), rv);
   3082  if (!key) {
   3083    return nullptr;
   3084  }
   3085 
   3086  PermissionHashKey* entry = mPermissionTable.GetEntry(key);
   3087 
   3088  if (entry) {
   3089    PermissionEntry permEntry = entry->GetPermission(aType);
   3090 
   3091    // if the entry is expired, remove and keep looking for others.
   3092    if (HasExpired(permEntry.mExpireType, permEntry.mExpireTime)) {
   3093      entry = nullptr;
   3094      RemoveFromPrincipalInternal(aPrincipal, mTypeArray[aType]);
   3095    } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   3096      entry = nullptr;
   3097    }
   3098  }
   3099 
   3100  if (entry) {
   3101    return entry;
   3102  }
   3103 
   3104  // If aExactHostMatch wasn't true, we can check if the base domain has a
   3105  // permission entry.
   3106  if (!aExactHostMatch) {
   3107    nsCOMPtr<nsIPrincipal> principal = aPrincipal->GetNextSubDomainPrincipal();
   3108    if (principal) {
   3109      return GetPermissionHashKey(principal, aType, aExactHostMatch);
   3110    }
   3111  }
   3112 
   3113  // No entry, really...
   3114  return nullptr;
   3115 }
   3116 
   3117 PermissionManager::PermissionHashKey* PermissionManager::GetPermissionHashKey(
   3118    nsIURI* aURI, const OriginAttributes* aOriginAttributes, uint32_t aType,
   3119    bool aExactHostMatch) {
   3120  MOZ_ASSERT(aURI);
   3121 
   3122 #ifdef DEBUG
   3123  {
   3124    nsCOMPtr<nsIPrincipal> principal;
   3125    nsresult rv = NS_OK;
   3126    if (aURI) {
   3127      rv = GetPrincipal(aURI, getter_AddRefs(principal));
   3128    }
   3129    MOZ_ASSERT_IF(NS_SUCCEEDED(rv),
   3130                  PermissionAvailableInternal(principal, mTypeArray[aType]));
   3131  }
   3132 #endif
   3133 
   3134  nsresult rv;
   3135  RefPtr<PermissionKey> key;
   3136 
   3137  if (aOriginAttributes) {
   3138    key = PermissionKey::CreateFromURIAndOriginAttributes(
   3139        aURI, aOriginAttributes, IsOAForceStripPermission(mTypeArray[aType]),
   3140        rv);
   3141  } else {
   3142    key = PermissionKey::CreateFromURI(aURI, rv);
   3143  }
   3144 
   3145  if (!key) {
   3146    return nullptr;
   3147  }
   3148 
   3149  PermissionHashKey* entry = mPermissionTable.GetEntry(key);
   3150 
   3151  if (entry) {
   3152    PermissionEntry permEntry = entry->GetPermission(aType);
   3153 
   3154    // if the entry is expired, remove and keep looking for others.
   3155    if (HasExpired(permEntry.mExpireType, permEntry.mExpireTime)) {
   3156      entry = nullptr;
   3157      // If we need to remove a permission we mint a principal.  This is a bit
   3158      // inefficient, but hopefully this code path isn't super common.
   3159      nsCOMPtr<nsIPrincipal> principal;
   3160      if (aURI) {
   3161        nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
   3162        if (NS_WARN_IF(NS_FAILED(rv))) {
   3163          return nullptr;
   3164        }
   3165      }
   3166      RemoveFromPrincipal(principal, mTypeArray[aType]);
   3167    } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   3168      entry = nullptr;
   3169    }
   3170  }
   3171 
   3172  if (entry) {
   3173    return entry;
   3174  }
   3175 
   3176  // If aExactHostMatch wasn't true, we can check if the base domain has a
   3177  // permission entry.
   3178  if (!aExactHostMatch) {
   3179    nsCOMPtr<nsIURI> uri;
   3180    if (aURI) {
   3181      uri = GetNextSubDomainURI(aURI);
   3182    }
   3183    if (uri) {
   3184      return GetPermissionHashKey(uri, aOriginAttributes, aType,
   3185                                  aExactHostMatch);
   3186    }
   3187  }
   3188 
   3189  // No entry, really...
   3190  return nullptr;
   3191 }
   3192 
   3193 nsresult PermissionManager::RemoveAllFromMemory() {
   3194  mLargestID = 0;
   3195  mTypeArray.clear();
   3196  mPermissionTable.Clear();
   3197 
   3198  return NS_OK;
   3199 }
   3200 
   3201 // wrapper function for mangling (host,type,perm,expireType,expireTime)
   3202 // set into an nsIPermission.
   3203 void PermissionManager::NotifyObserversWithPermission(
   3204    nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aPermission,
   3205    uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime,
   3206    const nsString& aData) {
   3207  nsCOMPtr<nsIPermission> permission =
   3208      Permission::Create(aPrincipal, aType, aPermission, aExpireType,
   3209                         aExpireTime, aModificationTime);
   3210  if (permission) {
   3211    NotifyObservers(permission, aData);
   3212  }
   3213 }
   3214 
   3215 // notify observers that the permission list changed. there are four possible
   3216 // values for aData:
   3217 // "deleted" means a permission was deleted. aPermission is the deleted
   3218 // permission. "added"   means a permission was added. aPermission is the added
   3219 // permission. "changed" means a permission was altered. aPermission is the new
   3220 // permission. "cleared" means the entire permission list was cleared.
   3221 // aPermission is null.
   3222 void PermissionManager::NotifyObservers(
   3223    const nsCOMPtr<nsIPermission>& aPermission, const nsString& aData) {
   3224  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   3225  if (observerService) {
   3226    // we need to release the monitor here because the observers for the below
   3227    // notification can call back in to permission manager and try to lock the
   3228    // monitor again.
   3229    MonitorAutoUnlock unlock{mMonitor};
   3230    observerService->NotifyObservers(aPermission, kPermissionChangeNotification,
   3231                                     aData.Data());
   3232  }
   3233 }
   3234 
   3235 nsresult PermissionManager::Read(const MonitorAutoLock& aProofOfLock) {
   3236  ENSURE_NOT_CHILD_PROCESS;
   3237 
   3238  MOZ_ASSERT(!NS_IsMainThread());
   3239  auto data = mThreadBoundData.Access();
   3240 
   3241  nsresult rv;
   3242  bool hasResult;
   3243  nsCOMPtr<mozIStorageStatement> stmt;
   3244 
   3245  // Let's retrieve the last used ID.
   3246  rv = data->mDBConn->CreateStatement(
   3247      nsLiteralCString("SELECT MAX(id) FROM moz_perms"), getter_AddRefs(stmt));
   3248  NS_ENSURE_SUCCESS(rv, rv);
   3249 
   3250  while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   3251    int64_t id = stmt->AsInt64(0);
   3252    mLargestID = id;
   3253  }
   3254 
   3255  rv = data->mDBConn->CreateStatement(
   3256      nsLiteralCString(
   3257          "SELECT id, origin, type, permission, expireType, "
   3258          "expireTime, modificationTime "
   3259          "FROM moz_perms WHERE expireType != ?1 OR expireTime > ?2"),
   3260      getter_AddRefs(stmt));
   3261  NS_ENSURE_SUCCESS(rv, rv);
   3262 
   3263  rv = stmt->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME);
   3264  NS_ENSURE_SUCCESS(rv, rv);
   3265 
   3266  rv = stmt->BindInt64ByIndex(1, EXPIRY_NOW);
   3267  NS_ENSURE_SUCCESS(rv, rv);
   3268 
   3269  bool readError = false;
   3270 
   3271  while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   3272    ReadEntry entry;
   3273 
   3274    // explicitly set our entry id counter for use in AddInternal(),
   3275    // and keep track of the largest id so we know where to pick up.
   3276    entry.mId = stmt->AsInt64(0);
   3277    MOZ_ASSERT(entry.mId <= mLargestID);
   3278 
   3279    rv = stmt->GetUTF8String(1, entry.mOrigin);
   3280    if (NS_FAILED(rv)) {
   3281      readError = true;
   3282      continue;
   3283    }
   3284 
   3285    rv = stmt->GetUTF8String(2, entry.mType);
   3286    if (NS_FAILED(rv)) {
   3287      readError = true;
   3288      continue;
   3289    }
   3290 
   3291    entry.mPermission = stmt->AsInt32(3);
   3292    entry.mExpireType = stmt->AsInt32(4);
   3293 
   3294    // convert into int64_t values (milliseconds)
   3295    entry.mExpireTime = stmt->AsInt64(5);
   3296    entry.mModificationTime = stmt->AsInt64(6);
   3297 
   3298    entry.mFromMigration = false;
   3299 
   3300    mReadEntries.AppendElement(entry);
   3301  }
   3302 
   3303  if (readError) {
   3304    NS_ERROR("Error occured while reading the permissions database!");
   3305    return NS_ERROR_FAILURE;
   3306  }
   3307 
   3308  return NS_OK;
   3309 }
   3310 
   3311 void PermissionManager::CompleteMigrations() {
   3312  MOZ_ASSERT(NS_IsMainThread());
   3313  MOZ_ASSERT(mState == eReady);
   3314 
   3315  nsresult rv;
   3316 
   3317  nsTArray<MigrationEntry> entries = std::move(mMigrationEntries);
   3318 
   3319  for (const MigrationEntry& entry : entries) {
   3320    rv = UpgradeHostToOriginAndInsert(
   3321        entry.mHost, entry.mType, entry.mPermission, entry.mExpireType,
   3322        entry.mExpireTime, entry.mModificationTime,
   3323        [&](const nsACString& aOrigin, const nsCString& aType,
   3324            uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
   3325            int64_t aModificationTime) MOZ_REQUIRES(mMonitor) {
   3326          MaybeAddReadEntryFromMigration(aOrigin, aType, aPermission,
   3327                                         aExpireType, aExpireTime,
   3328                                         aModificationTime, entry.mId);
   3329          return NS_OK;
   3330        });
   3331    (void)NS_WARN_IF(NS_FAILED(rv));
   3332  }
   3333 }
   3334 
   3335 void PermissionManager::CompleteRead() {
   3336  MOZ_ASSERT(NS_IsMainThread());
   3337  MOZ_ASSERT(mState == eReady);
   3338 
   3339  nsresult rv;
   3340 
   3341  nsTArray<ReadEntry> entries = std::move(mReadEntries);
   3342 
   3343  for (const ReadEntry& entry : entries) {
   3344    nsCOMPtr<nsIPrincipal> principal;
   3345    rv = GetPrincipalFromOrigin(entry.mOrigin,
   3346                                IsOAForceStripPermission(entry.mType),
   3347                                getter_AddRefs(principal));
   3348    if (NS_WARN_IF(NS_FAILED(rv))) {
   3349      continue;
   3350    }
   3351 
   3352    DBOperationType op = entry.mFromMigration ? eWriteToDB : eNoDBOperation;
   3353 
   3354    rv = AddInternal(principal, entry.mType, entry.mPermission, entry.mId,
   3355                     entry.mExpireType, entry.mExpireTime,
   3356                     entry.mModificationTime, eDontNotify, op, &entry.mOrigin);
   3357    (void)NS_WARN_IF(NS_FAILED(rv));
   3358  }
   3359 }
   3360 
   3361 void PermissionManager::MaybeAddReadEntryFromMigration(
   3362    const nsACString& aOrigin, const nsCString& aType, uint32_t aPermission,
   3363    uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime,
   3364    int64_t aId) {
   3365  // We convert a migration to a ReadEntry only if we don't have an existing
   3366  // ReadEntry for the same origin + type.
   3367  for (const ReadEntry& entry : mReadEntries) {
   3368    if (entry.mOrigin == aOrigin && entry.mType == aType) {
   3369      return;
   3370    }
   3371  }
   3372 
   3373  ReadEntry entry;
   3374  entry.mId = aId;
   3375  entry.mOrigin = aOrigin;
   3376  entry.mType = aType;
   3377  entry.mPermission = aPermission;
   3378  entry.mExpireType = aExpireType;
   3379  entry.mExpireTime = aExpireTime;
   3380  entry.mModificationTime = aModificationTime;
   3381  entry.mFromMigration = true;
   3382 
   3383  mReadEntries.AppendElement(entry);
   3384 }
   3385 
   3386 void PermissionManager::UpdateDB(OperationType aOp, int64_t aID,
   3387                                 const nsACString& aOrigin,
   3388                                 const nsACString& aType, uint32_t aPermission,
   3389                                 uint32_t aExpireType, int64_t aExpireTime,
   3390                                 int64_t aModificationTime) {
   3391  ENSURE_NOT_CHILD_PROCESS_NORET;
   3392 
   3393  MOZ_ASSERT(NS_IsMainThread());
   3394  EnsureReadCompleted();
   3395 
   3396  nsCString origin(aOrigin);
   3397  nsCString type(aType);
   3398 
   3399  RefPtr<PermissionManager> self = this;
   3400  mThread->Dispatch(NS_NewRunnableFunction(
   3401      "PermissionManager::UpdateDB",
   3402      [self, aOp, aID, origin, type, aPermission, aExpireType, aExpireTime,
   3403       aModificationTime] {
   3404        nsresult rv;
   3405 
   3406        auto data = self->mThreadBoundData.Access();
   3407 
   3408        if (self->mState == eClosed || !data->mDBConn) {
   3409          // no statement is ok - just means we don't have a profile
   3410          return;
   3411        }
   3412 
   3413        mozIStorageStatement* stmt = nullptr;
   3414        switch (aOp) {
   3415          case eOperationAdding: {
   3416            stmt = data->mStmtInsert;
   3417 
   3418            rv = stmt->BindInt64ByIndex(0, aID);
   3419            if (NS_FAILED(rv)) break;
   3420 
   3421            rv = stmt->BindUTF8StringByIndex(1, origin);
   3422            if (NS_FAILED(rv)) break;
   3423 
   3424            rv = stmt->BindUTF8StringByIndex(2, type);
   3425            if (NS_FAILED(rv)) break;
   3426 
   3427            rv = stmt->BindInt32ByIndex(3, aPermission);
   3428            if (NS_FAILED(rv)) break;
   3429 
   3430            rv = stmt->BindInt32ByIndex(4, aExpireType);
   3431            if (NS_FAILED(rv)) break;
   3432 
   3433            rv = stmt->BindInt64ByIndex(5, aExpireTime);
   3434            if (NS_FAILED(rv)) break;
   3435 
   3436            rv = stmt->BindInt64ByIndex(6, aModificationTime);
   3437            break;
   3438          }
   3439 
   3440          case eOperationRemoving: {
   3441            stmt = data->mStmtDelete;
   3442            rv = stmt->BindInt64ByIndex(0, aID);
   3443            break;
   3444          }
   3445 
   3446          case eOperationChanging: {
   3447            stmt = data->mStmtUpdate;
   3448 
   3449            rv = stmt->BindInt64ByIndex(0, aID);
   3450            if (NS_FAILED(rv)) break;
   3451 
   3452            rv = stmt->BindInt32ByIndex(1, aPermission);
   3453            if (NS_FAILED(rv)) break;
   3454 
   3455            rv = stmt->BindInt32ByIndex(2, aExpireType);
   3456            if (NS_FAILED(rv)) break;
   3457 
   3458            rv = stmt->BindInt64ByIndex(3, aExpireTime);
   3459            if (NS_FAILED(rv)) break;
   3460 
   3461            rv = stmt->BindInt64ByIndex(4, aModificationTime);
   3462            break;
   3463          }
   3464 
   3465          default: {
   3466            MOZ_ASSERT_UNREACHABLE("need a valid operation in UpdateDB()!");
   3467            rv = NS_ERROR_UNEXPECTED;
   3468            break;
   3469          }
   3470        }
   3471 
   3472        if (NS_FAILED(rv)) {
   3473          NS_WARNING("db change failed!");
   3474          return;
   3475        }
   3476 
   3477        rv = stmt->Execute();
   3478        MOZ_ASSERT(NS_SUCCEEDED(rv));
   3479      }));
   3480 }
   3481 
   3482 bool PermissionManager::GetPermissionsFromOriginOrKey(
   3483    const nsACString& aOrigin, const nsACString& aKey,
   3484    nsTArray<IPC::Permission>& aPerms) {
   3485  MonitorAutoLock lock{mMonitor};
   3486 
   3487  EnsureReadCompleted();
   3488 
   3489  aPerms.Clear();
   3490  if (NS_WARN_IF(XRE_IsContentProcess())) {
   3491    return false;
   3492  }
   3493 
   3494  for (const PermissionHashKey& entry : mPermissionTable) {
   3495    nsAutoCString permissionKey;
   3496    if (aOrigin.IsEmpty()) {
   3497      // We can't check for individual OA strip perms here.
   3498      // Don't force strip origin attributes.
   3499      GetKeyForOrigin(entry.GetKey()->mOrigin, false, false, permissionKey);
   3500 
   3501      // If the keys don't match, and we aren't getting the default "" key, then
   3502      // we can exit early. We have to keep looking if we're getting the default
   3503      // key, as we may see a preload permission which should be transmitted.
   3504      if (aKey != permissionKey && !aKey.IsEmpty()) {
   3505        continue;
   3506      }
   3507    } else if (aOrigin != entry.GetKey()->mOrigin) {
   3508      // If the origins don't match, then we can exit early. We have to keep
   3509      // looking if we're getting the default origin, as we may see a preload
   3510      // permission which should be transmitted.
   3511      continue;
   3512    }
   3513 
   3514    for (const auto& permEntry : entry.GetPermissions()) {
   3515      // Given how "default" permissions work and the possibility of them
   3516      // being overridden with UNKNOWN_ACTION, we might see this value here -
   3517      // but we do not want to send it to the content process.
   3518      if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
   3519        continue;
   3520      }
   3521 
   3522      bool isPreload = IsPreloadPermission(mTypeArray[permEntry.mType]);
   3523      bool shouldAppend;
   3524      if (aOrigin.IsEmpty()) {
   3525        shouldAppend = (isPreload && aKey.IsEmpty()) ||
   3526                       (!isPreload && aKey == permissionKey);
   3527      } else {
   3528        shouldAppend = (!isPreload && aOrigin == entry.GetKey()->mOrigin);
   3529      }
   3530      if (shouldAppend) {
   3531        aPerms.AppendElement(
   3532            IPC::Permission(entry.GetKey()->mOrigin,
   3533                            mTypeArray[permEntry.mType], permEntry.mPermission,
   3534                            permEntry.mExpireType, permEntry.mExpireTime));
   3535      }
   3536    }
   3537  }
   3538 
   3539  return true;
   3540 }
   3541 
   3542 void PermissionManager::SetPermissionsWithKey(
   3543    const nsACString& aPermissionKey, nsTArray<IPC::Permission>& aPerms) {
   3544  if (NS_WARN_IF(XRE_IsParentProcess())) {
   3545    return;
   3546  }
   3547 
   3548  MonitorAutoLock lock{mMonitor};
   3549 
   3550  RefPtr<GenericNonExclusivePromise::Private> promise;
   3551  bool foundKey =
   3552      mPermissionKeyPromiseMap.Get(aPermissionKey, getter_AddRefs(promise));
   3553  if (promise) {
   3554    MOZ_ASSERT(foundKey);
   3555    // NOTE: This will resolve asynchronously, so we can mark it as resolved
   3556    // now, and be confident that we will have filled in the database before any
   3557    // callbacks run.
   3558    promise->Resolve(true, __func__);
   3559  } else if (foundKey) {
   3560    // NOTE: We shouldn't be sent two InitializePermissionsWithKey for the same
   3561    // key, but it's possible.
   3562    return;
   3563  }
   3564  mPermissionKeyPromiseMap.InsertOrUpdate(
   3565      aPermissionKey, RefPtr<GenericNonExclusivePromise::Private>{});
   3566 
   3567  // Add the permissions locally to our process
   3568  for (IPC::Permission& perm : aPerms) {
   3569    nsCOMPtr<nsIPrincipal> principal;
   3570    nsresult rv =
   3571        GetPrincipalFromOrigin(perm.origin, IsOAForceStripPermission(perm.type),
   3572                               getter_AddRefs(principal));
   3573    if (NS_WARN_IF(NS_FAILED(rv))) {
   3574      continue;
   3575    }
   3576 
   3577 #ifdef DEBUG
   3578    nsAutoCString permissionKey;
   3579    GetKeyForPermission(principal, perm.type, permissionKey);
   3580    MOZ_ASSERT(permissionKey == aPermissionKey,
   3581               "The permission keys which were sent over should match!");
   3582 #endif
   3583 
   3584    // The child process doesn't care about modification times - it neither
   3585    // reads nor writes, nor removes them based on the date - so 0 (which
   3586    // will end up as now()) is fine.
   3587    uint64_t modificationTime = 0;
   3588    AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
   3589                perm.expireTime, modificationTime, eDontNotify, eNoDBOperation);
   3590  }
   3591 }
   3592 
   3593 /* static */
   3594 nsresult PermissionManager::GetKeyForOrigin(const nsACString& aOrigin,
   3595                                            bool aForceStripOA,
   3596                                            bool aSiteScopePermissions,
   3597                                            nsACString& aKey) {
   3598  aKey.Truncate();
   3599 
   3600  // We only key origins for http, https URIs. All origins begin with
   3601  // the URL which they apply to, which means that they should begin with their
   3602  // scheme in the case where they are one of these interesting URIs. We don't
   3603  // want to actually parse the URL here however, because this can be called on
   3604  // hot paths.
   3605  if (!StringBeginsWith(aOrigin, "http:"_ns) &&
   3606      !StringBeginsWith(aOrigin, "https:"_ns)) {
   3607    return NS_OK;
   3608  }
   3609 
   3610  // We need to look at the originAttributes if they are present, to make sure
   3611  // to remove any which we don't want. We put the rest of the origin, not
   3612  // including the attributes, into the key.
   3613  OriginAttributes attrs;
   3614  if (!attrs.PopulateFromOrigin(aOrigin, aKey)) {
   3615    aKey.Truncate();
   3616    return NS_OK;
   3617  }
   3618 
   3619  MaybeStripOriginAttributes(aForceStripOA, attrs);
   3620 
   3621 #ifdef DEBUG
   3622  // Parse the origin string into a principal, and extract some useful
   3623  // information from it for assertions.
   3624  nsCOMPtr<nsIPrincipal> dbgPrincipal;
   3625  MOZ_ALWAYS_SUCCEEDS(GetPrincipalFromOrigin(aOrigin, aForceStripOA,
   3626                                             getter_AddRefs(dbgPrincipal)));
   3627  MOZ_ASSERT(dbgPrincipal->SchemeIs("http") || dbgPrincipal->SchemeIs("https"));
   3628  MOZ_ASSERT(dbgPrincipal->OriginAttributesRef() == attrs);
   3629 #endif
   3630 
   3631  // If it is needed, turn the origin into its site-origin
   3632  if (aSiteScopePermissions) {
   3633    nsCOMPtr<nsIURI> uri;
   3634    nsresult rv = NS_NewURI(getter_AddRefs(uri), aKey);
   3635    if (!NS_WARN_IF(NS_FAILED(rv))) {
   3636      nsCString site;
   3637      nsCOMPtr<nsIEffectiveTLDService> etld =
   3638          mozilla::components::EffectiveTLD::Service();
   3639      rv = etld->GetSite(uri, site);
   3640      if (!NS_WARN_IF(NS_FAILED(rv))) {
   3641        aKey = site;
   3642      }
   3643    }
   3644  }
   3645 
   3646  // Append the stripped suffix to the output origin key.
   3647  nsAutoCString suffix;
   3648  attrs.CreateSuffix(suffix);
   3649  aKey.Append(suffix);
   3650 
   3651  return NS_OK;
   3652 }
   3653 
   3654 /* static */
   3655 nsresult PermissionManager::GetKeyForPrincipal(nsIPrincipal* aPrincipal,
   3656                                               bool aForceStripOA,
   3657                                               bool aSiteScopePermissions,
   3658                                               nsACString& aKey) {
   3659  nsAutoCString origin;
   3660  nsresult rv = aPrincipal->GetOrigin(origin);
   3661  if (NS_WARN_IF(NS_FAILED(rv))) {
   3662    aKey.Truncate();
   3663    return rv;
   3664  }
   3665  return GetKeyForOrigin(origin, aForceStripOA, aSiteScopePermissions, aKey);
   3666 }
   3667 
   3668 /* static */
   3669 nsresult PermissionManager::GetKeyForPermission(nsIPrincipal* aPrincipal,
   3670                                                const nsACString& aType,
   3671                                                nsACString& aKey) {
   3672  // Preload permissions have the "" key.
   3673  if (IsPreloadPermission(aType)) {
   3674    aKey.Truncate();
   3675    return NS_OK;
   3676  }
   3677 
   3678  return GetKeyForPrincipal(aPrincipal, IsOAForceStripPermission(aType),
   3679                            IsSiteScopedPermission(aType), aKey);
   3680 }
   3681 
   3682 /* static */
   3683 nsTArray<std::pair<nsCString, nsCString>>
   3684 PermissionManager::GetAllKeysForPrincipal(nsIPrincipal* aPrincipal) {
   3685  MOZ_ASSERT(aPrincipal);
   3686 
   3687  nsTArray<std::pair<nsCString, nsCString>> pairs;
   3688  nsCOMPtr<nsIPrincipal> prin = aPrincipal;
   3689 
   3690  while (prin) {
   3691    // Add the pair to the list
   3692    std::pair<nsCString, nsCString>* pair =
   3693        pairs.AppendElement(std::make_pair(""_ns, ""_ns));
   3694    // We can't check for individual OA strip perms here.
   3695    // Don't force strip origin attributes.
   3696    GetKeyForPrincipal(prin, false, false, pair->first);
   3697 
   3698    // On origins with a derived key set to an empty string
   3699    // (basically any non-web URI scheme), we want to make sure
   3700    // to return earlier, and leave [("", "")] as the resulting
   3701    // pairs (but still run the same debug assertions near the
   3702    // end of this method).
   3703    if (pair->first.IsEmpty()) {
   3704      break;
   3705    }
   3706 
   3707    (void)GetOriginFromPrincipal(prin, false, pair->second);
   3708    prin = prin->GetNextSubDomainPrincipal();
   3709    // Get the next subdomain principal and loop back around.
   3710  }
   3711 
   3712  MOZ_ASSERT(pairs.Length() >= 1,
   3713             "Every principal should have at least one pair item.");
   3714  return pairs;
   3715 }
   3716 
   3717 bool PermissionManager::PermissionAvailable(nsIPrincipal* aPrincipal,
   3718                                            const nsACString& aType) {
   3719  MonitorAutoLock lock{mMonitor};
   3720  return PermissionAvailableInternal(aPrincipal, aType);
   3721 }
   3722 
   3723 bool PermissionManager::PermissionAvailableInternal(nsIPrincipal* aPrincipal,
   3724                                                    const nsACString& aType) {
   3725  EnsureReadCompleted();
   3726 
   3727  if (XRE_IsContentProcess()) {
   3728    nsAutoCString permissionKey;
   3729    // NOTE: GetKeyForPermission accepts a null aType.
   3730    GetKeyForPermission(aPrincipal, aType, permissionKey);
   3731 
   3732    // If we have a pending promise for the permission key in question, we don't
   3733    // have the permission available, so report a warning and return false.
   3734    RefPtr<GenericNonExclusivePromise::Private> promise;
   3735    if (!mPermissionKeyPromiseMap.Get(permissionKey, getter_AddRefs(promise)) ||
   3736        promise) {
   3737      // Emit a useful diagnostic warning with the permissionKey for the process
   3738      // which hasn't received permissions yet.
   3739      NS_WARNING(nsPrintfCString("This content process hasn't received the "
   3740                                 "permissions for %s yet",
   3741                                 permissionKey.get())
   3742                     .get());
   3743      return false;
   3744    }
   3745  }
   3746  return true;
   3747 }
   3748 
   3749 void PermissionManager::WhenPermissionsAvailable(nsIPrincipal* aPrincipal,
   3750                                                 nsIRunnable* aRunnable) {
   3751  MOZ_ASSERT(aRunnable);
   3752 
   3753  if (!XRE_IsContentProcess()) {
   3754    aRunnable->Run();
   3755    return;
   3756  }
   3757 
   3758  MonitorAutoLock lock{mMonitor};
   3759 
   3760  nsTArray<RefPtr<GenericNonExclusivePromise>> promises;
   3761  for (auto& pair : GetAllKeysForPrincipal(aPrincipal)) {
   3762    RefPtr<GenericNonExclusivePromise::Private> promise;
   3763    if (!mPermissionKeyPromiseMap.Get(pair.first, getter_AddRefs(promise))) {
   3764      // In this case we have found a permission which isn't available in the
   3765      // content process and hasn't been requested yet. We need to create a new
   3766      // promise, and send the request to the parent (if we have not already
   3767      // done so).
   3768      promise = new GenericNonExclusivePromise::Private(__func__);
   3769      mPermissionKeyPromiseMap.InsertOrUpdate(pair.first, RefPtr{promise});
   3770    }
   3771 
   3772    if (promise) {
   3773      promises.AppendElement(std::move(promise));
   3774    }
   3775  }
   3776 
   3777  // If all of our permissions are available, immediately run the runnable. This
   3778  // avoids any extra overhead during fetch interception which is performance
   3779  // sensitive.
   3780  if (promises.IsEmpty()) {
   3781    aRunnable->Run();
   3782    return;
   3783  }
   3784 
   3785  auto* thread = AbstractThread::MainThread();
   3786 
   3787  RefPtr<nsIRunnable> runnable = aRunnable;
   3788  GenericNonExclusivePromise::All(thread, promises)
   3789      ->Then(
   3790          thread, __func__, [runnable]() { runnable->Run(); },
   3791          []() {
   3792            NS_WARNING(
   3793                "PermissionManager permission promise rejected. We're "
   3794                "probably shutting down.");
   3795          });
   3796 }
   3797 
   3798 void PermissionManager::EnsureReadCompleted() {
   3799  if (mState == eInitializing) {
   3800    while (mState == eInitializing) {
   3801      mMonitor.AssertCurrentThreadOwns();
   3802      mMonitor.Wait();
   3803    }
   3804  }
   3805 
   3806  switch (mState) {
   3807    case eInitializing:
   3808      MOZ_CRASH("This state is impossible!");
   3809 
   3810    case eDBInitialized:
   3811      // child processes transitions from eInitializing -> eReady
   3812      ENSURE_NOT_CHILD_PROCESS_NORET;
   3813 
   3814      mState = eReady;
   3815 
   3816      CompleteMigrations();
   3817      ImportLatestDefaults();
   3818      CompleteRead();
   3819 
   3820      [[fallthrough]];
   3821 
   3822    case eReady:
   3823      [[fallthrough]];
   3824 
   3825    case eClosed:
   3826      break;
   3827 
   3828    default:
   3829      MOZ_CRASH("Invalid state");
   3830  }
   3831 }
   3832 
   3833 already_AddRefed<nsIInputStream> PermissionManager::GetDefaultsInputStream() {
   3834  MOZ_ASSERT(NS_IsMainThread());
   3835 
   3836  nsAutoCString defaultsURL;
   3837  Preferences::GetCString(kDefaultsUrlPrefName, defaultsURL);
   3838  if (defaultsURL.IsEmpty()) {  // == Don't use built-in permissions.
   3839    return nullptr;
   3840  }
   3841 
   3842  nsCOMPtr<nsIURI> defaultsURI;
   3843  nsresult rv = NS_NewURI(getter_AddRefs(defaultsURI), defaultsURL);
   3844  NS_ENSURE_SUCCESS(rv, nullptr);
   3845 
   3846  nsCOMPtr<nsIChannel> channel;
   3847  rv = NS_NewChannel(getter_AddRefs(channel), defaultsURI,
   3848                     nsContentUtils::GetSystemPrincipal(),
   3849                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
   3850                     nsIContentPolicy::TYPE_OTHER);
   3851  NS_ENSURE_SUCCESS(rv, nullptr);
   3852 
   3853  nsCOMPtr<nsIInputStream> inputStream;
   3854  rv = channel->Open(getter_AddRefs(inputStream));
   3855  NS_ENSURE_SUCCESS(rv, nullptr);
   3856 
   3857  return inputStream.forget();
   3858 }
   3859 
   3860 void PermissionManager::ConsumeDefaultsInputStream(
   3861    nsIInputStream* aInputStream, const MonitorAutoLock& aProofOfLock) {
   3862  MOZ_ASSERT(!NS_IsMainThread());
   3863 
   3864  constexpr char kMatchTypeHost[] = "host";
   3865  constexpr char kMatchTypeOrigin[] = "origin";
   3866 
   3867  mDefaultEntriesForImport.Clear();
   3868 
   3869  if (!aInputStream) {
   3870    return;
   3871  }
   3872 
   3873  nsresult rv;
   3874 
   3875  /* format is:
   3876   * matchtype \t type \t permission \t host
   3877   * Only "host" is supported for matchtype
   3878   * type is a string that identifies the type of permission (e.g. "cookie")
   3879   * permission is an integer between 1 and 15
   3880   */
   3881 
   3882  // Ideally we'd do this with nsILineInputString, but this is called with an
   3883  // nsIInputStream that comes from a resource:// URI, which doesn't support
   3884  // that interface.  So NS_ReadLine to the rescue...
   3885  nsLineBuffer<char> lineBuffer;
   3886  nsCString line;
   3887  bool isMore = true;
   3888  do {
   3889    rv = NS_ReadLine(aInputStream, &lineBuffer, line, &isMore);
   3890    NS_ENSURE_SUCCESS_VOID(rv);
   3891 
   3892    if (line.IsEmpty() || line.First() == '#') {
   3893      continue;
   3894    }
   3895 
   3896    nsTArray<nsCString> lineArray;
   3897 
   3898    // Split the line at tabs
   3899    ParseString(line, '\t', lineArray);
   3900 
   3901    if (lineArray.Length() != 4) {
   3902      continue;
   3903    }
   3904 
   3905    nsresult error = NS_OK;
   3906    uint32_t permission = lineArray[2].ToInteger(&error);
   3907    if (NS_FAILED(error)) {
   3908      continue;
   3909    }
   3910 
   3911    const nsCString& hostOrOrigin = lineArray[3];
   3912    const nsCString& type = lineArray[1];
   3913 
   3914    if (lineArray[0].EqualsLiteral(kMatchTypeHost)) {
   3915      UpgradeHostToOriginAndInsert(
   3916          hostOrOrigin, type, permission, nsIPermissionManager::EXPIRE_NEVER, 0,
   3917          0,
   3918          [&](const nsACString& aOrigin, const nsCString& aType,
   3919              uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
   3920              int64_t aModificationTime) MOZ_REQUIRES(mMonitor) {
   3921            AddDefaultEntryForImport(aOrigin, aType, aPermission, aProofOfLock);
   3922            return NS_OK;
   3923          });
   3924    } else if (lineArray[0].EqualsLiteral(kMatchTypeOrigin)) {
   3925      AddDefaultEntryForImport(hostOrOrigin, type, permission, aProofOfLock);
   3926    } else {
   3927      continue;
   3928    }
   3929 
   3930  } while (isMore);
   3931 }
   3932 
   3933 void PermissionManager::AddDefaultEntryForImport(
   3934    const nsACString& aOrigin, const nsCString& aType, uint32_t aPermission,
   3935    const MonitorAutoLock& aProofOfLock) {
   3936  DefaultEntry* entry = mDefaultEntriesForImport.AppendElement();
   3937  MOZ_ASSERT(entry);
   3938 
   3939  entry->mPermission = aPermission;
   3940  entry->mOrigin = aOrigin;
   3941  entry->mType = aType;
   3942 }
   3943 
   3944 nsresult PermissionManager::ImportDefaultEntry(
   3945    const DefaultEntry& aDefaultEntry) {
   3946  nsCOMPtr<nsIPrincipal> principal;
   3947  nsresult rv = GetPrincipalFromOrigin(
   3948      aDefaultEntry.mOrigin, IsOAForceStripPermission(aDefaultEntry.mType),
   3949      getter_AddRefs(principal));
   3950  if (NS_FAILED(rv)) {
   3951    NS_WARNING("Couldn't import an origin permission - malformed origin");
   3952    return rv;
   3953  }
   3954 
   3955  // the import file format doesn't handle modification times, so we use
   3956  // 0, which AddInternal will convert to now()
   3957  int64_t modificationTime = 0;
   3958 
   3959  rv = AddInternal(principal, aDefaultEntry.mType, aDefaultEntry.mPermission,
   3960                   cIDPermissionIsDefault, nsIPermissionManager::EXPIRE_NEVER,
   3961                   0, modificationTime, eDontNotify, eNoDBOperation);
   3962  if (NS_FAILED(rv)) {
   3963    NS_WARNING("There was a problem importing an origin permission");
   3964    return rv;
   3965  }
   3966 
   3967  if (StaticPrefs::permissions_isolateBy_privateBrowsing() &&
   3968      !IsOAForceStripPermission(aDefaultEntry.mType)) {
   3969    // Also import the permission for private browsing.
   3970    OriginAttributes attrs = OriginAttributes(principal->OriginAttributesRef());
   3971    attrs.mPrivateBrowsingId = 1;
   3972    nsCOMPtr<nsIPrincipal> pbPrincipal =
   3973        BasePrincipal::Cast(principal)->CloneForcingOriginAttributes(attrs);
   3974    // May return nullptr if clone fails.
   3975    if (NS_WARN_IF(NS_FAILED(rv))) {
   3976      return rv;
   3977    }
   3978 
   3979    rv =
   3980        AddInternal(pbPrincipal, aDefaultEntry.mType, aDefaultEntry.mPermission,
   3981                    cIDPermissionIsDefault, nsIPermissionManager::EXPIRE_NEVER,
   3982                    0, modificationTime, eDontNotify, eNoDBOperation);
   3983    if (NS_FAILED(rv)) {
   3984      NS_WARNING(
   3985          "There was a problem importing an origin permission for private "
   3986          "browsing");
   3987      return rv;
   3988    }
   3989  }
   3990 
   3991  return NS_OK;
   3992 }
   3993 
   3994 // ImportLatestDefaults will import the latest default permissions read during
   3995 // the last DB initialization.
   3996 nsresult PermissionManager::ImportLatestDefaults() {
   3997  MOZ_ASSERT(NS_IsMainThread());
   3998  MOZ_ASSERT(mState == eReady);
   3999 
   4000  for (const DefaultEntry& entry : mDefaultEntriesForImport) {
   4001    (void)ImportDefaultEntry(entry);
   4002  }
   4003 
   4004  return NS_OK;
   4005 }
   4006 
   4007 /**
   4008 * Perform the early steps of a permission check and determine whether we need
   4009 * to call CommonTestPermissionInternal() for the actual permission check.
   4010 *
   4011 * @param aPrincipal optional principal argument to check the permission for,
   4012 *                   can be nullptr if we aren't performing a principal-based
   4013 *                   check.
   4014 * @param aTypeIndex if the caller isn't sure what the index of the permission
   4015 *                   type to check for is in the mTypeArray member variable,
   4016 *                   it should pass -1, otherwise this would be the index of
   4017 *                   the type inside mTypeArray.  This would only be something
   4018 *                   other than -1 in recursive invocations of this function.
   4019 * @param aType      the permission type to test.
   4020 * @param aPermission out argument which will be a permission type that we
   4021 *                    will return from this function once the function is
   4022 *                    done.
   4023 * @param aDefaultPermission the default permission to be used if we can't
   4024 *                           determine the result of the permission check.
   4025 * @param aDefaultPermissionIsValid whether the previous argument contains a
   4026 *                                  valid value.
   4027 * @param aExactHostMatch whether to look for the exact host name or also for
   4028 *                        subdomains that can have the same permission.
   4029 * @param aIncludingSession whether to include session permissions when
   4030 *                          testing for the permission.
   4031 */
   4032 PermissionManager::TestPreparationResult
   4033 PermissionManager::CommonPrepareToTestPermission(
   4034    nsIPrincipal* aPrincipal, int32_t aTypeIndex, const nsACString& aType,
   4035    uint32_t* aPermission, uint32_t aDefaultPermission,
   4036    bool aDefaultPermissionIsValid, bool aExactHostMatch,
   4037    bool aIncludingSession) {
   4038  auto* basePrin = BasePrincipal::Cast(aPrincipal);
   4039  if (basePrin && basePrin->IsSystemPrincipal()) {
   4040    *aPermission = ALLOW_ACTION;
   4041    return AsVariant(NS_OK);
   4042  }
   4043 
   4044  EnsureReadCompleted();
   4045 
   4046  // For some permissions, query the default from a pref. We want to avoid
   4047  // doing this for all permissions so that permissions can opt into having
   4048  // the pref lookup overhead on each call.
   4049  int32_t defaultPermission =
   4050      aDefaultPermissionIsValid ? aDefaultPermission : UNKNOWN_ACTION;
   4051  if (!aDefaultPermissionIsValid && HasDefaultPref(aType)) {
   4052    (void)mDefaultPrefBranch->GetIntPref(PromiseFlatCString(aType).get(),
   4053                                         &defaultPermission);
   4054    if (defaultPermission < 0 ||
   4055        defaultPermission > nsIPermissionManager::MAX_VALID_ACTION) {
   4056      defaultPermission = nsIPermissionManager::UNKNOWN_ACTION;
   4057    }
   4058  }
   4059 
   4060  // Set the default.
   4061  *aPermission = defaultPermission;
   4062 
   4063  int32_t typeIndex =
   4064      aTypeIndex == -1 ? GetTypeIndex(aType, false) : aTypeIndex;
   4065 
   4066  // For expanded principals, we want to iterate over the allowlist and see
   4067  // if the permission is granted for any of them.
   4068  if (basePrin && basePrin->Is<ExpandedPrincipal>()) {
   4069    auto* ep = basePrin->As<ExpandedPrincipal>();
   4070    for (const auto& prin : ep->AllowList()) {
   4071      uint32_t perm;
   4072      nsresult rv =
   4073          CommonTestPermission(prin, typeIndex, aType, &perm, defaultPermission,
   4074                               true, aExactHostMatch, aIncludingSession);
   4075      if (NS_WARN_IF(NS_FAILED(rv))) {
   4076        return AsVariant(rv);
   4077      }
   4078 
   4079      if (perm == nsIPermissionManager::ALLOW_ACTION) {
   4080        *aPermission = perm;
   4081        return AsVariant(NS_OK);
   4082      }
   4083      if (perm == nsIPermissionManager::PROMPT_ACTION) {
   4084        // Store it, but keep going to see if we can do better.
   4085        *aPermission = perm;
   4086      }
   4087    }
   4088 
   4089    return AsVariant(NS_OK);
   4090  }
   4091 
   4092  // If type == -1, the type isn't known, just signal that we are done.
   4093  if (typeIndex == -1) {
   4094    return AsVariant(NS_OK);
   4095  }
   4096 
   4097  return AsVariant(typeIndex);
   4098 }
   4099 
   4100 // If aTypeIndex is passed -1, we try to inder the type index from aType.
   4101 nsresult PermissionManager::CommonTestPermission(
   4102    nsIPrincipal* aPrincipal, int32_t aTypeIndex, const nsACString& aType,
   4103    uint32_t* aPermission, uint32_t aDefaultPermission,
   4104    bool aDefaultPermissionIsValid, bool aExactHostMatch,
   4105    bool aIncludingSession) {
   4106  auto preparationResult = CommonPrepareToTestPermission(
   4107      aPrincipal, aTypeIndex, aType, aPermission, aDefaultPermission,
   4108      aDefaultPermissionIsValid, aExactHostMatch, aIncludingSession);
   4109  if (preparationResult.is<nsresult>()) {
   4110    return preparationResult.as<nsresult>();
   4111  }
   4112 
   4113  return CommonTestPermissionInternal(
   4114      aPrincipal, nullptr, nullptr, preparationResult.as<int32_t>(), aType,
   4115      aPermission, aExactHostMatch, aIncludingSession);
   4116 }
   4117 
   4118 // If aTypeIndex is passed -1, we try to inder the type index from aType.
   4119 nsresult PermissionManager::CommonTestPermission(
   4120    nsIURI* aURI, int32_t aTypeIndex, const nsACString& aType,
   4121    uint32_t* aPermission, uint32_t aDefaultPermission,
   4122    bool aDefaultPermissionIsValid, bool aExactHostMatch,
   4123    bool aIncludingSession) {
   4124  auto preparationResult = CommonPrepareToTestPermission(
   4125      nullptr, aTypeIndex, aType, aPermission, aDefaultPermission,
   4126      aDefaultPermissionIsValid, aExactHostMatch, aIncludingSession);
   4127  if (preparationResult.is<nsresult>()) {
   4128    return preparationResult.as<nsresult>();
   4129  }
   4130 
   4131  return CommonTestPermissionInternal(
   4132      nullptr, aURI, nullptr, preparationResult.as<int32_t>(), aType,
   4133      aPermission, aExactHostMatch, aIncludingSession);
   4134 }
   4135 
   4136 nsresult PermissionManager::CommonTestPermission(
   4137    nsIURI* aURI, const OriginAttributes* aOriginAttributes, int32_t aTypeIndex,
   4138    const nsACString& aType, uint32_t* aPermission, uint32_t aDefaultPermission,
   4139    bool aDefaultPermissionIsValid, bool aExactHostMatch,
   4140    bool aIncludingSession) {
   4141  auto preparationResult = CommonPrepareToTestPermission(
   4142      nullptr, aTypeIndex, aType, aPermission, aDefaultPermission,
   4143      aDefaultPermissionIsValid, aExactHostMatch, aIncludingSession);
   4144  if (preparationResult.is<nsresult>()) {
   4145    return preparationResult.as<nsresult>();
   4146  }
   4147 
   4148  return CommonTestPermissionInternal(
   4149      nullptr, aURI, aOriginAttributes, preparationResult.as<int32_t>(), aType,
   4150      aPermission, aExactHostMatch, aIncludingSession);
   4151 }
   4152 
   4153 nsresult PermissionManager::TestPermissionWithoutDefaultsFromPrincipal(
   4154    nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t* aPermission) {
   4155  MOZ_ASSERT(!HasDefaultPref(aType));
   4156 
   4157  MonitorAutoLock lock{mMonitor};
   4158  return CommonTestPermission(aPrincipal, -1, aType, aPermission,
   4159                              nsIPermissionManager::UNKNOWN_ACTION, true, false,
   4160                              true);
   4161 }
   4162 
   4163 void PermissionManager::FinishAsyncShutdown() {
   4164  MOZ_ASSERT(NS_IsMainThread());
   4165 
   4166  nsCOMPtr<nsIAsyncShutdownClient> asc = GetAsyncShutdownBarrier();
   4167  MOZ_ASSERT(asc);
   4168 
   4169  DebugOnly<nsresult> rv = asc->RemoveBlocker(this);
   4170  MOZ_ASSERT(NS_SUCCEEDED(rv));
   4171 
   4172  // Now we can safely release our holder.
   4173  StaticMutexAutoLock lock(sCreationMutex);
   4174  MOZ_ASSERT(sInstanceDead);
   4175  if (sInstanceHolder) {
   4176    sInstanceHolder = nullptr;
   4177  }
   4178 }
   4179 
   4180 // Async shutdown blocker methods
   4181 
   4182 NS_IMETHODIMP PermissionManager::GetName(nsAString& aName) {
   4183  aName = u"PermissionManager: Flushing data"_ns;
   4184  return NS_OK;
   4185 }
   4186 
   4187 NS_IMETHODIMP PermissionManager::BlockShutdown(
   4188    nsIAsyncShutdownClient* aClient) {
   4189  {
   4190    // From now on we do not allow to capture new references to our singleton.
   4191    StaticMutexAutoLock lock(sCreationMutex);
   4192    sInstanceDead = true;
   4193  }
   4194 
   4195  MonitorAutoLock lock{mMonitor};
   4196 
   4197  RemoveIdleDailyMaintenanceJob();
   4198  RemoveAllFromMemory();
   4199  // CloseDB does async work and will call FinishAsyncShutdown once done.
   4200  CloseDB(eShutdown);
   4201  return NS_OK;
   4202 }
   4203 
   4204 NS_IMETHODIMP
   4205 PermissionManager::GetState(nsIPropertyBag** aBagOut) {
   4206  nsCOMPtr<nsIWritablePropertyBag2> propertyBag =
   4207      do_CreateInstance("@mozilla.org/hash-property-bag;1");
   4208 
   4209  nsresult rv = propertyBag->SetPropertyAsInt32(u"state"_ns, mState);
   4210  if (NS_WARN_IF(NS_FAILED(rv))) {
   4211    return rv;
   4212  }
   4213 
   4214  propertyBag.forget(aBagOut);
   4215 
   4216  return NS_OK;
   4217 }
   4218 
   4219 nsCOMPtr<nsIAsyncShutdownClient> PermissionManager::GetAsyncShutdownBarrier()
   4220    const {
   4221  nsresult rv;
   4222  nsCOMPtr<nsIAsyncShutdownService> svc =
   4223      do_GetService("@mozilla.org/async-shutdown-service;1", &rv);
   4224  if (NS_FAILED(rv)) {
   4225    return nullptr;
   4226  }
   4227 
   4228  nsCOMPtr<nsIAsyncShutdownClient> client;
   4229  // This feels very late but there seem to be other services that rely on
   4230  // us later than "profile-before-change".
   4231  rv = svc->GetXpcomWillShutdown(getter_AddRefs(client));
   4232  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
   4233 
   4234  return client;
   4235 }
   4236 
   4237 void PermissionManager::MaybeStripOriginAttributes(
   4238    bool aForceStrip, OriginAttributes& aOriginAttributes) {
   4239  uint32_t flags = 0;
   4240 
   4241  if (aForceStrip || !StaticPrefs::permissions_isolateBy_privateBrowsing()) {
   4242    flags |= OriginAttributes::STRIP_PRIVATE_BROWSING_ID;
   4243  }
   4244 
   4245  if (aForceStrip || !StaticPrefs::permissions_isolateBy_userContext()) {
   4246    flags |= OriginAttributes::STRIP_USER_CONTEXT_ID;
   4247  }
   4248 
   4249  if (flags != 0) {
   4250    aOriginAttributes.StripAttributes(flags);
   4251  }
   4252 }
   4253 
   4254 }  // namespace mozilla