Skip to content

Commit 253bd57

Browse files
committed
Bug 2045798: Support ephemeral containers in SitePolicies. r=bsmth
Differential Revision: https://phabricator.services.mozilla.com/D324911
1 parent 1a1c3e6 commit 253bd57

6 files changed

Lines changed: 644 additions & 0 deletions

File tree

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
const lazy = {};
6+
7+
ChromeUtils.defineESModuleGetters(lazy, {
8+
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
9+
ContextualIdentityService:
10+
"moz-src:///toolkit/components/contextualidentity/ContextualIdentityService.sys.mjs",
11+
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
12+
EveryWindow: "resource:///modules/EveryWindow.sys.mjs",
13+
});
14+
15+
const DEBOUNCE_MS = 30000;
16+
const PREF_EPHEMERAL_IDS = "browser.policies.ephemeralContainerIds";
17+
18+
export const EphemeralContainerWatcher = {
19+
_ephemeralIds: new Set(),
20+
_deferredTasks: new Map(),
21+
_initialized: false,
22+
_shutdownBlockerAdded: false,
23+
24+
init(ephemeralUserContextIds) {
25+
this._ephemeralIds = ephemeralUserContextIds;
26+
this._cancelStaleTimers();
27+
28+
// On first init, clear data for containers that were ephemeral in the
29+
// previous session but are no longer in the current policy. Handles the
30+
// case where we crashed before the shutdown blocker could run clearAll.
31+
if (!this._initialized) {
32+
this._clearStaleIds();
33+
}
34+
35+
this._persistIds();
36+
37+
if (this._initialized) {
38+
return;
39+
}
40+
this._initialized = true;
41+
42+
lazy.EveryWindow.registerCallback(
43+
"EphemeralContainerWatcher",
44+
win => this._onWindowInit(win),
45+
(win, closing) => this._onWindowUninit(win, closing)
46+
);
47+
48+
if (!this._shutdownBlockerAdded) {
49+
this._shutdownBlockerAdded = true;
50+
lazy.AsyncShutdown.profileBeforeChange.addBlocker(
51+
"EphemeralContainerWatcher: clear ephemeral container data",
52+
() => this.clearAll()
53+
);
54+
}
55+
},
56+
57+
destroy() {
58+
if (!this._initialized) {
59+
return;
60+
}
61+
this._initialized = false;
62+
63+
this._ephemeralIds.clear();
64+
this._cancelStaleTimers();
65+
this._persistIds();
66+
67+
lazy.EveryWindow.unregisterCallback("EphemeralContainerWatcher");
68+
},
69+
70+
_cancelStaleTimers() {
71+
for (let [userContextId, task] of this._deferredTasks) {
72+
if (!this._ephemeralIds.has(userContextId)) {
73+
task.disarm();
74+
this._deferredTasks.delete(userContextId);
75+
}
76+
}
77+
},
78+
79+
_onWindowInit(win) {
80+
win.gBrowser.tabContainer.addEventListener("TabClose", this);
81+
},
82+
83+
_onWindowUninit(win, closing) {
84+
win.gBrowser.tabContainer.removeEventListener("TabClose", this);
85+
if (closing) {
86+
for (let userContextId of this._ephemeralIds) {
87+
this._scheduleCheck(userContextId);
88+
}
89+
}
90+
},
91+
92+
handleEvent(event) {
93+
let tab = event.target;
94+
let userContextId = tab.getAttribute("usercontextid");
95+
if (!userContextId) {
96+
return;
97+
}
98+
userContextId = parseInt(userContextId, 10);
99+
if (this._ephemeralIds.has(userContextId)) {
100+
this._scheduleCheck(userContextId);
101+
}
102+
},
103+
104+
_scheduleCheck(userContextId) {
105+
let task = this._deferredTasks.getOrInsertComputed(userContextId, () => {
106+
return new lazy.DeferredTask(() => {
107+
if (
108+
lazy.ContextualIdentityService.countContainerTabs(userContextId) == 0
109+
) {
110+
return this._clearData(userContextId);
111+
}
112+
return undefined;
113+
}, DEBOUNCE_MS);
114+
});
115+
task.disarm();
116+
task.arm();
117+
},
118+
119+
_clearData(userContextId) {
120+
return new Promise(resolve => {
121+
Services.clearData.deleteDataFromOriginAttributesPattern(
122+
{ userContextId },
123+
resolve
124+
);
125+
});
126+
},
127+
128+
clearAll() {
129+
let promises = [];
130+
for (let userContextId of this._ephemeralIds) {
131+
promises.push(this._clearData(userContextId));
132+
}
133+
this.destroy();
134+
return Promise.all(promises);
135+
},
136+
137+
_clearStaleIds() {
138+
let staleIds;
139+
try {
140+
staleIds = JSON.parse(
141+
Services.prefs.getStringPref(PREF_EPHEMERAL_IDS, "[]")
142+
);
143+
} catch (e) {
144+
return;
145+
}
146+
147+
for (let id of staleIds) {
148+
if (!this._ephemeralIds.has(id)) {
149+
this._clearData(id);
150+
}
151+
}
152+
},
153+
154+
_persistIds() {
155+
Services.prefs.setStringPref(
156+
PREF_EPHEMERAL_IDS,
157+
JSON.stringify([...this._ephemeralIds])
158+
);
159+
},
160+
161+
async flushPendingChecks() {
162+
let tasks = [...this._deferredTasks.values()];
163+
this._deferredTasks.clear();
164+
await Promise.all(tasks.map(t => t.finalize()));
165+
},
166+
};

