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/. */
7 #include "mozilla/AbstractThread.h"
8 #include "mozilla/AppShutdown.h"
9 #include "mozilla/BasePrincipal.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/ContentPrincipal.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/dom/CanonicalBrowsingContext.h"
14 #include "mozilla/dom/ContentParent.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/WindowGlobalParent.h"
17 #include "mozilla/ExpandedPrincipal.h"
18 #include "mozilla/net/NeckoMessageUtils.h"
19 #include "mozilla/Permission.h"
20 #include "mozilla/PermissionManager.h"
21 #include "mozilla/Preferences.h"
22 #include "mozilla/ScopeExit.h"
23 #include "mozilla/StaticPrefs_permissions.h"
24 #include "mozilla/Telemetry.h"
26 #include "mozIStorageService.h"
27 #include "mozIStorageConnection.h"
28 #include "mozIStorageStatement.h"
29 #include "mozStorageCID.h"
31 #include "nsAppDirectoryServiceDefs.h"
32 #include "nsComponentManagerUtils.h"
33 #include "nsContentUtils.h"
36 #include "nsEffectiveTLDService.h"
37 #include "nsIConsoleService.h"
38 #include "nsIUserIdleService.h"
39 #include "nsIInputStream.h"
40 #include "nsINavHistoryService.h"
41 #include "nsIObserverService.h"
42 #include "nsIPrefBranch.h"
43 #include "nsIPrincipal.h"
44 #include "nsIURIMutator.h"
45 #include "nsIWritablePropertyBag2.h"
46 #include "nsReadLine.h"
47 #include "nsStringFwd.h"
48 #include "nsTHashSet.h"
49 #include "nsToolkitCompsCID.h"
51 using namespace mozilla::dom
;
55 #define PERMISSIONS_FILE_NAME "permissions.sqlite"
56 #define HOSTS_SCHEMA_VERSION 12
58 // Default permissions are read from a URL - this is the preference we read
59 // to find that URL. If not set, don't use any default permissions.
60 constexpr char kDefaultsUrlPrefName
[] = "permissions.manager.defaultsUrl";
62 constexpr char kPermissionChangeNotification
[] = PERM_CHANGE_NOTIFICATION
;
64 // A special value for a permission ID that indicates the ID was loaded as
65 // a default value. These will never be written to the database, but may
66 // be overridden with an explicit permission (including UNKNOWN_ACTION)
67 constexpr int64_t cIDPermissionIsDefault
= -1;
69 static StaticRefPtr
<PermissionManager
> gPermissionManager
;
71 #define ENSURE_NOT_CHILD_PROCESS_(onError) \
73 if (IsChildProcess()) { \
74 NS_ERROR("Cannot perform action in content process!"); \
79 #define ENSURE_NOT_CHILD_PROCESS \
80 ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
82 #define ENSURE_NOT_CHILD_PROCESS_NORET ENSURE_NOT_CHILD_PROCESS_(;)
84 #define EXPIRY_NOW PR_Now() / 1000
86 ////////////////////////////////////////////////////////////////////////////////
90 bool IsChildProcess() { return XRE_IsContentProcess(); }
92 void LogToConsole(const nsAString
& aMsg
) {
93 nsCOMPtr
<nsIConsoleService
> console(
94 do_GetService("@mozilla.org/consoleservice;1"));
96 NS_WARNING("Failed to log message to console.");
100 nsAutoString
msg(aMsg
);
101 console
->LogStringMessage(msg
.get());
104 // NOTE: an empty string can be passed as aType - if it is this function will
105 // return "false" unconditionally.
106 bool HasDefaultPref(const nsACString
& aType
) {
107 // A list of permissions that can have a fallback default permission
108 // set under the permissions.default.* pref.
109 static const nsLiteralCString kPermissionsWithDefaults
[] = {
110 "camera"_ns
, "microphone"_ns
, "geo"_ns
, "desktop-notification"_ns
,
113 if (!aType
.IsEmpty()) {
114 for (const auto& perm
: kPermissionsWithDefaults
) {
115 if (perm
.Equals(aType
)) {
124 // These permissions are special permissions which must be transmitted to the
125 // content process before documents with their principals have loaded within
128 // Permissions which are in this list are considered to have a "" permission
129 // key, even if their principal would not normally have that key.
130 static const nsLiteralCString kPreloadPermissions
[] = {
131 // This permission is preloaded to support properly blocking service worker
132 // interception when a user has disabled storage for a specific site. Once
133 // service worker interception moves to the parent process this should be
134 // removed. See bug 1428130.
135 "cookie"_ns
, "https-only-load-insecure"_ns
};
137 // NOTE: nullptr can be passed as aType - if it is this function will return
138 // "false" unconditionally.
139 bool IsPreloadPermission(const nsACString
& aType
) {
140 if (!aType
.IsEmpty()) {
141 for (const auto& perm
: kPreloadPermissions
) {
142 if (perm
.Equals(aType
)) {
151 // Array of permission types which should not be isolated by origin attributes,
152 // for user context and private browsing.
153 // Keep this array in sync with 'STRIPPED_PERMS' in
154 // 'test_permmanager_oa_strip.js'
155 // Currently only preloaded permissions are supported.
156 // This is because perms are sent to the content process in bulk by perm key.
157 // Non-preloaded, but OA stripped permissions would not be accessible by sites
158 // in private browsing / non-default user context.
159 static constexpr std::array
<nsLiteralCString
, 2> kStripOAPermissions
= {
160 {"cookie"_ns
, "https-only-load-insecure"_ns
}};
162 bool IsOAForceStripPermission(const nsACString
& aType
) {
163 if (aType
.IsEmpty()) {
166 for (const auto& perm
: kStripOAPermissions
) {
167 if (perm
.Equals(aType
)) {
174 // Array of permission prefixes which should be isolated only by site.
175 // These site-scoped permissions are stored under their site's principal.
176 // GetAllForPrincipal also needs to look for these especially.
177 static constexpr std::array
<nsLiteralCString
, 3> kSiteScopedPermissions
= {
178 {"3rdPartyStorage^"_ns
, "AllowStorageAccessRequest^"_ns
,
179 "3rdPartyFrameStorage^"_ns
}};
181 bool IsSiteScopedPermission(const nsACString
& aType
) {
182 if (aType
.IsEmpty()) {
185 for (const auto& perm
: kSiteScopedPermissions
) {
186 if (aType
.Length() >= perm
.Length() &&
187 Substring(aType
, 0, perm
.Length()) == perm
) {
194 // Array of permission type prefixes which have a secondary key encoded in the
195 // permission type. These permissions will not be stored in-process with the
196 // secondary key, but updates to them will cause "perm-changed" notifications on
197 // processes for that key.
198 static constexpr std::array
<nsLiteralCString
, 3> kSecondaryKeyedPermissions
= {
199 {"3rdPartyStorage^"_ns
, "AllowStorageAccessRequest^"_ns
,
200 "3rdPartyFrameStorage^"_ns
}};
202 bool GetSecondaryKey(const nsACString
& aType
, nsACString
& aSecondaryKey
) {
203 aSecondaryKey
.Truncate();
204 if (aType
.IsEmpty()) {
207 for (const auto& perm
: kSecondaryKeyedPermissions
) {
208 if (aType
.Length() > perm
.Length() &&
209 Substring(aType
, 0, perm
.Length()) == perm
) {
210 aSecondaryKey
= Substring(aType
, perm
.Length());
217 void OriginAppendOASuffix(OriginAttributes aOriginAttributes
,
218 bool aForceStripOA
, nsACString
& aOrigin
) {
219 PermissionManager::MaybeStripOriginAttributes(aForceStripOA
,
222 nsAutoCString oaSuffix
;
223 aOriginAttributes
.CreateSuffix(oaSuffix
);
224 aOrigin
.Append(oaSuffix
);
227 nsresult
GetOriginFromPrincipal(nsIPrincipal
* aPrincipal
, bool aForceStripOA
,
228 nsACString
& aOrigin
) {
229 nsresult rv
= aPrincipal
->GetOriginNoSuffix(aOrigin
);
230 // The principal may belong to the about:blank content viewer, so this can be
236 nsAutoCString suffix
;
237 rv
= aPrincipal
->GetOriginSuffix(suffix
);
238 NS_ENSURE_SUCCESS(rv
, rv
);
240 OriginAttributes attrs
;
241 NS_ENSURE_TRUE(attrs
.PopulateFromSuffix(suffix
), NS_ERROR_FAILURE
);
243 OriginAppendOASuffix(attrs
, aForceStripOA
, aOrigin
);
248 // Returns the site of the principal, including OA, given a principal.
249 nsresult
GetSiteFromPrincipal(nsIPrincipal
* aPrincipal
, bool aForceStripOA
,
251 nsCOMPtr
<nsIURI
> uri
= aPrincipal
->GetURI();
252 nsEffectiveTLDService
* etld
= nsEffectiveTLDService::GetInstance();
253 NS_ENSURE_TRUE(etld
, NS_ERROR_FAILURE
);
254 NS_ENSURE_TRUE(uri
, NS_ERROR_FAILURE
);
255 nsresult rv
= etld
->GetSite(uri
, aSite
);
257 // The principal may belong to the about:blank content viewer, so this can be
260 rv
= aPrincipal
->GetOrigin(aSite
);
261 NS_ENSURE_SUCCESS(rv
, rv
);
265 nsAutoCString suffix
;
266 rv
= aPrincipal
->GetOriginSuffix(suffix
);
267 NS_ENSURE_SUCCESS(rv
, rv
);
269 OriginAttributes attrs
;
270 NS_ENSURE_TRUE(attrs
.PopulateFromSuffix(suffix
), NS_ERROR_FAILURE
);
272 OriginAppendOASuffix(attrs
, aForceStripOA
, aSite
);
277 nsresult
GetOriginFromURIAndOA(nsIURI
* aURI
,
278 const OriginAttributes
* aOriginAttributes
,
279 bool aForceStripOA
, nsACString
& aOrigin
) {
280 nsAutoCString
origin(aOrigin
);
281 nsresult rv
= ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI
, origin
);
282 NS_ENSURE_SUCCESS(rv
, rv
);
284 OriginAppendOASuffix(*aOriginAttributes
, aForceStripOA
, origin
);
291 nsresult
GetPrincipalFromOrigin(const nsACString
& aOrigin
, bool aForceStripOA
,
292 nsIPrincipal
** aPrincipal
) {
293 nsAutoCString originNoSuffix
;
294 OriginAttributes attrs
;
295 if (!attrs
.PopulateFromOrigin(aOrigin
, originNoSuffix
)) {
296 return NS_ERROR_FAILURE
;
299 PermissionManager::MaybeStripOriginAttributes(aForceStripOA
, attrs
);
301 nsCOMPtr
<nsIURI
> uri
;
302 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), originNoSuffix
);
303 NS_ENSURE_SUCCESS(rv
, rv
);
305 nsCOMPtr
<nsIPrincipal
> principal
=
306 BasePrincipal::CreateContentPrincipal(uri
, attrs
);
307 principal
.forget(aPrincipal
);
311 nsresult
GetPrincipal(nsIURI
* aURI
, bool aIsInIsolatedMozBrowserElement
,
312 nsIPrincipal
** aPrincipal
) {
313 OriginAttributes
attrs(aIsInIsolatedMozBrowserElement
);
314 nsCOMPtr
<nsIPrincipal
> principal
=
315 BasePrincipal::CreateContentPrincipal(aURI
, attrs
);
316 NS_ENSURE_TRUE(principal
, NS_ERROR_FAILURE
);
318 principal
.forget(aPrincipal
);
322 nsresult
GetPrincipal(nsIURI
* aURI
, nsIPrincipal
** aPrincipal
) {
323 OriginAttributes attrs
;
324 nsCOMPtr
<nsIPrincipal
> principal
=
325 BasePrincipal::CreateContentPrincipal(aURI
, attrs
);
326 NS_ENSURE_TRUE(principal
, NS_ERROR_FAILURE
);
328 principal
.forget(aPrincipal
);
332 nsCString
GetNextSubDomainForHost(const nsACString
& aHost
) {
335 nsEffectiveTLDService::GetInstance()->GetNextSubDomain(aHost
, subDomain
);
336 // We can fail if there is no more subdomain or if the host can't have a
345 // This function produces a nsIURI which is identical to the current
346 // nsIURI, except that it has one less subdomain segment. It returns
347 // `nullptr` if there are no more segments to remove.
348 already_AddRefed
<nsIURI
> GetNextSubDomainURI(nsIURI
* aURI
) {
350 nsresult rv
= aURI
->GetHost(host
);
355 nsCString domain
= GetNextSubDomainForHost(host
);
356 if (domain
.IsEmpty()) {
360 nsCOMPtr
<nsIURI
> uri
;
361 rv
= NS_MutateURI(aURI
).SetHost(domain
).Finalize(uri
);
362 if (NS_FAILED(rv
) || !uri
) {
369 nsresult
UpgradeHostToOriginAndInsert(
370 const nsACString
& aHost
, const nsCString
& aType
, uint32_t aPermission
,
371 uint32_t aExpireType
, int64_t aExpireTime
, int64_t aModificationTime
,
372 bool aIsInIsolatedMozBrowserElement
,
373 std::function
<nsresult(const nsACString
& aOrigin
, const nsCString
& aType
,
374 uint32_t aPermission
, uint32_t aExpireType
,
375 int64_t aExpireTime
, int64_t aModificationTime
)>&&
377 if (aHost
.EqualsLiteral("<file>")) {
378 // We no longer support the magic host <file>
380 "The magic host <file> is no longer supported. "
381 "It is being removed from the permissions database.");
385 // First, we check to see if the host is a valid URI. If it is, it can be
387 nsCOMPtr
<nsIURI
> uri
;
388 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aHost
);
389 if (NS_SUCCEEDED(rv
)) {
390 // It was previously possible to insert useless entries to your permissions
391 // database for URIs which have a null principal. This acts as a cleanup,
392 // getting rid of these useless database entries
393 if (uri
->SchemeIs("moz-nullprincipal")) {
394 NS_WARNING("A moz-nullprincipal: permission is being discarded.");
398 nsCOMPtr
<nsIPrincipal
> principal
;
399 rv
= GetPrincipal(uri
, aIsInIsolatedMozBrowserElement
,
400 getter_AddRefs(principal
));
401 NS_ENSURE_SUCCESS(rv
, rv
);
403 nsAutoCString origin
;
404 rv
= GetOriginFromPrincipal(principal
, IsOAForceStripPermission(aType
),
406 NS_ENSURE_SUCCESS(rv
, rv
);
408 aCallback(origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
413 // The user may use this host at non-standard ports or protocols, we can use
414 // their history to guess what ports and protocols we want to add permissions
415 // for. We find every URI which they have visited with this host (or a
416 // subdomain of this host), and try to add it as a principal.
417 bool foundHistory
= false;
419 nsCOMPtr
<nsINavHistoryService
> histSrv
=
420 do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID
);
423 nsCOMPtr
<nsINavHistoryQuery
> histQuery
;
424 rv
= histSrv
->GetNewQuery(getter_AddRefs(histQuery
));
425 NS_ENSURE_SUCCESS(rv
, rv
);
427 // Get the eTLD+1 of the domain
429 rv
= nsEffectiveTLDService::GetInstance()->GetBaseDomainFromHost(aHost
, 0,
433 // If the lookup on the tldService for the base domain for the host
434 // failed, that means that we just want to directly use the host as the
435 // host name for the lookup.
439 // We want to only find history items for this particular eTLD+1, and
441 rv
= histQuery
->SetDomain(eTLD1
);
442 NS_ENSURE_SUCCESS(rv
, rv
);
444 rv
= histQuery
->SetDomainIsHost(false);
445 NS_ENSURE_SUCCESS(rv
, rv
);
447 nsCOMPtr
<nsINavHistoryQueryOptions
> histQueryOpts
;
448 rv
= histSrv
->GetNewQueryOptions(getter_AddRefs(histQueryOpts
));
449 NS_ENSURE_SUCCESS(rv
, rv
);
451 // We want to get the URIs for every item in the user's history with the
454 histQueryOpts
->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI
);
455 NS_ENSURE_SUCCESS(rv
, rv
);
457 // We only search history, because searching both bookmarks and history
458 // is not supported, and history tends to be more comprehensive.
459 rv
= histQueryOpts
->SetQueryType(
460 nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY
);
461 NS_ENSURE_SUCCESS(rv
, rv
);
463 // We include hidden URIs (such as those visited via iFrames) as they may
464 // have permissions too
465 rv
= histQueryOpts
->SetIncludeHidden(true);
466 NS_ENSURE_SUCCESS(rv
, rv
);
468 nsCOMPtr
<nsINavHistoryResult
> histResult
;
469 rv
= histSrv
->ExecuteQuery(histQuery
, histQueryOpts
,
470 getter_AddRefs(histResult
));
471 NS_ENSURE_SUCCESS(rv
, rv
);
473 nsCOMPtr
<nsINavHistoryContainerResultNode
> histResultContainer
;
474 rv
= histResult
->GetRoot(getter_AddRefs(histResultContainer
));
475 NS_ENSURE_SUCCESS(rv
, rv
);
477 rv
= histResultContainer
->SetContainerOpen(true);
478 NS_ENSURE_SUCCESS(rv
, rv
);
480 uint32_t childCount
= 0;
481 rv
= histResultContainer
->GetChildCount(&childCount
);
482 NS_ENSURE_SUCCESS(rv
, rv
);
484 nsTHashSet
<nsCString
> insertedOrigins
;
485 for (uint32_t i
= 0; i
< childCount
; i
++) {
486 nsCOMPtr
<nsINavHistoryResultNode
> child
;
487 histResultContainer
->GetChild(i
, getter_AddRefs(child
));
488 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
491 rv
= child
->GetType(&type
);
492 if (NS_WARN_IF(NS_FAILED(rv
)) ||
493 type
!= nsINavHistoryResultNode::RESULT_TYPE_URI
) {
495 "Unexpected non-RESULT_TYPE_URI node in "
496 "UpgradeHostToOriginAndInsert()");
500 nsAutoCString uriSpec
;
501 rv
= child
->GetUri(uriSpec
);
502 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
504 nsCOMPtr
<nsIURI
> uri
;
505 rv
= NS_NewURI(getter_AddRefs(uri
), uriSpec
);
506 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
508 // Use the provided host - this URI may be for a subdomain, rather than
509 // the host we care about.
510 rv
= NS_MutateURI(uri
).SetHost(aHost
).Finalize(uri
);
511 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
513 // We now have a URI which we can make a nsIPrincipal out of
514 nsCOMPtr
<nsIPrincipal
> principal
;
515 rv
= GetPrincipal(uri
, aIsInIsolatedMozBrowserElement
,
516 getter_AddRefs(principal
));
517 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
519 nsAutoCString origin
;
520 rv
= GetOriginFromPrincipal(principal
, IsOAForceStripPermission(aType
),
522 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
524 // Ensure that we don't insert the same origin repeatedly
525 if (insertedOrigins
.Contains(origin
)) {
530 rv
= aCallback(origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
532 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Insert failed");
533 insertedOrigins
.Insert(origin
);
536 rv
= histResultContainer
->SetContainerOpen(false);
537 NS_ENSURE_SUCCESS(rv
, rv
);
540 // If we didn't find any origins for this host in the poermissions database,
541 // we can insert the default http:// and https:// permissions into the
542 // database. This has a relatively high likelihood of applying the permission
543 // to the correct origin.
545 nsAutoCString hostSegment
;
546 nsCOMPtr
<nsIPrincipal
> principal
;
547 nsAutoCString origin
;
549 // If this is an ipv6 URI, we need to surround it in '[', ']' before trying
550 // to parse it as a URI.
551 if (aHost
.FindChar(':') != -1) {
552 hostSegment
.AssignLiteral("[");
553 hostSegment
.Append(aHost
);
554 hostSegment
.AppendLiteral("]");
556 hostSegment
.Assign(aHost
);
559 // http:// URI default
560 rv
= NS_NewURI(getter_AddRefs(uri
), "http://"_ns
+ hostSegment
);
561 NS_ENSURE_SUCCESS(rv
, rv
);
563 rv
= GetPrincipal(uri
, aIsInIsolatedMozBrowserElement
,
564 getter_AddRefs(principal
));
565 NS_ENSURE_SUCCESS(rv
, rv
);
567 rv
= GetOriginFromPrincipal(principal
, IsOAForceStripPermission(aType
),
569 NS_ENSURE_SUCCESS(rv
, rv
);
571 aCallback(origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
574 // https:// URI default
575 rv
= NS_NewURI(getter_AddRefs(uri
), "https://"_ns
+ hostSegment
);
576 NS_ENSURE_SUCCESS(rv
, rv
);
578 rv
= GetPrincipal(uri
, aIsInIsolatedMozBrowserElement
,
579 getter_AddRefs(principal
));
580 NS_ENSURE_SUCCESS(rv
, rv
);
582 rv
= GetOriginFromPrincipal(principal
, IsOAForceStripPermission(aType
),
584 NS_ENSURE_SUCCESS(rv
, rv
);
586 aCallback(origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
593 bool IsExpandedPrincipal(nsIPrincipal
* aPrincipal
) {
594 nsCOMPtr
<nsIExpandedPrincipal
> ep
= do_QueryInterface(aPrincipal
);
598 // We only want to persist permissions which don't have session or policy
600 bool IsPersistentExpire(uint32_t aExpire
, const nsACString
& aType
) {
601 bool res
= (aExpire
!= nsIPermissionManager::EXPIRE_SESSION
&&
602 aExpire
!= nsIPermissionManager::EXPIRE_POLICY
);
606 nsresult
NotifySecondaryKeyPermissionUpdateInContentProcess(
607 const nsACString
& aType
, uint32_t aPermission
,
608 const nsACString
& aSecondaryKey
, nsIPrincipal
* aTopPrincipal
) {
609 NS_ENSURE_ARG_POINTER(aTopPrincipal
);
610 MOZ_ASSERT(XRE_IsParentProcess());
611 AutoTArray
<RefPtr
<BrowsingContextGroup
>, 5> bcGroups
;
612 BrowsingContextGroup::GetAllGroups(bcGroups
);
613 for (const auto& bcGroup
: bcGroups
) {
614 for (const auto& topBC
: bcGroup
->Toplevels()) {
615 CanonicalBrowsingContext
* topCBC
= topBC
->Canonical();
616 RefPtr
<nsIURI
> topURI
= topCBC
->GetCurrentURI();
621 nsresult rv
= aTopPrincipal
->IsThirdPartyURI(topURI
, &thirdParty
);
626 AutoTArray
<RefPtr
<BrowsingContext
>, 5> bcs
;
627 topBC
->GetAllBrowsingContextsInSubtree(bcs
);
628 for (const auto& bc
: bcs
) {
629 CanonicalBrowsingContext
* cbc
= bc
->Canonical();
630 ContentParent
* cp
= cbc
->GetContentParent();
634 if (cp
->NeedsSecondaryKeyPermissionsUpdate(aSecondaryKey
)) {
635 WindowGlobalParent
* wgp
= cbc
->GetCurrentWindowGlobal();
639 bool success
= wgp
->SendNotifyPermissionChange(aType
, aPermission
);
640 Unused
<< NS_WARN_IF(!success
);
651 ////////////////////////////////////////////////////////////////////////////////
653 PermissionManager::PermissionKey
*
654 PermissionManager::PermissionKey::CreateFromPrincipal(nsIPrincipal
* aPrincipal
,
658 nsAutoCString keyString
;
660 aResult
= GetSiteFromPrincipal(aPrincipal
, aForceStripOA
, keyString
);
662 aResult
= GetOriginFromPrincipal(aPrincipal
, aForceStripOA
, keyString
);
664 if (NS_WARN_IF(NS_FAILED(aResult
))) {
667 return new PermissionKey(keyString
);
670 PermissionManager::PermissionKey
*
671 PermissionManager::PermissionKey::CreateFromURIAndOriginAttributes(
672 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
, bool aForceStripOA
,
674 nsAutoCString origin
;
676 GetOriginFromURIAndOA(aURI
, aOriginAttributes
, aForceStripOA
, origin
);
677 if (NS_WARN_IF(NS_FAILED(aResult
))) {
681 return new PermissionKey(origin
);
684 PermissionManager::PermissionKey
*
685 PermissionManager::PermissionKey::CreateFromURI(nsIURI
* aURI
,
687 nsAutoCString origin
;
688 aResult
= ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI
, origin
);
689 if (NS_WARN_IF(NS_FAILED(aResult
))) {
693 return new PermissionKey(origin
);
696 ////////////////////////////////////////////////////////////////////////////////
697 // PermissionManager Implementation
699 NS_IMPL_ISUPPORTS(PermissionManager
, nsIPermissionManager
, nsIObserver
,
700 nsISupportsWeakReference
, nsIAsyncShutdownBlocker
)
702 PermissionManager::PermissionManager()
703 : mMonitor("PermissionManager::mMonitor"),
704 mState(eInitializing
),
705 mMemoryOnlyDB(false),
708 PermissionManager::~PermissionManager() {
709 // NOTE: Make sure to reject each of the promises in mPermissionKeyPromiseMap
710 // before destroying.
711 for (const auto& promise
: mPermissionKeyPromiseMap
.Values()) {
713 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
716 mPermissionKeyPromiseMap
.Clear();
725 StaticMutex
PermissionManager::sCreationMutex
;
728 already_AddRefed
<nsIPermissionManager
> PermissionManager::GetXPCOMSingleton() {
729 // The lazy initialization could race.
730 StaticMutexAutoLock
lock(sCreationMutex
);
732 if (gPermissionManager
) {
733 return do_AddRef(gPermissionManager
);
736 // Create a new singleton PermissionManager.
737 // We AddRef only once since XPCOM has rules about the ordering of module
738 // teardowns - by the time our module destructor is called, it's too late to
739 // Release our members, since GC cycles have already been completed and
740 // would result in serious leaks.
742 auto permManager
= MakeRefPtr
<PermissionManager
>();
743 if (NS_SUCCEEDED(permManager
->Init())) {
744 gPermissionManager
= permManager
.get();
745 return permManager
.forget();
752 PermissionManager
* PermissionManager::GetInstance() {
753 // TODO: There is a minimal chance that we can race here with a
754 // GetXPCOMSingleton call that did not yet set gPermissionManager.
756 if (!gPermissionManager
) {
757 // Hand off the creation of the permission manager to GetXPCOMSingleton.
758 nsCOMPtr
<nsIPermissionManager
> permManager
= GetXPCOMSingleton();
761 return gPermissionManager
;
764 nsresult
PermissionManager::Init() {
765 // If we are already shutting down, do not permit a creation.
766 // This must match the phase in GetAsyncShutdownBarrier.
767 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown
)) {
768 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN
;
771 // If the 'permissions.memory_only' pref is set to true, then don't write any
772 // permission settings to disk, but keep them in a memory-only database.
773 mMemoryOnlyDB
= Preferences::GetBool("permissions.memory_only", false);
776 nsCOMPtr
<nsIPrefService
> prefService
=
777 do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
);
778 NS_ENSURE_SUCCESS(rv
, rv
);
780 rv
= prefService
->GetBranch("permissions.default.",
781 getter_AddRefs(mDefaultPrefBranch
));
782 NS_ENSURE_SUCCESS(rv
, rv
);
784 if (IsChildProcess()) {
785 // Stop here; we don't need the DB in the child process. Instead we will be
786 // sent permissions as we need them by our parent process.
789 // We use ClearOnShutdown on the content process only because on the parent
790 // process we need to block the shutdown for the final closeDB() call.
791 ClearOnShutdown(&gPermissionManager
);
795 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
796 if (observerService
) {
797 observerService
->AddObserver(this, "profile-do-change", true);
798 observerService
->AddObserver(this, "testonly-reload-permissions-from-disk",
802 if (XRE_IsParentProcess()) {
803 nsCOMPtr
<nsIAsyncShutdownClient
> asc
= GetAsyncShutdownBarrier();
805 return NS_ERROR_NOT_AVAILABLE
;
807 nsAutoString blockerName
;
808 MOZ_ALWAYS_SUCCEEDS(GetName(blockerName
));
810 nsresult rv
= asc
->AddBlocker(
811 this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__
), __LINE__
, blockerName
);
812 NS_ENSURE_SUCCESS(rv
, rv
);
815 AddIdleDailyMaintenanceJob();
817 MOZ_ASSERT(!mThread
);
818 NS_ENSURE_SUCCESS(NS_NewNamedThread("Permission", getter_AddRefs(mThread
)),
822 MOZ_ALWAYS_SUCCEEDS(mThread
->GetPRThread(&prThread
));
823 MOZ_ASSERT(prThread
);
825 mThreadBoundData
.Transfer(prThread
);
832 nsresult
PermissionManager::OpenDatabase(nsIFile
* aPermissionsFile
) {
833 MOZ_ASSERT(!NS_IsMainThread());
834 auto data
= mThreadBoundData
.Access();
837 nsCOMPtr
<mozIStorageService
> storage
=
838 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
);
840 return NS_ERROR_UNEXPECTED
;
842 // cache a connection to the hosts database
844 rv
= storage
->OpenSpecialDatabase(
845 kMozStorageMemoryStorageKey
, VoidCString(),
846 mozIStorageService::CONNECTION_DEFAULT
, getter_AddRefs(data
->mDBConn
));
848 rv
= storage
->OpenDatabase(aPermissionsFile
,
849 mozIStorageService::CONNECTION_DEFAULT
,
850 getter_AddRefs(data
->mDBConn
));
855 void PermissionManager::InitDB(bool aRemoveFile
) {
856 mState
= eInitializing
;
859 MonitorAutoLock
lock(mMonitor
);
860 mReadEntries
.Clear();
863 auto readyIfFailed
= MakeScopeExit([&]() {
864 // ignore failure here, since it's non-fatal (we can run fine without
865 // persistent storage - e.g. if there's no profile).
866 // XXX should we tell the user about this?
870 if (!mPermissionsFile
) {
871 nsresult rv
= NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR
,
872 getter_AddRefs(mPermissionsFile
));
874 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
875 getter_AddRefs(mPermissionsFile
));
882 mPermissionsFile
->AppendNative(nsLiteralCString(PERMISSIONS_FILE_NAME
));
883 NS_ENSURE_SUCCESS_VOID(rv
);
886 nsCOMPtr
<nsIInputStream
> defaultsInputStream
= GetDefaultsInputStream();
888 RefPtr
<PermissionManager
> self
= this;
889 mThread
->Dispatch(NS_NewRunnableFunction(
890 "PermissionManager::InitDB", [self
, aRemoveFile
, defaultsInputStream
] {
891 nsresult rv
= self
->TryInitDB(aRemoveFile
, defaultsInputStream
);
892 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
894 // This extra runnable calls EnsureReadCompleted to finialize the
895 // initialization. If there is something blocked by the monitor, it will
897 NS_DispatchToMainThread(
898 NS_NewRunnableFunction("PermissionManager::InitDB-MainThread",
899 [self
] { self
->EnsureReadCompleted(); }));
901 self
->mMonitor
.Notify();
904 readyIfFailed
.release();
907 nsresult
PermissionManager::TryInitDB(bool aRemoveFile
,
908 nsIInputStream
* aDefaultsInputStream
) {
909 MOZ_ASSERT(!NS_IsMainThread());
911 MonitorAutoLock
lock(mMonitor
);
913 auto raii
= MakeScopeExit([&]() {
914 if (aDefaultsInputStream
) {
915 aDefaultsInputStream
->Close();
918 mState
= eDBInitialized
;
921 auto data
= mThreadBoundData
.Access();
923 auto raiiFailure
= MakeScopeExit([&]() {
925 DebugOnly
<nsresult
> rv
= data
->mDBConn
->Close();
926 MOZ_ASSERT(NS_SUCCEEDED(rv
));
927 data
->mDBConn
= nullptr;
935 rv
= mPermissionsFile
->Exists(&exists
);
936 NS_ENSURE_SUCCESS(rv
, rv
);
938 rv
= mPermissionsFile
->Remove(false);
939 NS_ENSURE_SUCCESS(rv
, rv
);
943 rv
= OpenDatabase(mPermissionsFile
);
944 if (rv
== NS_ERROR_FILE_CORRUPTED
) {
945 LogToConsole(u
"permissions.sqlite is corrupted! Try again!"_ns
);
947 // Add telemetry probe
948 Telemetry::Accumulate(Telemetry::PERMISSIONS_SQL_CORRUPTED
, 1);
950 // delete corrupted permissions.sqlite and try again
951 rv
= mPermissionsFile
->Remove(false);
952 NS_ENSURE_SUCCESS(rv
, rv
);
953 LogToConsole(u
"Corrupted permissions.sqlite has been removed."_ns
);
955 rv
= OpenDatabase(mPermissionsFile
);
956 NS_ENSURE_SUCCESS(rv
, rv
);
957 LogToConsole(u
"OpenDatabase to permissions.sqlite is successful!"_ns
);
960 if (NS_WARN_IF(NS_FAILED(rv
))) {
965 data
->mDBConn
->GetConnectionReady(&ready
);
967 LogToConsole(nsLiteralString(
968 u
"Fail to get connection to permissions.sqlite! Try again!"));
970 // delete and try again
971 rv
= mPermissionsFile
->Remove(false);
972 NS_ENSURE_SUCCESS(rv
, rv
);
973 LogToConsole(u
"Defective permissions.sqlite has been removed."_ns
);
975 // Add telemetry probe
976 Telemetry::Accumulate(Telemetry::DEFECTIVE_PERMISSIONS_SQL_REMOVED
, 1);
978 rv
= OpenDatabase(mPermissionsFile
);
979 NS_ENSURE_SUCCESS(rv
, rv
);
980 LogToConsole(u
"OpenDatabase to permissions.sqlite is successful!"_ns
);
982 data
->mDBConn
->GetConnectionReady(&ready
);
983 if (!ready
) return NS_ERROR_UNEXPECTED
;
986 bool tableExists
= false;
987 data
->mDBConn
->TableExists("moz_perms"_ns
, &tableExists
);
989 data
->mDBConn
->TableExists("moz_hosts"_ns
, &tableExists
);
993 NS_ENSURE_SUCCESS(rv
, rv
);
995 // table already exists; check the schema version before reading
996 int32_t dbSchemaVersion
;
997 rv
= data
->mDBConn
->GetSchemaVersion(&dbSchemaVersion
);
998 NS_ENSURE_SUCCESS(rv
, rv
);
1000 switch (dbSchemaVersion
) {
1002 // every time you increment the database schema, you need to
1003 // implement the upgrading code from the previous version to the
1004 // new one. fall through to current version
1007 // previous non-expiry version of database. Upgrade it by adding
1008 // the expiration columns
1009 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1010 "ALTER TABLE moz_hosts ADD expireType INTEGER"_ns
);
1011 NS_ENSURE_SUCCESS(rv
, rv
);
1013 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1014 "ALTER TABLE moz_hosts ADD expireTime INTEGER"_ns
);
1015 NS_ENSURE_SUCCESS(rv
, rv
);
1018 // fall through to the next upgrade
1021 // TODO: we want to make default version as version 2 in order to
1025 // Add appId/isInBrowserElement fields.
1026 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1027 "ALTER TABLE moz_hosts ADD appId INTEGER"_ns
);
1028 NS_ENSURE_SUCCESS(rv
, rv
);
1030 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1031 "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
1032 NS_ENSURE_SUCCESS(rv
, rv
);
1034 rv
= data
->mDBConn
->SetSchemaVersion(3);
1035 NS_ENSURE_SUCCESS(rv
, rv
);
1038 // fall through to the next upgrade
1041 // Version 3->4 is the creation of the modificationTime field.
1043 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1044 "ALTER TABLE moz_hosts ADD modificationTime INTEGER"));
1045 NS_ENSURE_SUCCESS(rv
, rv
);
1047 // We leave the modificationTime at zero for all existing records;
1048 // using now() would mean, eg, that doing "remove all from the
1049 // last hour" within the first hour after migration would remove
1052 rv
= data
->mDBConn
->SetSchemaVersion(4);
1053 NS_ENSURE_SUCCESS(rv
, rv
);
1056 // fall through to the next upgrade
1059 // In version 5, host appId, and isInBrowserElement were merged into
1060 // a single origin entry
1062 // In version 6, the tables were renamed for backwards compatability
1063 // reasons with version 4 and earlier.
1065 // In version 7, a bug in the migration used for version 4->5 was
1066 // discovered which could have triggered data-loss. Because of that,
1067 // all users with a version 4, 5, or 6 database will be re-migrated
1068 // from the backup database. (bug 1186034). This migration bug is
1069 // not present after bug 1185340, and the re-migration ensures that
1070 // all users have the fix.
1072 // This branch could also be reached via dbSchemaVersion == 3, in
1073 // which case we want to fall through to the dbSchemaVersion == 4
1074 // case. The easiest way to do that is to perform this extra check
1075 // here to make sure that we didn't get here via a fallthrough
1077 if (dbSchemaVersion
== 5) {
1078 // In version 5, the backup database is named moz_hosts_v4. We
1079 // perform the version 5->6 migration to get the tables to have
1080 // consistent naming conventions.
1082 // Version 5->6 is the renaming of moz_hosts to moz_perms, and
1083 // moz_hosts_v4 to moz_hosts (bug 1185343)
1085 // In version 5, we performed the modifications to the
1086 // permissions database in place, this meant that if you
1087 // upgraded to a version which used V5, and then downgraded to a
1088 // version which used v4 or earlier, the fallback path would
1089 // drop the table, and your permissions data would be lost. This
1090 // migration undoes that mistake, by restoring the old moz_hosts
1091 // table (if it was present), and instead using the new table
1092 // moz_perms for the new permissions schema.
1094 // NOTE: If you downgrade, store new permissions, and then
1095 // upgrade again, these new permissions won't be migrated or
1096 // reflected in the updated database. This migration only occurs
1097 // once, as if moz_perms exists, it will skip creating it. In
1098 // addition, permissions added after the migration will not be
1099 // visible in previous versions of firefox.
1101 bool permsTableExists
= false;
1102 data
->mDBConn
->TableExists("moz_perms"_ns
, &permsTableExists
);
1103 if (!permsTableExists
) {
1104 // Move the upgraded database to moz_perms
1105 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1106 "ALTER TABLE moz_hosts RENAME TO moz_perms"_ns
);
1107 NS_ENSURE_SUCCESS(rv
, rv
);
1110 "moz_hosts was not renamed to moz_perms, "
1111 "as a moz_perms table already exists");
1113 // In the situation where a moz_perms table already exists,
1114 // but the schema is lower than 6, a migration has already
1115 // previously occured to V6, but a downgrade has caused the
1116 // moz_hosts table to be dropped. This should only occur in
1117 // the case of a downgrade to a V5 database, which was only
1118 // present in a few day's nightlies. As that version was
1119 // likely used only on a temporary basis, we assume that the
1120 // database from the previous V6 has the permissions which the
1121 // user actually wants to use. We have to get rid of moz_hosts
1122 // such that moz_hosts_v4 can be moved into its place if it
1124 rv
= data
->mDBConn
->ExecuteSimpleSQL("DROP TABLE moz_hosts"_ns
);
1125 NS_ENSURE_SUCCESS(rv
, rv
);
1129 // The moz_hosts table shouldn't exist anymore
1130 bool hostsTableExists
= false;
1131 data
->mDBConn
->TableExists("moz_hosts"_ns
, &hostsTableExists
);
1132 MOZ_ASSERT(!hostsTableExists
);
1135 // Rename moz_hosts_v4 back to it's original location, if it
1137 bool v4TableExists
= false;
1138 data
->mDBConn
->TableExists("moz_hosts_v4"_ns
, &v4TableExists
);
1139 if (v4TableExists
) {
1140 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1141 "ALTER TABLE moz_hosts_v4 RENAME TO moz_hosts"));
1142 NS_ENSURE_SUCCESS(rv
, rv
);
1145 rv
= data
->mDBConn
->SetSchemaVersion(6);
1146 NS_ENSURE_SUCCESS(rv
, rv
);
1149 // fall through to the next upgrade
1152 // At this point, the version 5 table has been migrated to a version
1153 // 6 table We are guaranteed to have at least one of moz_hosts and
1154 // moz_perms. If we have moz_hosts, we will migrate moz_hosts into
1155 // moz_perms (even if we already have a moz_perms, as we need a
1156 // re-migration due to bug 1186034).
1158 // After this migration, we are guaranteed to have both a moz_hosts
1159 // (for backwards compatability), and a moz_perms table. The
1160 // moz_hosts table will have a v4 schema, and the moz_perms table
1161 // will have a v6 schema.
1164 bool hostsTableExists
= false;
1165 data
->mDBConn
->TableExists("moz_hosts"_ns
, &hostsTableExists
);
1166 if (hostsTableExists
) {
1167 // Both versions 4 and 6 have a version 4 formatted hosts table
1168 // named moz_hosts. We can migrate this table to our version 7
1169 // table moz_perms. If moz_perms is present, then we can use it
1170 // as a basis for comparison.
1172 rv
= data
->mDBConn
->BeginTransaction();
1173 NS_ENSURE_SUCCESS(rv
, rv
);
1175 bool tableExists
= false;
1176 data
->mDBConn
->TableExists("moz_hosts_new"_ns
, &tableExists
);
1179 "The temporary database moz_hosts_new already exists, "
1182 rv
= data
->mDBConn
->ExecuteSimpleSQL("DROP TABLE moz_hosts_new"_ns
);
1183 NS_ENSURE_SUCCESS(rv
, rv
);
1185 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1186 nsLiteralCString("CREATE TABLE moz_hosts_new ("
1187 " id INTEGER PRIMARY KEY"
1190 ",permission INTEGER"
1191 ",expireType INTEGER"
1192 ",expireTime INTEGER"
1193 ",modificationTime INTEGER"
1195 NS_ENSURE_SUCCESS(rv
, rv
);
1197 nsCOMPtr
<mozIStorageStatement
> stmt
;
1198 rv
= data
->mDBConn
->CreateStatement(
1200 "SELECT host, type, permission, expireType, "
1202 "modificationTime, isInBrowserElement FROM moz_hosts"),
1203 getter_AddRefs(stmt
));
1204 NS_ENSURE_SUCCESS(rv
, rv
);
1209 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1210 MigrationEntry entry
;
1212 // Read in the old row
1213 rv
= stmt
->GetUTF8String(0, entry
.mHost
);
1214 if (NS_WARN_IF(NS_FAILED(rv
))) {
1217 rv
= stmt
->GetUTF8String(1, entry
.mType
);
1218 if (NS_WARN_IF(NS_FAILED(rv
))) {
1223 entry
.mPermission
= stmt
->AsInt32(2);
1224 entry
.mExpireType
= stmt
->AsInt32(3);
1225 entry
.mExpireTime
= stmt
->AsInt64(4);
1226 entry
.mModificationTime
= stmt
->AsInt64(5);
1227 entry
.mIsInBrowserElement
= static_cast<bool>(stmt
->AsInt32(6));
1229 mMigrationEntries
.AppendElement(entry
);
1232 // We don't drop the moz_hosts table such that it is available
1233 // for backwards-compatability and for future migrations in case
1234 // of migration errors in the current code. Create a marker
1235 // empty table which will indicate that the moz_hosts table is
1236 // intended to act as a backup. If this table is not present,
1237 // then the moz_hosts table was created as a random empty table.
1238 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1239 nsLiteralCString("CREATE TABLE moz_hosts_is_backup (dummy "
1240 "INTEGER PRIMARY KEY)"));
1241 NS_ENSURE_SUCCESS(rv
, rv
);
1243 bool permsTableExists
= false;
1244 data
->mDBConn
->TableExists("moz_perms"_ns
, &permsTableExists
);
1245 if (permsTableExists
) {
1246 // The user already had a moz_perms table, and we are
1247 // performing a re-migration. We count the rows in the old
1248 // table for telemetry, and then back up their old database as
1251 nsCOMPtr
<mozIStorageStatement
> countStmt
;
1252 rv
= data
->mDBConn
->CreateStatement(
1253 "SELECT COUNT(*) FROM moz_perms"_ns
, getter_AddRefs(countStmt
));
1254 bool hasResult
= false;
1255 if (NS_FAILED(rv
) ||
1256 NS_FAILED(countStmt
->ExecuteStep(&hasResult
)) || !hasResult
) {
1257 NS_WARNING("Could not count the rows in moz_perms");
1260 // Back up the old moz_perms database as moz_perms_v6 before
1261 // we move the new table into its position
1262 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1263 "ALTER TABLE moz_perms RENAME TO moz_perms_v6"));
1264 NS_ENSURE_SUCCESS(rv
, rv
);
1267 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1268 "ALTER TABLE moz_hosts_new RENAME TO moz_perms"));
1269 NS_ENSURE_SUCCESS(rv
, rv
);
1271 rv
= data
->mDBConn
->CommitTransaction();
1272 NS_ENSURE_SUCCESS(rv
, rv
);
1274 // We don't have a moz_hosts table, so we create one for
1275 // downgrading purposes. This table is empty.
1276 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1277 nsLiteralCString("CREATE TABLE moz_hosts ("
1278 " id INTEGER PRIMARY KEY"
1281 ",permission INTEGER"
1282 ",expireType INTEGER"
1283 ",expireTime INTEGER"
1284 ",modificationTime INTEGER"
1286 ",isInBrowserElement INTEGER"
1288 NS_ENSURE_SUCCESS(rv
, rv
);
1290 // We are guaranteed to have a moz_perms table at this point.
1295 // At this point, both the moz_hosts and moz_perms tables should
1297 bool hostsTableExists
= false;
1298 bool permsTableExists
= false;
1299 data
->mDBConn
->TableExists("moz_hosts"_ns
, &hostsTableExists
);
1300 data
->mDBConn
->TableExists("moz_perms"_ns
, &permsTableExists
);
1301 MOZ_ASSERT(hostsTableExists
&& permsTableExists
);
1305 rv
= data
->mDBConn
->SetSchemaVersion(7);
1306 NS_ENSURE_SUCCESS(rv
, rv
);
1309 // fall through to the next upgrade
1312 // The version 7-8 migration is the re-migration of localhost and
1313 // ip-address entries due to errors in the previous version 7
1314 // migration which caused localhost and ip-address entries to be
1315 // incorrectly discarded. The version 7 migration logic has been
1316 // corrected, and thus this logic only needs to execute if the user
1317 // is currently on version 7.
1319 // This migration will be relatively expensive as we need to
1320 // perform database lookups for each origin which we want to
1321 // insert. Fortunately, it shouldn't be too expensive as we only
1322 // want to insert a small number of entries created for localhost
1325 // We only want to perform the re-migration if moz_hosts is a
1327 bool hostsIsBackupExists
= false;
1328 data
->mDBConn
->TableExists("moz_hosts_is_backup"_ns
,
1329 &hostsIsBackupExists
);
1331 // Only perform this migration if the original schema version was
1332 // 7, and the moz_hosts table is a backup.
1333 if (dbSchemaVersion
== 7 && hostsIsBackupExists
) {
1334 nsCOMPtr
<mozIStorageStatement
> stmt
;
1335 rv
= data
->mDBConn
->CreateStatement(
1337 "SELECT host, type, permission, expireType, "
1339 "modificationTime, isInBrowserElement FROM moz_hosts"),
1340 getter_AddRefs(stmt
));
1341 NS_ENSURE_SUCCESS(rv
, rv
);
1343 nsCOMPtr
<mozIStorageStatement
> idStmt
;
1344 rv
= data
->mDBConn
->CreateStatement(
1345 "SELECT MAX(id) FROM moz_hosts"_ns
, getter_AddRefs(idStmt
));
1348 bool hasResult
= false;
1349 if (NS_SUCCEEDED(rv
) &&
1350 NS_SUCCEEDED(idStmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1351 id
= idStmt
->AsInt32(0) + 1;
1354 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1355 MigrationEntry entry
;
1357 // Read in the old row
1358 rv
= stmt
->GetUTF8String(0, entry
.mHost
);
1359 if (NS_WARN_IF(NS_FAILED(rv
))) {
1363 nsAutoCString eTLD1
;
1364 rv
= nsEffectiveTLDService::GetInstance()->GetBaseDomainFromHost(
1365 entry
.mHost
, 0, eTLD1
);
1366 if (NS_SUCCEEDED(rv
)) {
1367 // We only care about entries which the tldService can't
1372 rv
= stmt
->GetUTF8String(1, entry
.mType
);
1373 if (NS_WARN_IF(NS_FAILED(rv
))) {
1378 entry
.mPermission
= stmt
->AsInt32(2);
1379 entry
.mExpireType
= stmt
->AsInt32(3);
1380 entry
.mExpireTime
= stmt
->AsInt64(4);
1381 entry
.mModificationTime
= stmt
->AsInt64(5);
1382 entry
.mIsInBrowserElement
= static_cast<bool>(stmt
->AsInt32(6));
1384 mMigrationEntries
.AppendElement(entry
);
1388 // Even if we didn't perform the migration, we want to bump the
1389 // schema version to 8.
1390 rv
= data
->mDBConn
->SetSchemaVersion(8);
1391 NS_ENSURE_SUCCESS(rv
, rv
);
1394 // fall through to the next upgrade
1397 // The version 8-9 migration removes the unnecessary backup
1398 // moz-hosts database contents. as the data no longer needs to be
1401 // We only want to clear out the old table if it is a backup. If
1402 // it isn't a backup, we don't need to touch it.
1403 bool hostsIsBackupExists
= false;
1404 data
->mDBConn
->TableExists("moz_hosts_is_backup"_ns
,
1405 &hostsIsBackupExists
);
1406 if (hostsIsBackupExists
) {
1407 // Delete everything from the backup, we want to keep around the
1408 // table so that you can still downgrade and not break things,
1409 // but we don't need to keep the rows around.
1410 rv
= data
->mDBConn
->ExecuteSimpleSQL("DELETE FROM moz_hosts"_ns
);
1411 NS_ENSURE_SUCCESS(rv
, rv
);
1413 // The table is no longer a backup, so get rid of it.
1414 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1415 "DROP TABLE moz_hosts_is_backup"_ns
);
1416 NS_ENSURE_SUCCESS(rv
, rv
);
1419 rv
= data
->mDBConn
->SetSchemaVersion(9);
1420 NS_ENSURE_SUCCESS(rv
, rv
);
1423 // fall through to the next upgrade
1427 rv
= data
->mDBConn
->SetSchemaVersion(10);
1428 NS_ENSURE_SUCCESS(rv
, rv
);
1431 // fall through to the next upgrade
1435 // Filter out the rows with storage access API permissions with a
1436 // granted origin, and remove the granted origin part from the
1438 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1440 "SET type=SUBSTR(type, 0, INSTR(SUBSTR(type, INSTR(type, "
1442 "1), '^') + INSTR(type, '^')) "
1443 "WHERE INSTR(SUBSTR(type, INSTR(type, '^') + 1), '^') AND "
1444 "SUBSTR(type, 0, 18) == \"storageAccessAPI^\";"));
1445 NS_ENSURE_SUCCESS(rv
, rv
);
1447 rv
= data
->mDBConn
->SetSchemaVersion(11);
1448 NS_ENSURE_SUCCESS(rv
, rv
);
1451 // fall through to the next upgrade
1455 // Migrate 3rdPartyStorage keys to a site scope
1456 rv
= data
->mDBConn
->BeginTransaction();
1457 NS_ENSURE_SUCCESS(rv
, rv
);
1458 nsCOMPtr
<mozIStorageStatement
> updateStmt
;
1459 rv
= data
->mDBConn
->CreateStatement(
1460 nsLiteralCString("UPDATE moz_perms SET origin = ?2 WHERE id = ?1"),
1461 getter_AddRefs(updateStmt
));
1462 NS_ENSURE_SUCCESS(rv
, rv
);
1464 nsCOMPtr
<mozIStorageStatement
> deleteStmt
;
1465 rv
= data
->mDBConn
->CreateStatement(
1466 nsLiteralCString("DELETE FROM moz_perms WHERE id = ?1"),
1467 getter_AddRefs(deleteStmt
));
1468 NS_ENSURE_SUCCESS(rv
, rv
);
1470 nsCOMPtr
<mozIStorageStatement
> selectStmt
;
1471 rv
= data
->mDBConn
->CreateStatement(
1472 nsLiteralCString("SELECT id, origin, type FROM moz_perms WHERE "
1473 " SUBSTR(type, 0, 17) == \"3rdPartyStorage^\""),
1474 getter_AddRefs(selectStmt
));
1475 NS_ENSURE_SUCCESS(rv
, rv
);
1477 nsTHashSet
<nsCStringHashKey
> deduplicationSet
;
1479 while (NS_SUCCEEDED(selectStmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1481 rv
= selectStmt
->GetInt64(0, &id
);
1482 NS_ENSURE_SUCCESS(rv
, rv
);
1485 rv
= selectStmt
->GetUTF8String(1, origin
);
1486 NS_ENSURE_SUCCESS(rv
, rv
);
1489 rv
= selectStmt
->GetUTF8String(2, type
);
1490 NS_ENSURE_SUCCESS(rv
, rv
);
1492 nsCOMPtr
<nsIURI
> uri
;
1493 rv
= NS_NewURI(getter_AddRefs(uri
), origin
);
1494 if (NS_FAILED(rv
)) {
1498 rv
= nsEffectiveTLDService::GetInstance()->GetSite(uri
, site
);
1499 if (NS_WARN_IF(NS_FAILED(rv
))) {
1503 nsCString deduplicationKey
=
1504 nsPrintfCString("%s,%s", site
.get(), type
.get());
1505 if (deduplicationSet
.Contains(deduplicationKey
)) {
1506 rv
= deleteStmt
->BindInt64ByIndex(0, id
);
1507 NS_ENSURE_SUCCESS(rv
, rv
);
1509 rv
= deleteStmt
->Execute();
1510 NS_ENSURE_SUCCESS(rv
, rv
);
1512 deduplicationSet
.Insert(deduplicationKey
);
1513 rv
= updateStmt
->BindInt64ByIndex(0, id
);
1514 NS_ENSURE_SUCCESS(rv
, rv
);
1515 rv
= updateStmt
->BindUTF8StringByIndex(1, site
);
1516 NS_ENSURE_SUCCESS(rv
, rv
);
1518 rv
= updateStmt
->Execute();
1519 NS_ENSURE_SUCCESS(rv
, rv
);
1522 rv
= data
->mDBConn
->CommitTransaction();
1523 NS_ENSURE_SUCCESS(rv
, rv
);
1525 rv
= data
->mDBConn
->SetSchemaVersion(HOSTS_SCHEMA_VERSION
);
1526 NS_ENSURE_SUCCESS(rv
, rv
);
1529 // fall through to the next upgrade
1533 case HOSTS_SCHEMA_VERSION
:
1537 // if columns have been added to the table, we can still use the
1538 // ones we understand safely. if columns have been deleted or
1539 // altered, just blow away the table and start from scratch! if you
1540 // change the way a column is interpreted, make sure you also change
1541 // its name so this check will catch it.
1543 // check if all the expected columns exist
1544 nsCOMPtr
<mozIStorageStatement
> stmt
;
1545 rv
= data
->mDBConn
->CreateStatement(
1546 nsLiteralCString("SELECT origin, type, permission, "
1547 "expireType, expireTime, "
1548 "modificationTime FROM moz_perms"),
1549 getter_AddRefs(stmt
));
1550 if (NS_SUCCEEDED(rv
)) break;
1552 // our columns aren't there - drop the table!
1553 rv
= data
->mDBConn
->ExecuteSimpleSQL("DROP TABLE moz_perms"_ns
);
1554 NS_ENSURE_SUCCESS(rv
, rv
);
1557 NS_ENSURE_SUCCESS(rv
, rv
);
1562 // cache frequently used statements (for insertion, deletion, and
1564 rv
= data
->mDBConn
->CreateStatement(
1565 nsLiteralCString("INSERT INTO moz_perms "
1566 "(id, origin, type, permission, expireType, "
1567 "expireTime, modificationTime) "
1568 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"),
1569 getter_AddRefs(data
->mStmtInsert
));
1570 NS_ENSURE_SUCCESS(rv
, rv
);
1572 rv
= data
->mDBConn
->CreateStatement(nsLiteralCString("DELETE FROM moz_perms "
1574 getter_AddRefs(data
->mStmtDelete
));
1575 NS_ENSURE_SUCCESS(rv
, rv
);
1577 rv
= data
->mDBConn
->CreateStatement(
1578 nsLiteralCString("UPDATE moz_perms "
1579 "SET permission = ?2, expireType= ?3, expireTime = "
1580 "?4, modificationTime = ?5 WHERE id = ?1"),
1581 getter_AddRefs(data
->mStmtUpdate
));
1582 NS_ENSURE_SUCCESS(rv
, rv
);
1584 // Always import default permissions.
1585 ConsumeDefaultsInputStream(aDefaultsInputStream
, lock
);
1587 // check whether to import or just read in the db
1590 NS_ENSURE_SUCCESS(rv
, rv
);
1593 raiiFailure
.release();
1598 void PermissionManager::AddIdleDailyMaintenanceJob() {
1599 MOZ_ASSERT(NS_IsMainThread());
1601 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
1602 NS_ENSURE_TRUE_VOID(observerService
);
1605 observerService
->AddObserver(this, OBSERVER_TOPIC_IDLE_DAILY
, false);
1606 NS_ENSURE_SUCCESS_VOID(rv
);
1609 void PermissionManager::RemoveIdleDailyMaintenanceJob() {
1610 MOZ_ASSERT(NS_IsMainThread());
1612 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
1613 NS_ENSURE_TRUE_VOID(observerService
);
1616 observerService
->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY
);
1617 NS_ENSURE_SUCCESS_VOID(rv
);
1620 void PermissionManager::PerformIdleDailyMaintenance() {
1621 MOZ_ASSERT(NS_IsMainThread());
1623 RefPtr
<PermissionManager
> self
= this;
1624 mThread
->Dispatch(NS_NewRunnableFunction(
1625 "PermissionManager::PerformIdleDailyMaintenance", [self
] {
1626 auto data
= self
->mThreadBoundData
.Access();
1628 if (self
->mState
== eClosed
|| !data
->mDBConn
) {
1632 nsCOMPtr
<mozIStorageStatement
> stmtDeleteExpired
;
1633 nsresult rv
= data
->mDBConn
->CreateStatement(
1634 nsLiteralCString("DELETE FROM moz_perms WHERE expireType = "
1635 "?1 AND expireTime <= ?2"),
1636 getter_AddRefs(stmtDeleteExpired
));
1637 NS_ENSURE_SUCCESS_VOID(rv
);
1639 rv
= stmtDeleteExpired
->BindInt32ByIndex(
1640 0, nsIPermissionManager::EXPIRE_TIME
);
1641 NS_ENSURE_SUCCESS_VOID(rv
);
1643 rv
= stmtDeleteExpired
->BindInt64ByIndex(1, EXPIRY_NOW
);
1644 NS_ENSURE_SUCCESS_VOID(rv
);
1646 rv
= stmtDeleteExpired
->Execute();
1647 NS_ENSURE_SUCCESS_VOID(rv
);
1651 // sets the schema version and creates the moz_perms table.
1652 nsresult
PermissionManager::CreateTable() {
1653 MOZ_ASSERT(!NS_IsMainThread());
1654 auto data
= mThreadBoundData
.Access();
1656 // set the schema version, before creating the table
1657 nsresult rv
= data
->mDBConn
->SetSchemaVersion(HOSTS_SCHEMA_VERSION
);
1658 if (NS_FAILED(rv
)) return rv
;
1661 // SQL also lives in automation.py.in. If you change this SQL change that
1663 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1664 nsLiteralCString("CREATE TABLE moz_perms ("
1665 " id INTEGER PRIMARY KEY"
1668 ",permission INTEGER"
1669 ",expireType INTEGER"
1670 ",expireTime INTEGER"
1671 ",modificationTime INTEGER"
1673 if (NS_FAILED(rv
)) return rv
;
1675 // We also create a legacy V4 table, for backwards compatability,
1676 // and to ensure that downgrades don't trigger a schema version change.
1677 return data
->mDBConn
->ExecuteSimpleSQL(
1678 nsLiteralCString("CREATE TABLE moz_hosts ("
1679 " id INTEGER PRIMARY KEY"
1682 ",permission INTEGER"
1683 ",expireType INTEGER"
1684 ",expireTime INTEGER"
1685 ",modificationTime INTEGER"
1686 ",isInBrowserElement INTEGER"
1690 // Returns whether the given combination of expire type and expire time are
1691 // expired. Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
1692 bool PermissionManager::HasExpired(uint32_t aExpireType
, int64_t aExpireTime
) {
1693 return (aExpireType
== nsIPermissionManager::EXPIRE_TIME
||
1694 (aExpireType
== nsIPermissionManager::EXPIRE_SESSION
&&
1695 aExpireTime
!= 0)) &&
1696 aExpireTime
<= EXPIRY_NOW
;
1700 PermissionManager::AddFromPrincipalAndPersistInPrivateBrowsing(
1701 nsIPrincipal
* aPrincipal
, const nsACString
& aType
, uint32_t aPermission
) {
1702 ENSURE_NOT_CHILD_PROCESS
;
1703 NS_ENSURE_ARG_POINTER(aPrincipal
);
1704 // We don't add the system principal because it actually has no URI and we
1705 // always allow action for them.
1706 if (aPrincipal
->IsSystemPrincipal()) {
1710 // Null principals can't meaningfully have persisted permissions attached to
1711 // them, so we don't allow adding permissions for them.
1712 if (aPrincipal
->GetIsNullPrincipal()) {
1716 // Permissions may not be added to expanded principals.
1717 if (IsExpandedPrincipal(aPrincipal
)) {
1718 return NS_ERROR_INVALID_ARG
;
1721 // A modificationTime of zero will cause AddInternal to use now().
1722 int64_t modificationTime
= 0;
1724 return AddInternal(aPrincipal
, aType
, aPermission
, 0,
1725 nsIPermissionManager::EXPIRE_NEVER
,
1726 /* aExpireTime */ 0, modificationTime
, eNotify
, eWriteToDB
,
1727 /* aIgnoreSessionPermissions */ false,
1728 /* aOriginString*/ nullptr,
1729 /* aAllowPersistInPrivateBrowsing */ true);
1733 PermissionManager::AddFromPrincipal(nsIPrincipal
* aPrincipal
,
1734 const nsACString
& aType
,
1735 uint32_t aPermission
, uint32_t aExpireType
,
1736 int64_t aExpireTime
) {
1737 ENSURE_NOT_CHILD_PROCESS
;
1738 NS_ENSURE_ARG_POINTER(aPrincipal
);
1739 NS_ENSURE_TRUE(aExpireType
== nsIPermissionManager::EXPIRE_NEVER
||
1740 aExpireType
== nsIPermissionManager::EXPIRE_TIME
||
1741 aExpireType
== nsIPermissionManager::EXPIRE_SESSION
||
1742 aExpireType
== nsIPermissionManager::EXPIRE_POLICY
,
1743 NS_ERROR_INVALID_ARG
);
1745 // Skip addition if the permission is already expired.
1746 if (HasExpired(aExpireType
, aExpireTime
)) {
1750 // We don't add the system principal because it actually has no URI and we
1751 // always allow action for them.
1752 if (aPrincipal
->IsSystemPrincipal()) {
1756 // Null principals can't meaningfully have persisted permissions attached to
1757 // them, so we don't allow adding permissions for them.
1758 if (aPrincipal
->GetIsNullPrincipal()) {
1762 // Permissions may not be added to expanded principals.
1763 if (IsExpandedPrincipal(aPrincipal
)) {
1764 return NS_ERROR_INVALID_ARG
;
1767 // A modificationTime of zero will cause AddInternal to use now().
1768 int64_t modificationTime
= 0;
1770 return AddInternal(aPrincipal
, aType
, aPermission
, 0, aExpireType
,
1771 aExpireTime
, modificationTime
, eNotify
, eWriteToDB
);
1774 nsresult
PermissionManager::AddInternal(
1775 nsIPrincipal
* aPrincipal
, const nsACString
& aType
, uint32_t aPermission
,
1776 int64_t aID
, uint32_t aExpireType
, int64_t aExpireTime
,
1777 int64_t aModificationTime
, NotifyOperationType aNotifyOperation
,
1778 DBOperationType aDBOperation
, const bool aIgnoreSessionPermissions
,
1779 const nsACString
* aOriginString
,
1780 const bool aAllowPersistInPrivateBrowsing
) {
1781 MOZ_ASSERT(NS_IsMainThread());
1783 EnsureReadCompleted();
1785 nsresult rv
= NS_OK
;
1786 nsAutoCString origin
;
1787 // Only attempt to compute the origin string when it is going to be needed
1788 // later on in the function.
1789 if (!IsChildProcess() ||
1790 (aDBOperation
== eWriteToDB
&& IsPersistentExpire(aExpireType
, aType
))) {
1791 if (aOriginString
) {
1792 // Use the origin string provided by the caller.
1793 origin
= *aOriginString
;
1795 if (IsSiteScopedPermission(aType
)) {
1796 rv
= GetSiteFromPrincipal(aPrincipal
, IsOAForceStripPermission(aType
),
1799 // Compute it from the principal provided.
1800 rv
= GetOriginFromPrincipal(aPrincipal
, IsOAForceStripPermission(aType
),
1803 NS_ENSURE_SUCCESS(rv
, rv
);
1807 // Unless the caller sets aAllowPersistInPrivateBrowsing, only store
1808 // permissions for the session in Private Browsing. Except for default
1809 // permissions which are stored in-memory only and imported each startup. We
1810 // also allow setting persistent UKNOWN_ACTION, to support removing default
1811 // private browsing permissions.
1812 if (!aAllowPersistInPrivateBrowsing
&& aID
!= cIDPermissionIsDefault
&&
1813 aPermission
!= UNKNOWN_ACTION
&& aExpireType
!= EXPIRE_SESSION
) {
1814 uint32_t privateBrowsingId
=
1815 nsScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
;
1816 nsresult rv
= aPrincipal
->GetPrivateBrowsingId(&privateBrowsingId
);
1817 if (NS_SUCCEEDED(rv
) &&
1818 privateBrowsingId
!=
1819 nsScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
) {
1820 aExpireType
= EXPIRE_SESSION
;
1824 // Let's send the new permission to the content process only if it has to be
1826 if (!IsChildProcess() && aNotifyOperation
== eNotify
) {
1827 IPC::Permission
permission(origin
, aType
, aPermission
, aExpireType
,
1830 nsAutoCString permissionKey
;
1831 GetKeyForPermission(aPrincipal
, aType
, permissionKey
);
1832 bool isSecondaryKeyed
;
1833 nsAutoCString secondaryKey
;
1834 isSecondaryKeyed
= GetSecondaryKey(aType
, secondaryKey
);
1835 if (isSecondaryKeyed
) {
1836 NotifySecondaryKeyPermissionUpdateInContentProcess(
1837 aType
, aPermission
, secondaryKey
, aPrincipal
);
1840 nsTArray
<ContentParent
*> cplist
;
1841 ContentParent::GetAll(cplist
);
1842 for (uint32_t i
= 0; i
< cplist
.Length(); ++i
) {
1843 ContentParent
* cp
= cplist
[i
];
1844 if (cp
->NeedsPermissionsUpdate(permissionKey
)) {
1845 Unused
<< cp
->SendAddPermission(permission
);
1850 MOZ_ASSERT(PermissionAvailable(aPrincipal
, aType
));
1852 // look up the type index
1853 int32_t typeIndex
= GetTypeIndex(aType
, true);
1854 NS_ENSURE_TRUE(typeIndex
!= -1, NS_ERROR_OUT_OF_MEMORY
);
1856 // When an entry already exists, PutEntry will return that, instead
1857 // of adding a new one
1858 RefPtr
<PermissionKey
> key
= PermissionKey::CreateFromPrincipal(
1859 aPrincipal
, IsOAForceStripPermission(aType
),
1860 IsSiteScopedPermission(aType
), rv
);
1862 MOZ_ASSERT(NS_FAILED(rv
));
1866 PermissionHashKey
* entry
= mPermissionTable
.PutEntry(key
);
1867 if (!entry
) return NS_ERROR_FAILURE
;
1868 if (!entry
->GetKey()) {
1869 mPermissionTable
.RemoveEntry(entry
);
1870 return NS_ERROR_OUT_OF_MEMORY
;
1873 // figure out the transaction type, and get any existing permission value
1875 int32_t index
= entry
->GetPermissionIndex(typeIndex
);
1877 if (aPermission
== nsIPermissionManager::UNKNOWN_ACTION
)
1878 op
= eOperationNone
;
1880 op
= eOperationAdding
;
1883 PermissionEntry oldPermissionEntry
= entry
->GetPermissions()[index
];
1885 // remove the permission if the permission is UNKNOWN, update the
1886 // permission if its value or expire type have changed OR if the time has
1887 // changed and the expire type is time, otherwise, don't modify. There's
1888 // no need to modify a permission that doesn't expire with time when the
1889 // only thing changed is the expire time.
1890 if (aPermission
== oldPermissionEntry
.mPermission
&&
1891 aExpireType
== oldPermissionEntry
.mExpireType
&&
1892 (aExpireType
== nsIPermissionManager::EXPIRE_NEVER
||
1893 aExpireTime
== oldPermissionEntry
.mExpireTime
))
1894 op
= eOperationNone
;
1895 else if (oldPermissionEntry
.mID
== cIDPermissionIsDefault
)
1896 // The existing permission is one added as a default and the new
1897 // permission doesn't exactly match so we are replacing the default. This
1898 // is true even if the new permission is UNKNOWN_ACTION (which means a
1899 // "logical remove" of the default)
1900 op
= eOperationReplacingDefault
;
1901 else if (aID
== cIDPermissionIsDefault
)
1902 // We are adding a default permission but a "real" permission already
1903 // exists. This almost-certainly means we just did a removeAllSince and
1904 // are re-importing defaults - so we can ignore this.
1905 op
= eOperationNone
;
1906 else if (aPermission
== nsIPermissionManager::UNKNOWN_ACTION
)
1907 op
= eOperationRemoving
;
1909 op
= eOperationChanging
;
1912 // child processes should *always* be passed a modificationTime of zero.
1913 MOZ_ASSERT(!IsChildProcess() || aModificationTime
== 0);
1915 // do the work for adding, deleting, or changing a permission:
1916 // update the in-memory list, write to the db, and notify consumers.
1918 if (aModificationTime
== 0) {
1919 aModificationTime
= EXPIRY_NOW
;
1923 case eOperationNone
: {
1928 case eOperationAdding
: {
1929 if (aDBOperation
== eWriteToDB
) {
1930 // we'll be writing to the database - generate a known unique id
1933 // we're reading from the database - use the id already assigned
1937 entry
->GetPermissions().AppendElement(
1938 PermissionEntry(id
, typeIndex
, aPermission
, aExpireType
, aExpireTime
,
1939 aModificationTime
));
1941 if (aDBOperation
== eWriteToDB
&&
1942 IsPersistentExpire(aExpireType
, aType
)) {
1943 UpdateDB(op
, id
, origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
1947 if (aNotifyOperation
== eNotify
) {
1948 NotifyObserversWithPermission(aPrincipal
, mTypeArray
[typeIndex
],
1949 aPermission
, aExpireType
, aExpireTime
,
1950 aModificationTime
, u
"added");
1956 case eOperationRemoving
: {
1957 PermissionEntry oldPermissionEntry
= entry
->GetPermissions()[index
];
1958 id
= oldPermissionEntry
.mID
;
1960 // If the type we want to remove is EXPIRE_POLICY, we need to reject
1961 // attempts to change the permission.
1962 if (entry
->GetPermissions()[index
].mExpireType
== EXPIRE_POLICY
) {
1963 NS_WARNING("Attempting to remove EXPIRE_POLICY permission");
1967 entry
->GetPermissions().RemoveElementAt(index
);
1969 if (aDBOperation
== eWriteToDB
)
1970 // We care only about the id here so we pass dummy values for all other
1972 UpdateDB(op
, id
, ""_ns
, ""_ns
, 0, nsIPermissionManager::EXPIRE_NEVER
, 0,
1975 if (aNotifyOperation
== eNotify
) {
1976 NotifyObserversWithPermission(
1977 aPrincipal
, mTypeArray
[typeIndex
], oldPermissionEntry
.mPermission
,
1978 oldPermissionEntry
.mExpireType
, oldPermissionEntry
.mExpireTime
,
1979 oldPermissionEntry
.mModificationTime
, u
"deleted");
1982 // If there are no more permissions stored for that entry, clear it.
1983 if (entry
->GetPermissions().IsEmpty()) {
1984 mPermissionTable
.RemoveEntry(entry
);
1990 case eOperationChanging
: {
1991 id
= entry
->GetPermissions()[index
].mID
;
1993 // If the existing type is EXPIRE_POLICY, we need to reject attempts to
1994 // change the permission.
1995 if (entry
->GetPermissions()[index
].mExpireType
== EXPIRE_POLICY
) {
1996 NS_WARNING("Attempting to modify EXPIRE_POLICY permission");
2000 PermissionEntry oldPermissionEntry
= entry
->GetPermissions()[index
];
2002 // If the new expireType is EXPIRE_SESSION, then we have to keep a
2003 // copy of the previous permission/expireType values. This cached value
2004 // will be used when restoring the permissions of an app.
2005 if (entry
->GetPermissions()[index
].mExpireType
!=
2006 nsIPermissionManager::EXPIRE_SESSION
&&
2007 aExpireType
== nsIPermissionManager::EXPIRE_SESSION
) {
2008 entry
->GetPermissions()[index
].mNonSessionPermission
=
2009 entry
->GetPermissions()[index
].mPermission
;
2010 entry
->GetPermissions()[index
].mNonSessionExpireType
=
2011 entry
->GetPermissions()[index
].mExpireType
;
2012 entry
->GetPermissions()[index
].mNonSessionExpireTime
=
2013 entry
->GetPermissions()[index
].mExpireTime
;
2014 } else if (aExpireType
!= nsIPermissionManager::EXPIRE_SESSION
) {
2015 entry
->GetPermissions()[index
].mNonSessionPermission
= aPermission
;
2016 entry
->GetPermissions()[index
].mNonSessionExpireType
= aExpireType
;
2017 entry
->GetPermissions()[index
].mNonSessionExpireTime
= aExpireTime
;
2020 entry
->GetPermissions()[index
].mPermission
= aPermission
;
2021 entry
->GetPermissions()[index
].mExpireType
= aExpireType
;
2022 entry
->GetPermissions()[index
].mExpireTime
= aExpireTime
;
2023 entry
->GetPermissions()[index
].mModificationTime
= aModificationTime
;
2025 if (aDBOperation
== eWriteToDB
) {
2026 bool newIsPersistentExpire
= IsPersistentExpire(aExpireType
, aType
);
2027 bool oldIsPersistentExpire
=
2028 IsPersistentExpire(oldPermissionEntry
.mExpireType
, aType
);
2030 if (!newIsPersistentExpire
&& oldIsPersistentExpire
) {
2031 // Maybe we have to remove the previous permission if that was
2033 UpdateDB(eOperationRemoving
, id
, ""_ns
, ""_ns
, 0,
2034 nsIPermissionManager::EXPIRE_NEVER
, 0, 0);
2035 } else if (newIsPersistentExpire
&& !oldIsPersistentExpire
) {
2036 // It could also be that the previous permission was session-only but
2037 // this needs to be written into the DB. In this case, we have to run
2038 // an Adding operation.
2039 UpdateDB(eOperationAdding
, id
, origin
, aType
, aPermission
,
2040 aExpireType
, aExpireTime
, aModificationTime
);
2041 } else if (newIsPersistentExpire
) {
2042 // This is the a simple update. We care only about the id, the
2043 // permission and expireType/expireTime/modificationTime here. We pass
2044 // dummy values for all other parameters.
2045 UpdateDB(op
, id
, ""_ns
, ""_ns
, aPermission
, aExpireType
, aExpireTime
,
2050 if (aNotifyOperation
== eNotify
) {
2051 NotifyObserversWithPermission(aPrincipal
, mTypeArray
[typeIndex
],
2052 aPermission
, aExpireType
, aExpireTime
,
2053 aModificationTime
, u
"changed");
2058 case eOperationReplacingDefault
: {
2059 // this is handling the case when we have an existing permission
2060 // entry that was created as a "default" (and thus isn't in the DB) with
2061 // an explicit permission (that may include UNKNOWN_ACTION.)
2062 // Note we will *not* get here if we are replacing an already replaced
2063 // default value - that is handled as eOperationChanging.
2065 // So this is a hybrid of eOperationAdding (as we are writing a new entry
2066 // to the DB) and eOperationChanging (as we are replacing the in-memory
2067 // repr and sending a "changed" notification).
2069 // We want a new ID even if not writing to the DB, so the modified entry
2070 // in memory doesn't have the magic cIDPermissionIsDefault value.
2073 // The default permission being replaced can't have session expiry or
2075 NS_ENSURE_TRUE(entry
->GetPermissions()[index
].mExpireType
!=
2076 nsIPermissionManager::EXPIRE_SESSION
,
2077 NS_ERROR_UNEXPECTED
);
2078 NS_ENSURE_TRUE(entry
->GetPermissions()[index
].mExpireType
!=
2079 nsIPermissionManager::EXPIRE_POLICY
,
2080 NS_ERROR_UNEXPECTED
);
2081 // We don't support the new entry having any expiry - supporting that
2082 // would make things far more complex and none of the permissions we set
2083 // as a default support that.
2084 NS_ENSURE_TRUE(aExpireType
== EXPIRE_NEVER
, NS_ERROR_UNEXPECTED
);
2086 // update the existing entry in memory.
2087 entry
->GetPermissions()[index
].mID
= id
;
2088 entry
->GetPermissions()[index
].mPermission
= aPermission
;
2089 entry
->GetPermissions()[index
].mExpireType
= aExpireType
;
2090 entry
->GetPermissions()[index
].mExpireTime
= aExpireTime
;
2091 entry
->GetPermissions()[index
].mModificationTime
= aModificationTime
;
2093 // If requested, create the entry in the DB.
2094 if (aDBOperation
== eWriteToDB
&&
2095 IsPersistentExpire(aExpireType
, aType
)) {
2096 UpdateDB(eOperationAdding
, id
, origin
, aType
, aPermission
, aExpireType
,
2097 aExpireTime
, aModificationTime
);
2100 if (aNotifyOperation
== eNotify
) {
2101 NotifyObserversWithPermission(aPrincipal
, mTypeArray
[typeIndex
],
2102 aPermission
, aExpireType
, aExpireTime
,
2103 aModificationTime
, u
"changed");
2113 PermissionManager::RemoveFromPrincipal(nsIPrincipal
* aPrincipal
,
2114 const nsACString
& aType
) {
2115 ENSURE_NOT_CHILD_PROCESS
;
2116 NS_ENSURE_ARG_POINTER(aPrincipal
);
2118 // System principals are never added to the database, no need to remove them.
2119 if (aPrincipal
->IsSystemPrincipal()) {
2123 // Permissions may not be added to expanded principals.
2124 if (IsExpandedPrincipal(aPrincipal
)) {
2125 return NS_ERROR_INVALID_ARG
;
2128 // AddInternal() handles removal, just let it do the work
2129 return AddInternal(aPrincipal
, aType
, nsIPermissionManager::UNKNOWN_ACTION
, 0,
2130 nsIPermissionManager::EXPIRE_NEVER
, 0, 0, eNotify
,
2135 PermissionManager::RemovePermission(nsIPermission
* aPerm
) {
2139 nsCOMPtr
<nsIPrincipal
> principal
;
2140 nsresult rv
= aPerm
->GetPrincipal(getter_AddRefs(principal
));
2141 NS_ENSURE_SUCCESS(rv
, rv
);
2144 rv
= aPerm
->GetType(type
);
2145 NS_ENSURE_SUCCESS(rv
, rv
);
2147 // Permissions are uniquely identified by their principal and type.
2148 // We remove the permission using these two pieces of data.
2149 return RemoveFromPrincipal(principal
, type
);
2153 PermissionManager::RemoveAll() {
2154 ENSURE_NOT_CHILD_PROCESS
;
2155 return RemoveAllInternal(true);
2159 PermissionManager::RemoveAllSince(int64_t aSince
) {
2160 ENSURE_NOT_CHILD_PROCESS
;
2161 return RemoveAllModifiedSince(aSince
);
2165 nsresult
PermissionManager::RemovePermissionEntries(T aCondition
) {
2166 EnsureReadCompleted();
2168 Vector
<std::tuple
<nsCOMPtr
<nsIPrincipal
>, nsCString
, nsCString
>, 10> array
;
2169 for (const PermissionHashKey
& entry
: mPermissionTable
) {
2170 for (const auto& permEntry
: entry
.GetPermissions()) {
2171 if (!aCondition(permEntry
)) {
2175 nsCOMPtr
<nsIPrincipal
> principal
;
2176 nsresult rv
= GetPrincipalFromOrigin(
2177 entry
.GetKey()->mOrigin
,
2178 IsOAForceStripPermission(mTypeArray
[permEntry
.mType
]),
2179 getter_AddRefs(principal
));
2180 if (NS_FAILED(rv
)) {
2184 if (!array
.emplaceBack(principal
, mTypeArray
[permEntry
.mType
],
2185 entry
.GetKey()->mOrigin
)) {
2191 for (auto& i
: array
) {
2192 // AddInternal handles removal, so let it do the work...
2194 std::get
<0>(i
), std::get
<1>(i
), nsIPermissionManager::UNKNOWN_ACTION
, 0,
2195 nsIPermissionManager::EXPIRE_NEVER
, 0, 0, PermissionManager::eNotify
,
2196 PermissionManager::eWriteToDB
, false, &std::get
<2>(i
));
2199 // now re-import any defaults as they may now be required if we just deleted
2201 ImportLatestDefaults();
2206 PermissionManager::RemoveByType(const nsACString
& aType
) {
2207 ENSURE_NOT_CHILD_PROCESS
;
2209 int32_t typeIndex
= GetTypeIndex(aType
, false);
2210 // If type == -1, the type isn't known,
2211 // so just return NS_OK
2212 if (typeIndex
== -1) {
2216 return RemovePermissionEntries(
2217 [typeIndex
](const PermissionEntry
& aPermEntry
) {
2218 return static_cast<uint32_t>(typeIndex
) == aPermEntry
.mType
;
2223 PermissionManager::RemoveByTypeSince(const nsACString
& aType
,
2224 int64_t aModificationTime
) {
2225 ENSURE_NOT_CHILD_PROCESS
;
2227 int32_t typeIndex
= GetTypeIndex(aType
, false);
2228 // If type == -1, the type isn't known,
2229 // so just return NS_OK
2230 if (typeIndex
== -1) {
2234 return RemovePermissionEntries(
2235 [typeIndex
, aModificationTime
](const PermissionEntry
& aPermEntry
) {
2236 return uint32_t(typeIndex
) == aPermEntry
.mType
&&
2237 aModificationTime
<= aPermEntry
.mModificationTime
;
2241 void PermissionManager::CloseDB(CloseDBNextOp aNextOp
) {
2242 EnsureReadCompleted();
2246 nsCOMPtr
<nsIInputStream
> defaultsInputStream
;
2247 if (aNextOp
== eRebuldOnSuccess
) {
2248 defaultsInputStream
= GetDefaultsInputStream();
2251 RefPtr
<PermissionManager
> self
= this;
2252 mThread
->Dispatch(NS_NewRunnableFunction(
2253 "PermissionManager::CloseDB", [self
, aNextOp
, defaultsInputStream
] {
2254 auto data
= self
->mThreadBoundData
.Access();
2255 // Null the statements, this will finalize them.
2256 data
->mStmtInsert
= nullptr;
2257 data
->mStmtDelete
= nullptr;
2258 data
->mStmtUpdate
= nullptr;
2259 if (data
->mDBConn
) {
2260 DebugOnly
<nsresult
> rv
= data
->mDBConn
->Close();
2261 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2262 data
->mDBConn
= nullptr;
2264 if (aNextOp
== eRebuldOnSuccess
) {
2265 self
->TryInitDB(true, defaultsInputStream
);
2269 if (aNextOp
== eShutdown
) {
2270 NS_DispatchToMainThread(NS_NewRunnableFunction(
2271 "PermissionManager::MaybeCompleteShutdown",
2272 [self
] { self
->MaybeCompleteShutdown(); }));
2277 nsresult
PermissionManager::RemoveAllFromIPC() {
2278 MOZ_ASSERT(IsChildProcess());
2280 // Remove from memory and notify immediately. Since the in-memory
2281 // database is authoritative, we do not need confirmation from the
2282 // on-disk database to notify observers.
2283 RemoveAllFromMemory();
2288 nsresult
PermissionManager::RemoveAllInternal(bool aNotifyObservers
) {
2289 ENSURE_NOT_CHILD_PROCESS
;
2291 EnsureReadCompleted();
2293 // Let's broadcast the removeAll() to any content process.
2294 nsTArray
<ContentParent
*> parents
;
2295 ContentParent::GetAll(parents
);
2296 for (ContentParent
* parent
: parents
) {
2297 Unused
<< parent
->SendRemoveAllPermissions();
2300 // Remove from memory and notify immediately. Since the in-memory
2301 // database is authoritative, we do not need confirmation from the
2302 // on-disk database to notify observers.
2303 RemoveAllFromMemory();
2305 // Re-import the defaults
2306 ImportLatestDefaults();
2308 if (aNotifyObservers
) {
2309 NotifyObservers(nullptr, u
"cleared");
2312 RefPtr
<PermissionManager
> self
= this;
2314 NS_NewRunnableFunction("PermissionManager::RemoveAllInternal", [self
] {
2315 auto data
= self
->mThreadBoundData
.Access();
2317 if (self
->mState
== eClosed
|| !data
->mDBConn
) {
2323 data
->mDBConn
->ExecuteSimpleSQL("DELETE FROM moz_perms"_ns
);
2324 if (NS_WARN_IF(NS_FAILED(rv
))) {
2325 NS_DispatchToMainThread(NS_NewRunnableFunction(
2326 "PermissionManager::RemoveAllInternal-Failure",
2327 [self
] { self
->CloseDB(eRebuldOnSuccess
); }));
2335 PermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal
* aPrincipal
,
2336 const nsACString
& aType
,
2337 uint32_t* aPermission
) {
2338 return CommonTestPermission(aPrincipal
, -1, aType
, aPermission
,
2339 nsIPermissionManager::UNKNOWN_ACTION
, false, true,
2344 PermissionManager::TestExactPermanentPermission(nsIPrincipal
* aPrincipal
,
2345 const nsACString
& aType
,
2346 uint32_t* aPermission
) {
2347 return CommonTestPermission(aPrincipal
, -1, aType
, aPermission
,
2348 nsIPermissionManager::UNKNOWN_ACTION
, false, true,
2352 nsresult
PermissionManager::LegacyTestPermissionFromURI(
2353 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
,
2354 const nsACString
& aType
, uint32_t* aPermission
) {
2355 return CommonTestPermission(aURI
, aOriginAttributes
, -1, aType
, aPermission
,
2356 nsIPermissionManager::UNKNOWN_ACTION
, false,
2361 PermissionManager::TestPermissionFromPrincipal(nsIPrincipal
* aPrincipal
,
2362 const nsACString
& aType
,
2363 uint32_t* aPermission
) {
2364 return CommonTestPermission(aPrincipal
, -1, aType
, aPermission
,
2365 nsIPermissionManager::UNKNOWN_ACTION
, false,
2370 PermissionManager::GetPermissionObject(nsIPrincipal
* aPrincipal
,
2371 const nsACString
& aType
,
2372 bool aExactHostMatch
,
2373 nsIPermission
** aResult
) {
2374 NS_ENSURE_ARG_POINTER(aPrincipal
);
2377 EnsureReadCompleted();
2379 if (aPrincipal
->IsSystemPrincipal()) {
2383 // Querying the permission object of an nsEP is non-sensical.
2384 if (IsExpandedPrincipal(aPrincipal
)) {
2385 return NS_ERROR_INVALID_ARG
;
2388 MOZ_ASSERT(PermissionAvailable(aPrincipal
, aType
));
2390 int32_t typeIndex
= GetTypeIndex(aType
, false);
2391 // If type == -1, the type isn't known,
2392 // so just return NS_OK
2393 if (typeIndex
== -1) return NS_OK
;
2395 PermissionHashKey
* entry
=
2396 GetPermissionHashKey(aPrincipal
, typeIndex
, aExactHostMatch
);
2401 // We don't call GetPermission(typeIndex) because that returns a fake
2402 // UNKNOWN_ACTION entry if there is no match.
2403 int32_t idx
= entry
->GetPermissionIndex(typeIndex
);
2408 nsCOMPtr
<nsIPrincipal
> principal
;
2409 nsresult rv
= GetPrincipalFromOrigin(entry
->GetKey()->mOrigin
,
2410 IsOAForceStripPermission(aType
),
2411 getter_AddRefs(principal
));
2412 NS_ENSURE_SUCCESS(rv
, rv
);
2414 PermissionEntry
& perm
= entry
->GetPermissions()[idx
];
2415 nsCOMPtr
<nsIPermission
> r
= Permission::Create(
2416 principal
, mTypeArray
[perm
.mType
], perm
.mPermission
, perm
.mExpireType
,
2417 perm
.mExpireTime
, perm
.mModificationTime
);
2418 if (NS_WARN_IF(!r
)) {
2419 return NS_ERROR_FAILURE
;
2425 nsresult
PermissionManager::CommonTestPermissionInternal(
2426 nsIPrincipal
* aPrincipal
, nsIURI
* aURI
,
2427 const OriginAttributes
* aOriginAttributes
, int32_t aTypeIndex
,
2428 const nsACString
& aType
, uint32_t* aPermission
, bool aExactHostMatch
,
2429 bool aIncludingSession
) {
2430 MOZ_ASSERT(aPrincipal
|| aURI
);
2431 NS_ENSURE_ARG_POINTER(aPrincipal
|| aURI
);
2432 MOZ_ASSERT_IF(aPrincipal
, !aURI
&& !aOriginAttributes
);
2433 MOZ_ASSERT_IF(aURI
|| aOriginAttributes
, !aPrincipal
);
2435 EnsureReadCompleted();
2439 nsCOMPtr
<nsIPrincipal
> prin
= aPrincipal
;
2442 prin
= BasePrincipal::CreateContentPrincipal(aURI
, OriginAttributes());
2446 MOZ_ASSERT(PermissionAvailable(prin
, aType
));
2450 PermissionHashKey
* entry
=
2451 aPrincipal
? GetPermissionHashKey(aPrincipal
, aTypeIndex
, aExactHostMatch
)
2452 : GetPermissionHashKey(aURI
, aOriginAttributes
, aTypeIndex
,
2454 if (!entry
|| (!aIncludingSession
&&
2455 entry
->GetPermission(aTypeIndex
).mNonSessionExpireType
==
2456 nsIPermissionManager::EXPIRE_SESSION
)) {
2460 *aPermission
= aIncludingSession
2461 ? entry
->GetPermission(aTypeIndex
).mPermission
2462 : entry
->GetPermission(aTypeIndex
).mNonSessionPermission
;
2467 // Helper function to filter permissions using a condition function.
2469 nsresult
PermissionManager::GetPermissionEntries(
2470 T aCondition
, nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2472 if (XRE_IsContentProcess()) {
2474 "Iterating over all permissions is not available in the "
2475 "content process, as not all permissions may be available.");
2476 return NS_ERROR_NOT_AVAILABLE
;
2479 EnsureReadCompleted();
2481 for (const PermissionHashKey
& entry
: mPermissionTable
) {
2482 for (const auto& permEntry
: entry
.GetPermissions()) {
2483 // Given how "default" permissions work and the possibility of them being
2484 // overridden with UNKNOWN_ACTION, we might see this value here - but we
2485 // do *not* want to return them via the enumerator.
2486 if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2490 // If the permission is expired, skip it. We're not deleting it here
2491 // because we're iterating over a lot of permissions.
2492 // It will be removed as part of the daily maintenance later.
2493 if (HasExpired(permEntry
.mExpireType
, permEntry
.mExpireTime
)) {
2497 if (!aCondition(permEntry
)) {
2501 nsCOMPtr
<nsIPrincipal
> principal
;
2502 nsresult rv
= GetPrincipalFromOrigin(
2503 entry
.GetKey()->mOrigin
,
2504 IsOAForceStripPermission(mTypeArray
[permEntry
.mType
]),
2505 getter_AddRefs(principal
));
2506 if (NS_FAILED(rv
)) {
2510 RefPtr
<nsIPermission
> permission
= Permission::Create(
2511 principal
, mTypeArray
[permEntry
.mType
], permEntry
.mPermission
,
2512 permEntry
.mExpireType
, permEntry
.mExpireTime
,
2513 permEntry
.mModificationTime
);
2514 if (NS_WARN_IF(!permission
)) {
2517 aResult
.AppendElement(std::move(permission
));
2524 NS_IMETHODIMP
PermissionManager::GetAll(
2525 nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2526 return GetPermissionEntries(
2527 [](const PermissionEntry
& aPermEntry
) { return true; }, aResult
);
2530 NS_IMETHODIMP
PermissionManager::GetAllByTypeSince(
2531 const nsACString
& aPrefix
, int64_t aSince
,
2532 nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2533 // Check that aSince is a reasonable point in time, not in the future
2534 if (aSince
> (PR_Now() / PR_USEC_PER_MSEC
)) {
2535 return NS_ERROR_INVALID_ARG
;
2537 return GetPermissionEntries(
2538 [&](const PermissionEntry
& aPermEntry
) {
2539 return mTypeArray
[aPermEntry
.mType
].Equals(aPrefix
) &&
2540 aSince
<= aPermEntry
.mModificationTime
;
2545 NS_IMETHODIMP
PermissionManager::GetAllWithTypePrefix(
2546 const nsACString
& aPrefix
, nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2547 return GetPermissionEntries(
2548 [&](const PermissionEntry
& aPermEntry
) {
2549 return StringBeginsWith(mTypeArray
[aPermEntry
.mType
], aPrefix
);
2554 NS_IMETHODIMP
PermissionManager::GetAllByTypes(
2555 const nsTArray
<nsCString
>& aTypes
,
2556 nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2557 if (aTypes
.IsEmpty()) {
2561 return GetPermissionEntries(
2562 [&](const PermissionEntry
& aPermEntry
) {
2563 return aTypes
.Contains(mTypeArray
[aPermEntry
.mType
]);
2568 nsresult
PermissionManager::GetAllForPrincipalHelper(
2569 nsIPrincipal
* aPrincipal
, bool aSiteScopePermissions
,
2570 nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2572 RefPtr
<PermissionKey
> key
= PermissionKey::CreateFromPrincipal(
2573 aPrincipal
, false, aSiteScopePermissions
, rv
);
2575 MOZ_ASSERT(NS_FAILED(rv
));
2578 PermissionHashKey
* entry
= mPermissionTable
.GetEntry(key
);
2580 nsTArray
<PermissionEntry
> strippedPerms
;
2581 rv
= GetStripPermsForPrincipal(aPrincipal
, aSiteScopePermissions
,
2583 if (NS_FAILED(rv
)) {
2588 for (const auto& permEntry
: entry
->GetPermissions()) {
2589 // Only return custom permissions
2590 if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2594 // If the permission is expired, skip it. We're not deleting it here
2595 // because we're iterating over a lot of permissions.
2596 // It will be removed as part of the daily maintenance later.
2597 if (HasExpired(permEntry
.mExpireType
, permEntry
.mExpireTime
)) {
2601 // Make sure that we only get site scoped permissions if this
2602 // helper is being invoked for that purpose.
2603 if (aSiteScopePermissions
!=
2604 IsSiteScopedPermission(mTypeArray
[permEntry
.mType
])) {
2608 // Stripped principal permissions overwrite regular ones
2609 // For each permission check if there is a stripped permission we should
2611 PermissionEntry perm
= permEntry
;
2612 nsTArray
<PermissionEntry
>::index_type index
= 0;
2613 for (const auto& strippedPerm
: strippedPerms
) {
2614 if (strippedPerm
.mType
== permEntry
.mType
) {
2615 perm
= strippedPerm
;
2616 strippedPerms
.RemoveElementAt(index
);
2622 RefPtr
<nsIPermission
> permission
= Permission::Create(
2623 aPrincipal
, mTypeArray
[perm
.mType
], perm
.mPermission
,
2624 perm
.mExpireType
, perm
.mExpireTime
, perm
.mModificationTime
);
2625 if (NS_WARN_IF(!permission
)) {
2628 aResult
.AppendElement(permission
);
2632 for (const auto& perm
: strippedPerms
) {
2633 RefPtr
<nsIPermission
> permission
= Permission::Create(
2634 aPrincipal
, mTypeArray
[perm
.mType
], perm
.mPermission
, perm
.mExpireType
,
2635 perm
.mExpireTime
, perm
.mModificationTime
);
2636 if (NS_WARN_IF(!permission
)) {
2639 aResult
.AppendElement(permission
);
2646 PermissionManager::GetAllForPrincipal(
2647 nsIPrincipal
* aPrincipal
, nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2650 EnsureReadCompleted();
2652 MOZ_ASSERT(PermissionAvailable(aPrincipal
, ""_ns
));
2654 // First, append the non-site-scoped permissions.
2655 rv
= GetAllForPrincipalHelper(aPrincipal
, false, aResult
);
2656 NS_ENSURE_SUCCESS(rv
, rv
);
2658 // Second, append the site-scoped permissions.
2659 return GetAllForPrincipalHelper(aPrincipal
, true, aResult
);
2662 NS_IMETHODIMP
PermissionManager::Observe(nsISupports
* aSubject
,
2664 const char16_t
* someData
) {
2665 ENSURE_NOT_CHILD_PROCESS
;
2667 if (!nsCRT::strcmp(aTopic
, "profile-do-change") && !mPermissionsFile
) {
2668 // profile startup is complete, and we didn't have the permissions file
2669 // before; init the db from the new location
2671 } else if (!nsCRT::strcmp(aTopic
, "testonly-reload-permissions-from-disk")) {
2672 // Testing mechanism to reload all permissions from disk. Because the
2673 // permission manager automatically initializes itself at startup, tests
2674 // that directly manipulate the permissions database need some way to reload
2675 // the database for their changes to have any effect. This mechanism was
2676 // introduced when moving the permissions manager from on-demand startup to
2677 // always being initialized. This is not guarded by a pref because it's not
2678 // dangerous to reload permissions from disk, just bad for performance.
2679 RemoveAllFromMemory();
2682 } else if (!nsCRT::strcmp(aTopic
, OBSERVER_TOPIC_IDLE_DAILY
)) {
2683 PerformIdleDailyMaintenance();
2689 nsresult
PermissionManager::RemoveAllModifiedSince(int64_t aModificationTime
) {
2690 ENSURE_NOT_CHILD_PROCESS
;
2691 // Skip remove calls for default permissions to avoid
2692 // creating UNKNOWN_ACTION overrides in AddInternal
2693 return RemovePermissionEntries(
2694 [aModificationTime
](const PermissionEntry
& aPermEntry
) {
2695 return aModificationTime
<= aPermEntry
.mModificationTime
&&
2696 aPermEntry
.mID
!= cIDPermissionIsDefault
;
2701 PermissionManager::RemovePermissionsWithAttributes(const nsAString
& aPattern
) {
2702 ENSURE_NOT_CHILD_PROCESS
;
2703 OriginAttributesPattern pattern
;
2704 if (!pattern
.Init(aPattern
)) {
2705 return NS_ERROR_INVALID_ARG
;
2708 return RemovePermissionsWithAttributes(pattern
);
2711 nsresult
PermissionManager::RemovePermissionsWithAttributes(
2712 OriginAttributesPattern
& aPattern
) {
2713 EnsureReadCompleted();
2715 Vector
<std::tuple
<nsCOMPtr
<nsIPrincipal
>, nsCString
, nsCString
>, 10>
2717 for (const PermissionHashKey
& entry
: mPermissionTable
) {
2718 nsCOMPtr
<nsIPrincipal
> principal
;
2719 nsresult rv
= GetPrincipalFromOrigin(entry
.GetKey()->mOrigin
, false,
2720 getter_AddRefs(principal
));
2721 if (NS_FAILED(rv
)) {
2725 if (!aPattern
.Matches(principal
->OriginAttributesRef())) {
2729 for (const auto& permEntry
: entry
.GetPermissions()) {
2730 if (!permissions
.emplaceBack(principal
, mTypeArray
[permEntry
.mType
],
2731 entry
.GetKey()->mOrigin
)) {
2737 for (auto& i
: permissions
) {
2739 std::get
<0>(i
), std::get
<1>(i
), nsIPermissionManager::UNKNOWN_ACTION
, 0,
2740 nsIPermissionManager::EXPIRE_NEVER
, 0, 0, PermissionManager::eNotify
,
2741 PermissionManager::eWriteToDB
, false, &std::get
<2>(i
));
2747 nsresult
PermissionManager::GetStripPermsForPrincipal(
2748 nsIPrincipal
* aPrincipal
, bool aSiteScopePermissions
,
2749 nsTArray
<PermissionEntry
>& aResult
) {
2751 aResult
.SetCapacity(kStripOAPermissions
.size());
2754 # pragma clang diagnostic push
2755 # pragma clang diagnostic ignored "-Wunreachable-code-return"
2757 // No special strip permissions
2758 if (kStripOAPermissions
.empty()) {
2762 # pragma clang diagnostic pop
2766 // Create a key for the principal, but strip any origin attributes.
2767 // The key must be created aware of whether or not we are scoping to site.
2768 RefPtr
<PermissionKey
> key
= PermissionKey::CreateFromPrincipal(
2769 aPrincipal
, true, aSiteScopePermissions
, rv
);
2771 MOZ_ASSERT(NS_FAILED(rv
));
2775 PermissionHashKey
* hashKey
= mPermissionTable
.GetEntry(key
);
2780 for (const auto& permType
: kStripOAPermissions
) {
2781 // if the permission type's site scoping does not match this function call,
2782 // we don't care about it, so continue.
2783 // As of time of writing, this never happens when aSiteScopePermissions
2784 // is true because there is no common permission between kStripOAPermissions
2785 // and kSiteScopedPermissions
2786 if (aSiteScopePermissions
!= IsSiteScopedPermission(permType
)) {
2789 int32_t index
= GetTypeIndex(permType
, false);
2793 PermissionEntry perm
= hashKey
->GetPermission(index
);
2794 if (perm
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2797 aResult
.AppendElement(perm
);
2803 int32_t PermissionManager::GetTypeIndex(const nsACString
& aType
, bool aAdd
) {
2804 for (uint32_t i
= 0; i
< mTypeArray
.length(); ++i
) {
2805 if (mTypeArray
[i
].Equals(aType
)) {
2811 // Not found, but that is ok - we were just looking.
2815 // This type was not registered before.
2816 // append it to the array, without copy-constructing the string
2817 if (!mTypeArray
.emplaceBack(aType
)) {
2821 return mTypeArray
.length() - 1;
2824 PermissionManager::PermissionHashKey
* PermissionManager::GetPermissionHashKey(
2825 nsIPrincipal
* aPrincipal
, uint32_t aType
, bool aExactHostMatch
) {
2826 EnsureReadCompleted();
2828 MOZ_ASSERT(PermissionAvailable(aPrincipal
, mTypeArray
[aType
]));
2831 RefPtr
<PermissionKey
> key
= PermissionKey::CreateFromPrincipal(
2832 aPrincipal
, IsOAForceStripPermission(mTypeArray
[aType
]),
2833 IsSiteScopedPermission(mTypeArray
[aType
]), rv
);
2838 PermissionHashKey
* entry
= mPermissionTable
.GetEntry(key
);
2841 PermissionEntry permEntry
= entry
->GetPermission(aType
);
2843 // if the entry is expired, remove and keep looking for others.
2844 if (HasExpired(permEntry
.mExpireType
, permEntry
.mExpireTime
)) {
2846 RemoveFromPrincipal(aPrincipal
, mTypeArray
[aType
]);
2847 } else if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2856 // If aExactHostMatch wasn't true, we can check if the base domain has a
2857 // permission entry.
2858 if (!aExactHostMatch
) {
2859 nsCOMPtr
<nsIPrincipal
> principal
= aPrincipal
->GetNextSubDomainPrincipal();
2861 return GetPermissionHashKey(principal
, aType
, aExactHostMatch
);
2865 // No entry, really...
2869 PermissionManager::PermissionHashKey
* PermissionManager::GetPermissionHashKey(
2870 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
, uint32_t aType
,
2871 bool aExactHostMatch
) {
2876 nsCOMPtr
<nsIPrincipal
> principal
;
2877 nsresult rv
= NS_OK
;
2879 rv
= GetPrincipal(aURI
, getter_AddRefs(principal
));
2881 MOZ_ASSERT_IF(NS_SUCCEEDED(rv
),
2882 PermissionAvailable(principal
, mTypeArray
[aType
]));
2887 RefPtr
<PermissionKey
> key
;
2889 if (aOriginAttributes
) {
2890 key
= PermissionKey::CreateFromURIAndOriginAttributes(
2891 aURI
, aOriginAttributes
, IsOAForceStripPermission(mTypeArray
[aType
]),
2894 key
= PermissionKey::CreateFromURI(aURI
, rv
);
2901 PermissionHashKey
* entry
= mPermissionTable
.GetEntry(key
);
2904 PermissionEntry permEntry
= entry
->GetPermission(aType
);
2906 // if the entry is expired, remove and keep looking for others.
2907 if (HasExpired(permEntry
.mExpireType
, permEntry
.mExpireTime
)) {
2909 // If we need to remove a permission we mint a principal. This is a bit
2910 // inefficient, but hopefully this code path isn't super common.
2911 nsCOMPtr
<nsIPrincipal
> principal
;
2913 nsresult rv
= GetPrincipal(aURI
, getter_AddRefs(principal
));
2914 if (NS_WARN_IF(NS_FAILED(rv
))) {
2918 RemoveFromPrincipal(principal
, mTypeArray
[aType
]);
2919 } else if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2928 // If aExactHostMatch wasn't true, we can check if the base domain has a
2929 // permission entry.
2930 if (!aExactHostMatch
) {
2931 nsCOMPtr
<nsIURI
> uri
;
2933 uri
= GetNextSubDomainURI(aURI
);
2936 return GetPermissionHashKey(uri
, aOriginAttributes
, aType
,
2941 // No entry, really...
2945 nsresult
PermissionManager::RemoveAllFromMemory() {
2948 mPermissionTable
.Clear();
2953 // wrapper function for mangling (host,type,perm,expireType,expireTime)
2954 // set into an nsIPermission.
2955 void PermissionManager::NotifyObserversWithPermission(
2956 nsIPrincipal
* aPrincipal
, const nsACString
& aType
, uint32_t aPermission
,
2957 uint32_t aExpireType
, int64_t aExpireTime
, int64_t aModificationTime
,
2958 const char16_t
* aData
) {
2959 nsCOMPtr
<nsIPermission
> permission
=
2960 Permission::Create(aPrincipal
, aType
, aPermission
, aExpireType
,
2961 aExpireTime
, aModificationTime
);
2962 if (permission
) NotifyObservers(permission
, aData
);
2965 // notify observers that the permission list changed. there are four possible
2966 // values for aData:
2967 // "deleted" means a permission was deleted. aPermission is the deleted
2968 // permission. "added" means a permission was added. aPermission is the added
2969 // permission. "changed" means a permission was altered. aPermission is the new
2970 // permission. "cleared" means the entire permission list was cleared.
2971 // aPermission is null.
2972 void PermissionManager::NotifyObservers(nsIPermission
* aPermission
,
2973 const char16_t
* aData
) {
2974 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
2975 if (observerService
)
2976 observerService
->NotifyObservers(aPermission
, kPermissionChangeNotification
,
2980 nsresult
PermissionManager::Read(const MonitorAutoLock
& aProofOfLock
) {
2981 ENSURE_NOT_CHILD_PROCESS
;
2983 MOZ_ASSERT(!NS_IsMainThread());
2984 auto data
= mThreadBoundData
.Access();
2988 nsCOMPtr
<mozIStorageStatement
> stmt
;
2990 // Let's retrieve the last used ID.
2991 rv
= data
->mDBConn
->CreateStatement(
2992 nsLiteralCString("SELECT MAX(id) FROM moz_perms"), getter_AddRefs(stmt
));
2993 NS_ENSURE_SUCCESS(rv
, rv
);
2995 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
2996 int64_t id
= stmt
->AsInt64(0);
3000 rv
= data
->mDBConn
->CreateStatement(
3002 "SELECT id, origin, type, permission, expireType, "
3003 "expireTime, modificationTime "
3004 "FROM moz_perms WHERE expireType != ?1 OR expireTime > ?2"),
3005 getter_AddRefs(stmt
));
3006 NS_ENSURE_SUCCESS(rv
, rv
);
3008 rv
= stmt
->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME
);
3009 NS_ENSURE_SUCCESS(rv
, rv
);
3011 rv
= stmt
->BindInt64ByIndex(1, EXPIRY_NOW
);
3012 NS_ENSURE_SUCCESS(rv
, rv
);
3014 bool readError
= false;
3016 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
3019 // explicitly set our entry id counter for use in AddInternal(),
3020 // and keep track of the largest id so we know where to pick up.
3021 entry
.mId
= stmt
->AsInt64(0);
3022 MOZ_ASSERT(entry
.mId
<= mLargestID
);
3024 rv
= stmt
->GetUTF8String(1, entry
.mOrigin
);
3025 if (NS_FAILED(rv
)) {
3030 rv
= stmt
->GetUTF8String(2, entry
.mType
);
3031 if (NS_FAILED(rv
)) {
3036 entry
.mPermission
= stmt
->AsInt32(3);
3037 entry
.mExpireType
= stmt
->AsInt32(4);
3039 // convert into int64_t values (milliseconds)
3040 entry
.mExpireTime
= stmt
->AsInt64(5);
3041 entry
.mModificationTime
= stmt
->AsInt64(6);
3043 entry
.mFromMigration
= false;
3045 mReadEntries
.AppendElement(entry
);
3049 NS_ERROR("Error occured while reading the permissions database!");
3050 return NS_ERROR_FAILURE
;
3056 void PermissionManager::CompleteMigrations() {
3057 MOZ_ASSERT(NS_IsMainThread());
3058 MOZ_ASSERT(mState
== eReady
);
3062 nsTArray
<MigrationEntry
> entries
;
3064 MonitorAutoLock
lock(mMonitor
);
3065 entries
= std::move(mMigrationEntries
);
3068 for (const MigrationEntry
& entry
: entries
) {
3069 rv
= UpgradeHostToOriginAndInsert(
3070 entry
.mHost
, entry
.mType
, entry
.mPermission
, entry
.mExpireType
,
3071 entry
.mExpireTime
, entry
.mModificationTime
, entry
.mIsInBrowserElement
,
3072 [&](const nsACString
& aOrigin
, const nsCString
& aType
,
3073 uint32_t aPermission
, uint32_t aExpireType
, int64_t aExpireTime
,
3074 int64_t aModificationTime
) {
3075 MaybeAddReadEntryFromMigration(aOrigin
, aType
, aPermission
,
3076 aExpireType
, aExpireTime
,
3077 aModificationTime
, entry
.mId
);
3080 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
3084 void PermissionManager::CompleteRead() {
3085 MOZ_ASSERT(NS_IsMainThread());
3086 MOZ_ASSERT(mState
== eReady
);
3090 nsTArray
<ReadEntry
> entries
;
3092 MonitorAutoLock
lock(mMonitor
);
3093 entries
= std::move(mReadEntries
);
3096 for (const ReadEntry
& entry
: entries
) {
3097 nsCOMPtr
<nsIPrincipal
> principal
;
3098 rv
= GetPrincipalFromOrigin(entry
.mOrigin
,
3099 IsOAForceStripPermission(entry
.mType
),
3100 getter_AddRefs(principal
));
3101 if (NS_WARN_IF(NS_FAILED(rv
))) {
3105 DBOperationType op
= entry
.mFromMigration
? eWriteToDB
: eNoDBOperation
;
3107 rv
= AddInternal(principal
, entry
.mType
, entry
.mPermission
, entry
.mId
,
3108 entry
.mExpireType
, entry
.mExpireTime
,
3109 entry
.mModificationTime
, eDontNotify
, op
, false,
3111 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
3115 void PermissionManager::MaybeAddReadEntryFromMigration(
3116 const nsACString
& aOrigin
, const nsCString
& aType
, uint32_t aPermission
,
3117 uint32_t aExpireType
, int64_t aExpireTime
, int64_t aModificationTime
,
3119 MonitorAutoLock
lock(mMonitor
);
3121 // We convert a migration to a ReadEntry only if we don't have an existing
3122 // ReadEntry for the same origin + type.
3123 for (const ReadEntry
& entry
: mReadEntries
) {
3124 if (entry
.mOrigin
== aOrigin
&& entry
.mType
== aType
) {
3131 entry
.mOrigin
= aOrigin
;
3132 entry
.mType
= aType
;
3133 entry
.mPermission
= aPermission
;
3134 entry
.mExpireType
= aExpireType
;
3135 entry
.mExpireTime
= aExpireTime
;
3136 entry
.mModificationTime
= aModificationTime
;
3137 entry
.mFromMigration
= true;
3139 mReadEntries
.AppendElement(entry
);
3142 void PermissionManager::UpdateDB(OperationType aOp
, int64_t aID
,
3143 const nsACString
& aOrigin
,
3144 const nsACString
& aType
, uint32_t aPermission
,
3145 uint32_t aExpireType
, int64_t aExpireTime
,
3146 int64_t aModificationTime
) {
3147 ENSURE_NOT_CHILD_PROCESS_NORET
;
3149 MOZ_ASSERT(NS_IsMainThread());
3150 EnsureReadCompleted();
3152 nsCString
origin(aOrigin
);
3153 nsCString
type(aType
);
3155 RefPtr
<PermissionManager
> self
= this;
3156 mThread
->Dispatch(NS_NewRunnableFunction(
3157 "PermissionManager::UpdateDB",
3158 [self
, aOp
, aID
, origin
, type
, aPermission
, aExpireType
, aExpireTime
,
3159 aModificationTime
] {
3162 auto data
= self
->mThreadBoundData
.Access();
3164 if (self
->mState
== eClosed
|| !data
->mDBConn
) {
3165 // no statement is ok - just means we don't have a profile
3169 mozIStorageStatement
* stmt
= nullptr;
3171 case eOperationAdding
: {
3172 stmt
= data
->mStmtInsert
;
3174 rv
= stmt
->BindInt64ByIndex(0, aID
);
3175 if (NS_FAILED(rv
)) break;
3177 rv
= stmt
->BindUTF8StringByIndex(1, origin
);
3178 if (NS_FAILED(rv
)) break;
3180 rv
= stmt
->BindUTF8StringByIndex(2, type
);
3181 if (NS_FAILED(rv
)) break;
3183 rv
= stmt
->BindInt32ByIndex(3, aPermission
);
3184 if (NS_FAILED(rv
)) break;
3186 rv
= stmt
->BindInt32ByIndex(4, aExpireType
);
3187 if (NS_FAILED(rv
)) break;
3189 rv
= stmt
->BindInt64ByIndex(5, aExpireTime
);
3190 if (NS_FAILED(rv
)) break;
3192 rv
= stmt
->BindInt64ByIndex(6, aModificationTime
);
3196 case eOperationRemoving
: {
3197 stmt
= data
->mStmtDelete
;
3198 rv
= stmt
->BindInt64ByIndex(0, aID
);
3202 case eOperationChanging
: {
3203 stmt
= data
->mStmtUpdate
;
3205 rv
= stmt
->BindInt64ByIndex(0, aID
);
3206 if (NS_FAILED(rv
)) break;
3208 rv
= stmt
->BindInt32ByIndex(1, aPermission
);
3209 if (NS_FAILED(rv
)) break;
3211 rv
= stmt
->BindInt32ByIndex(2, aExpireType
);
3212 if (NS_FAILED(rv
)) break;
3214 rv
= stmt
->BindInt64ByIndex(3, aExpireTime
);
3215 if (NS_FAILED(rv
)) break;
3217 rv
= stmt
->BindInt64ByIndex(4, aModificationTime
);
3222 MOZ_ASSERT_UNREACHABLE("need a valid operation in UpdateDB()!");
3223 rv
= NS_ERROR_UNEXPECTED
;
3228 if (NS_FAILED(rv
)) {
3229 NS_WARNING("db change failed!");
3233 rv
= stmt
->Execute();
3234 MOZ_ASSERT(NS_SUCCEEDED(rv
));
3238 bool PermissionManager::GetPermissionsFromOriginOrKey(
3239 const nsACString
& aOrigin
, const nsACString
& aKey
,
3240 nsTArray
<IPC::Permission
>& aPerms
) {
3241 EnsureReadCompleted();
3244 if (NS_WARN_IF(XRE_IsContentProcess())) {
3248 for (const PermissionHashKey
& entry
: mPermissionTable
) {
3249 nsAutoCString permissionKey
;
3250 if (aOrigin
.IsEmpty()) {
3251 // We can't check for individual OA strip perms here.
3252 // Don't force strip origin attributes.
3253 GetKeyForOrigin(entry
.GetKey()->mOrigin
, false, false, permissionKey
);
3255 // If the keys don't match, and we aren't getting the default "" key, then
3256 // we can exit early. We have to keep looking if we're getting the default
3257 // key, as we may see a preload permission which should be transmitted.
3258 if (aKey
!= permissionKey
&& !aKey
.IsEmpty()) {
3261 } else if (aOrigin
!= entry
.GetKey()->mOrigin
) {
3262 // If the origins don't match, then we can exit early. We have to keep
3263 // looking if we're getting the default origin, as we may see a preload
3264 // permission which should be transmitted.
3268 for (const auto& permEntry
: entry
.GetPermissions()) {
3269 // Given how "default" permissions work and the possibility of them
3270 // being overridden with UNKNOWN_ACTION, we might see this value here -
3271 // but we do not want to send it to the content process.
3272 if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
3276 bool isPreload
= IsPreloadPermission(mTypeArray
[permEntry
.mType
]);
3278 if (aOrigin
.IsEmpty()) {
3279 shouldAppend
= (isPreload
&& aKey
.IsEmpty()) ||
3280 (!isPreload
&& aKey
== permissionKey
);
3282 shouldAppend
= (!isPreload
&& aOrigin
== entry
.GetKey()->mOrigin
);
3285 aPerms
.AppendElement(
3286 IPC::Permission(entry
.GetKey()->mOrigin
,
3287 mTypeArray
[permEntry
.mType
], permEntry
.mPermission
,
3288 permEntry
.mExpireType
, permEntry
.mExpireTime
));
3296 void PermissionManager::SetPermissionsWithKey(
3297 const nsACString
& aPermissionKey
, nsTArray
<IPC::Permission
>& aPerms
) {
3298 if (NS_WARN_IF(XRE_IsParentProcess())) {
3302 RefPtr
<GenericNonExclusivePromise::Private
> promise
;
3304 mPermissionKeyPromiseMap
.Get(aPermissionKey
, getter_AddRefs(promise
));
3306 MOZ_ASSERT(foundKey
);
3307 // NOTE: This will resolve asynchronously, so we can mark it as resolved
3308 // now, and be confident that we will have filled in the database before any
3310 promise
->Resolve(true, __func__
);
3311 } else if (foundKey
) {
3312 // NOTE: We shouldn't be sent two InitializePermissionsWithKey for the same
3313 // key, but it's possible.
3316 mPermissionKeyPromiseMap
.InsertOrUpdate(
3317 aPermissionKey
, RefPtr
<GenericNonExclusivePromise::Private
>{});
3319 // Add the permissions locally to our process
3320 for (IPC::Permission
& perm
: aPerms
) {
3321 nsCOMPtr
<nsIPrincipal
> principal
;
3323 GetPrincipalFromOrigin(perm
.origin
, IsOAForceStripPermission(perm
.type
),
3324 getter_AddRefs(principal
));
3325 if (NS_WARN_IF(NS_FAILED(rv
))) {
3330 nsAutoCString permissionKey
;
3331 GetKeyForPermission(principal
, perm
.type
, permissionKey
);
3332 MOZ_ASSERT(permissionKey
== aPermissionKey
,
3333 "The permission keys which were sent over should match!");
3336 // The child process doesn't care about modification times - it neither
3337 // reads nor writes, nor removes them based on the date - so 0 (which
3338 // will end up as now()) is fine.
3339 uint64_t modificationTime
= 0;
3340 AddInternal(principal
, perm
.type
, perm
.capability
, 0, perm
.expireType
,
3341 perm
.expireTime
, modificationTime
, eNotify
, eNoDBOperation
,
3342 true /* ignoreSessionPermissions */);
3347 nsresult
PermissionManager::GetKeyForOrigin(const nsACString
& aOrigin
,
3349 bool aSiteScopePermissions
,
3353 // We only key origins for http, https, and ftp URIs. All origins begin with
3354 // the URL which they apply to, which means that they should begin with their
3355 // scheme in the case where they are one of these interesting URIs. We don't
3356 // want to actually parse the URL here however, because this can be called on
3358 if (!StringBeginsWith(aOrigin
, "http:"_ns
) &&
3359 !StringBeginsWith(aOrigin
, "https:"_ns
) &&
3360 !StringBeginsWith(aOrigin
, "ftp:"_ns
)) {
3364 // We need to look at the originAttributes if they are present, to make sure
3365 // to remove any which we don't want. We put the rest of the origin, not
3366 // including the attributes, into the key.
3367 OriginAttributes attrs
;
3368 if (!attrs
.PopulateFromOrigin(aOrigin
, aKey
)) {
3373 MaybeStripOriginAttributes(aForceStripOA
, attrs
);
3376 // Parse the origin string into a principal, and extract some useful
3377 // information from it for assertions.
3378 nsCOMPtr
<nsIPrincipal
> dbgPrincipal
;
3379 MOZ_ALWAYS_SUCCEEDS(GetPrincipalFromOrigin(aOrigin
, aForceStripOA
,
3380 getter_AddRefs(dbgPrincipal
)));
3381 MOZ_ASSERT(dbgPrincipal
->SchemeIs("http") ||
3382 dbgPrincipal
->SchemeIs("https") || dbgPrincipal
->SchemeIs("ftp"));
3383 MOZ_ASSERT(dbgPrincipal
->OriginAttributesRef() == attrs
);
3386 // If it is needed, turn the origin into its site-origin
3387 if (aSiteScopePermissions
) {
3388 nsCOMPtr
<nsIURI
> uri
;
3389 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aKey
);
3390 if (!NS_WARN_IF(NS_FAILED(rv
))) {
3392 rv
= nsEffectiveTLDService::GetInstance()->GetSite(uri
, site
);
3393 if (!NS_WARN_IF(NS_FAILED(rv
))) {
3399 // Append the stripped suffix to the output origin key.
3400 nsAutoCString suffix
;
3401 attrs
.CreateSuffix(suffix
);
3402 aKey
.Append(suffix
);
3408 nsresult
PermissionManager::GetKeyForPrincipal(nsIPrincipal
* aPrincipal
,
3410 bool aSiteScopePermissions
,
3412 nsAutoCString origin
;
3413 nsresult rv
= aPrincipal
->GetOrigin(origin
);
3414 if (NS_WARN_IF(NS_FAILED(rv
))) {
3418 return GetKeyForOrigin(origin
, aForceStripOA
, aSiteScopePermissions
, aKey
);
3422 nsresult
PermissionManager::GetKeyForPermission(nsIPrincipal
* aPrincipal
,
3423 const nsACString
& aType
,
3425 // Preload permissions have the "" key.
3426 if (IsPreloadPermission(aType
)) {
3431 return GetKeyForPrincipal(aPrincipal
, IsOAForceStripPermission(aType
),
3432 IsSiteScopedPermission(aType
), aKey
);
3436 nsTArray
<std::pair
<nsCString
, nsCString
>>
3437 PermissionManager::GetAllKeysForPrincipal(nsIPrincipal
* aPrincipal
) {
3438 MOZ_ASSERT(aPrincipal
);
3440 nsTArray
<std::pair
<nsCString
, nsCString
>> pairs
;
3441 nsCOMPtr
<nsIPrincipal
> prin
= aPrincipal
;
3444 // Add the pair to the list
3445 std::pair
<nsCString
, nsCString
>* pair
=
3446 pairs
.AppendElement(std::make_pair(""_ns
, ""_ns
));
3447 // We can't check for individual OA strip perms here.
3448 // Don't force strip origin attributes.
3449 GetKeyForPrincipal(prin
, false, false, pair
->first
);
3451 // On origins with a derived key set to an empty string
3452 // (basically any non-web URI scheme), we want to make sure
3453 // to return earlier, and leave [("", "")] as the resulting
3454 // pairs (but still run the same debug assertions near the
3455 // end of this method).
3456 if (pair
->first
.IsEmpty()) {
3460 Unused
<< GetOriginFromPrincipal(prin
, false, pair
->second
);
3461 prin
= prin
->GetNextSubDomainPrincipal();
3462 // Get the next subdomain principal and loop back around.
3465 MOZ_ASSERT(pairs
.Length() >= 1,
3466 "Every principal should have at least one pair item.");
3470 bool PermissionManager::PermissionAvailable(nsIPrincipal
* aPrincipal
,
3471 const nsACString
& aType
) {
3472 EnsureReadCompleted();
3474 if (XRE_IsContentProcess()) {
3475 nsAutoCString permissionKey
;
3476 // NOTE: GetKeyForPermission accepts a null aType.
3477 GetKeyForPermission(aPrincipal
, aType
, permissionKey
);
3479 // If we have a pending promise for the permission key in question, we don't
3480 // have the permission available, so report a warning and return false.
3481 RefPtr
<GenericNonExclusivePromise::Private
> promise
;
3482 if (!mPermissionKeyPromiseMap
.Get(permissionKey
, getter_AddRefs(promise
)) ||
3484 // Emit a useful diagnostic warning with the permissionKey for the process
3485 // which hasn't received permissions yet.
3486 NS_WARNING(nsPrintfCString("This content process hasn't received the "
3487 "permissions for %s yet",
3488 permissionKey
.get())
3496 void PermissionManager::WhenPermissionsAvailable(nsIPrincipal
* aPrincipal
,
3497 nsIRunnable
* aRunnable
) {
3498 MOZ_ASSERT(aRunnable
);
3500 if (!XRE_IsContentProcess()) {
3505 nsTArray
<RefPtr
<GenericNonExclusivePromise
>> promises
;
3506 for (auto& pair
: GetAllKeysForPrincipal(aPrincipal
)) {
3507 RefPtr
<GenericNonExclusivePromise::Private
> promise
;
3508 if (!mPermissionKeyPromiseMap
.Get(pair
.first
, getter_AddRefs(promise
))) {
3509 // In this case we have found a permission which isn't available in the
3510 // content process and hasn't been requested yet. We need to create a new
3511 // promise, and send the request to the parent (if we have not already
3513 promise
= new GenericNonExclusivePromise::Private(__func__
);
3514 mPermissionKeyPromiseMap
.InsertOrUpdate(pair
.first
, RefPtr
{promise
});
3518 promises
.AppendElement(std::move(promise
));
3522 // If all of our permissions are available, immediately run the runnable. This
3523 // avoids any extra overhead during fetch interception which is performance
3525 if (promises
.IsEmpty()) {
3530 auto* thread
= AbstractThread::MainThread();
3532 RefPtr
<nsIRunnable
> runnable
= aRunnable
;
3533 GenericNonExclusivePromise::All(thread
, promises
)
3535 thread
, __func__
, [runnable
]() { runnable
->Run(); },
3538 "PermissionManager permission promise rejected. We're "
3539 "probably shutting down.");
3543 void PermissionManager::EnsureReadCompleted() {
3544 MOZ_ASSERT(NS_IsMainThread());
3546 if (mState
== eInitializing
) {
3547 MonitorAutoLock
lock(mMonitor
);
3549 while (mState
== eInitializing
) {
3556 MOZ_CRASH("This state is impossible!");
3558 case eDBInitialized
:
3561 CompleteMigrations();
3562 ImportLatestDefaults();
3574 MOZ_CRASH("Invalid state");
3578 already_AddRefed
<nsIInputStream
> PermissionManager::GetDefaultsInputStream() {
3579 MOZ_ASSERT(NS_IsMainThread());
3581 nsAutoCString defaultsURL
;
3582 Preferences::GetCString(kDefaultsUrlPrefName
, defaultsURL
);
3583 if (defaultsURL
.IsEmpty()) { // == Don't use built-in permissions.
3587 nsCOMPtr
<nsIURI
> defaultsURI
;
3588 nsresult rv
= NS_NewURI(getter_AddRefs(defaultsURI
), defaultsURL
);
3589 NS_ENSURE_SUCCESS(rv
, nullptr);
3591 nsCOMPtr
<nsIChannel
> channel
;
3592 rv
= NS_NewChannel(getter_AddRefs(channel
), defaultsURI
,
3593 nsContentUtils::GetSystemPrincipal(),
3594 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
3595 nsIContentPolicy::TYPE_OTHER
);
3596 NS_ENSURE_SUCCESS(rv
, nullptr);
3598 nsCOMPtr
<nsIInputStream
> inputStream
;
3599 rv
= channel
->Open(getter_AddRefs(inputStream
));
3600 NS_ENSURE_SUCCESS(rv
, nullptr);
3602 return inputStream
.forget();
3605 void PermissionManager::ConsumeDefaultsInputStream(
3606 nsIInputStream
* aInputStream
, const MonitorAutoLock
& aProofOfLock
) {
3607 MOZ_ASSERT(!NS_IsMainThread());
3609 constexpr char kMatchTypeHost
[] = "host";
3610 constexpr char kMatchTypeOrigin
[] = "origin";
3612 mDefaultEntries
.Clear();
3614 if (!aInputStream
) {
3621 * matchtype \t type \t permission \t host
3622 * Only "host" is supported for matchtype
3623 * type is a string that identifies the type of permission (e.g. "cookie")
3624 * permission is an integer between 1 and 15
3627 // Ideally we'd do this with nsILineInputString, but this is called with an
3628 // nsIInputStream that comes from a resource:// URI, which doesn't support
3629 // that interface. So NS_ReadLine to the rescue...
3630 nsLineBuffer
<char> lineBuffer
;
3634 rv
= NS_ReadLine(aInputStream
, &lineBuffer
, line
, &isMore
);
3635 NS_ENSURE_SUCCESS_VOID(rv
);
3637 if (line
.IsEmpty() || line
.First() == '#') {
3641 nsTArray
<nsCString
> lineArray
;
3643 // Split the line at tabs
3644 ParseString(line
, '\t', lineArray
);
3646 if (lineArray
.Length() != 4) {
3650 nsresult error
= NS_OK
;
3651 uint32_t permission
= lineArray
[2].ToInteger(&error
);
3652 if (NS_FAILED(error
)) {
3656 DefaultEntry::Op op
;
3658 if (lineArray
[0].EqualsLiteral(kMatchTypeHost
)) {
3659 op
= DefaultEntry::eImportMatchTypeHost
;
3660 } else if (lineArray
[0].EqualsLiteral(kMatchTypeOrigin
)) {
3661 op
= DefaultEntry::eImportMatchTypeOrigin
;
3666 DefaultEntry
* entry
= mDefaultEntries
.AppendElement();
3670 entry
->mPermission
= permission
;
3671 entry
->mHostOrOrigin
= lineArray
[3];
3672 entry
->mType
= lineArray
[1];
3676 // ImportLatestDefaults will import the latest default cookies read during the
3677 // last DB initialization.
3678 nsresult
PermissionManager::ImportLatestDefaults() {
3679 MOZ_ASSERT(NS_IsMainThread());
3680 MOZ_ASSERT(mState
== eReady
);
3684 MonitorAutoLock
lock(mMonitor
);
3686 for (const DefaultEntry
& entry
: mDefaultEntries
) {
3687 if (entry
.mOp
== DefaultEntry::eImportMatchTypeHost
) {
3688 // the import file format doesn't handle modification times, so we use
3689 // 0, which AddInternal will convert to now()
3690 int64_t modificationTime
= 0;
3692 rv
= UpgradeHostToOriginAndInsert(
3693 entry
.mHostOrOrigin
, entry
.mType
, entry
.mPermission
,
3694 nsIPermissionManager::EXPIRE_NEVER
, 0, modificationTime
, false,
3695 [&](const nsACString
& aOrigin
, const nsCString
& aType
,
3696 uint32_t aPermission
, uint32_t aExpireType
, int64_t aExpireTime
,
3697 int64_t aModificationTime
) {
3698 nsCOMPtr
<nsIPrincipal
> principal
;
3700 GetPrincipalFromOrigin(aOrigin
, IsOAForceStripPermission(aType
),
3701 getter_AddRefs(principal
));
3702 NS_ENSURE_SUCCESS(rv
, rv
);
3704 AddInternal(principal
, aType
, aPermission
,
3705 cIDPermissionIsDefault
, aExpireType
, aExpireTime
,
3706 aModificationTime
, PermissionManager::eDontNotify
,
3707 PermissionManager::eNoDBOperation
, false, &aOrigin
);
3708 NS_ENSURE_SUCCESS(rv
, rv
);
3710 if (StaticPrefs::permissions_isolateBy_privateBrowsing()) {
3711 // Also import the permission for private browsing.
3712 OriginAttributes attrs
=
3713 OriginAttributes(principal
->OriginAttributesRef());
3714 attrs
.mPrivateBrowsingId
= 1;
3715 nsCOMPtr
<nsIPrincipal
> pbPrincipal
=
3716 BasePrincipal::Cast(principal
)->CloneForcingOriginAttributes(
3720 pbPrincipal
, aType
, aPermission
, cIDPermissionIsDefault
,
3721 aExpireType
, aExpireTime
, aModificationTime
,
3722 PermissionManager::eDontNotify
,
3723 PermissionManager::eNoDBOperation
, false, &aOrigin
);
3724 NS_ENSURE_SUCCESS(rv
, rv
);
3730 if (NS_FAILED(rv
)) {
3731 NS_WARNING("There was a problem importing a host permission");
3736 MOZ_ASSERT(entry
.mOp
== DefaultEntry::eImportMatchTypeOrigin
);
3738 nsCOMPtr
<nsIPrincipal
> principal
;
3739 rv
= GetPrincipalFromOrigin(entry
.mHostOrOrigin
,
3740 IsOAForceStripPermission(entry
.mType
),
3741 getter_AddRefs(principal
));
3742 if (NS_FAILED(rv
)) {
3743 NS_WARNING("Couldn't import an origin permission - malformed origin");
3747 // the import file format doesn't handle modification times, so we use
3748 // 0, which AddInternal will convert to now()
3749 int64_t modificationTime
= 0;
3751 rv
= AddInternal(principal
, entry
.mType
, entry
.mPermission
,
3752 cIDPermissionIsDefault
, nsIPermissionManager::EXPIRE_NEVER
,
3753 0, modificationTime
, eDontNotify
, eNoDBOperation
);
3754 if (NS_FAILED(rv
)) {
3755 NS_WARNING("There was a problem importing an origin permission");
3758 if (StaticPrefs::permissions_isolateBy_privateBrowsing()) {
3759 // Also import the permission for private browsing.
3760 OriginAttributes attrs
=
3761 OriginAttributes(principal
->OriginAttributesRef());
3762 attrs
.mPrivateBrowsingId
= 1;
3763 nsCOMPtr
<nsIPrincipal
> pbPrincipal
=
3764 BasePrincipal::Cast(principal
)->CloneForcingOriginAttributes(attrs
);
3765 // May return nullptr if clone fails.
3766 NS_ENSURE_TRUE(pbPrincipal
, NS_ERROR_FAILURE
);
3768 rv
= AddInternal(pbPrincipal
, entry
.mType
, entry
.mPermission
,
3769 cIDPermissionIsDefault
,
3770 nsIPermissionManager::EXPIRE_NEVER
, 0, modificationTime
,
3771 eDontNotify
, eNoDBOperation
);
3772 if (NS_FAILED(rv
)) {
3774 "There was a problem importing an origin permission for private "
3784 * Perform the early steps of a permission check and determine whether we need
3785 * to call CommonTestPermissionInternal() for the actual permission check.
3787 * @param aPrincipal optional principal argument to check the permission for,
3788 * can be nullptr if we aren't performing a principal-based
3790 * @param aTypeIndex if the caller isn't sure what the index of the permission
3791 * type to check for is in the mTypeArray member variable,
3792 * it should pass -1, otherwise this would be the index of
3793 * the type inside mTypeArray. This would only be something
3794 * other than -1 in recursive invocations of this function.
3795 * @param aType the permission type to test.
3796 * @param aPermission out argument which will be a permission type that we
3797 * will return from this function once the function is
3799 * @param aDefaultPermission the default permission to be used if we can't
3800 * determine the result of the permission check.
3801 * @param aDefaultPermissionIsValid whether the previous argument contains a
3803 * @param aExactHostMatch whether to look for the exact host name or also for
3804 * subdomains that can have the same permission.
3805 * @param aIncludingSession whether to include session permissions when
3806 * testing for the permission.
3808 PermissionManager::TestPreparationResult
3809 PermissionManager::CommonPrepareToTestPermission(
3810 nsIPrincipal
* aPrincipal
, int32_t aTypeIndex
, const nsACString
& aType
,
3811 uint32_t* aPermission
, uint32_t aDefaultPermission
,
3812 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
3813 bool aIncludingSession
) {
3814 auto* basePrin
= BasePrincipal::Cast(aPrincipal
);
3815 if (basePrin
&& basePrin
->IsSystemPrincipal()) {
3816 *aPermission
= ALLOW_ACTION
;
3817 return AsVariant(NS_OK
);
3820 EnsureReadCompleted();
3822 // For some permissions, query the default from a pref. We want to avoid
3823 // doing this for all permissions so that permissions can opt into having
3824 // the pref lookup overhead on each call.
3825 int32_t defaultPermission
=
3826 aDefaultPermissionIsValid
? aDefaultPermission
: UNKNOWN_ACTION
;
3827 if (!aDefaultPermissionIsValid
&& HasDefaultPref(aType
)) {
3828 Unused
<< mDefaultPrefBranch
->GetIntPref(PromiseFlatCString(aType
).get(),
3829 &defaultPermission
);
3833 *aPermission
= defaultPermission
;
3836 aTypeIndex
== -1 ? GetTypeIndex(aType
, false) : aTypeIndex
;
3838 // For expanded principals, we want to iterate over the allowlist and see
3839 // if the permission is granted for any of them.
3840 if (basePrin
&& basePrin
->Is
<ExpandedPrincipal
>()) {
3841 auto ep
= basePrin
->As
<ExpandedPrincipal
>();
3842 for (auto& prin
: ep
->AllowList()) {
3845 CommonTestPermission(prin
, typeIndex
, aType
, &perm
, defaultPermission
,
3846 true, aExactHostMatch
, aIncludingSession
);
3847 if (NS_WARN_IF(NS_FAILED(rv
))) {
3848 return AsVariant(rv
);
3851 if (perm
== nsIPermissionManager::ALLOW_ACTION
) {
3852 *aPermission
= perm
;
3853 return AsVariant(NS_OK
);
3855 if (perm
== nsIPermissionManager::PROMPT_ACTION
) {
3856 // Store it, but keep going to see if we can do better.
3857 *aPermission
= perm
;
3861 return AsVariant(NS_OK
);
3864 // If type == -1, the type isn't known, just signal that we are done.
3865 if (typeIndex
== -1) {
3866 return AsVariant(NS_OK
);
3869 return AsVariant(typeIndex
);
3872 // If aTypeIndex is passed -1, we try to inder the type index from aType.
3873 nsresult
PermissionManager::CommonTestPermission(
3874 nsIPrincipal
* aPrincipal
, int32_t aTypeIndex
, const nsACString
& aType
,
3875 uint32_t* aPermission
, uint32_t aDefaultPermission
,
3876 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
3877 bool aIncludingSession
) {
3878 auto preparationResult
= CommonPrepareToTestPermission(
3879 aPrincipal
, aTypeIndex
, aType
, aPermission
, aDefaultPermission
,
3880 aDefaultPermissionIsValid
, aExactHostMatch
, aIncludingSession
);
3881 if (preparationResult
.is
<nsresult
>()) {
3882 return preparationResult
.as
<nsresult
>();
3885 return CommonTestPermissionInternal(
3886 aPrincipal
, nullptr, nullptr, preparationResult
.as
<int32_t>(), aType
,
3887 aPermission
, aExactHostMatch
, aIncludingSession
);
3890 // If aTypeIndex is passed -1, we try to inder the type index from aType.
3891 nsresult
PermissionManager::CommonTestPermission(
3892 nsIURI
* aURI
, int32_t aTypeIndex
, const nsACString
& aType
,
3893 uint32_t* aPermission
, uint32_t aDefaultPermission
,
3894 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
3895 bool aIncludingSession
) {
3896 auto preparationResult
= CommonPrepareToTestPermission(
3897 nullptr, aTypeIndex
, aType
, aPermission
, aDefaultPermission
,
3898 aDefaultPermissionIsValid
, aExactHostMatch
, aIncludingSession
);
3899 if (preparationResult
.is
<nsresult
>()) {
3900 return preparationResult
.as
<nsresult
>();
3903 return CommonTestPermissionInternal(
3904 nullptr, aURI
, nullptr, preparationResult
.as
<int32_t>(), aType
,
3905 aPermission
, aExactHostMatch
, aIncludingSession
);
3908 nsresult
PermissionManager::CommonTestPermission(
3909 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
, int32_t aTypeIndex
,
3910 const nsACString
& aType
, uint32_t* aPermission
, uint32_t aDefaultPermission
,
3911 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
3912 bool aIncludingSession
) {
3913 auto preparationResult
= CommonPrepareToTestPermission(
3914 nullptr, aTypeIndex
, aType
, aPermission
, aDefaultPermission
,
3915 aDefaultPermissionIsValid
, aExactHostMatch
, aIncludingSession
);
3916 if (preparationResult
.is
<nsresult
>()) {
3917 return preparationResult
.as
<nsresult
>();
3920 return CommonTestPermissionInternal(
3921 nullptr, aURI
, aOriginAttributes
, preparationResult
.as
<int32_t>(), aType
,
3922 aPermission
, aExactHostMatch
, aIncludingSession
);
3925 nsresult
PermissionManager::TestPermissionWithoutDefaultsFromPrincipal(
3926 nsIPrincipal
* aPrincipal
, const nsACString
& aType
, uint32_t* aPermission
) {
3927 MOZ_ASSERT(!HasDefaultPref(aType
));
3929 return CommonTestPermission(aPrincipal
, -1, aType
, aPermission
,
3930 nsIPermissionManager::UNKNOWN_ACTION
, true, false,
3934 void PermissionManager::MaybeCompleteShutdown() {
3935 MOZ_ASSERT(NS_IsMainThread());
3937 nsCOMPtr
<nsIAsyncShutdownClient
> asc
= GetAsyncShutdownBarrier();
3940 DebugOnly
<nsresult
> rv
= asc
->RemoveBlocker(this);
3941 MOZ_ASSERT(NS_SUCCEEDED(rv
));
3944 // Async shutdown blocker methods
3946 NS_IMETHODIMP
PermissionManager::GetName(nsAString
& aName
) {
3947 aName
= u
"PermissionManager: Flushing data"_ns
;
3951 NS_IMETHODIMP
PermissionManager::BlockShutdown(
3952 nsIAsyncShutdownClient
* aClient
) {
3953 RemoveIdleDailyMaintenanceJob();
3954 RemoveAllFromMemory();
3957 gPermissionManager
= nullptr;
3962 PermissionManager::GetState(nsIPropertyBag
** aBagOut
) {
3963 nsCOMPtr
<nsIWritablePropertyBag2
> propertyBag
=
3964 do_CreateInstance("@mozilla.org/hash-property-bag;1");
3966 nsresult rv
= propertyBag
->SetPropertyAsInt32(u
"state"_ns
, mState
);
3967 if (NS_WARN_IF(NS_FAILED(rv
))) {
3971 propertyBag
.forget(aBagOut
);
3976 nsCOMPtr
<nsIAsyncShutdownClient
> PermissionManager::GetAsyncShutdownBarrier()
3979 nsCOMPtr
<nsIAsyncShutdownService
> svc
=
3980 do_GetService("@mozilla.org/async-shutdown-service;1", &rv
);
3981 if (NS_FAILED(rv
)) {
3985 nsCOMPtr
<nsIAsyncShutdownClient
> client
;
3986 // This feels very late but there seem to be other services that rely on
3987 // us later than "profile-before-change".
3988 rv
= svc
->GetXpcomWillShutdown(getter_AddRefs(client
));
3989 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
3994 void PermissionManager::MaybeStripOriginAttributes(
3995 bool aForceStrip
, OriginAttributes
& aOriginAttributes
) {
3998 if (aForceStrip
|| !StaticPrefs::permissions_isolateBy_privateBrowsing()) {
3999 flags
|= OriginAttributes::STRIP_PRIVATE_BROWSING_ID
;
4002 if (aForceStrip
|| !StaticPrefs::permissions_isolateBy_userContext()) {
4003 flags
|= OriginAttributes::STRIP_USER_CONTEXT_ID
;
4007 aOriginAttributes
.StripAttributes(flags
);
4011 } // namespace mozilla