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
,
111 "shortcuts"_ns
, "screen-wake-lock"_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
, nsIPrincipal
** aPrincipal
) {
312 OriginAttributes attrs
;
313 nsCOMPtr
<nsIPrincipal
> principal
=
314 BasePrincipal::CreateContentPrincipal(aURI
, attrs
);
315 NS_ENSURE_TRUE(principal
, NS_ERROR_FAILURE
);
317 principal
.forget(aPrincipal
);
321 nsCString
GetNextSubDomainForHost(const nsACString
& aHost
) {
324 nsEffectiveTLDService::GetInstance()->GetNextSubDomain(aHost
, subDomain
);
325 // We can fail if there is no more subdomain or if the host can't have a
334 // This function produces a nsIURI which is identical to the current
335 // nsIURI, except that it has one less subdomain segment. It returns
336 // `nullptr` if there are no more segments to remove.
337 already_AddRefed
<nsIURI
> GetNextSubDomainURI(nsIURI
* aURI
) {
339 nsresult rv
= aURI
->GetHost(host
);
344 nsCString domain
= GetNextSubDomainForHost(host
);
345 if (domain
.IsEmpty()) {
349 nsCOMPtr
<nsIURI
> uri
;
350 rv
= NS_MutateURI(aURI
).SetHost(domain
).Finalize(uri
);
351 if (NS_FAILED(rv
) || !uri
) {
358 nsresult
UpgradeHostToOriginAndInsert(
359 const nsACString
& aHost
, const nsCString
& aType
, uint32_t aPermission
,
360 uint32_t aExpireType
, int64_t aExpireTime
, int64_t aModificationTime
,
361 std::function
<nsresult(const nsACString
& aOrigin
, const nsCString
& aType
,
362 uint32_t aPermission
, uint32_t aExpireType
,
363 int64_t aExpireTime
, int64_t aModificationTime
)>&&
365 if (aHost
.EqualsLiteral("<file>")) {
366 // We no longer support the magic host <file>
368 "The magic host <file> is no longer supported. "
369 "It is being removed from the permissions database.");
373 // First, we check to see if the host is a valid URI. If it is, it can be
375 nsCOMPtr
<nsIURI
> uri
;
376 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aHost
);
377 if (NS_SUCCEEDED(rv
)) {
378 // It was previously possible to insert useless entries to your permissions
379 // database for URIs which have a null principal. This acts as a cleanup,
380 // getting rid of these useless database entries
381 if (uri
->SchemeIs("moz-nullprincipal")) {
382 NS_WARNING("A moz-nullprincipal: permission is being discarded.");
386 nsCOMPtr
<nsIPrincipal
> principal
;
387 rv
= GetPrincipal(uri
, getter_AddRefs(principal
));
388 NS_ENSURE_SUCCESS(rv
, rv
);
390 nsAutoCString origin
;
391 rv
= GetOriginFromPrincipal(principal
, IsOAForceStripPermission(aType
),
393 NS_ENSURE_SUCCESS(rv
, rv
);
395 aCallback(origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
400 // The user may use this host at non-standard ports or protocols, we can use
401 // their history to guess what ports and protocols we want to add permissions
402 // for. We find every URI which they have visited with this host (or a
403 // subdomain of this host), and try to add it as a principal.
404 bool foundHistory
= false;
406 nsCOMPtr
<nsINavHistoryService
> histSrv
=
407 do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID
);
410 nsCOMPtr
<nsINavHistoryQuery
> histQuery
;
411 rv
= histSrv
->GetNewQuery(getter_AddRefs(histQuery
));
412 NS_ENSURE_SUCCESS(rv
, rv
);
414 // Get the eTLD+1 of the domain
416 rv
= nsEffectiveTLDService::GetInstance()->GetBaseDomainFromHost(aHost
, 0,
420 // If the lookup on the tldService for the base domain for the host
421 // failed, that means that we just want to directly use the host as the
422 // host name for the lookup.
426 // We want to only find history items for this particular eTLD+1, and
428 rv
= histQuery
->SetDomain(eTLD1
);
429 NS_ENSURE_SUCCESS(rv
, rv
);
431 rv
= histQuery
->SetDomainIsHost(false);
432 NS_ENSURE_SUCCESS(rv
, rv
);
434 nsCOMPtr
<nsINavHistoryQueryOptions
> histQueryOpts
;
435 rv
= histSrv
->GetNewQueryOptions(getter_AddRefs(histQueryOpts
));
436 NS_ENSURE_SUCCESS(rv
, rv
);
438 // We want to get the URIs for every item in the user's history with the
441 histQueryOpts
->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI
);
442 NS_ENSURE_SUCCESS(rv
, rv
);
444 // We only search history, because searching both bookmarks and history
445 // is not supported, and history tends to be more comprehensive.
446 rv
= histQueryOpts
->SetQueryType(
447 nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY
);
448 NS_ENSURE_SUCCESS(rv
, rv
);
450 // We include hidden URIs (such as those visited via iFrames) as they may
451 // have permissions too
452 rv
= histQueryOpts
->SetIncludeHidden(true);
453 NS_ENSURE_SUCCESS(rv
, rv
);
455 nsCOMPtr
<nsINavHistoryResult
> histResult
;
456 rv
= histSrv
->ExecuteQuery(histQuery
, histQueryOpts
,
457 getter_AddRefs(histResult
));
458 NS_ENSURE_SUCCESS(rv
, rv
);
460 nsCOMPtr
<nsINavHistoryContainerResultNode
> histResultContainer
;
461 rv
= histResult
->GetRoot(getter_AddRefs(histResultContainer
));
462 NS_ENSURE_SUCCESS(rv
, rv
);
464 rv
= histResultContainer
->SetContainerOpen(true);
465 NS_ENSURE_SUCCESS(rv
, rv
);
467 uint32_t childCount
= 0;
468 rv
= histResultContainer
->GetChildCount(&childCount
);
469 NS_ENSURE_SUCCESS(rv
, rv
);
471 nsTHashSet
<nsCString
> insertedOrigins
;
472 for (uint32_t i
= 0; i
< childCount
; i
++) {
473 nsCOMPtr
<nsINavHistoryResultNode
> child
;
474 histResultContainer
->GetChild(i
, getter_AddRefs(child
));
475 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
478 rv
= child
->GetType(&type
);
479 if (NS_WARN_IF(NS_FAILED(rv
)) ||
480 type
!= nsINavHistoryResultNode::RESULT_TYPE_URI
) {
482 "Unexpected non-RESULT_TYPE_URI node in "
483 "UpgradeHostToOriginAndInsert()");
487 nsAutoCString uriSpec
;
488 rv
= child
->GetUri(uriSpec
);
489 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
491 nsCOMPtr
<nsIURI
> uri
;
492 rv
= NS_NewURI(getter_AddRefs(uri
), uriSpec
);
493 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
495 // Use the provided host - this URI may be for a subdomain, rather than
496 // the host we care about.
497 rv
= NS_MutateURI(uri
).SetHost(aHost
).Finalize(uri
);
498 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
500 // We now have a URI which we can make a nsIPrincipal out of
501 nsCOMPtr
<nsIPrincipal
> principal
;
502 rv
= GetPrincipal(uri
, getter_AddRefs(principal
));
503 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
505 nsAutoCString origin
;
506 rv
= GetOriginFromPrincipal(principal
, IsOAForceStripPermission(aType
),
508 if (NS_WARN_IF(NS_FAILED(rv
))) continue;
510 // Ensure that we don't insert the same origin repeatedly
511 if (insertedOrigins
.Contains(origin
)) {
516 rv
= aCallback(origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
518 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Insert failed");
519 insertedOrigins
.Insert(origin
);
522 rv
= histResultContainer
->SetContainerOpen(false);
523 NS_ENSURE_SUCCESS(rv
, rv
);
526 // If we didn't find any origins for this host in the poermissions database,
527 // we can insert the default http:// and https:// permissions into the
528 // database. This has a relatively high likelihood of applying the permission
529 // to the correct origin.
531 nsAutoCString hostSegment
;
532 nsCOMPtr
<nsIPrincipal
> principal
;
533 nsAutoCString origin
;
535 // If this is an ipv6 URI, we need to surround it in '[', ']' before trying
536 // to parse it as a URI.
537 if (aHost
.FindChar(':') != -1) {
538 hostSegment
.AssignLiteral("[");
539 hostSegment
.Append(aHost
);
540 hostSegment
.AppendLiteral("]");
542 hostSegment
.Assign(aHost
);
545 // http:// URI default
546 rv
= NS_NewURI(getter_AddRefs(uri
), "http://"_ns
+ hostSegment
);
547 NS_ENSURE_SUCCESS(rv
, rv
);
549 rv
= GetPrincipal(uri
, getter_AddRefs(principal
));
550 NS_ENSURE_SUCCESS(rv
, rv
);
552 rv
= GetOriginFromPrincipal(principal
, IsOAForceStripPermission(aType
),
554 NS_ENSURE_SUCCESS(rv
, rv
);
556 aCallback(origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
559 // https:// URI default
560 rv
= NS_NewURI(getter_AddRefs(uri
), "https://"_ns
+ hostSegment
);
561 NS_ENSURE_SUCCESS(rv
, rv
);
563 rv
= GetPrincipal(uri
, getter_AddRefs(principal
));
564 NS_ENSURE_SUCCESS(rv
, rv
);
566 rv
= GetOriginFromPrincipal(principal
, IsOAForceStripPermission(aType
),
568 NS_ENSURE_SUCCESS(rv
, rv
);
570 aCallback(origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
577 bool IsExpandedPrincipal(nsIPrincipal
* aPrincipal
) {
578 nsCOMPtr
<nsIExpandedPrincipal
> ep
= do_QueryInterface(aPrincipal
);
582 // We only want to persist permissions which don't have session or policy
584 bool IsPersistentExpire(uint32_t aExpire
, const nsACString
& aType
) {
585 bool res
= (aExpire
!= nsIPermissionManager::EXPIRE_SESSION
&&
586 aExpire
!= nsIPermissionManager::EXPIRE_POLICY
);
590 nsresult
NotifySecondaryKeyPermissionUpdateInContentProcess(
591 const nsACString
& aType
, uint32_t aPermission
,
592 const nsACString
& aSecondaryKey
, nsIPrincipal
* aTopPrincipal
) {
593 NS_ENSURE_ARG_POINTER(aTopPrincipal
);
594 MOZ_ASSERT(XRE_IsParentProcess());
595 AutoTArray
<RefPtr
<BrowsingContextGroup
>, 5> bcGroups
;
596 BrowsingContextGroup::GetAllGroups(bcGroups
);
597 for (const auto& bcGroup
: bcGroups
) {
598 for (const auto& topBC
: bcGroup
->Toplevels()) {
599 CanonicalBrowsingContext
* topCBC
= topBC
->Canonical();
600 RefPtr
<nsIURI
> topURI
= topCBC
->GetCurrentURI();
605 nsresult rv
= aTopPrincipal
->IsThirdPartyURI(topURI
, &thirdParty
);
610 AutoTArray
<RefPtr
<BrowsingContext
>, 5> bcs
;
611 topBC
->GetAllBrowsingContextsInSubtree(bcs
);
612 for (const auto& bc
: bcs
) {
613 CanonicalBrowsingContext
* cbc
= bc
->Canonical();
614 ContentParent
* cp
= cbc
->GetContentParent();
618 if (cp
->NeedsSecondaryKeyPermissionsUpdate(aSecondaryKey
)) {
619 WindowGlobalParent
* wgp
= cbc
->GetCurrentWindowGlobal();
623 bool success
= wgp
->SendNotifyPermissionChange(aType
, aPermission
);
624 Unused
<< NS_WARN_IF(!success
);
635 ////////////////////////////////////////////////////////////////////////////////
637 PermissionManager::PermissionKey
*
638 PermissionManager::PermissionKey::CreateFromPrincipal(nsIPrincipal
* aPrincipal
,
642 nsAutoCString keyString
;
644 aResult
= GetSiteFromPrincipal(aPrincipal
, aForceStripOA
, keyString
);
646 aResult
= GetOriginFromPrincipal(aPrincipal
, aForceStripOA
, keyString
);
648 if (NS_WARN_IF(NS_FAILED(aResult
))) {
651 return new PermissionKey(keyString
);
654 PermissionManager::PermissionKey
*
655 PermissionManager::PermissionKey::CreateFromURIAndOriginAttributes(
656 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
, bool aForceStripOA
,
658 nsAutoCString origin
;
660 GetOriginFromURIAndOA(aURI
, aOriginAttributes
, aForceStripOA
, origin
);
661 if (NS_WARN_IF(NS_FAILED(aResult
))) {
665 return new PermissionKey(origin
);
668 PermissionManager::PermissionKey
*
669 PermissionManager::PermissionKey::CreateFromURI(nsIURI
* aURI
,
671 nsAutoCString origin
;
672 aResult
= ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI
, origin
);
673 if (NS_WARN_IF(NS_FAILED(aResult
))) {
677 return new PermissionKey(origin
);
680 ////////////////////////////////////////////////////////////////////////////////
681 // PermissionManager Implementation
683 NS_IMPL_ISUPPORTS(PermissionManager
, nsIPermissionManager
, nsIObserver
,
684 nsISupportsWeakReference
, nsIAsyncShutdownBlocker
)
686 PermissionManager::PermissionManager()
687 : mMonitor("PermissionManager::mMonitor"),
688 mState(eInitializing
),
689 mMemoryOnlyDB(false),
692 PermissionManager::~PermissionManager() {
693 // NOTE: Make sure to reject each of the promises in mPermissionKeyPromiseMap
694 // before destroying.
695 for (const auto& promise
: mPermissionKeyPromiseMap
.Values()) {
697 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
700 mPermissionKeyPromiseMap
.Clear();
709 StaticMutex
PermissionManager::sCreationMutex
;
712 already_AddRefed
<nsIPermissionManager
> PermissionManager::GetXPCOMSingleton() {
713 // The lazy initialization could race.
714 StaticMutexAutoLock
lock(sCreationMutex
);
716 if (gPermissionManager
) {
717 return do_AddRef(gPermissionManager
);
720 // Create a new singleton PermissionManager.
721 // We AddRef only once since XPCOM has rules about the ordering of module
722 // teardowns - by the time our module destructor is called, it's too late to
723 // Release our members, since GC cycles have already been completed and
724 // would result in serious leaks.
726 auto permManager
= MakeRefPtr
<PermissionManager
>();
727 if (NS_SUCCEEDED(permManager
->Init())) {
728 gPermissionManager
= permManager
.get();
729 return permManager
.forget();
736 PermissionManager
* PermissionManager::GetInstance() {
737 // TODO: There is a minimal chance that we can race here with a
738 // GetXPCOMSingleton call that did not yet set gPermissionManager.
740 if (!gPermissionManager
) {
741 // Hand off the creation of the permission manager to GetXPCOMSingleton.
742 nsCOMPtr
<nsIPermissionManager
> permManager
= GetXPCOMSingleton();
745 return gPermissionManager
;
748 nsresult
PermissionManager::Init() {
749 // If we are already shutting down, do not permit a creation.
750 // This must match the phase in GetAsyncShutdownBarrier.
751 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown
)) {
752 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN
;
755 // If the 'permissions.memory_only' pref is set to true, then don't write any
756 // permission settings to disk, but keep them in a memory-only database.
757 mMemoryOnlyDB
= Preferences::GetBool("permissions.memory_only", false);
760 nsCOMPtr
<nsIPrefService
> prefService
=
761 do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
);
762 NS_ENSURE_SUCCESS(rv
, rv
);
764 rv
= prefService
->GetBranch("permissions.default.",
765 getter_AddRefs(mDefaultPrefBranch
));
766 NS_ENSURE_SUCCESS(rv
, rv
);
768 if (IsChildProcess()) {
769 // Stop here; we don't need the DB in the child process. Instead we will be
770 // sent permissions as we need them by our parent process.
773 // We use ClearOnShutdown on the content process only because on the parent
774 // process we need to block the shutdown for the final closeDB() call.
775 ClearOnShutdown(&gPermissionManager
);
779 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
780 if (observerService
) {
781 observerService
->AddObserver(this, "profile-do-change", true);
782 observerService
->AddObserver(this, "testonly-reload-permissions-from-disk",
786 if (XRE_IsParentProcess()) {
787 nsCOMPtr
<nsIAsyncShutdownClient
> asc
= GetAsyncShutdownBarrier();
789 return NS_ERROR_NOT_AVAILABLE
;
791 nsAutoString blockerName
;
792 MOZ_ALWAYS_SUCCEEDS(GetName(blockerName
));
794 nsresult rv
= asc
->AddBlocker(
795 this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__
), __LINE__
, blockerName
);
796 NS_ENSURE_SUCCESS(rv
, rv
);
799 AddIdleDailyMaintenanceJob();
801 MOZ_ASSERT(!mThread
);
802 NS_ENSURE_SUCCESS(NS_NewNamedThread("Permission", getter_AddRefs(mThread
)),
806 MOZ_ALWAYS_SUCCEEDS(mThread
->GetPRThread(&prThread
));
807 MOZ_ASSERT(prThread
);
809 mThreadBoundData
.Transfer(prThread
);
816 nsresult
PermissionManager::OpenDatabase(nsIFile
* aPermissionsFile
) {
817 MOZ_ASSERT(!NS_IsMainThread());
818 auto data
= mThreadBoundData
.Access();
821 nsCOMPtr
<mozIStorageService
> storage
=
822 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
);
824 return NS_ERROR_UNEXPECTED
;
826 // cache a connection to the hosts database
828 rv
= storage
->OpenSpecialDatabase(
829 kMozStorageMemoryStorageKey
, VoidCString(),
830 mozIStorageService::CONNECTION_DEFAULT
, getter_AddRefs(data
->mDBConn
));
832 rv
= storage
->OpenDatabase(aPermissionsFile
,
833 mozIStorageService::CONNECTION_DEFAULT
,
834 getter_AddRefs(data
->mDBConn
));
839 void PermissionManager::InitDB(bool aRemoveFile
) {
840 mState
= eInitializing
;
843 MonitorAutoLock
lock(mMonitor
);
844 mReadEntries
.Clear();
847 auto readyIfFailed
= MakeScopeExit([&]() {
848 // ignore failure here, since it's non-fatal (we can run fine without
849 // persistent storage - e.g. if there's no profile).
850 // XXX should we tell the user about this?
854 if (!mPermissionsFile
) {
855 nsresult rv
= NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR
,
856 getter_AddRefs(mPermissionsFile
));
858 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
859 getter_AddRefs(mPermissionsFile
));
866 mPermissionsFile
->AppendNative(nsLiteralCString(PERMISSIONS_FILE_NAME
));
867 NS_ENSURE_SUCCESS_VOID(rv
);
870 nsCOMPtr
<nsIInputStream
> defaultsInputStream
= GetDefaultsInputStream();
872 RefPtr
<PermissionManager
> self
= this;
873 mThread
->Dispatch(NS_NewRunnableFunction(
874 "PermissionManager::InitDB", [self
, aRemoveFile
, defaultsInputStream
] {
875 nsresult rv
= self
->TryInitDB(aRemoveFile
, defaultsInputStream
);
876 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
878 // This extra runnable calls EnsureReadCompleted to finialize the
879 // initialization. If there is something blocked by the monitor, it will
881 NS_DispatchToMainThread(
882 NS_NewRunnableFunction("PermissionManager::InitDB-MainThread",
883 [self
] { self
->EnsureReadCompleted(); }));
885 self
->mMonitor
.Notify();
888 readyIfFailed
.release();
891 nsresult
PermissionManager::TryInitDB(bool aRemoveFile
,
892 nsIInputStream
* aDefaultsInputStream
) {
893 MOZ_ASSERT(!NS_IsMainThread());
895 MonitorAutoLock
lock(mMonitor
);
897 auto raii
= MakeScopeExit([&]() {
898 if (aDefaultsInputStream
) {
899 aDefaultsInputStream
->Close();
902 mState
= eDBInitialized
;
905 auto data
= mThreadBoundData
.Access();
907 auto raiiFailure
= MakeScopeExit([&]() {
909 DebugOnly
<nsresult
> rv
= data
->mDBConn
->Close();
910 MOZ_ASSERT(NS_SUCCEEDED(rv
));
911 data
->mDBConn
= nullptr;
919 rv
= mPermissionsFile
->Exists(&exists
);
920 NS_ENSURE_SUCCESS(rv
, rv
);
922 rv
= mPermissionsFile
->Remove(false);
923 NS_ENSURE_SUCCESS(rv
, rv
);
927 rv
= OpenDatabase(mPermissionsFile
);
928 if (rv
== NS_ERROR_FILE_CORRUPTED
) {
929 LogToConsole(u
"permissions.sqlite is corrupted! Try again!"_ns
);
931 // Add telemetry probe
932 Telemetry::Accumulate(Telemetry::PERMISSIONS_SQL_CORRUPTED
, 1);
934 // delete corrupted permissions.sqlite and try again
935 rv
= mPermissionsFile
->Remove(false);
936 NS_ENSURE_SUCCESS(rv
, rv
);
937 LogToConsole(u
"Corrupted permissions.sqlite has been removed."_ns
);
939 rv
= OpenDatabase(mPermissionsFile
);
940 NS_ENSURE_SUCCESS(rv
, rv
);
941 LogToConsole(u
"OpenDatabase to permissions.sqlite is successful!"_ns
);
944 if (NS_WARN_IF(NS_FAILED(rv
))) {
949 data
->mDBConn
->GetConnectionReady(&ready
);
951 LogToConsole(nsLiteralString(
952 u
"Fail to get connection to permissions.sqlite! Try again!"));
954 // delete and try again
955 rv
= mPermissionsFile
->Remove(false);
956 NS_ENSURE_SUCCESS(rv
, rv
);
957 LogToConsole(u
"Defective permissions.sqlite has been removed."_ns
);
959 // Add telemetry probe
960 Telemetry::Accumulate(Telemetry::DEFECTIVE_PERMISSIONS_SQL_REMOVED
, 1);
962 rv
= OpenDatabase(mPermissionsFile
);
963 NS_ENSURE_SUCCESS(rv
, rv
);
964 LogToConsole(u
"OpenDatabase to permissions.sqlite is successful!"_ns
);
966 data
->mDBConn
->GetConnectionReady(&ready
);
967 if (!ready
) return NS_ERROR_UNEXPECTED
;
970 bool tableExists
= false;
971 data
->mDBConn
->TableExists("moz_perms"_ns
, &tableExists
);
973 data
->mDBConn
->TableExists("moz_hosts"_ns
, &tableExists
);
977 NS_ENSURE_SUCCESS(rv
, rv
);
979 // table already exists; check the schema version before reading
980 int32_t dbSchemaVersion
;
981 rv
= data
->mDBConn
->GetSchemaVersion(&dbSchemaVersion
);
982 NS_ENSURE_SUCCESS(rv
, rv
);
984 switch (dbSchemaVersion
) {
986 // every time you increment the database schema, you need to
987 // implement the upgrading code from the previous version to the
988 // new one. fall through to current version
991 // previous non-expiry version of database. Upgrade it by adding
992 // the expiration columns
993 rv
= data
->mDBConn
->ExecuteSimpleSQL(
994 "ALTER TABLE moz_hosts ADD expireType INTEGER"_ns
);
995 NS_ENSURE_SUCCESS(rv
, rv
);
997 rv
= data
->mDBConn
->ExecuteSimpleSQL(
998 "ALTER TABLE moz_hosts ADD expireTime INTEGER"_ns
);
999 NS_ENSURE_SUCCESS(rv
, rv
);
1002 // fall through to the next upgrade
1005 // TODO: we want to make default version as version 2 in order to
1009 // Add appId/isInBrowserElement fields.
1010 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1011 "ALTER TABLE moz_hosts ADD appId INTEGER"_ns
);
1012 NS_ENSURE_SUCCESS(rv
, rv
);
1014 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1015 "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
1016 NS_ENSURE_SUCCESS(rv
, rv
);
1018 rv
= data
->mDBConn
->SetSchemaVersion(3);
1019 NS_ENSURE_SUCCESS(rv
, rv
);
1022 // fall through to the next upgrade
1025 // Version 3->4 is the creation of the modificationTime field.
1027 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1028 "ALTER TABLE moz_hosts ADD modificationTime INTEGER"));
1029 NS_ENSURE_SUCCESS(rv
, rv
);
1031 // We leave the modificationTime at zero for all existing records;
1032 // using now() would mean, eg, that doing "remove all from the
1033 // last hour" within the first hour after migration would remove
1036 rv
= data
->mDBConn
->SetSchemaVersion(4);
1037 NS_ENSURE_SUCCESS(rv
, rv
);
1040 // fall through to the next upgrade
1043 // In version 5, host appId, and isInBrowserElement were merged into
1044 // a single origin entry
1046 // In version 6, the tables were renamed for backwards compatability
1047 // reasons with version 4 and earlier.
1049 // In version 7, a bug in the migration used for version 4->5 was
1050 // discovered which could have triggered data-loss. Because of that,
1051 // all users with a version 4, 5, or 6 database will be re-migrated
1052 // from the backup database. (bug 1186034). This migration bug is
1053 // not present after bug 1185340, and the re-migration ensures that
1054 // all users have the fix.
1056 // This branch could also be reached via dbSchemaVersion == 3, in
1057 // which case we want to fall through to the dbSchemaVersion == 4
1058 // case. The easiest way to do that is to perform this extra check
1059 // here to make sure that we didn't get here via a fallthrough
1061 if (dbSchemaVersion
== 5) {
1062 // In version 5, the backup database is named moz_hosts_v4. We
1063 // perform the version 5->6 migration to get the tables to have
1064 // consistent naming conventions.
1066 // Version 5->6 is the renaming of moz_hosts to moz_perms, and
1067 // moz_hosts_v4 to moz_hosts (bug 1185343)
1069 // In version 5, we performed the modifications to the
1070 // permissions database in place, this meant that if you
1071 // upgraded to a version which used V5, and then downgraded to a
1072 // version which used v4 or earlier, the fallback path would
1073 // drop the table, and your permissions data would be lost. This
1074 // migration undoes that mistake, by restoring the old moz_hosts
1075 // table (if it was present), and instead using the new table
1076 // moz_perms for the new permissions schema.
1078 // NOTE: If you downgrade, store new permissions, and then
1079 // upgrade again, these new permissions won't be migrated or
1080 // reflected in the updated database. This migration only occurs
1081 // once, as if moz_perms exists, it will skip creating it. In
1082 // addition, permissions added after the migration will not be
1083 // visible in previous versions of firefox.
1085 bool permsTableExists
= false;
1086 data
->mDBConn
->TableExists("moz_perms"_ns
, &permsTableExists
);
1087 if (!permsTableExists
) {
1088 // Move the upgraded database to moz_perms
1089 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1090 "ALTER TABLE moz_hosts RENAME TO moz_perms"_ns
);
1091 NS_ENSURE_SUCCESS(rv
, rv
);
1094 "moz_hosts was not renamed to moz_perms, "
1095 "as a moz_perms table already exists");
1097 // In the situation where a moz_perms table already exists,
1098 // but the schema is lower than 6, a migration has already
1099 // previously occured to V6, but a downgrade has caused the
1100 // moz_hosts table to be dropped. This should only occur in
1101 // the case of a downgrade to a V5 database, which was only
1102 // present in a few day's nightlies. As that version was
1103 // likely used only on a temporary basis, we assume that the
1104 // database from the previous V6 has the permissions which the
1105 // user actually wants to use. We have to get rid of moz_hosts
1106 // such that moz_hosts_v4 can be moved into its place if it
1108 rv
= data
->mDBConn
->ExecuteSimpleSQL("DROP TABLE moz_hosts"_ns
);
1109 NS_ENSURE_SUCCESS(rv
, rv
);
1113 // The moz_hosts table shouldn't exist anymore
1114 bool hostsTableExists
= false;
1115 data
->mDBConn
->TableExists("moz_hosts"_ns
, &hostsTableExists
);
1116 MOZ_ASSERT(!hostsTableExists
);
1119 // Rename moz_hosts_v4 back to it's original location, if it
1121 bool v4TableExists
= false;
1122 data
->mDBConn
->TableExists("moz_hosts_v4"_ns
, &v4TableExists
);
1123 if (v4TableExists
) {
1124 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1125 "ALTER TABLE moz_hosts_v4 RENAME TO moz_hosts"));
1126 NS_ENSURE_SUCCESS(rv
, rv
);
1129 rv
= data
->mDBConn
->SetSchemaVersion(6);
1130 NS_ENSURE_SUCCESS(rv
, rv
);
1133 // fall through to the next upgrade
1136 // At this point, the version 5 table has been migrated to a version
1137 // 6 table We are guaranteed to have at least one of moz_hosts and
1138 // moz_perms. If we have moz_hosts, we will migrate moz_hosts into
1139 // moz_perms (even if we already have a moz_perms, as we need a
1140 // re-migration due to bug 1186034).
1142 // After this migration, we are guaranteed to have both a moz_hosts
1143 // (for backwards compatability), and a moz_perms table. The
1144 // moz_hosts table will have a v4 schema, and the moz_perms table
1145 // will have a v6 schema.
1148 bool hostsTableExists
= false;
1149 data
->mDBConn
->TableExists("moz_hosts"_ns
, &hostsTableExists
);
1150 if (hostsTableExists
) {
1151 // Both versions 4 and 6 have a version 4 formatted hosts table
1152 // named moz_hosts. We can migrate this table to our version 7
1153 // table moz_perms. If moz_perms is present, then we can use it
1154 // as a basis for comparison.
1156 rv
= data
->mDBConn
->BeginTransaction();
1157 NS_ENSURE_SUCCESS(rv
, rv
);
1159 bool tableExists
= false;
1160 data
->mDBConn
->TableExists("moz_hosts_new"_ns
, &tableExists
);
1163 "The temporary database moz_hosts_new already exists, "
1166 rv
= data
->mDBConn
->ExecuteSimpleSQL("DROP TABLE moz_hosts_new"_ns
);
1167 NS_ENSURE_SUCCESS(rv
, rv
);
1169 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1170 nsLiteralCString("CREATE TABLE moz_hosts_new ("
1171 " id INTEGER PRIMARY KEY"
1174 ",permission INTEGER"
1175 ",expireType INTEGER"
1176 ",expireTime INTEGER"
1177 ",modificationTime INTEGER"
1179 NS_ENSURE_SUCCESS(rv
, rv
);
1181 nsCOMPtr
<mozIStorageStatement
> stmt
;
1182 rv
= data
->mDBConn
->CreateStatement(
1184 "SELECT host, type, permission, expireType, "
1186 "modificationTime, isInBrowserElement FROM moz_hosts"),
1187 getter_AddRefs(stmt
));
1188 NS_ENSURE_SUCCESS(rv
, rv
);
1193 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1194 MigrationEntry entry
;
1196 // Read in the old row
1197 rv
= stmt
->GetUTF8String(0, entry
.mHost
);
1198 if (NS_WARN_IF(NS_FAILED(rv
))) {
1201 rv
= stmt
->GetUTF8String(1, entry
.mType
);
1202 if (NS_WARN_IF(NS_FAILED(rv
))) {
1207 entry
.mPermission
= stmt
->AsInt32(2);
1208 entry
.mExpireType
= stmt
->AsInt32(3);
1209 entry
.mExpireTime
= stmt
->AsInt64(4);
1210 entry
.mModificationTime
= stmt
->AsInt64(5);
1212 mMigrationEntries
.AppendElement(entry
);
1215 // We don't drop the moz_hosts table such that it is available
1216 // for backwards-compatability and for future migrations in case
1217 // of migration errors in the current code. Create a marker
1218 // empty table which will indicate that the moz_hosts table is
1219 // intended to act as a backup. If this table is not present,
1220 // then the moz_hosts table was created as a random empty table.
1221 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1222 nsLiteralCString("CREATE TABLE moz_hosts_is_backup (dummy "
1223 "INTEGER PRIMARY KEY)"));
1224 NS_ENSURE_SUCCESS(rv
, rv
);
1226 bool permsTableExists
= false;
1227 data
->mDBConn
->TableExists("moz_perms"_ns
, &permsTableExists
);
1228 if (permsTableExists
) {
1229 // The user already had a moz_perms table, and we are
1230 // performing a re-migration. We count the rows in the old
1231 // table for telemetry, and then back up their old database as
1234 nsCOMPtr
<mozIStorageStatement
> countStmt
;
1235 rv
= data
->mDBConn
->CreateStatement(
1236 "SELECT COUNT(*) FROM moz_perms"_ns
, getter_AddRefs(countStmt
));
1237 bool hasResult
= false;
1238 if (NS_FAILED(rv
) ||
1239 NS_FAILED(countStmt
->ExecuteStep(&hasResult
)) || !hasResult
) {
1240 NS_WARNING("Could not count the rows in moz_perms");
1243 // Back up the old moz_perms database as moz_perms_v6 before
1244 // we move the new table into its position
1245 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1246 "ALTER TABLE moz_perms RENAME TO moz_perms_v6"));
1247 NS_ENSURE_SUCCESS(rv
, rv
);
1250 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1251 "ALTER TABLE moz_hosts_new RENAME TO moz_perms"));
1252 NS_ENSURE_SUCCESS(rv
, rv
);
1254 rv
= data
->mDBConn
->CommitTransaction();
1255 NS_ENSURE_SUCCESS(rv
, rv
);
1257 // We don't have a moz_hosts table, so we create one for
1258 // downgrading purposes. This table is empty.
1259 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1260 nsLiteralCString("CREATE TABLE moz_hosts ("
1261 " id INTEGER PRIMARY KEY"
1264 ",permission INTEGER"
1265 ",expireType INTEGER"
1266 ",expireTime INTEGER"
1267 ",modificationTime INTEGER"
1269 ",isInBrowserElement INTEGER"
1271 NS_ENSURE_SUCCESS(rv
, rv
);
1273 // We are guaranteed to have a moz_perms table at this point.
1278 // At this point, both the moz_hosts and moz_perms tables should
1280 bool hostsTableExists
= false;
1281 bool permsTableExists
= false;
1282 data
->mDBConn
->TableExists("moz_hosts"_ns
, &hostsTableExists
);
1283 data
->mDBConn
->TableExists("moz_perms"_ns
, &permsTableExists
);
1284 MOZ_ASSERT(hostsTableExists
&& permsTableExists
);
1288 rv
= data
->mDBConn
->SetSchemaVersion(7);
1289 NS_ENSURE_SUCCESS(rv
, rv
);
1292 // fall through to the next upgrade
1295 // The version 7-8 migration is the re-migration of localhost and
1296 // ip-address entries due to errors in the previous version 7
1297 // migration which caused localhost and ip-address entries to be
1298 // incorrectly discarded. The version 7 migration logic has been
1299 // corrected, and thus this logic only needs to execute if the user
1300 // is currently on version 7.
1302 // This migration will be relatively expensive as we need to
1303 // perform database lookups for each origin which we want to
1304 // insert. Fortunately, it shouldn't be too expensive as we only
1305 // want to insert a small number of entries created for localhost
1308 // We only want to perform the re-migration if moz_hosts is a
1310 bool hostsIsBackupExists
= false;
1311 data
->mDBConn
->TableExists("moz_hosts_is_backup"_ns
,
1312 &hostsIsBackupExists
);
1314 // Only perform this migration if the original schema version was
1315 // 7, and the moz_hosts table is a backup.
1316 if (dbSchemaVersion
== 7 && hostsIsBackupExists
) {
1317 nsCOMPtr
<mozIStorageStatement
> stmt
;
1318 rv
= data
->mDBConn
->CreateStatement(
1320 "SELECT host, type, permission, expireType, "
1322 "modificationTime, isInBrowserElement FROM moz_hosts"),
1323 getter_AddRefs(stmt
));
1324 NS_ENSURE_SUCCESS(rv
, rv
);
1326 nsCOMPtr
<mozIStorageStatement
> idStmt
;
1327 rv
= data
->mDBConn
->CreateStatement(
1328 "SELECT MAX(id) FROM moz_hosts"_ns
, getter_AddRefs(idStmt
));
1331 bool hasResult
= false;
1332 if (NS_SUCCEEDED(rv
) &&
1333 NS_SUCCEEDED(idStmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1334 id
= idStmt
->AsInt32(0) + 1;
1337 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1338 MigrationEntry entry
;
1340 // Read in the old row
1341 rv
= stmt
->GetUTF8String(0, entry
.mHost
);
1342 if (NS_WARN_IF(NS_FAILED(rv
))) {
1346 nsAutoCString eTLD1
;
1347 rv
= nsEffectiveTLDService::GetInstance()->GetBaseDomainFromHost(
1348 entry
.mHost
, 0, eTLD1
);
1349 if (NS_SUCCEEDED(rv
)) {
1350 // We only care about entries which the tldService can't
1355 rv
= stmt
->GetUTF8String(1, entry
.mType
);
1356 if (NS_WARN_IF(NS_FAILED(rv
))) {
1361 entry
.mPermission
= stmt
->AsInt32(2);
1362 entry
.mExpireType
= stmt
->AsInt32(3);
1363 entry
.mExpireTime
= stmt
->AsInt64(4);
1364 entry
.mModificationTime
= stmt
->AsInt64(5);
1366 mMigrationEntries
.AppendElement(entry
);
1370 // Even if we didn't perform the migration, we want to bump the
1371 // schema version to 8.
1372 rv
= data
->mDBConn
->SetSchemaVersion(8);
1373 NS_ENSURE_SUCCESS(rv
, rv
);
1376 // fall through to the next upgrade
1379 // The version 8-9 migration removes the unnecessary backup
1380 // moz-hosts database contents. as the data no longer needs to be
1383 // We only want to clear out the old table if it is a backup. If
1384 // it isn't a backup, we don't need to touch it.
1385 bool hostsIsBackupExists
= false;
1386 data
->mDBConn
->TableExists("moz_hosts_is_backup"_ns
,
1387 &hostsIsBackupExists
);
1388 if (hostsIsBackupExists
) {
1389 // Delete everything from the backup, we want to keep around the
1390 // table so that you can still downgrade and not break things,
1391 // but we don't need to keep the rows around.
1392 rv
= data
->mDBConn
->ExecuteSimpleSQL("DELETE FROM moz_hosts"_ns
);
1393 NS_ENSURE_SUCCESS(rv
, rv
);
1395 // The table is no longer a backup, so get rid of it.
1396 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1397 "DROP TABLE moz_hosts_is_backup"_ns
);
1398 NS_ENSURE_SUCCESS(rv
, rv
);
1401 rv
= data
->mDBConn
->SetSchemaVersion(9);
1402 NS_ENSURE_SUCCESS(rv
, rv
);
1405 // fall through to the next upgrade
1409 rv
= data
->mDBConn
->SetSchemaVersion(10);
1410 NS_ENSURE_SUCCESS(rv
, rv
);
1413 // fall through to the next upgrade
1417 // Filter out the rows with storage access API permissions with a
1418 // granted origin, and remove the granted origin part from the
1420 rv
= data
->mDBConn
->ExecuteSimpleSQL(nsLiteralCString(
1422 "SET type=SUBSTR(type, 0, INSTR(SUBSTR(type, INSTR(type, "
1424 "1), '^') + INSTR(type, '^')) "
1425 "WHERE INSTR(SUBSTR(type, INSTR(type, '^') + 1), '^') AND "
1426 "SUBSTR(type, 0, 18) == \"storageAccessAPI^\";"));
1427 NS_ENSURE_SUCCESS(rv
, rv
);
1429 rv
= data
->mDBConn
->SetSchemaVersion(11);
1430 NS_ENSURE_SUCCESS(rv
, rv
);
1433 // fall through to the next upgrade
1437 // Migrate 3rdPartyStorage keys to a site scope
1438 rv
= data
->mDBConn
->BeginTransaction();
1439 NS_ENSURE_SUCCESS(rv
, rv
);
1440 nsCOMPtr
<mozIStorageStatement
> updateStmt
;
1441 rv
= data
->mDBConn
->CreateStatement(
1442 nsLiteralCString("UPDATE moz_perms SET origin = ?2 WHERE id = ?1"),
1443 getter_AddRefs(updateStmt
));
1444 NS_ENSURE_SUCCESS(rv
, rv
);
1446 nsCOMPtr
<mozIStorageStatement
> deleteStmt
;
1447 rv
= data
->mDBConn
->CreateStatement(
1448 nsLiteralCString("DELETE FROM moz_perms WHERE id = ?1"),
1449 getter_AddRefs(deleteStmt
));
1450 NS_ENSURE_SUCCESS(rv
, rv
);
1452 nsCOMPtr
<mozIStorageStatement
> selectStmt
;
1453 rv
= data
->mDBConn
->CreateStatement(
1454 nsLiteralCString("SELECT id, origin, type FROM moz_perms WHERE "
1455 " SUBSTR(type, 0, 17) == \"3rdPartyStorage^\""),
1456 getter_AddRefs(selectStmt
));
1457 NS_ENSURE_SUCCESS(rv
, rv
);
1459 nsTHashSet
<nsCStringHashKey
> deduplicationSet
;
1461 while (NS_SUCCEEDED(selectStmt
->ExecuteStep(&hasResult
)) && hasResult
) {
1463 rv
= selectStmt
->GetInt64(0, &id
);
1464 NS_ENSURE_SUCCESS(rv
, rv
);
1467 rv
= selectStmt
->GetUTF8String(1, origin
);
1468 NS_ENSURE_SUCCESS(rv
, rv
);
1471 rv
= selectStmt
->GetUTF8String(2, type
);
1472 NS_ENSURE_SUCCESS(rv
, rv
);
1474 nsCOMPtr
<nsIURI
> uri
;
1475 rv
= NS_NewURI(getter_AddRefs(uri
), origin
);
1476 if (NS_FAILED(rv
)) {
1480 rv
= nsEffectiveTLDService::GetInstance()->GetSite(uri
, site
);
1481 if (NS_WARN_IF(NS_FAILED(rv
))) {
1485 nsCString deduplicationKey
=
1486 nsPrintfCString("%s,%s", site
.get(), type
.get());
1487 if (deduplicationSet
.Contains(deduplicationKey
)) {
1488 rv
= deleteStmt
->BindInt64ByIndex(0, id
);
1489 NS_ENSURE_SUCCESS(rv
, rv
);
1491 rv
= deleteStmt
->Execute();
1492 NS_ENSURE_SUCCESS(rv
, rv
);
1494 deduplicationSet
.Insert(deduplicationKey
);
1495 rv
= updateStmt
->BindInt64ByIndex(0, id
);
1496 NS_ENSURE_SUCCESS(rv
, rv
);
1497 rv
= updateStmt
->BindUTF8StringByIndex(1, site
);
1498 NS_ENSURE_SUCCESS(rv
, rv
);
1500 rv
= updateStmt
->Execute();
1501 NS_ENSURE_SUCCESS(rv
, rv
);
1504 rv
= data
->mDBConn
->CommitTransaction();
1505 NS_ENSURE_SUCCESS(rv
, rv
);
1507 rv
= data
->mDBConn
->SetSchemaVersion(HOSTS_SCHEMA_VERSION
);
1508 NS_ENSURE_SUCCESS(rv
, rv
);
1511 // fall through to the next upgrade
1515 case HOSTS_SCHEMA_VERSION
:
1519 // if columns have been added to the table, we can still use the
1520 // ones we understand safely. if columns have been deleted or
1521 // altered, just blow away the table and start from scratch! if you
1522 // change the way a column is interpreted, make sure you also change
1523 // its name so this check will catch it.
1525 // check if all the expected columns exist
1526 nsCOMPtr
<mozIStorageStatement
> stmt
;
1527 rv
= data
->mDBConn
->CreateStatement(
1528 nsLiteralCString("SELECT origin, type, permission, "
1529 "expireType, expireTime, "
1530 "modificationTime FROM moz_perms"),
1531 getter_AddRefs(stmt
));
1532 if (NS_SUCCEEDED(rv
)) break;
1534 // our columns aren't there - drop the table!
1535 rv
= data
->mDBConn
->ExecuteSimpleSQL("DROP TABLE moz_perms"_ns
);
1536 NS_ENSURE_SUCCESS(rv
, rv
);
1539 NS_ENSURE_SUCCESS(rv
, rv
);
1544 // cache frequently used statements (for insertion, deletion, and
1546 rv
= data
->mDBConn
->CreateStatement(
1547 nsLiteralCString("INSERT INTO moz_perms "
1548 "(id, origin, type, permission, expireType, "
1549 "expireTime, modificationTime) "
1550 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"),
1551 getter_AddRefs(data
->mStmtInsert
));
1552 NS_ENSURE_SUCCESS(rv
, rv
);
1554 rv
= data
->mDBConn
->CreateStatement(nsLiteralCString("DELETE FROM moz_perms "
1556 getter_AddRefs(data
->mStmtDelete
));
1557 NS_ENSURE_SUCCESS(rv
, rv
);
1559 rv
= data
->mDBConn
->CreateStatement(
1560 nsLiteralCString("UPDATE moz_perms "
1561 "SET permission = ?2, expireType= ?3, expireTime = "
1562 "?4, modificationTime = ?5 WHERE id = ?1"),
1563 getter_AddRefs(data
->mStmtUpdate
));
1564 NS_ENSURE_SUCCESS(rv
, rv
);
1566 // Always import default permissions.
1567 ConsumeDefaultsInputStream(aDefaultsInputStream
, lock
);
1569 // check whether to import or just read in the db
1572 NS_ENSURE_SUCCESS(rv
, rv
);
1575 raiiFailure
.release();
1580 void PermissionManager::AddIdleDailyMaintenanceJob() {
1581 MOZ_ASSERT(NS_IsMainThread());
1583 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
1584 NS_ENSURE_TRUE_VOID(observerService
);
1587 observerService
->AddObserver(this, OBSERVER_TOPIC_IDLE_DAILY
, false);
1588 NS_ENSURE_SUCCESS_VOID(rv
);
1591 void PermissionManager::RemoveIdleDailyMaintenanceJob() {
1592 MOZ_ASSERT(NS_IsMainThread());
1594 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
1595 NS_ENSURE_TRUE_VOID(observerService
);
1598 observerService
->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY
);
1599 NS_ENSURE_SUCCESS_VOID(rv
);
1602 void PermissionManager::PerformIdleDailyMaintenance() {
1603 MOZ_ASSERT(NS_IsMainThread());
1605 RefPtr
<PermissionManager
> self
= this;
1606 mThread
->Dispatch(NS_NewRunnableFunction(
1607 "PermissionManager::PerformIdleDailyMaintenance", [self
] {
1608 auto data
= self
->mThreadBoundData
.Access();
1610 if (self
->mState
== eClosed
|| !data
->mDBConn
) {
1614 nsCOMPtr
<mozIStorageStatement
> stmtDeleteExpired
;
1615 nsresult rv
= data
->mDBConn
->CreateStatement(
1616 nsLiteralCString("DELETE FROM moz_perms WHERE expireType = "
1617 "?1 AND expireTime <= ?2"),
1618 getter_AddRefs(stmtDeleteExpired
));
1619 NS_ENSURE_SUCCESS_VOID(rv
);
1621 rv
= stmtDeleteExpired
->BindInt32ByIndex(
1622 0, nsIPermissionManager::EXPIRE_TIME
);
1623 NS_ENSURE_SUCCESS_VOID(rv
);
1625 rv
= stmtDeleteExpired
->BindInt64ByIndex(1, EXPIRY_NOW
);
1626 NS_ENSURE_SUCCESS_VOID(rv
);
1628 rv
= stmtDeleteExpired
->Execute();
1629 NS_ENSURE_SUCCESS_VOID(rv
);
1633 // sets the schema version and creates the moz_perms table.
1634 nsresult
PermissionManager::CreateTable() {
1635 MOZ_ASSERT(!NS_IsMainThread());
1636 auto data
= mThreadBoundData
.Access();
1638 // set the schema version, before creating the table
1639 nsresult rv
= data
->mDBConn
->SetSchemaVersion(HOSTS_SCHEMA_VERSION
);
1640 if (NS_FAILED(rv
)) return rv
;
1643 // SQL also lives in automation.py.in. If you change this SQL change that
1645 rv
= data
->mDBConn
->ExecuteSimpleSQL(
1646 nsLiteralCString("CREATE TABLE moz_perms ("
1647 " id INTEGER PRIMARY KEY"
1650 ",permission INTEGER"
1651 ",expireType INTEGER"
1652 ",expireTime INTEGER"
1653 ",modificationTime INTEGER"
1655 if (NS_FAILED(rv
)) return rv
;
1657 // We also create a legacy V4 table, for backwards compatability,
1658 // and to ensure that downgrades don't trigger a schema version change.
1659 return data
->mDBConn
->ExecuteSimpleSQL(
1660 nsLiteralCString("CREATE TABLE moz_hosts ("
1661 " id INTEGER PRIMARY KEY"
1664 ",permission INTEGER"
1665 ",expireType INTEGER"
1666 ",expireTime INTEGER"
1667 ",modificationTime INTEGER"
1668 ",isInBrowserElement INTEGER"
1672 // Returns whether the given combination of expire type and expire time are
1673 // expired. Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
1674 bool PermissionManager::HasExpired(uint32_t aExpireType
, int64_t aExpireTime
) {
1675 return (aExpireType
== nsIPermissionManager::EXPIRE_TIME
||
1676 (aExpireType
== nsIPermissionManager::EXPIRE_SESSION
&&
1677 aExpireTime
!= 0)) &&
1678 aExpireTime
<= EXPIRY_NOW
;
1682 PermissionManager::AddFromPrincipalAndPersistInPrivateBrowsing(
1683 nsIPrincipal
* aPrincipal
, const nsACString
& aType
, uint32_t aPermission
) {
1684 ENSURE_NOT_CHILD_PROCESS
;
1685 NS_ENSURE_ARG_POINTER(aPrincipal
);
1686 // We don't add the system principal because it actually has no URI and we
1687 // always allow action for them.
1688 if (aPrincipal
->IsSystemPrincipal()) {
1692 // Null principals can't meaningfully have persisted permissions attached to
1693 // them, so we don't allow adding permissions for them.
1694 if (aPrincipal
->GetIsNullPrincipal()) {
1698 // Permissions may not be added to expanded principals.
1699 if (IsExpandedPrincipal(aPrincipal
)) {
1700 return NS_ERROR_INVALID_ARG
;
1703 // A modificationTime of zero will cause AddInternal to use now().
1704 int64_t modificationTime
= 0;
1706 return AddInternal(aPrincipal
, aType
, aPermission
, 0,
1707 nsIPermissionManager::EXPIRE_NEVER
,
1708 /* aExpireTime */ 0, modificationTime
, eNotify
, eWriteToDB
,
1709 /* aIgnoreSessionPermissions */ false,
1710 /* aOriginString*/ nullptr,
1711 /* aAllowPersistInPrivateBrowsing */ true);
1715 PermissionManager::AddFromPrincipal(nsIPrincipal
* aPrincipal
,
1716 const nsACString
& aType
,
1717 uint32_t aPermission
, uint32_t aExpireType
,
1718 int64_t aExpireTime
) {
1719 ENSURE_NOT_CHILD_PROCESS
;
1720 NS_ENSURE_ARG_POINTER(aPrincipal
);
1721 NS_ENSURE_TRUE(aExpireType
== nsIPermissionManager::EXPIRE_NEVER
||
1722 aExpireType
== nsIPermissionManager::EXPIRE_TIME
||
1723 aExpireType
== nsIPermissionManager::EXPIRE_SESSION
||
1724 aExpireType
== nsIPermissionManager::EXPIRE_POLICY
,
1725 NS_ERROR_INVALID_ARG
);
1727 // Skip addition if the permission is already expired.
1728 if (HasExpired(aExpireType
, aExpireTime
)) {
1732 // We don't add the system principal because it actually has no URI and we
1733 // always allow action for them.
1734 if (aPrincipal
->IsSystemPrincipal()) {
1738 // Null principals can't meaningfully have persisted permissions attached to
1739 // them, so we don't allow adding permissions for them.
1740 if (aPrincipal
->GetIsNullPrincipal()) {
1744 // Permissions may not be added to expanded principals.
1745 if (IsExpandedPrincipal(aPrincipal
)) {
1746 return NS_ERROR_INVALID_ARG
;
1749 // A modificationTime of zero will cause AddInternal to use now().
1750 int64_t modificationTime
= 0;
1752 return AddInternal(aPrincipal
, aType
, aPermission
, 0, aExpireType
,
1753 aExpireTime
, modificationTime
, eNotify
, eWriteToDB
);
1756 nsresult
PermissionManager::AddInternal(
1757 nsIPrincipal
* aPrincipal
, const nsACString
& aType
, uint32_t aPermission
,
1758 int64_t aID
, uint32_t aExpireType
, int64_t aExpireTime
,
1759 int64_t aModificationTime
, NotifyOperationType aNotifyOperation
,
1760 DBOperationType aDBOperation
, const bool aIgnoreSessionPermissions
,
1761 const nsACString
* aOriginString
,
1762 const bool aAllowPersistInPrivateBrowsing
) {
1763 MOZ_ASSERT(NS_IsMainThread());
1765 EnsureReadCompleted();
1767 nsresult rv
= NS_OK
;
1768 nsAutoCString origin
;
1769 // Only attempt to compute the origin string when it is going to be needed
1770 // later on in the function.
1771 if (!IsChildProcess() ||
1772 (aDBOperation
== eWriteToDB
&& IsPersistentExpire(aExpireType
, aType
))) {
1773 if (aOriginString
) {
1774 // Use the origin string provided by the caller.
1775 origin
= *aOriginString
;
1777 if (IsSiteScopedPermission(aType
)) {
1778 rv
= GetSiteFromPrincipal(aPrincipal
, IsOAForceStripPermission(aType
),
1781 // Compute it from the principal provided.
1782 rv
= GetOriginFromPrincipal(aPrincipal
, IsOAForceStripPermission(aType
),
1785 NS_ENSURE_SUCCESS(rv
, rv
);
1789 // Unless the caller sets aAllowPersistInPrivateBrowsing, only store
1790 // permissions for the session in Private Browsing. Except for default
1791 // permissions which are stored in-memory only and imported each startup. We
1792 // also allow setting persistent UKNOWN_ACTION, to support removing default
1793 // private browsing permissions.
1794 if (!aAllowPersistInPrivateBrowsing
&& aID
!= cIDPermissionIsDefault
&&
1795 aPermission
!= UNKNOWN_ACTION
&& aExpireType
!= EXPIRE_SESSION
) {
1796 uint32_t privateBrowsingId
=
1797 nsScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
;
1798 nsresult rv
= aPrincipal
->GetPrivateBrowsingId(&privateBrowsingId
);
1799 if (NS_SUCCEEDED(rv
) &&
1800 privateBrowsingId
!=
1801 nsScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
) {
1802 aExpireType
= EXPIRE_SESSION
;
1806 // Let's send the new permission to the content process only if it has to be
1808 if (!IsChildProcess() && aNotifyOperation
== eNotify
) {
1809 IPC::Permission
permission(origin
, aType
, aPermission
, aExpireType
,
1812 nsAutoCString permissionKey
;
1813 GetKeyForPermission(aPrincipal
, aType
, permissionKey
);
1814 bool isSecondaryKeyed
;
1815 nsAutoCString secondaryKey
;
1816 isSecondaryKeyed
= GetSecondaryKey(aType
, secondaryKey
);
1817 if (isSecondaryKeyed
) {
1818 NotifySecondaryKeyPermissionUpdateInContentProcess(
1819 aType
, aPermission
, secondaryKey
, aPrincipal
);
1822 nsTArray
<ContentParent
*> cplist
;
1823 ContentParent::GetAll(cplist
);
1824 for (uint32_t i
= 0; i
< cplist
.Length(); ++i
) {
1825 ContentParent
* cp
= cplist
[i
];
1826 if (cp
->NeedsPermissionsUpdate(permissionKey
)) {
1827 Unused
<< cp
->SendAddPermission(permission
);
1832 MOZ_ASSERT(PermissionAvailable(aPrincipal
, aType
));
1834 // look up the type index
1835 int32_t typeIndex
= GetTypeIndex(aType
, true);
1836 NS_ENSURE_TRUE(typeIndex
!= -1, NS_ERROR_OUT_OF_MEMORY
);
1838 // When an entry already exists, PutEntry will return that, instead
1839 // of adding a new one
1840 RefPtr
<PermissionKey
> key
= PermissionKey::CreateFromPrincipal(
1841 aPrincipal
, IsOAForceStripPermission(aType
),
1842 IsSiteScopedPermission(aType
), rv
);
1844 MOZ_ASSERT(NS_FAILED(rv
));
1848 PermissionHashKey
* entry
= mPermissionTable
.PutEntry(key
);
1849 if (!entry
) return NS_ERROR_FAILURE
;
1850 if (!entry
->GetKey()) {
1851 mPermissionTable
.RemoveEntry(entry
);
1852 return NS_ERROR_OUT_OF_MEMORY
;
1855 // figure out the transaction type, and get any existing permission value
1857 int32_t index
= entry
->GetPermissionIndex(typeIndex
);
1859 if (aPermission
== nsIPermissionManager::UNKNOWN_ACTION
)
1860 op
= eOperationNone
;
1862 op
= eOperationAdding
;
1865 PermissionEntry oldPermissionEntry
= entry
->GetPermissions()[index
];
1867 // remove the permission if the permission is UNKNOWN, update the
1868 // permission if its value or expire type have changed OR if the time has
1869 // changed and the expire type is time, otherwise, don't modify. There's
1870 // no need to modify a permission that doesn't expire with time when the
1871 // only thing changed is the expire time.
1872 if (aPermission
== oldPermissionEntry
.mPermission
&&
1873 aExpireType
== oldPermissionEntry
.mExpireType
&&
1874 (aExpireType
== nsIPermissionManager::EXPIRE_NEVER
||
1875 aExpireTime
== oldPermissionEntry
.mExpireTime
))
1876 op
= eOperationNone
;
1877 else if (oldPermissionEntry
.mID
== cIDPermissionIsDefault
)
1878 // The existing permission is one added as a default and the new
1879 // permission doesn't exactly match so we are replacing the default. This
1880 // is true even if the new permission is UNKNOWN_ACTION (which means a
1881 // "logical remove" of the default)
1882 op
= eOperationReplacingDefault
;
1883 else if (aID
== cIDPermissionIsDefault
)
1884 // We are adding a default permission but a "real" permission already
1885 // exists. This almost-certainly means we just did a removeAllSince and
1886 // are re-importing defaults - so we can ignore this.
1887 op
= eOperationNone
;
1888 else if (aPermission
== nsIPermissionManager::UNKNOWN_ACTION
)
1889 op
= eOperationRemoving
;
1891 op
= eOperationChanging
;
1894 // child processes should *always* be passed a modificationTime of zero.
1895 MOZ_ASSERT(!IsChildProcess() || aModificationTime
== 0);
1897 // do the work for adding, deleting, or changing a permission:
1898 // update the in-memory list, write to the db, and notify consumers.
1900 if (aModificationTime
== 0) {
1901 aModificationTime
= EXPIRY_NOW
;
1905 case eOperationNone
: {
1910 case eOperationAdding
: {
1911 if (aDBOperation
== eWriteToDB
) {
1912 // we'll be writing to the database - generate a known unique id
1915 // we're reading from the database - use the id already assigned
1919 entry
->GetPermissions().AppendElement(
1920 PermissionEntry(id
, typeIndex
, aPermission
, aExpireType
, aExpireTime
,
1921 aModificationTime
));
1923 if (aDBOperation
== eWriteToDB
&&
1924 IsPersistentExpire(aExpireType
, aType
)) {
1925 UpdateDB(op
, id
, origin
, aType
, aPermission
, aExpireType
, aExpireTime
,
1929 if (aNotifyOperation
== eNotify
) {
1930 NotifyObserversWithPermission(aPrincipal
, mTypeArray
[typeIndex
],
1931 aPermission
, aExpireType
, aExpireTime
,
1932 aModificationTime
, u
"added");
1938 case eOperationRemoving
: {
1939 PermissionEntry oldPermissionEntry
= entry
->GetPermissions()[index
];
1940 id
= oldPermissionEntry
.mID
;
1942 // If the type we want to remove is EXPIRE_POLICY, we need to reject
1943 // attempts to change the permission.
1944 if (entry
->GetPermissions()[index
].mExpireType
== EXPIRE_POLICY
) {
1945 NS_WARNING("Attempting to remove EXPIRE_POLICY permission");
1949 entry
->GetPermissions().RemoveElementAt(index
);
1951 if (aDBOperation
== eWriteToDB
)
1952 // We care only about the id here so we pass dummy values for all other
1954 UpdateDB(op
, id
, ""_ns
, ""_ns
, 0, nsIPermissionManager::EXPIRE_NEVER
, 0,
1957 if (aNotifyOperation
== eNotify
) {
1958 NotifyObserversWithPermission(
1959 aPrincipal
, mTypeArray
[typeIndex
], oldPermissionEntry
.mPermission
,
1960 oldPermissionEntry
.mExpireType
, oldPermissionEntry
.mExpireTime
,
1961 oldPermissionEntry
.mModificationTime
, u
"deleted");
1964 // If there are no more permissions stored for that entry, clear it.
1965 if (entry
->GetPermissions().IsEmpty()) {
1966 mPermissionTable
.RemoveEntry(entry
);
1972 case eOperationChanging
: {
1973 id
= entry
->GetPermissions()[index
].mID
;
1975 // If the existing type is EXPIRE_POLICY, we need to reject attempts to
1976 // change the permission.
1977 if (entry
->GetPermissions()[index
].mExpireType
== EXPIRE_POLICY
) {
1978 NS_WARNING("Attempting to modify EXPIRE_POLICY permission");
1982 PermissionEntry oldPermissionEntry
= entry
->GetPermissions()[index
];
1984 // If the new expireType is EXPIRE_SESSION, then we have to keep a
1985 // copy of the previous permission/expireType values. This cached value
1986 // will be used when restoring the permissions of an app.
1987 if (entry
->GetPermissions()[index
].mExpireType
!=
1988 nsIPermissionManager::EXPIRE_SESSION
&&
1989 aExpireType
== nsIPermissionManager::EXPIRE_SESSION
) {
1990 entry
->GetPermissions()[index
].mNonSessionPermission
=
1991 entry
->GetPermissions()[index
].mPermission
;
1992 entry
->GetPermissions()[index
].mNonSessionExpireType
=
1993 entry
->GetPermissions()[index
].mExpireType
;
1994 entry
->GetPermissions()[index
].mNonSessionExpireTime
=
1995 entry
->GetPermissions()[index
].mExpireTime
;
1996 } else if (aExpireType
!= nsIPermissionManager::EXPIRE_SESSION
) {
1997 entry
->GetPermissions()[index
].mNonSessionPermission
= aPermission
;
1998 entry
->GetPermissions()[index
].mNonSessionExpireType
= aExpireType
;
1999 entry
->GetPermissions()[index
].mNonSessionExpireTime
= aExpireTime
;
2002 entry
->GetPermissions()[index
].mPermission
= aPermission
;
2003 entry
->GetPermissions()[index
].mExpireType
= aExpireType
;
2004 entry
->GetPermissions()[index
].mExpireTime
= aExpireTime
;
2005 entry
->GetPermissions()[index
].mModificationTime
= aModificationTime
;
2007 if (aDBOperation
== eWriteToDB
) {
2008 bool newIsPersistentExpire
= IsPersistentExpire(aExpireType
, aType
);
2009 bool oldIsPersistentExpire
=
2010 IsPersistentExpire(oldPermissionEntry
.mExpireType
, aType
);
2012 if (!newIsPersistentExpire
&& oldIsPersistentExpire
) {
2013 // Maybe we have to remove the previous permission if that was
2015 UpdateDB(eOperationRemoving
, id
, ""_ns
, ""_ns
, 0,
2016 nsIPermissionManager::EXPIRE_NEVER
, 0, 0);
2017 } else if (newIsPersistentExpire
&& !oldIsPersistentExpire
) {
2018 // It could also be that the previous permission was session-only but
2019 // this needs to be written into the DB. In this case, we have to run
2020 // an Adding operation.
2021 UpdateDB(eOperationAdding
, id
, origin
, aType
, aPermission
,
2022 aExpireType
, aExpireTime
, aModificationTime
);
2023 } else if (newIsPersistentExpire
) {
2024 // This is the a simple update. We care only about the id, the
2025 // permission and expireType/expireTime/modificationTime here. We pass
2026 // dummy values for all other parameters.
2027 UpdateDB(op
, id
, ""_ns
, ""_ns
, aPermission
, aExpireType
, aExpireTime
,
2032 if (aNotifyOperation
== eNotify
) {
2033 NotifyObserversWithPermission(aPrincipal
, mTypeArray
[typeIndex
],
2034 aPermission
, aExpireType
, aExpireTime
,
2035 aModificationTime
, u
"changed");
2040 case eOperationReplacingDefault
: {
2041 // this is handling the case when we have an existing permission
2042 // entry that was created as a "default" (and thus isn't in the DB) with
2043 // an explicit permission (that may include UNKNOWN_ACTION.)
2044 // Note we will *not* get here if we are replacing an already replaced
2045 // default value - that is handled as eOperationChanging.
2047 // So this is a hybrid of eOperationAdding (as we are writing a new entry
2048 // to the DB) and eOperationChanging (as we are replacing the in-memory
2049 // repr and sending a "changed" notification).
2051 // We want a new ID even if not writing to the DB, so the modified entry
2052 // in memory doesn't have the magic cIDPermissionIsDefault value.
2055 // The default permission being replaced can't have session expiry or
2057 NS_ENSURE_TRUE(entry
->GetPermissions()[index
].mExpireType
!=
2058 nsIPermissionManager::EXPIRE_SESSION
,
2059 NS_ERROR_UNEXPECTED
);
2060 NS_ENSURE_TRUE(entry
->GetPermissions()[index
].mExpireType
!=
2061 nsIPermissionManager::EXPIRE_POLICY
,
2062 NS_ERROR_UNEXPECTED
);
2063 // We don't support the new entry having any expiry - supporting that
2064 // would make things far more complex and none of the permissions we set
2065 // as a default support that.
2066 NS_ENSURE_TRUE(aExpireType
== EXPIRE_NEVER
, NS_ERROR_UNEXPECTED
);
2068 // update the existing entry in memory.
2069 entry
->GetPermissions()[index
].mID
= id
;
2070 entry
->GetPermissions()[index
].mPermission
= aPermission
;
2071 entry
->GetPermissions()[index
].mExpireType
= aExpireType
;
2072 entry
->GetPermissions()[index
].mExpireTime
= aExpireTime
;
2073 entry
->GetPermissions()[index
].mModificationTime
= aModificationTime
;
2075 // If requested, create the entry in the DB.
2076 if (aDBOperation
== eWriteToDB
&&
2077 IsPersistentExpire(aExpireType
, aType
)) {
2078 UpdateDB(eOperationAdding
, id
, origin
, aType
, aPermission
, aExpireType
,
2079 aExpireTime
, aModificationTime
);
2082 if (aNotifyOperation
== eNotify
) {
2083 NotifyObserversWithPermission(aPrincipal
, mTypeArray
[typeIndex
],
2084 aPermission
, aExpireType
, aExpireTime
,
2085 aModificationTime
, u
"changed");
2095 PermissionManager::RemoveFromPrincipal(nsIPrincipal
* aPrincipal
,
2096 const nsACString
& aType
) {
2097 ENSURE_NOT_CHILD_PROCESS
;
2098 NS_ENSURE_ARG_POINTER(aPrincipal
);
2100 // System principals are never added to the database, no need to remove them.
2101 if (aPrincipal
->IsSystemPrincipal()) {
2105 // Permissions may not be added to expanded principals.
2106 if (IsExpandedPrincipal(aPrincipal
)) {
2107 return NS_ERROR_INVALID_ARG
;
2110 // AddInternal() handles removal, just let it do the work
2111 return AddInternal(aPrincipal
, aType
, nsIPermissionManager::UNKNOWN_ACTION
, 0,
2112 nsIPermissionManager::EXPIRE_NEVER
, 0, 0, eNotify
,
2117 PermissionManager::RemovePermission(nsIPermission
* aPerm
) {
2121 nsCOMPtr
<nsIPrincipal
> principal
;
2122 nsresult rv
= aPerm
->GetPrincipal(getter_AddRefs(principal
));
2123 NS_ENSURE_SUCCESS(rv
, rv
);
2126 rv
= aPerm
->GetType(type
);
2127 NS_ENSURE_SUCCESS(rv
, rv
);
2129 // Permissions are uniquely identified by their principal and type.
2130 // We remove the permission using these two pieces of data.
2131 return RemoveFromPrincipal(principal
, type
);
2135 PermissionManager::RemoveAll() {
2136 ENSURE_NOT_CHILD_PROCESS
;
2137 return RemoveAllInternal(true);
2141 PermissionManager::RemoveAllSince(int64_t aSince
) {
2142 ENSURE_NOT_CHILD_PROCESS
;
2143 return RemoveAllModifiedSince(aSince
);
2147 nsresult
PermissionManager::RemovePermissionEntries(T aCondition
) {
2148 EnsureReadCompleted();
2150 Vector
<std::tuple
<nsCOMPtr
<nsIPrincipal
>, nsCString
, nsCString
>, 10> array
;
2151 for (const PermissionHashKey
& entry
: mPermissionTable
) {
2152 for (const auto& permEntry
: entry
.GetPermissions()) {
2153 if (!aCondition(permEntry
)) {
2157 nsCOMPtr
<nsIPrincipal
> principal
;
2158 nsresult rv
= GetPrincipalFromOrigin(
2159 entry
.GetKey()->mOrigin
,
2160 IsOAForceStripPermission(mTypeArray
[permEntry
.mType
]),
2161 getter_AddRefs(principal
));
2162 if (NS_FAILED(rv
)) {
2166 if (!array
.emplaceBack(principal
, mTypeArray
[permEntry
.mType
],
2167 entry
.GetKey()->mOrigin
)) {
2173 for (auto& i
: array
) {
2174 // AddInternal handles removal, so let it do the work...
2176 std::get
<0>(i
), std::get
<1>(i
), nsIPermissionManager::UNKNOWN_ACTION
, 0,
2177 nsIPermissionManager::EXPIRE_NEVER
, 0, 0, PermissionManager::eNotify
,
2178 PermissionManager::eWriteToDB
, false, &std::get
<2>(i
));
2181 // now re-import any defaults as they may now be required if we just deleted
2183 ImportLatestDefaults();
2188 PermissionManager::RemoveByType(const nsACString
& aType
) {
2189 ENSURE_NOT_CHILD_PROCESS
;
2191 int32_t typeIndex
= GetTypeIndex(aType
, false);
2192 // If type == -1, the type isn't known,
2193 // so just return NS_OK
2194 if (typeIndex
== -1) {
2198 return RemovePermissionEntries(
2199 [typeIndex
](const PermissionEntry
& aPermEntry
) {
2200 return static_cast<uint32_t>(typeIndex
) == aPermEntry
.mType
;
2205 PermissionManager::RemoveByTypeSince(const nsACString
& aType
,
2206 int64_t aModificationTime
) {
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
, aModificationTime
](const PermissionEntry
& aPermEntry
) {
2218 return uint32_t(typeIndex
) == aPermEntry
.mType
&&
2219 aModificationTime
<= aPermEntry
.mModificationTime
;
2223 void PermissionManager::CloseDB(CloseDBNextOp aNextOp
) {
2224 EnsureReadCompleted();
2228 nsCOMPtr
<nsIInputStream
> defaultsInputStream
;
2229 if (aNextOp
== eRebuldOnSuccess
) {
2230 defaultsInputStream
= GetDefaultsInputStream();
2233 RefPtr
<PermissionManager
> self
= this;
2234 mThread
->Dispatch(NS_NewRunnableFunction(
2235 "PermissionManager::CloseDB", [self
, aNextOp
, defaultsInputStream
] {
2236 auto data
= self
->mThreadBoundData
.Access();
2237 // Null the statements, this will finalize them.
2238 data
->mStmtInsert
= nullptr;
2239 data
->mStmtDelete
= nullptr;
2240 data
->mStmtUpdate
= nullptr;
2241 if (data
->mDBConn
) {
2242 DebugOnly
<nsresult
> rv
= data
->mDBConn
->Close();
2243 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2244 data
->mDBConn
= nullptr;
2246 if (aNextOp
== eRebuldOnSuccess
) {
2247 self
->TryInitDB(true, defaultsInputStream
);
2251 if (aNextOp
== eShutdown
) {
2252 NS_DispatchToMainThread(NS_NewRunnableFunction(
2253 "PermissionManager::MaybeCompleteShutdown",
2254 [self
] { self
->MaybeCompleteShutdown(); }));
2259 nsresult
PermissionManager::RemoveAllFromIPC() {
2260 MOZ_ASSERT(IsChildProcess());
2262 // Remove from memory and notify immediately. Since the in-memory
2263 // database is authoritative, we do not need confirmation from the
2264 // on-disk database to notify observers.
2265 RemoveAllFromMemory();
2270 nsresult
PermissionManager::RemoveAllInternal(bool aNotifyObservers
) {
2271 ENSURE_NOT_CHILD_PROCESS
;
2273 EnsureReadCompleted();
2275 // Let's broadcast the removeAll() to any content process.
2276 nsTArray
<ContentParent
*> parents
;
2277 ContentParent::GetAll(parents
);
2278 for (ContentParent
* parent
: parents
) {
2279 Unused
<< parent
->SendRemoveAllPermissions();
2282 // Remove from memory and notify immediately. Since the in-memory
2283 // database is authoritative, we do not need confirmation from the
2284 // on-disk database to notify observers.
2285 RemoveAllFromMemory();
2287 // Re-import the defaults
2288 ImportLatestDefaults();
2290 if (aNotifyObservers
) {
2291 NotifyObservers(nullptr, u
"cleared");
2294 RefPtr
<PermissionManager
> self
= this;
2296 NS_NewRunnableFunction("PermissionManager::RemoveAllInternal", [self
] {
2297 auto data
= self
->mThreadBoundData
.Access();
2299 if (self
->mState
== eClosed
|| !data
->mDBConn
) {
2305 data
->mDBConn
->ExecuteSimpleSQL("DELETE FROM moz_perms"_ns
);
2306 if (NS_WARN_IF(NS_FAILED(rv
))) {
2307 NS_DispatchToMainThread(NS_NewRunnableFunction(
2308 "PermissionManager::RemoveAllInternal-Failure",
2309 [self
] { self
->CloseDB(eRebuldOnSuccess
); }));
2317 PermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal
* aPrincipal
,
2318 const nsACString
& aType
,
2319 uint32_t* aPermission
) {
2320 return CommonTestPermission(aPrincipal
, -1, aType
, aPermission
,
2321 nsIPermissionManager::UNKNOWN_ACTION
, false, true,
2326 PermissionManager::TestExactPermanentPermission(nsIPrincipal
* aPrincipal
,
2327 const nsACString
& aType
,
2328 uint32_t* aPermission
) {
2329 return CommonTestPermission(aPrincipal
, -1, aType
, aPermission
,
2330 nsIPermissionManager::UNKNOWN_ACTION
, false, true,
2334 nsresult
PermissionManager::LegacyTestPermissionFromURI(
2335 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
,
2336 const nsACString
& aType
, uint32_t* aPermission
) {
2337 return CommonTestPermission(aURI
, aOriginAttributes
, -1, aType
, aPermission
,
2338 nsIPermissionManager::UNKNOWN_ACTION
, false,
2343 PermissionManager::TestPermissionFromPrincipal(nsIPrincipal
* aPrincipal
,
2344 const nsACString
& aType
,
2345 uint32_t* aPermission
) {
2346 return CommonTestPermission(aPrincipal
, -1, aType
, aPermission
,
2347 nsIPermissionManager::UNKNOWN_ACTION
, false,
2352 PermissionManager::GetPermissionObject(nsIPrincipal
* aPrincipal
,
2353 const nsACString
& aType
,
2354 bool aExactHostMatch
,
2355 nsIPermission
** aResult
) {
2356 NS_ENSURE_ARG_POINTER(aPrincipal
);
2359 EnsureReadCompleted();
2361 if (aPrincipal
->IsSystemPrincipal()) {
2365 // Querying the permission object of an nsEP is non-sensical.
2366 if (IsExpandedPrincipal(aPrincipal
)) {
2367 return NS_ERROR_INVALID_ARG
;
2370 MOZ_ASSERT(PermissionAvailable(aPrincipal
, aType
));
2372 int32_t typeIndex
= GetTypeIndex(aType
, false);
2373 // If type == -1, the type isn't known,
2374 // so just return NS_OK
2375 if (typeIndex
== -1) return NS_OK
;
2377 PermissionHashKey
* entry
=
2378 GetPermissionHashKey(aPrincipal
, typeIndex
, aExactHostMatch
);
2383 // We don't call GetPermission(typeIndex) because that returns a fake
2384 // UNKNOWN_ACTION entry if there is no match.
2385 int32_t idx
= entry
->GetPermissionIndex(typeIndex
);
2390 nsCOMPtr
<nsIPrincipal
> principal
;
2391 nsresult rv
= GetPrincipalFromOrigin(entry
->GetKey()->mOrigin
,
2392 IsOAForceStripPermission(aType
),
2393 getter_AddRefs(principal
));
2394 NS_ENSURE_SUCCESS(rv
, rv
);
2396 PermissionEntry
& perm
= entry
->GetPermissions()[idx
];
2397 nsCOMPtr
<nsIPermission
> r
= Permission::Create(
2398 principal
, mTypeArray
[perm
.mType
], perm
.mPermission
, perm
.mExpireType
,
2399 perm
.mExpireTime
, perm
.mModificationTime
);
2400 if (NS_WARN_IF(!r
)) {
2401 return NS_ERROR_FAILURE
;
2407 nsresult
PermissionManager::CommonTestPermissionInternal(
2408 nsIPrincipal
* aPrincipal
, nsIURI
* aURI
,
2409 const OriginAttributes
* aOriginAttributes
, int32_t aTypeIndex
,
2410 const nsACString
& aType
, uint32_t* aPermission
, bool aExactHostMatch
,
2411 bool aIncludingSession
) {
2412 MOZ_ASSERT(aPrincipal
|| aURI
);
2413 NS_ENSURE_ARG_POINTER(aPrincipal
|| aURI
);
2414 MOZ_ASSERT_IF(aPrincipal
, !aURI
&& !aOriginAttributes
);
2415 MOZ_ASSERT_IF(aURI
|| aOriginAttributes
, !aPrincipal
);
2417 EnsureReadCompleted();
2421 nsCOMPtr
<nsIPrincipal
> prin
= aPrincipal
;
2424 prin
= BasePrincipal::CreateContentPrincipal(aURI
, OriginAttributes());
2428 MOZ_ASSERT(PermissionAvailable(prin
, aType
));
2432 PermissionHashKey
* entry
=
2433 aPrincipal
? GetPermissionHashKey(aPrincipal
, aTypeIndex
, aExactHostMatch
)
2434 : GetPermissionHashKey(aURI
, aOriginAttributes
, aTypeIndex
,
2436 if (!entry
|| (!aIncludingSession
&&
2437 entry
->GetPermission(aTypeIndex
).mNonSessionExpireType
==
2438 nsIPermissionManager::EXPIRE_SESSION
)) {
2442 *aPermission
= aIncludingSession
2443 ? entry
->GetPermission(aTypeIndex
).mPermission
2444 : entry
->GetPermission(aTypeIndex
).mNonSessionPermission
;
2449 // Helper function to filter permissions using a condition function.
2451 nsresult
PermissionManager::GetPermissionEntries(
2452 T aCondition
, nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2454 if (XRE_IsContentProcess()) {
2456 "Iterating over all permissions is not available in the "
2457 "content process, as not all permissions may be available.");
2458 return NS_ERROR_NOT_AVAILABLE
;
2461 EnsureReadCompleted();
2463 for (const PermissionHashKey
& entry
: mPermissionTable
) {
2464 for (const auto& permEntry
: entry
.GetPermissions()) {
2465 // Given how "default" permissions work and the possibility of them being
2466 // overridden with UNKNOWN_ACTION, we might see this value here - but we
2467 // do *not* want to return them via the enumerator.
2468 if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2472 // If the permission is expired, skip it. We're not deleting it here
2473 // because we're iterating over a lot of permissions.
2474 // It will be removed as part of the daily maintenance later.
2475 if (HasExpired(permEntry
.mExpireType
, permEntry
.mExpireTime
)) {
2479 if (!aCondition(permEntry
)) {
2483 nsCOMPtr
<nsIPrincipal
> principal
;
2484 nsresult rv
= GetPrincipalFromOrigin(
2485 entry
.GetKey()->mOrigin
,
2486 IsOAForceStripPermission(mTypeArray
[permEntry
.mType
]),
2487 getter_AddRefs(principal
));
2488 if (NS_FAILED(rv
)) {
2492 RefPtr
<nsIPermission
> permission
= Permission::Create(
2493 principal
, mTypeArray
[permEntry
.mType
], permEntry
.mPermission
,
2494 permEntry
.mExpireType
, permEntry
.mExpireTime
,
2495 permEntry
.mModificationTime
);
2496 if (NS_WARN_IF(!permission
)) {
2499 aResult
.AppendElement(std::move(permission
));
2506 NS_IMETHODIMP
PermissionManager::GetAll(
2507 nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2508 return GetPermissionEntries(
2509 [](const PermissionEntry
& aPermEntry
) { return true; }, aResult
);
2512 NS_IMETHODIMP
PermissionManager::GetAllByTypeSince(
2513 const nsACString
& aPrefix
, int64_t aSince
,
2514 nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2515 // Check that aSince is a reasonable point in time, not in the future
2516 if (aSince
> (PR_Now() / PR_USEC_PER_MSEC
)) {
2517 return NS_ERROR_INVALID_ARG
;
2519 return GetPermissionEntries(
2520 [&](const PermissionEntry
& aPermEntry
) {
2521 return mTypeArray
[aPermEntry
.mType
].Equals(aPrefix
) &&
2522 aSince
<= aPermEntry
.mModificationTime
;
2527 NS_IMETHODIMP
PermissionManager::GetAllWithTypePrefix(
2528 const nsACString
& aPrefix
, nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2529 return GetPermissionEntries(
2530 [&](const PermissionEntry
& aPermEntry
) {
2531 return StringBeginsWith(mTypeArray
[aPermEntry
.mType
], aPrefix
);
2536 NS_IMETHODIMP
PermissionManager::GetAllByTypes(
2537 const nsTArray
<nsCString
>& aTypes
,
2538 nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2539 if (aTypes
.IsEmpty()) {
2543 return GetPermissionEntries(
2544 [&](const PermissionEntry
& aPermEntry
) {
2545 return aTypes
.Contains(mTypeArray
[aPermEntry
.mType
]);
2550 nsresult
PermissionManager::GetAllForPrincipalHelper(
2551 nsIPrincipal
* aPrincipal
, bool aSiteScopePermissions
,
2552 nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2554 RefPtr
<PermissionKey
> key
= PermissionKey::CreateFromPrincipal(
2555 aPrincipal
, false, aSiteScopePermissions
, rv
);
2557 MOZ_ASSERT(NS_FAILED(rv
));
2560 PermissionHashKey
* entry
= mPermissionTable
.GetEntry(key
);
2562 nsTArray
<PermissionEntry
> strippedPerms
;
2563 rv
= GetStripPermsForPrincipal(aPrincipal
, aSiteScopePermissions
,
2565 if (NS_FAILED(rv
)) {
2570 for (const auto& permEntry
: entry
->GetPermissions()) {
2571 // Only return custom permissions
2572 if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2576 // If the permission is expired, skip it. We're not deleting it here
2577 // because we're iterating over a lot of permissions.
2578 // It will be removed as part of the daily maintenance later.
2579 if (HasExpired(permEntry
.mExpireType
, permEntry
.mExpireTime
)) {
2583 // Make sure that we only get site scoped permissions if this
2584 // helper is being invoked for that purpose.
2585 if (aSiteScopePermissions
!=
2586 IsSiteScopedPermission(mTypeArray
[permEntry
.mType
])) {
2590 // Stripped principal permissions overwrite regular ones
2591 // For each permission check if there is a stripped permission we should
2593 PermissionEntry perm
= permEntry
;
2594 nsTArray
<PermissionEntry
>::index_type index
= 0;
2595 for (const auto& strippedPerm
: strippedPerms
) {
2596 if (strippedPerm
.mType
== permEntry
.mType
) {
2597 perm
= strippedPerm
;
2598 strippedPerms
.RemoveElementAt(index
);
2604 RefPtr
<nsIPermission
> permission
= Permission::Create(
2605 aPrincipal
, mTypeArray
[perm
.mType
], perm
.mPermission
,
2606 perm
.mExpireType
, perm
.mExpireTime
, perm
.mModificationTime
);
2607 if (NS_WARN_IF(!permission
)) {
2610 aResult
.AppendElement(permission
);
2614 for (const auto& perm
: strippedPerms
) {
2615 RefPtr
<nsIPermission
> permission
= Permission::Create(
2616 aPrincipal
, mTypeArray
[perm
.mType
], perm
.mPermission
, perm
.mExpireType
,
2617 perm
.mExpireTime
, perm
.mModificationTime
);
2618 if (NS_WARN_IF(!permission
)) {
2621 aResult
.AppendElement(permission
);
2628 PermissionManager::GetAllForPrincipal(
2629 nsIPrincipal
* aPrincipal
, nsTArray
<RefPtr
<nsIPermission
>>& aResult
) {
2632 EnsureReadCompleted();
2634 MOZ_ASSERT(PermissionAvailable(aPrincipal
, ""_ns
));
2636 // First, append the non-site-scoped permissions.
2637 rv
= GetAllForPrincipalHelper(aPrincipal
, false, aResult
);
2638 NS_ENSURE_SUCCESS(rv
, rv
);
2640 // Second, append the site-scoped permissions.
2641 return GetAllForPrincipalHelper(aPrincipal
, true, aResult
);
2644 NS_IMETHODIMP
PermissionManager::Observe(nsISupports
* aSubject
,
2646 const char16_t
* someData
) {
2647 ENSURE_NOT_CHILD_PROCESS
;
2649 if (!nsCRT::strcmp(aTopic
, "profile-do-change") && !mPermissionsFile
) {
2650 // profile startup is complete, and we didn't have the permissions file
2651 // before; init the db from the new location
2653 } else if (!nsCRT::strcmp(aTopic
, "testonly-reload-permissions-from-disk")) {
2654 // Testing mechanism to reload all permissions from disk. Because the
2655 // permission manager automatically initializes itself at startup, tests
2656 // that directly manipulate the permissions database need some way to reload
2657 // the database for their changes to have any effect. This mechanism was
2658 // introduced when moving the permissions manager from on-demand startup to
2659 // always being initialized. This is not guarded by a pref because it's not
2660 // dangerous to reload permissions from disk, just bad for performance.
2661 RemoveAllFromMemory();
2664 } else if (!nsCRT::strcmp(aTopic
, OBSERVER_TOPIC_IDLE_DAILY
)) {
2665 PerformIdleDailyMaintenance();
2671 nsresult
PermissionManager::RemoveAllModifiedSince(int64_t aModificationTime
) {
2672 ENSURE_NOT_CHILD_PROCESS
;
2673 // Skip remove calls for default permissions to avoid
2674 // creating UNKNOWN_ACTION overrides in AddInternal
2675 return RemovePermissionEntries(
2676 [aModificationTime
](const PermissionEntry
& aPermEntry
) {
2677 return aModificationTime
<= aPermEntry
.mModificationTime
&&
2678 aPermEntry
.mID
!= cIDPermissionIsDefault
;
2683 PermissionManager::RemovePermissionsWithAttributes(const nsAString
& aPattern
) {
2684 ENSURE_NOT_CHILD_PROCESS
;
2685 OriginAttributesPattern pattern
;
2686 if (!pattern
.Init(aPattern
)) {
2687 return NS_ERROR_INVALID_ARG
;
2690 return RemovePermissionsWithAttributes(pattern
);
2693 nsresult
PermissionManager::RemovePermissionsWithAttributes(
2694 OriginAttributesPattern
& aPattern
) {
2695 EnsureReadCompleted();
2697 Vector
<std::tuple
<nsCOMPtr
<nsIPrincipal
>, nsCString
, nsCString
>, 10>
2699 for (const PermissionHashKey
& entry
: mPermissionTable
) {
2700 nsCOMPtr
<nsIPrincipal
> principal
;
2701 nsresult rv
= GetPrincipalFromOrigin(entry
.GetKey()->mOrigin
, false,
2702 getter_AddRefs(principal
));
2703 if (NS_FAILED(rv
)) {
2707 if (!aPattern
.Matches(principal
->OriginAttributesRef())) {
2711 for (const auto& permEntry
: entry
.GetPermissions()) {
2712 if (!permissions
.emplaceBack(principal
, mTypeArray
[permEntry
.mType
],
2713 entry
.GetKey()->mOrigin
)) {
2719 for (auto& i
: permissions
) {
2721 std::get
<0>(i
), std::get
<1>(i
), nsIPermissionManager::UNKNOWN_ACTION
, 0,
2722 nsIPermissionManager::EXPIRE_NEVER
, 0, 0, PermissionManager::eNotify
,
2723 PermissionManager::eWriteToDB
, false, &std::get
<2>(i
));
2729 nsresult
PermissionManager::GetStripPermsForPrincipal(
2730 nsIPrincipal
* aPrincipal
, bool aSiteScopePermissions
,
2731 nsTArray
<PermissionEntry
>& aResult
) {
2733 aResult
.SetCapacity(kStripOAPermissions
.size());
2736 # pragma clang diagnostic push
2737 # pragma clang diagnostic ignored "-Wunreachable-code-return"
2739 // No special strip permissions
2740 if (kStripOAPermissions
.empty()) {
2744 # pragma clang diagnostic pop
2748 // Create a key for the principal, but strip any origin attributes.
2749 // The key must be created aware of whether or not we are scoping to site.
2750 RefPtr
<PermissionKey
> key
= PermissionKey::CreateFromPrincipal(
2751 aPrincipal
, true, aSiteScopePermissions
, rv
);
2753 MOZ_ASSERT(NS_FAILED(rv
));
2757 PermissionHashKey
* hashKey
= mPermissionTable
.GetEntry(key
);
2762 for (const auto& permType
: kStripOAPermissions
) {
2763 // if the permission type's site scoping does not match this function call,
2764 // we don't care about it, so continue.
2765 // As of time of writing, this never happens when aSiteScopePermissions
2766 // is true because there is no common permission between kStripOAPermissions
2767 // and kSiteScopedPermissions
2768 if (aSiteScopePermissions
!= IsSiteScopedPermission(permType
)) {
2771 int32_t index
= GetTypeIndex(permType
, false);
2775 PermissionEntry perm
= hashKey
->GetPermission(index
);
2776 if (perm
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2779 aResult
.AppendElement(perm
);
2785 int32_t PermissionManager::GetTypeIndex(const nsACString
& aType
, bool aAdd
) {
2786 for (uint32_t i
= 0; i
< mTypeArray
.length(); ++i
) {
2787 if (mTypeArray
[i
].Equals(aType
)) {
2793 // Not found, but that is ok - we were just looking.
2797 // This type was not registered before.
2798 // append it to the array, without copy-constructing the string
2799 if (!mTypeArray
.emplaceBack(aType
)) {
2803 return mTypeArray
.length() - 1;
2806 PermissionManager::PermissionHashKey
* PermissionManager::GetPermissionHashKey(
2807 nsIPrincipal
* aPrincipal
, uint32_t aType
, bool aExactHostMatch
) {
2808 EnsureReadCompleted();
2810 MOZ_ASSERT(PermissionAvailable(aPrincipal
, mTypeArray
[aType
]));
2813 RefPtr
<PermissionKey
> key
= PermissionKey::CreateFromPrincipal(
2814 aPrincipal
, IsOAForceStripPermission(mTypeArray
[aType
]),
2815 IsSiteScopedPermission(mTypeArray
[aType
]), rv
);
2820 PermissionHashKey
* entry
= mPermissionTable
.GetEntry(key
);
2823 PermissionEntry permEntry
= entry
->GetPermission(aType
);
2825 // if the entry is expired, remove and keep looking for others.
2826 if (HasExpired(permEntry
.mExpireType
, permEntry
.mExpireTime
)) {
2828 RemoveFromPrincipal(aPrincipal
, mTypeArray
[aType
]);
2829 } else if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2838 // If aExactHostMatch wasn't true, we can check if the base domain has a
2839 // permission entry.
2840 if (!aExactHostMatch
) {
2841 nsCOMPtr
<nsIPrincipal
> principal
= aPrincipal
->GetNextSubDomainPrincipal();
2843 return GetPermissionHashKey(principal
, aType
, aExactHostMatch
);
2847 // No entry, really...
2851 PermissionManager::PermissionHashKey
* PermissionManager::GetPermissionHashKey(
2852 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
, uint32_t aType
,
2853 bool aExactHostMatch
) {
2858 nsCOMPtr
<nsIPrincipal
> principal
;
2859 nsresult rv
= NS_OK
;
2861 rv
= GetPrincipal(aURI
, getter_AddRefs(principal
));
2863 MOZ_ASSERT_IF(NS_SUCCEEDED(rv
),
2864 PermissionAvailable(principal
, mTypeArray
[aType
]));
2869 RefPtr
<PermissionKey
> key
;
2871 if (aOriginAttributes
) {
2872 key
= PermissionKey::CreateFromURIAndOriginAttributes(
2873 aURI
, aOriginAttributes
, IsOAForceStripPermission(mTypeArray
[aType
]),
2876 key
= PermissionKey::CreateFromURI(aURI
, rv
);
2883 PermissionHashKey
* entry
= mPermissionTable
.GetEntry(key
);
2886 PermissionEntry permEntry
= entry
->GetPermission(aType
);
2888 // if the entry is expired, remove and keep looking for others.
2889 if (HasExpired(permEntry
.mExpireType
, permEntry
.mExpireTime
)) {
2891 // If we need to remove a permission we mint a principal. This is a bit
2892 // inefficient, but hopefully this code path isn't super common.
2893 nsCOMPtr
<nsIPrincipal
> principal
;
2895 nsresult rv
= GetPrincipal(aURI
, getter_AddRefs(principal
));
2896 if (NS_WARN_IF(NS_FAILED(rv
))) {
2900 RemoveFromPrincipal(principal
, mTypeArray
[aType
]);
2901 } else if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
2910 // If aExactHostMatch wasn't true, we can check if the base domain has a
2911 // permission entry.
2912 if (!aExactHostMatch
) {
2913 nsCOMPtr
<nsIURI
> uri
;
2915 uri
= GetNextSubDomainURI(aURI
);
2918 return GetPermissionHashKey(uri
, aOriginAttributes
, aType
,
2923 // No entry, really...
2927 nsresult
PermissionManager::RemoveAllFromMemory() {
2930 mPermissionTable
.Clear();
2935 // wrapper function for mangling (host,type,perm,expireType,expireTime)
2936 // set into an nsIPermission.
2937 void PermissionManager::NotifyObserversWithPermission(
2938 nsIPrincipal
* aPrincipal
, const nsACString
& aType
, uint32_t aPermission
,
2939 uint32_t aExpireType
, int64_t aExpireTime
, int64_t aModificationTime
,
2940 const char16_t
* aData
) {
2941 nsCOMPtr
<nsIPermission
> permission
=
2942 Permission::Create(aPrincipal
, aType
, aPermission
, aExpireType
,
2943 aExpireTime
, aModificationTime
);
2944 if (permission
) NotifyObservers(permission
, aData
);
2947 // notify observers that the permission list changed. there are four possible
2948 // values for aData:
2949 // "deleted" means a permission was deleted. aPermission is the deleted
2950 // permission. "added" means a permission was added. aPermission is the added
2951 // permission. "changed" means a permission was altered. aPermission is the new
2952 // permission. "cleared" means the entire permission list was cleared.
2953 // aPermission is null.
2954 void PermissionManager::NotifyObservers(nsIPermission
* aPermission
,
2955 const char16_t
* aData
) {
2956 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
2957 if (observerService
)
2958 observerService
->NotifyObservers(aPermission
, kPermissionChangeNotification
,
2962 nsresult
PermissionManager::Read(const MonitorAutoLock
& aProofOfLock
) {
2963 ENSURE_NOT_CHILD_PROCESS
;
2965 MOZ_ASSERT(!NS_IsMainThread());
2966 auto data
= mThreadBoundData
.Access();
2970 nsCOMPtr
<mozIStorageStatement
> stmt
;
2972 // Let's retrieve the last used ID.
2973 rv
= data
->mDBConn
->CreateStatement(
2974 nsLiteralCString("SELECT MAX(id) FROM moz_perms"), getter_AddRefs(stmt
));
2975 NS_ENSURE_SUCCESS(rv
, rv
);
2977 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
2978 int64_t id
= stmt
->AsInt64(0);
2982 rv
= data
->mDBConn
->CreateStatement(
2984 "SELECT id, origin, type, permission, expireType, "
2985 "expireTime, modificationTime "
2986 "FROM moz_perms WHERE expireType != ?1 OR expireTime > ?2"),
2987 getter_AddRefs(stmt
));
2988 NS_ENSURE_SUCCESS(rv
, rv
);
2990 rv
= stmt
->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME
);
2991 NS_ENSURE_SUCCESS(rv
, rv
);
2993 rv
= stmt
->BindInt64ByIndex(1, EXPIRY_NOW
);
2994 NS_ENSURE_SUCCESS(rv
, rv
);
2996 bool readError
= false;
2998 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
3001 // explicitly set our entry id counter for use in AddInternal(),
3002 // and keep track of the largest id so we know where to pick up.
3003 entry
.mId
= stmt
->AsInt64(0);
3004 MOZ_ASSERT(entry
.mId
<= mLargestID
);
3006 rv
= stmt
->GetUTF8String(1, entry
.mOrigin
);
3007 if (NS_FAILED(rv
)) {
3012 rv
= stmt
->GetUTF8String(2, entry
.mType
);
3013 if (NS_FAILED(rv
)) {
3018 entry
.mPermission
= stmt
->AsInt32(3);
3019 entry
.mExpireType
= stmt
->AsInt32(4);
3021 // convert into int64_t values (milliseconds)
3022 entry
.mExpireTime
= stmt
->AsInt64(5);
3023 entry
.mModificationTime
= stmt
->AsInt64(6);
3025 entry
.mFromMigration
= false;
3027 mReadEntries
.AppendElement(entry
);
3031 NS_ERROR("Error occured while reading the permissions database!");
3032 return NS_ERROR_FAILURE
;
3038 void PermissionManager::CompleteMigrations() {
3039 MOZ_ASSERT(NS_IsMainThread());
3040 MOZ_ASSERT(mState
== eReady
);
3044 nsTArray
<MigrationEntry
> entries
;
3046 MonitorAutoLock
lock(mMonitor
);
3047 entries
= std::move(mMigrationEntries
);
3050 for (const MigrationEntry
& entry
: entries
) {
3051 rv
= UpgradeHostToOriginAndInsert(
3052 entry
.mHost
, entry
.mType
, entry
.mPermission
, entry
.mExpireType
,
3053 entry
.mExpireTime
, entry
.mModificationTime
,
3054 [&](const nsACString
& aOrigin
, const nsCString
& aType
,
3055 uint32_t aPermission
, uint32_t aExpireType
, int64_t aExpireTime
,
3056 int64_t aModificationTime
) {
3057 MaybeAddReadEntryFromMigration(aOrigin
, aType
, aPermission
,
3058 aExpireType
, aExpireTime
,
3059 aModificationTime
, entry
.mId
);
3062 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
3066 void PermissionManager::CompleteRead() {
3067 MOZ_ASSERT(NS_IsMainThread());
3068 MOZ_ASSERT(mState
== eReady
);
3072 nsTArray
<ReadEntry
> entries
;
3074 MonitorAutoLock
lock(mMonitor
);
3075 entries
= std::move(mReadEntries
);
3078 for (const ReadEntry
& entry
: entries
) {
3079 nsCOMPtr
<nsIPrincipal
> principal
;
3080 rv
= GetPrincipalFromOrigin(entry
.mOrigin
,
3081 IsOAForceStripPermission(entry
.mType
),
3082 getter_AddRefs(principal
));
3083 if (NS_WARN_IF(NS_FAILED(rv
))) {
3087 DBOperationType op
= entry
.mFromMigration
? eWriteToDB
: eNoDBOperation
;
3089 rv
= AddInternal(principal
, entry
.mType
, entry
.mPermission
, entry
.mId
,
3090 entry
.mExpireType
, entry
.mExpireTime
,
3091 entry
.mModificationTime
, eDontNotify
, op
, false,
3093 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
3097 void PermissionManager::MaybeAddReadEntryFromMigration(
3098 const nsACString
& aOrigin
, const nsCString
& aType
, uint32_t aPermission
,
3099 uint32_t aExpireType
, int64_t aExpireTime
, int64_t aModificationTime
,
3101 MonitorAutoLock
lock(mMonitor
);
3103 // We convert a migration to a ReadEntry only if we don't have an existing
3104 // ReadEntry for the same origin + type.
3105 for (const ReadEntry
& entry
: mReadEntries
) {
3106 if (entry
.mOrigin
== aOrigin
&& entry
.mType
== aType
) {
3113 entry
.mOrigin
= aOrigin
;
3114 entry
.mType
= aType
;
3115 entry
.mPermission
= aPermission
;
3116 entry
.mExpireType
= aExpireType
;
3117 entry
.mExpireTime
= aExpireTime
;
3118 entry
.mModificationTime
= aModificationTime
;
3119 entry
.mFromMigration
= true;
3121 mReadEntries
.AppendElement(entry
);
3124 void PermissionManager::UpdateDB(OperationType aOp
, int64_t aID
,
3125 const nsACString
& aOrigin
,
3126 const nsACString
& aType
, uint32_t aPermission
,
3127 uint32_t aExpireType
, int64_t aExpireTime
,
3128 int64_t aModificationTime
) {
3129 ENSURE_NOT_CHILD_PROCESS_NORET
;
3131 MOZ_ASSERT(NS_IsMainThread());
3132 EnsureReadCompleted();
3134 nsCString
origin(aOrigin
);
3135 nsCString
type(aType
);
3137 RefPtr
<PermissionManager
> self
= this;
3138 mThread
->Dispatch(NS_NewRunnableFunction(
3139 "PermissionManager::UpdateDB",
3140 [self
, aOp
, aID
, origin
, type
, aPermission
, aExpireType
, aExpireTime
,
3141 aModificationTime
] {
3144 auto data
= self
->mThreadBoundData
.Access();
3146 if (self
->mState
== eClosed
|| !data
->mDBConn
) {
3147 // no statement is ok - just means we don't have a profile
3151 mozIStorageStatement
* stmt
= nullptr;
3153 case eOperationAdding
: {
3154 stmt
= data
->mStmtInsert
;
3156 rv
= stmt
->BindInt64ByIndex(0, aID
);
3157 if (NS_FAILED(rv
)) break;
3159 rv
= stmt
->BindUTF8StringByIndex(1, origin
);
3160 if (NS_FAILED(rv
)) break;
3162 rv
= stmt
->BindUTF8StringByIndex(2, type
);
3163 if (NS_FAILED(rv
)) break;
3165 rv
= stmt
->BindInt32ByIndex(3, aPermission
);
3166 if (NS_FAILED(rv
)) break;
3168 rv
= stmt
->BindInt32ByIndex(4, aExpireType
);
3169 if (NS_FAILED(rv
)) break;
3171 rv
= stmt
->BindInt64ByIndex(5, aExpireTime
);
3172 if (NS_FAILED(rv
)) break;
3174 rv
= stmt
->BindInt64ByIndex(6, aModificationTime
);
3178 case eOperationRemoving
: {
3179 stmt
= data
->mStmtDelete
;
3180 rv
= stmt
->BindInt64ByIndex(0, aID
);
3184 case eOperationChanging
: {
3185 stmt
= data
->mStmtUpdate
;
3187 rv
= stmt
->BindInt64ByIndex(0, aID
);
3188 if (NS_FAILED(rv
)) break;
3190 rv
= stmt
->BindInt32ByIndex(1, aPermission
);
3191 if (NS_FAILED(rv
)) break;
3193 rv
= stmt
->BindInt32ByIndex(2, aExpireType
);
3194 if (NS_FAILED(rv
)) break;
3196 rv
= stmt
->BindInt64ByIndex(3, aExpireTime
);
3197 if (NS_FAILED(rv
)) break;
3199 rv
= stmt
->BindInt64ByIndex(4, aModificationTime
);
3204 MOZ_ASSERT_UNREACHABLE("need a valid operation in UpdateDB()!");
3205 rv
= NS_ERROR_UNEXPECTED
;
3210 if (NS_FAILED(rv
)) {
3211 NS_WARNING("db change failed!");
3215 rv
= stmt
->Execute();
3216 MOZ_ASSERT(NS_SUCCEEDED(rv
));
3220 bool PermissionManager::GetPermissionsFromOriginOrKey(
3221 const nsACString
& aOrigin
, const nsACString
& aKey
,
3222 nsTArray
<IPC::Permission
>& aPerms
) {
3223 EnsureReadCompleted();
3226 if (NS_WARN_IF(XRE_IsContentProcess())) {
3230 for (const PermissionHashKey
& entry
: mPermissionTable
) {
3231 nsAutoCString permissionKey
;
3232 if (aOrigin
.IsEmpty()) {
3233 // We can't check for individual OA strip perms here.
3234 // Don't force strip origin attributes.
3235 GetKeyForOrigin(entry
.GetKey()->mOrigin
, false, false, permissionKey
);
3237 // If the keys don't match, and we aren't getting the default "" key, then
3238 // we can exit early. We have to keep looking if we're getting the default
3239 // key, as we may see a preload permission which should be transmitted.
3240 if (aKey
!= permissionKey
&& !aKey
.IsEmpty()) {
3243 } else if (aOrigin
!= entry
.GetKey()->mOrigin
) {
3244 // If the origins don't match, then we can exit early. We have to keep
3245 // looking if we're getting the default origin, as we may see a preload
3246 // permission which should be transmitted.
3250 for (const auto& permEntry
: entry
.GetPermissions()) {
3251 // Given how "default" permissions work and the possibility of them
3252 // being overridden with UNKNOWN_ACTION, we might see this value here -
3253 // but we do not want to send it to the content process.
3254 if (permEntry
.mPermission
== nsIPermissionManager::UNKNOWN_ACTION
) {
3258 bool isPreload
= IsPreloadPermission(mTypeArray
[permEntry
.mType
]);
3260 if (aOrigin
.IsEmpty()) {
3261 shouldAppend
= (isPreload
&& aKey
.IsEmpty()) ||
3262 (!isPreload
&& aKey
== permissionKey
);
3264 shouldAppend
= (!isPreload
&& aOrigin
== entry
.GetKey()->mOrigin
);
3267 aPerms
.AppendElement(
3268 IPC::Permission(entry
.GetKey()->mOrigin
,
3269 mTypeArray
[permEntry
.mType
], permEntry
.mPermission
,
3270 permEntry
.mExpireType
, permEntry
.mExpireTime
));
3278 void PermissionManager::SetPermissionsWithKey(
3279 const nsACString
& aPermissionKey
, nsTArray
<IPC::Permission
>& aPerms
) {
3280 if (NS_WARN_IF(XRE_IsParentProcess())) {
3284 RefPtr
<GenericNonExclusivePromise::Private
> promise
;
3286 mPermissionKeyPromiseMap
.Get(aPermissionKey
, getter_AddRefs(promise
));
3288 MOZ_ASSERT(foundKey
);
3289 // NOTE: This will resolve asynchronously, so we can mark it as resolved
3290 // now, and be confident that we will have filled in the database before any
3292 promise
->Resolve(true, __func__
);
3293 } else if (foundKey
) {
3294 // NOTE: We shouldn't be sent two InitializePermissionsWithKey for the same
3295 // key, but it's possible.
3298 mPermissionKeyPromiseMap
.InsertOrUpdate(
3299 aPermissionKey
, RefPtr
<GenericNonExclusivePromise::Private
>{});
3301 // Add the permissions locally to our process
3302 for (IPC::Permission
& perm
: aPerms
) {
3303 nsCOMPtr
<nsIPrincipal
> principal
;
3305 GetPrincipalFromOrigin(perm
.origin
, IsOAForceStripPermission(perm
.type
),
3306 getter_AddRefs(principal
));
3307 if (NS_WARN_IF(NS_FAILED(rv
))) {
3312 nsAutoCString permissionKey
;
3313 GetKeyForPermission(principal
, perm
.type
, permissionKey
);
3314 MOZ_ASSERT(permissionKey
== aPermissionKey
,
3315 "The permission keys which were sent over should match!");
3318 // The child process doesn't care about modification times - it neither
3319 // reads nor writes, nor removes them based on the date - so 0 (which
3320 // will end up as now()) is fine.
3321 uint64_t modificationTime
= 0;
3322 AddInternal(principal
, perm
.type
, perm
.capability
, 0, perm
.expireType
,
3323 perm
.expireTime
, modificationTime
, eNotify
, eNoDBOperation
,
3324 true /* ignoreSessionPermissions */);
3329 nsresult
PermissionManager::GetKeyForOrigin(const nsACString
& aOrigin
,
3331 bool aSiteScopePermissions
,
3335 // We only key origins for http, https URIs. All origins begin with
3336 // the URL which they apply to, which means that they should begin with their
3337 // scheme in the case where they are one of these interesting URIs. We don't
3338 // want to actually parse the URL here however, because this can be called on
3340 if (!StringBeginsWith(aOrigin
, "http:"_ns
) &&
3341 !StringBeginsWith(aOrigin
, "https:"_ns
)) {
3345 // We need to look at the originAttributes if they are present, to make sure
3346 // to remove any which we don't want. We put the rest of the origin, not
3347 // including the attributes, into the key.
3348 OriginAttributes attrs
;
3349 if (!attrs
.PopulateFromOrigin(aOrigin
, aKey
)) {
3354 MaybeStripOriginAttributes(aForceStripOA
, attrs
);
3357 // Parse the origin string into a principal, and extract some useful
3358 // information from it for assertions.
3359 nsCOMPtr
<nsIPrincipal
> dbgPrincipal
;
3360 MOZ_ALWAYS_SUCCEEDS(GetPrincipalFromOrigin(aOrigin
, aForceStripOA
,
3361 getter_AddRefs(dbgPrincipal
)));
3362 MOZ_ASSERT(dbgPrincipal
->SchemeIs("http") || dbgPrincipal
->SchemeIs("https"));
3363 MOZ_ASSERT(dbgPrincipal
->OriginAttributesRef() == attrs
);
3366 // If it is needed, turn the origin into its site-origin
3367 if (aSiteScopePermissions
) {
3368 nsCOMPtr
<nsIURI
> uri
;
3369 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aKey
);
3370 if (!NS_WARN_IF(NS_FAILED(rv
))) {
3372 rv
= nsEffectiveTLDService::GetInstance()->GetSite(uri
, site
);
3373 if (!NS_WARN_IF(NS_FAILED(rv
))) {
3379 // Append the stripped suffix to the output origin key.
3380 nsAutoCString suffix
;
3381 attrs
.CreateSuffix(suffix
);
3382 aKey
.Append(suffix
);
3388 nsresult
PermissionManager::GetKeyForPrincipal(nsIPrincipal
* aPrincipal
,
3390 bool aSiteScopePermissions
,
3392 nsAutoCString origin
;
3393 nsresult rv
= aPrincipal
->GetOrigin(origin
);
3394 if (NS_WARN_IF(NS_FAILED(rv
))) {
3398 return GetKeyForOrigin(origin
, aForceStripOA
, aSiteScopePermissions
, aKey
);
3402 nsresult
PermissionManager::GetKeyForPermission(nsIPrincipal
* aPrincipal
,
3403 const nsACString
& aType
,
3405 // Preload permissions have the "" key.
3406 if (IsPreloadPermission(aType
)) {
3411 return GetKeyForPrincipal(aPrincipal
, IsOAForceStripPermission(aType
),
3412 IsSiteScopedPermission(aType
), aKey
);
3416 nsTArray
<std::pair
<nsCString
, nsCString
>>
3417 PermissionManager::GetAllKeysForPrincipal(nsIPrincipal
* aPrincipal
) {
3418 MOZ_ASSERT(aPrincipal
);
3420 nsTArray
<std::pair
<nsCString
, nsCString
>> pairs
;
3421 nsCOMPtr
<nsIPrincipal
> prin
= aPrincipal
;
3424 // Add the pair to the list
3425 std::pair
<nsCString
, nsCString
>* pair
=
3426 pairs
.AppendElement(std::make_pair(""_ns
, ""_ns
));
3427 // We can't check for individual OA strip perms here.
3428 // Don't force strip origin attributes.
3429 GetKeyForPrincipal(prin
, false, false, pair
->first
);
3431 // On origins with a derived key set to an empty string
3432 // (basically any non-web URI scheme), we want to make sure
3433 // to return earlier, and leave [("", "")] as the resulting
3434 // pairs (but still run the same debug assertions near the
3435 // end of this method).
3436 if (pair
->first
.IsEmpty()) {
3440 Unused
<< GetOriginFromPrincipal(prin
, false, pair
->second
);
3441 prin
= prin
->GetNextSubDomainPrincipal();
3442 // Get the next subdomain principal and loop back around.
3445 MOZ_ASSERT(pairs
.Length() >= 1,
3446 "Every principal should have at least one pair item.");
3450 bool PermissionManager::PermissionAvailable(nsIPrincipal
* aPrincipal
,
3451 const nsACString
& aType
) {
3452 EnsureReadCompleted();
3454 if (XRE_IsContentProcess()) {
3455 nsAutoCString permissionKey
;
3456 // NOTE: GetKeyForPermission accepts a null aType.
3457 GetKeyForPermission(aPrincipal
, aType
, permissionKey
);
3459 // If we have a pending promise for the permission key in question, we don't
3460 // have the permission available, so report a warning and return false.
3461 RefPtr
<GenericNonExclusivePromise::Private
> promise
;
3462 if (!mPermissionKeyPromiseMap
.Get(permissionKey
, getter_AddRefs(promise
)) ||
3464 // Emit a useful diagnostic warning with the permissionKey for the process
3465 // which hasn't received permissions yet.
3466 NS_WARNING(nsPrintfCString("This content process hasn't received the "
3467 "permissions for %s yet",
3468 permissionKey
.get())
3476 void PermissionManager::WhenPermissionsAvailable(nsIPrincipal
* aPrincipal
,
3477 nsIRunnable
* aRunnable
) {
3478 MOZ_ASSERT(aRunnable
);
3480 if (!XRE_IsContentProcess()) {
3485 nsTArray
<RefPtr
<GenericNonExclusivePromise
>> promises
;
3486 for (auto& pair
: GetAllKeysForPrincipal(aPrincipal
)) {
3487 RefPtr
<GenericNonExclusivePromise::Private
> promise
;
3488 if (!mPermissionKeyPromiseMap
.Get(pair
.first
, getter_AddRefs(promise
))) {
3489 // In this case we have found a permission which isn't available in the
3490 // content process and hasn't been requested yet. We need to create a new
3491 // promise, and send the request to the parent (if we have not already
3493 promise
= new GenericNonExclusivePromise::Private(__func__
);
3494 mPermissionKeyPromiseMap
.InsertOrUpdate(pair
.first
, RefPtr
{promise
});
3498 promises
.AppendElement(std::move(promise
));
3502 // If all of our permissions are available, immediately run the runnable. This
3503 // avoids any extra overhead during fetch interception which is performance
3505 if (promises
.IsEmpty()) {
3510 auto* thread
= AbstractThread::MainThread();
3512 RefPtr
<nsIRunnable
> runnable
= aRunnable
;
3513 GenericNonExclusivePromise::All(thread
, promises
)
3515 thread
, __func__
, [runnable
]() { runnable
->Run(); },
3518 "PermissionManager permission promise rejected. We're "
3519 "probably shutting down.");
3523 void PermissionManager::EnsureReadCompleted() {
3524 MOZ_ASSERT(NS_IsMainThread());
3526 if (mState
== eInitializing
) {
3527 MonitorAutoLock
lock(mMonitor
);
3529 while (mState
== eInitializing
) {
3536 MOZ_CRASH("This state is impossible!");
3538 case eDBInitialized
:
3541 CompleteMigrations();
3542 ImportLatestDefaults();
3554 MOZ_CRASH("Invalid state");
3558 already_AddRefed
<nsIInputStream
> PermissionManager::GetDefaultsInputStream() {
3559 MOZ_ASSERT(NS_IsMainThread());
3561 nsAutoCString defaultsURL
;
3562 Preferences::GetCString(kDefaultsUrlPrefName
, defaultsURL
);
3563 if (defaultsURL
.IsEmpty()) { // == Don't use built-in permissions.
3567 nsCOMPtr
<nsIURI
> defaultsURI
;
3568 nsresult rv
= NS_NewURI(getter_AddRefs(defaultsURI
), defaultsURL
);
3569 NS_ENSURE_SUCCESS(rv
, nullptr);
3571 nsCOMPtr
<nsIChannel
> channel
;
3572 rv
= NS_NewChannel(getter_AddRefs(channel
), defaultsURI
,
3573 nsContentUtils::GetSystemPrincipal(),
3574 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
3575 nsIContentPolicy::TYPE_OTHER
);
3576 NS_ENSURE_SUCCESS(rv
, nullptr);
3578 nsCOMPtr
<nsIInputStream
> inputStream
;
3579 rv
= channel
->Open(getter_AddRefs(inputStream
));
3580 NS_ENSURE_SUCCESS(rv
, nullptr);
3582 return inputStream
.forget();
3585 void PermissionManager::ConsumeDefaultsInputStream(
3586 nsIInputStream
* aInputStream
, const MonitorAutoLock
& aProofOfLock
) {
3587 MOZ_ASSERT(!NS_IsMainThread());
3589 constexpr char kMatchTypeHost
[] = "host";
3590 constexpr char kMatchTypeOrigin
[] = "origin";
3592 mDefaultEntries
.Clear();
3594 if (!aInputStream
) {
3601 * matchtype \t type \t permission \t host
3602 * Only "host" is supported for matchtype
3603 * type is a string that identifies the type of permission (e.g. "cookie")
3604 * permission is an integer between 1 and 15
3607 // Ideally we'd do this with nsILineInputString, but this is called with an
3608 // nsIInputStream that comes from a resource:// URI, which doesn't support
3609 // that interface. So NS_ReadLine to the rescue...
3610 nsLineBuffer
<char> lineBuffer
;
3614 rv
= NS_ReadLine(aInputStream
, &lineBuffer
, line
, &isMore
);
3615 NS_ENSURE_SUCCESS_VOID(rv
);
3617 if (line
.IsEmpty() || line
.First() == '#') {
3621 nsTArray
<nsCString
> lineArray
;
3623 // Split the line at tabs
3624 ParseString(line
, '\t', lineArray
);
3626 if (lineArray
.Length() != 4) {
3630 nsresult error
= NS_OK
;
3631 uint32_t permission
= lineArray
[2].ToInteger(&error
);
3632 if (NS_FAILED(error
)) {
3636 DefaultEntry::Op op
;
3638 if (lineArray
[0].EqualsLiteral(kMatchTypeHost
)) {
3639 op
= DefaultEntry::eImportMatchTypeHost
;
3640 } else if (lineArray
[0].EqualsLiteral(kMatchTypeOrigin
)) {
3641 op
= DefaultEntry::eImportMatchTypeOrigin
;
3646 DefaultEntry
* entry
= mDefaultEntries
.AppendElement();
3650 entry
->mPermission
= permission
;
3651 entry
->mHostOrOrigin
= lineArray
[3];
3652 entry
->mType
= lineArray
[1];
3656 // ImportLatestDefaults will import the latest default cookies read during the
3657 // last DB initialization.
3658 nsresult
PermissionManager::ImportLatestDefaults() {
3659 MOZ_ASSERT(NS_IsMainThread());
3660 MOZ_ASSERT(mState
== eReady
);
3664 MonitorAutoLock
lock(mMonitor
);
3666 for (const DefaultEntry
& entry
: mDefaultEntries
) {
3667 if (entry
.mOp
== DefaultEntry::eImportMatchTypeHost
) {
3668 // the import file format doesn't handle modification times, so we use
3669 // 0, which AddInternal will convert to now()
3670 int64_t modificationTime
= 0;
3672 rv
= UpgradeHostToOriginAndInsert(
3673 entry
.mHostOrOrigin
, entry
.mType
, entry
.mPermission
,
3674 nsIPermissionManager::EXPIRE_NEVER
, 0, modificationTime
,
3675 [&](const nsACString
& aOrigin
, const nsCString
& aType
,
3676 uint32_t aPermission
, uint32_t aExpireType
, int64_t aExpireTime
,
3677 int64_t aModificationTime
) {
3678 nsCOMPtr
<nsIPrincipal
> principal
;
3680 GetPrincipalFromOrigin(aOrigin
, IsOAForceStripPermission(aType
),
3681 getter_AddRefs(principal
));
3682 NS_ENSURE_SUCCESS(rv
, rv
);
3684 AddInternal(principal
, aType
, aPermission
,
3685 cIDPermissionIsDefault
, aExpireType
, aExpireTime
,
3686 aModificationTime
, PermissionManager::eDontNotify
,
3687 PermissionManager::eNoDBOperation
, false, &aOrigin
);
3688 NS_ENSURE_SUCCESS(rv
, rv
);
3690 if (StaticPrefs::permissions_isolateBy_privateBrowsing()) {
3691 // Also import the permission for private browsing.
3692 OriginAttributes attrs
=
3693 OriginAttributes(principal
->OriginAttributesRef());
3694 attrs
.mPrivateBrowsingId
= 1;
3695 nsCOMPtr
<nsIPrincipal
> pbPrincipal
=
3696 BasePrincipal::Cast(principal
)->CloneForcingOriginAttributes(
3700 pbPrincipal
, aType
, aPermission
, cIDPermissionIsDefault
,
3701 aExpireType
, aExpireTime
, aModificationTime
,
3702 PermissionManager::eDontNotify
,
3703 PermissionManager::eNoDBOperation
, false, &aOrigin
);
3704 NS_ENSURE_SUCCESS(rv
, rv
);
3710 if (NS_FAILED(rv
)) {
3711 NS_WARNING("There was a problem importing a host permission");
3716 MOZ_ASSERT(entry
.mOp
== DefaultEntry::eImportMatchTypeOrigin
);
3718 nsCOMPtr
<nsIPrincipal
> principal
;
3719 rv
= GetPrincipalFromOrigin(entry
.mHostOrOrigin
,
3720 IsOAForceStripPermission(entry
.mType
),
3721 getter_AddRefs(principal
));
3722 if (NS_FAILED(rv
)) {
3723 NS_WARNING("Couldn't import an origin permission - malformed origin");
3727 // the import file format doesn't handle modification times, so we use
3728 // 0, which AddInternal will convert to now()
3729 int64_t modificationTime
= 0;
3731 rv
= AddInternal(principal
, entry
.mType
, entry
.mPermission
,
3732 cIDPermissionIsDefault
, nsIPermissionManager::EXPIRE_NEVER
,
3733 0, modificationTime
, eDontNotify
, eNoDBOperation
);
3734 if (NS_FAILED(rv
)) {
3735 NS_WARNING("There was a problem importing an origin permission");
3738 if (StaticPrefs::permissions_isolateBy_privateBrowsing()) {
3739 // Also import the permission for private browsing.
3740 OriginAttributes attrs
=
3741 OriginAttributes(principal
->OriginAttributesRef());
3742 attrs
.mPrivateBrowsingId
= 1;
3743 nsCOMPtr
<nsIPrincipal
> pbPrincipal
=
3744 BasePrincipal::Cast(principal
)->CloneForcingOriginAttributes(attrs
);
3745 // May return nullptr if clone fails.
3746 NS_ENSURE_TRUE(pbPrincipal
, NS_ERROR_FAILURE
);
3748 rv
= AddInternal(pbPrincipal
, entry
.mType
, entry
.mPermission
,
3749 cIDPermissionIsDefault
,
3750 nsIPermissionManager::EXPIRE_NEVER
, 0, modificationTime
,
3751 eDontNotify
, eNoDBOperation
);
3752 if (NS_FAILED(rv
)) {
3754 "There was a problem importing an origin permission for private "
3764 * Perform the early steps of a permission check and determine whether we need
3765 * to call CommonTestPermissionInternal() for the actual permission check.
3767 * @param aPrincipal optional principal argument to check the permission for,
3768 * can be nullptr if we aren't performing a principal-based
3770 * @param aTypeIndex if the caller isn't sure what the index of the permission
3771 * type to check for is in the mTypeArray member variable,
3772 * it should pass -1, otherwise this would be the index of
3773 * the type inside mTypeArray. This would only be something
3774 * other than -1 in recursive invocations of this function.
3775 * @param aType the permission type to test.
3776 * @param aPermission out argument which will be a permission type that we
3777 * will return from this function once the function is
3779 * @param aDefaultPermission the default permission to be used if we can't
3780 * determine the result of the permission check.
3781 * @param aDefaultPermissionIsValid whether the previous argument contains a
3783 * @param aExactHostMatch whether to look for the exact host name or also for
3784 * subdomains that can have the same permission.
3785 * @param aIncludingSession whether to include session permissions when
3786 * testing for the permission.
3788 PermissionManager::TestPreparationResult
3789 PermissionManager::CommonPrepareToTestPermission(
3790 nsIPrincipal
* aPrincipal
, int32_t aTypeIndex
, const nsACString
& aType
,
3791 uint32_t* aPermission
, uint32_t aDefaultPermission
,
3792 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
3793 bool aIncludingSession
) {
3794 auto* basePrin
= BasePrincipal::Cast(aPrincipal
);
3795 if (basePrin
&& basePrin
->IsSystemPrincipal()) {
3796 *aPermission
= ALLOW_ACTION
;
3797 return AsVariant(NS_OK
);
3800 EnsureReadCompleted();
3802 // For some permissions, query the default from a pref. We want to avoid
3803 // doing this for all permissions so that permissions can opt into having
3804 // the pref lookup overhead on each call.
3805 int32_t defaultPermission
=
3806 aDefaultPermissionIsValid
? aDefaultPermission
: UNKNOWN_ACTION
;
3807 if (!aDefaultPermissionIsValid
&& HasDefaultPref(aType
)) {
3808 Unused
<< mDefaultPrefBranch
->GetIntPref(PromiseFlatCString(aType
).get(),
3809 &defaultPermission
);
3813 *aPermission
= defaultPermission
;
3816 aTypeIndex
== -1 ? GetTypeIndex(aType
, false) : aTypeIndex
;
3818 // For expanded principals, we want to iterate over the allowlist and see
3819 // if the permission is granted for any of them.
3820 if (basePrin
&& basePrin
->Is
<ExpandedPrincipal
>()) {
3821 auto ep
= basePrin
->As
<ExpandedPrincipal
>();
3822 for (auto& prin
: ep
->AllowList()) {
3825 CommonTestPermission(prin
, typeIndex
, aType
, &perm
, defaultPermission
,
3826 true, aExactHostMatch
, aIncludingSession
);
3827 if (NS_WARN_IF(NS_FAILED(rv
))) {
3828 return AsVariant(rv
);
3831 if (perm
== nsIPermissionManager::ALLOW_ACTION
) {
3832 *aPermission
= perm
;
3833 return AsVariant(NS_OK
);
3835 if (perm
== nsIPermissionManager::PROMPT_ACTION
) {
3836 // Store it, but keep going to see if we can do better.
3837 *aPermission
= perm
;
3841 return AsVariant(NS_OK
);
3844 // If type == -1, the type isn't known, just signal that we are done.
3845 if (typeIndex
== -1) {
3846 return AsVariant(NS_OK
);
3849 return AsVariant(typeIndex
);
3852 // If aTypeIndex is passed -1, we try to inder the type index from aType.
3853 nsresult
PermissionManager::CommonTestPermission(
3854 nsIPrincipal
* aPrincipal
, int32_t aTypeIndex
, const nsACString
& aType
,
3855 uint32_t* aPermission
, uint32_t aDefaultPermission
,
3856 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
3857 bool aIncludingSession
) {
3858 auto preparationResult
= CommonPrepareToTestPermission(
3859 aPrincipal
, aTypeIndex
, aType
, aPermission
, aDefaultPermission
,
3860 aDefaultPermissionIsValid
, aExactHostMatch
, aIncludingSession
);
3861 if (preparationResult
.is
<nsresult
>()) {
3862 return preparationResult
.as
<nsresult
>();
3865 return CommonTestPermissionInternal(
3866 aPrincipal
, nullptr, nullptr, preparationResult
.as
<int32_t>(), aType
,
3867 aPermission
, aExactHostMatch
, aIncludingSession
);
3870 // If aTypeIndex is passed -1, we try to inder the type index from aType.
3871 nsresult
PermissionManager::CommonTestPermission(
3872 nsIURI
* aURI
, int32_t aTypeIndex
, const nsACString
& aType
,
3873 uint32_t* aPermission
, uint32_t aDefaultPermission
,
3874 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
3875 bool aIncludingSession
) {
3876 auto preparationResult
= CommonPrepareToTestPermission(
3877 nullptr, aTypeIndex
, aType
, aPermission
, aDefaultPermission
,
3878 aDefaultPermissionIsValid
, aExactHostMatch
, aIncludingSession
);
3879 if (preparationResult
.is
<nsresult
>()) {
3880 return preparationResult
.as
<nsresult
>();
3883 return CommonTestPermissionInternal(
3884 nullptr, aURI
, nullptr, preparationResult
.as
<int32_t>(), aType
,
3885 aPermission
, aExactHostMatch
, aIncludingSession
);
3888 nsresult
PermissionManager::CommonTestPermission(
3889 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
, int32_t aTypeIndex
,
3890 const nsACString
& aType
, uint32_t* aPermission
, uint32_t aDefaultPermission
,
3891 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
3892 bool aIncludingSession
) {
3893 auto preparationResult
= CommonPrepareToTestPermission(
3894 nullptr, aTypeIndex
, aType
, aPermission
, aDefaultPermission
,
3895 aDefaultPermissionIsValid
, aExactHostMatch
, aIncludingSession
);
3896 if (preparationResult
.is
<nsresult
>()) {
3897 return preparationResult
.as
<nsresult
>();
3900 return CommonTestPermissionInternal(
3901 nullptr, aURI
, aOriginAttributes
, preparationResult
.as
<int32_t>(), aType
,
3902 aPermission
, aExactHostMatch
, aIncludingSession
);
3905 nsresult
PermissionManager::TestPermissionWithoutDefaultsFromPrincipal(
3906 nsIPrincipal
* aPrincipal
, const nsACString
& aType
, uint32_t* aPermission
) {
3907 MOZ_ASSERT(!HasDefaultPref(aType
));
3909 return CommonTestPermission(aPrincipal
, -1, aType
, aPermission
,
3910 nsIPermissionManager::UNKNOWN_ACTION
, true, false,
3914 void PermissionManager::MaybeCompleteShutdown() {
3915 MOZ_ASSERT(NS_IsMainThread());
3917 nsCOMPtr
<nsIAsyncShutdownClient
> asc
= GetAsyncShutdownBarrier();
3920 DebugOnly
<nsresult
> rv
= asc
->RemoveBlocker(this);
3921 MOZ_ASSERT(NS_SUCCEEDED(rv
));
3924 // Async shutdown blocker methods
3926 NS_IMETHODIMP
PermissionManager::GetName(nsAString
& aName
) {
3927 aName
= u
"PermissionManager: Flushing data"_ns
;
3931 NS_IMETHODIMP
PermissionManager::BlockShutdown(
3932 nsIAsyncShutdownClient
* aClient
) {
3933 RemoveIdleDailyMaintenanceJob();
3934 RemoveAllFromMemory();
3937 gPermissionManager
= nullptr;
3942 PermissionManager::GetState(nsIPropertyBag
** aBagOut
) {
3943 nsCOMPtr
<nsIWritablePropertyBag2
> propertyBag
=
3944 do_CreateInstance("@mozilla.org/hash-property-bag;1");
3946 nsresult rv
= propertyBag
->SetPropertyAsInt32(u
"state"_ns
, mState
);
3947 if (NS_WARN_IF(NS_FAILED(rv
))) {
3951 propertyBag
.forget(aBagOut
);
3956 nsCOMPtr
<nsIAsyncShutdownClient
> PermissionManager::GetAsyncShutdownBarrier()
3959 nsCOMPtr
<nsIAsyncShutdownService
> svc
=
3960 do_GetService("@mozilla.org/async-shutdown-service;1", &rv
);
3961 if (NS_FAILED(rv
)) {
3965 nsCOMPtr
<nsIAsyncShutdownClient
> client
;
3966 // This feels very late but there seem to be other services that rely on
3967 // us later than "profile-before-change".
3968 rv
= svc
->GetXpcomWillShutdown(getter_AddRefs(client
));
3969 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
3974 void PermissionManager::MaybeStripOriginAttributes(
3975 bool aForceStrip
, OriginAttributes
& aOriginAttributes
) {
3978 if (aForceStrip
|| !StaticPrefs::permissions_isolateBy_privateBrowsing()) {
3979 flags
|= OriginAttributes::STRIP_PRIVATE_BROWSING_ID
;
3982 if (aForceStrip
|| !StaticPrefs::permissions_isolateBy_userContext()) {
3983 flags
|= OriginAttributes::STRIP_USER_CONTEXT_ID
;
3987 aOriginAttributes
.StripAttributes(flags
);
3991 } // namespace mozilla