Skip to content

Commit 6d4dbd8

Browse files
committed
Bug 1708289 - Implement AllowFileSelectionDialogs policy r=fluent-reviewers,mkaply,emilio,Gijs,bolsson,win-reviewers,rkraesig
Differential Revision: https://phabricator.services.mozilla.com/D199328
1 parent a9a7cc2 commit 6d4dbd8

File tree

14 files changed

+294
-6
lines changed

14 files changed

+294
-6
lines changed

browser/base/content/browser.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,34 @@ var gBrowserInit = {
20092009
.getElementById("toolbar-menubar")
20102010
.removeAttribute("toolbarname");
20112011
}
2012+
if (!Services.policies.isAllowed("filepickers")) {
2013+
let savePageCommand = document.getElementById("Browser:SavePage");
2014+
let openFileCommand = document.getElementById("Browser:OpenFile");
2015+
2016+
savePageCommand.setAttribute("disabled", "true");
2017+
openFileCommand.setAttribute("disabled", "true");
2018+
2019+
document.addEventListener("FilePickerBlocked", function (event) {
2020+
let browser = event.target;
2021+
2022+
let notificationBox = browser
2023+
.getTabBrowser()
2024+
?.getNotificationBox(browser);
2025+
2026+
// Prevent duplicate notifications
2027+
if (
2028+
notificationBox &&
2029+
!notificationBox.getNotificationWithValue("filepicker-blocked")
2030+
) {
2031+
notificationBox.appendNotification("filepicker-blocked", {
2032+
label: {
2033+
"l10n-id": "filepicker-blocked-infobar",
2034+
},
2035+
priority: notificationBox.PRIORITY_INFO_LOW,
2036+
});
2037+
}
2038+
});
2039+
}
20122040
let policies = Services.policies.getActivePolicies();
20132041
if ("ManagedBookmarks" in policies) {
20142042
let managedBookmarks = policies.ManagedBookmarks;

browser/base/content/nsContextMenu.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,25 @@ class nsContextMenu {
615615
"disabled",
616616
!this.mediaURL || mediaIsBlob
617617
);
618+
619+
if (
620+
Services.policies.status === Services.policies.ACTIVE &&
621+
!Services.policies.isAllowed("filepickers")
622+
) {
623+
// When file pickers are disallowed by enterprise policy,
624+
// these items silently fail. So to avoid confusion, we
625+
// disable them.
626+
for (let item of [
627+
"context-savepage",
628+
"context-savelink",
629+
"context-savevideo",
630+
"context-saveaudio",
631+
"context-video-saveimage",
632+
"context-saveaudio",
633+
]) {
634+
this.setItemAttr(item, "disabled", true);
635+
}
636+
}
618637
}
619638