‎browser/components/enterprisepolicies/Policies.sys.mjs‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
3333
QuickSuggest: "moz-src:///browser/components/urlbar/QuickSuggest.sys.mjs",
3434
ContextualIdentityService:
3535
"moz-src:///toolkit/components/contextualidentity/ContextualIdentityService.sys.mjs",
36+
EphemeralContainerWatcher:
37+
"resource:///modules/policies/EphemeralContainerWatcher.sys.mjs",
3638
WebsiteFilter: "resource:///modules/policies/WebsiteFilter.sys.mjs",
3739
LaunchOnLogin: "resource://gre/modules/LaunchOnLogin.sys.mjs",
3840

@@ -3630,6 +3632,7 @@ export var Policies = {
36303632

36313633
const sitePolicies = [];
36323634
let hasContainerPolicy = false;
3635+
const ephemeralUserContextIds = new Set();
36333636

36343637
for (const policies of params) {
36353638
const matches = policies.Match ?? [];
@@ -3679,6 +3682,10 @@ export var Policies = {
36793682

36803683
features.container = userContextId;
36813684
hasContainerPolicy = true;
3685+
3686+
if (policies.Policies.Container.ephemeral) {
3687+
ephemeralUserContextIds.add(userContextId);
3688+
}
36823689
}
36833690

36843691
sitePolicies.push({
@@ -3700,6 +3707,14 @@ export var Policies = {
37003707
"privacy.containers.switchDuringNavigation.enabled",
37013708
true
37023709
);
3710+
3711+
if (ephemeralUserContextIds.size > 0) {
3712+
lazy.EphemeralContainerWatcher.init(ephemeralUserContextIds);
3713+
} else {
3714+
lazy.EphemeralContainerWatcher.destroy();
3715+
}
3716+
} else {
3717+
lazy.EphemeralContainerWatcher.destroy();
37033718
}
37043719
},
37053720
},

‎browser/components/enterprisepolicies/moz.build‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ DIRS += [
1515
TEST_DIRS += ["tests"]
1616

1717
EXTRA_JS_MODULES.policies += [
18+
"EphemeralContainerWatcher.sys.mjs",
1819
"Policies.sys.mjs",
1920
]
2021

‎browser/components/enterprisepolicies/schemas/policies-schema.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3814,6 +3814,9 @@
38143814
"id": {
38153815
"type": "string",
38163816
"minLength": 1
3817+
},
3818+
"ephemeral": {
3819+
"type": "boolean"
38173820
}
38183821
},
38193822
"required": ["id"]

0 commit comments

Comments
 (0)