1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"quotaManagerService",
"@mozilla.org/dom/quota-manager-service;1",
"nsIQuotaManagerService"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"serviceWorkerManager",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager"
);
/**
* A helper module to collect all principals that have any of the following:
* * cookies
* * quota storage (indexedDB, localStorage)
* * service workers
*
* Note that in case of cookies, because these are not strictly associated with a
* full origin (including scheme), the https:// scheme will be used as a convention,
* so when the cookie hostname is .example.com the principal will have the origin
* https://example.com. Origin Attributes from cookies are copied to the principal.
*
* This class is not a singleton and needs to be instantiated using the constructor
* before usage. The class instance will cache the last list of principals.
*
* There is currently no `refresh` method, though you are free to add one.
*/
class PrincipalsCollector {
/**
* Creates a new PrincipalsCollector.
*/
constructor() {
this.principals = null;
}
/**
* Checks whether the passed in principal has a scheme that is considered by the
* PrincipalsCollector. This is used to avoid including principals for non-web
* entities such as moz-extension.
*
* @param {nsIPrincipal} the principal to check
* @returns {boolean}
*/
static isSupportedPrincipal(principal) {
return ["http", "https", "file"].some(scheme => principal.schemeIs(scheme));
}
/**
* Fetches and collects all principals with cookies and/or site data (see module
* description). Originally for usage in Sanitizer.jsm to compute principals to be
* cleared on shutdown based on user settings.
*
* This operation might take a while to complete on big profiles.
* DO NOT call or await this in a way that makes it block user interaction, or you
* risk several painful seconds or possibly even minutes of lag.
*
* This function will cache its result and return the same list on second call,
* even if the actual number of principals with cookies and site data changed.
*
* @param {Object} [optional] progress A Sanitizer.jsm progress object that will be
* updated to reflect the current step of fetching principals.
* @returns {Array<nsIPrincipal>} the list of principals
*/
async getAllPrincipals(progress = {}) {
if (this.principals == null) {
// Here is the list of principals with site data.
this.principals = await this._getAllPrincipalsInternal(progress);
}
return this.principals;
}
async _getAllPrincipalsInternal(progress = {}) {
progress.step = "principals-quota-manager";
let principals = await new Promise(resolve => {
quotaManagerService.listOrigins().callback = request => {
progress.step = "principals-quota-manager-listOrigins";
if (request.resultCode != Cr.NS_OK) {
// We are probably shutting down. We don't want to propagate the
// error, rejecting the promise.
resolve([]);
return;
}
let list = [];
for (const origin of request.result) {
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
origin
);
if (PrincipalsCollector.isSupportedPrincipal(principal)) {
list.push(principal);
}
}
progress.step = "principals-quota-manager-completed";
resolve(list);
};
}).catch(ex => {
Cu.reportError("QuotaManagerService promise failed: " + ex);
return [];
});
progress.step = "principals-service-workers";
let serviceWorkers = serviceWorkerManager.getAllRegistrations();
for (let i = 0; i < serviceWorkers.length; i++) {
let sw = serviceWorkers.queryElementAt(
i,
Ci.nsIServiceWorkerRegistrationInfo
);
// We don't need to check the scheme. SW are just exposed to http/https URLs.
principals.push(sw.principal);
}
// Let's take the list of unique hosts+OA from cookies.
progress.step = "principals-cookies";
let cookies = Services.cookies.cookies;
let hosts = new Set();
for (let cookie of cookies) {
hosts.add(
cookie.rawHost +
ChromeUtils.originAttributesToSuffix(cookie.originAttributes)
);
}
progress.step = "principals-host-cookie";
hosts.forEach(host => {
// Cookies and permissions are handled by origin/host. Doesn't matter if we
// use http: or https: schema here.
principals.push(
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
"https://" + host
)
);
});
progress.step = "total-principals:" + principals.length;
return principals;
}
}
this.EXPORTED_SYMBOLS = ["PrincipalsCollector"];
|