620639
initImageItems() {
@@ -651,6 +670,17 @@ class nsContextMenu {
651670
(this.onLoadedImage || this.onCanvas) && !this.inPDFEditor
652671
);
653672

673+
if (Services.policies.status === Services.policies.ACTIVE) {
674+
// When file pickers are disallowed by enterprise policy,
675+
// this item silently fails. So to avoid confusion, we
676+
// disable it.
677+
this.setItemAttr(
678+
"context-saveimage",
679+
"disabled",
680+
!Services.policies.isAllowed("filepickers")
681+
);
682+
}
683+
654684
// Copy image contents depends on whether we're on an image.
655685
// Note: the element doesn't exist on all platforms, but showItem() takes
656686
// care of that by itself.

browser/components/customizableui/CustomizableWidgets.sys.mjs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,8 @@ export const CustomizableWidgets = [
223223
id: "save-page-button",
224224
l10nId: "toolbar-button-save-page",
225225
shortcutId: "key_savePage",
226-
onCommand(aEvent) {
227-
let win = aEvent.target.ownerGlobal;
228-
win.saveBrowser(win.gBrowser.selectedBrowser);
226+
onCreated(aNode) {
227+
aNode.setAttribute("command", "Browser:SavePage");
229228
},
230229
},
231230
{
@@ -252,9 +251,8 @@ export const CustomizableWidgets = [
252251
id: "open-file-button",
253252
l10nId: "toolbar-button-open-file",
254253
shortcutId: "openFileKb",
255-
onCommand(aEvent) {
256-
let win = aEvent.target.ownerGlobal;
257-
win.BrowserOpenFileWindow();
254+
onCreated(aNode) {
255+
aNode.setAttribute("command", "Browser:OpenFile");
258256
},
259257
},
260258
{

browser/components/enterprisepolicies/Policies.sys.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@ export var Policies = {
121121
},
122122
},
123123

124+
AllowFileSelectionDialogs: {
125+
onBeforeUIStartup(manager, param) {
126+
if (!param) {
127+
setAndLockPref("widget.disable_file_pickers", true);
128+
setAndLockPref("browser.download.useDownloadDir", true);
129+
manager.disallowFeature("filepickers");
130+
}
131+
},
132+
},
133+
124134
AppAutoUpdate: {
125135
onBeforeUIStartup(manager, param) {
126136
// Logic feels a bit reversed here, but it's correct. If AppAutoUpdate is

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
"type": "string"
2121
},
2222

23+
"AllowFileSelectionDialogs": {
24+
"type": "boolean"
25+
},
26+
2327
"AppAutoUpdate": {
2428
"type": "boolean"
2529
},

browser/components/enterprisepolicies/tests/browser/browser.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ skip-if = ["os != 'mac'"]
2323

2424
["browser_policies_setAndLockPref_API.js"]
2525

26+
["browser_policy_allowfileselectiondialogs.js"]
27+
2628
["browser_policy_app_auto_update.js"]
2729
skip-if = ["os == 'win' && msix"] # Updater is disabled in MSIX builds
2830

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/* Any copyright is dedicated to the Public Domain.
2+
* http://creativecommons.org/publicdomain/zero/1.0/ */
3+
4+
add_setup(async function setup() {
5+
await setupPolicyEngineWithJson({
6+
policies: {
7+
AllowFileSelectionDialogs: false,
8+
},
9+
});
10+
});
11+
12+
add_task(async function test_file_commands_disabled() {
13+
// Since testing will apply the policy after the browser has already started,
14+
// we will need to open a new window to actually see changes from the policy
15+
let newWin = await BrowserTestUtils.openNewBrowserWindow();
16+
17+
let savePageCommand = newWin.document.getElementById("Browser:SavePage");
18+
let openFileCommand = newWin.document.getElementById("Browser:OpenFile");
19+
20+
Assert.equal(
21+
savePageCommand.getAttribute("disabled"),
22+
"true",
23+
"Browser:SavePage command is disabled"
24+
);
25+
Assert.equal(
26+
openFileCommand.getAttribute("disabled"),
27+
"true",
28+
"Browser:OpenFile command is disabled"
29+
);
30+
await BrowserTestUtils.closeWindow(newWin);
31+
});
32+
33+
add_task(async function test_file_buttons_disabled() {
34+
// Since testing will apply the policy after the browser has already started,
35+
// we will need to open a new window to actually see changes from the policy
36+
let newWin = await BrowserTestUtils.openNewBrowserWindow();
37+
38+
newWin.CustomizableUI.addWidgetToArea("save-page-button", "nav-bar");
39+
newWin.CustomizableUI.addWidgetToArea("open-file-button", "nav-bar");
40+
41+
let savePageButton = newWin.document.getElementById("save-page-button");
42+
let openFileButton = newWin.document.getElementById("open-file-button");
43+
44+
Assert.equal(
45+
savePageButton.getAttribute("disabled"),
46+
"true",
47+
"save-page-button is disabled"
48+
);
49+
Assert.equal(
50+
openFileButton.getAttribute("disabled"),
51+
"true",
52+
"open-file-button is disabled"
53+
);
54+
55+
newWin.CustomizableUI.reset();
56+
await BrowserTestUtils.closeWindow(newWin);
57+
});
58+
59+
add_task(async function test_context_menu_items_disabled() {
60+
await BrowserTestUtils.withNewTab("https://example.org/", async browser => {
61+
let contextMenu = document.getElementById("contentAreaContextMenu");
62+
let promiseContextMenuOpen = BrowserTestUtils.waitForEvent(
63+
contextMenu,
64+
"popupshown"
65+
);
66+
67+
await BrowserTestUtils.synthesizeMouse(
68+
"a",
69+
0,
70+
0,
71+
{
72+
type: "contextmenu",
73+
button: 2,
74+
centered: true,
75+
},
76+
browser
77+
);
78+
79+
await promiseContextMenuOpen;
80+
81+
for (let item of [
82+
"context-saveimage",
83+
"context-savepage",
84+
"context-savelink",
85+
"context-savevideo",
86+
"context-saveaudio",
87+
"context-video-saveimage",
88+
"context-saveaudio",
89+
]) {
90+
Assert.equal(
91+
document.getElementById(item).disabled,
92+
true,
93+
`${item} item is disabled`
94+
);
95+
}
96+
97+
contextMenu.hidePopup();
98+
});
99+
});
100+
101+
add_task(async function test_notification() {
102+
// Since testing will apply the policy after the browser has already started,
103+
// we will need to open a new window to actually see changes from the policy
104+
let newWin = await BrowserTestUtils.openNewBrowserWindow();
105+
106+
await BrowserTestUtils.withNewTab(
107+
{ gBrowser: newWin.gBrowser, url: "https://example.org/" },
108+
async browser => {
109+
await SpecialPowers.spawn(browser, [], async function () {
110+
let elem = content.document.createElement("input");
111+
elem.setAttribute("type", "file");
112+
113+
content.document.notifyUserGestureActivation();
114+
elem.showPicker();
115+
});
116+
117+
let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
118+
119+
let notification = await TestUtils.waitForCondition(() =>
120+
notificationBox.getNotificationWithValue("filepicker-blocked")
121+
);
122+
123+
Assert.ok(
124+
notification,
125+
"filepicker-blocked notification appears when showPicker is called"
126+
);
127+
}
128+
);
129+
await BrowserTestUtils.closeWindow(newWin);
130+
});
131+
132+
add_task(async function test_cancel_event() {
133+
await BrowserTestUtils.withNewTab("https://example.org/", async browser => {
134+
let eventType = await SpecialPowers.spawn(browser, [], async function () {
135+
let elem = content.document.createElement("input");
136+
elem.setAttribute("type", "file");
137+
let cancelPromise = new Promise(resolve =>
138+
elem.addEventListener("cancel", resolve, { once: true })
139+
);
140+
content.document.notifyUserGestureActivation();
141+
elem.showPicker();
142+
let event = await cancelPromise;
143+
return event.type;
144+
});
145+
146+
Assert.equal(
147+
eventType,
148+
"cancel",
149+
"cancel event should be dispatched when showPicker is called"
150+
);
151+
});
152+
});
153+
154+
add_task(async function test_nsIFilePicker_open() {
155+
let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
156+
157+
picker.init(window, "", Ci.nsIFilePicker.modeSave);
158+
159+
let result = await new Promise(resolve => picker.open(res => resolve(res)));
160+
161+
Assert.equal(
162+
result,
163+
Ci.nsIFilePicker.returnCancel,
164+
"nsIFilePicker.open callback should immediately answer returnCancel"
165+
);
166+
});

browser/locales/en-US/browser/browser.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,10 @@ tabs-toolbar-list-all-tabs =
956956
restore-session-startup-suggestion-message = <strong>Open previous tabs?</strong> You can restore your previous session from the { -brand-short-name } application menu <img data-l10n-name="icon"/>, under History.
957957
restore-session-startup-suggestion-button = Show me how
958958
959+
## Infobar shown when the user tries to open a file picker and file pickers are blocked by enterprise policy
960+
961+
filepicker-blocked-infobar = Your organization has blocked access to local files on this computer
962+
959963
## Mozilla data reporting notification (Telemetry, Firefox Health Report, etc)
960964

961965
data-reporting-notification-message = { -brand-short-name } automatically sends some data to { -vendor-short-name } so that we can improve your experience.

browser/locales/en-US/browser/policies/policies-descriptions.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ policy-3rdparty = Set policies that WebExtensions can access via chrome.storage.
1313
1414
policy-AllowedDomainsForApps = Define domains allowed to access Google Workspace.
1515
16+
policy-AllowFileSelectionDialogs = Allow file selection dialogs.
17+
1618
policy-AppAutoUpdate = Enable or disable automatic application update.
1719
1820
policy-AppUpdatePin = Prevent { -brand-short-name } from being updated beyond the specified version.

modules/libpref/init/StaticPrefList.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15483,6 +15483,11 @@
1548315483
value: false
1548415484
mirror: always
1548515485

15486+
- name: widget.disable_file_pickers
15487+
type: RelaxedAtomicBool
15488+
value: false
15489+
mirror: always
15490+
1548615491
- name: widget.window-transforms.disabled
1548715492
type: RelaxedAtomicBool
1548815493
value: false

0 commit comments

Comments
 (0)