HiddenBrowser: Block all downloads

This commit is contained in:
Abe Jellinek 2024-05-06 12:02:29 -04:00 committed by Dan Stillman
parent 0cd868ec87
commit 9b7d3edbb3
2 changed files with 78 additions and 23 deletions

View file

@ -144,38 +144,51 @@ class HiddenBrowser {
*/
async load(source, options) {
await this._createdPromise;
let url;
let uri;
if (/^(file|https?|chrome|resource|blob|data):/.test(source)) {
url = source;
uri = source;
}
// Convert string path to file: URL
else {
url = Zotero.File.pathToFileURI(source);
uri = Zotero.File.pathToFileURI(source);
}
Zotero.debug(`Loading ${url} in hidden browser`);
Zotero.debug(`Loading ${uri} in hidden browser`);
// Next bit adapted from Mozilla's HeadlessShell.jsm
const principal = Services.scriptSecurityManager.getSystemPrincipal();
try {
await new Promise((resolve, reject) => {
// Avoid a hang if page is never loaded for some reason
setTimeout(function () {
reject(new Error("Page never loaded in hidden browser"));
}, 5000);
// Figure out whether the browser should be remote. We actually
// perform the load in PageDataChild, but remoteness changes
// need to happen here.
let oa = E10SUtils.predictOriginAttributes({ browser: this });
let loadURIOptions = {
triggeringPrincipal: principal,
remoteType: E10SUtils.getRemoteTypeForURI(
url,
let remoteType = E10SUtils.getRemoteTypeForURI(
uri,
true,
false,
E10SUtils.DEFAULT_REMOTE_TYPE,
null,
oa
)
};
this.loadURI(Services.io.newURI(url), loadURIOptions);
);
if (this.remoteType !== remoteType) {
// The following functions need to be called on the <browser> directly,
// not through our proxy (aka 'this')
if (remoteType === E10SUtils.NOT_REMOTE) {
this._browser.removeAttribute("remote");
this._browser.removeAttribute("remoteType");
}
else {
this._browser.setAttribute("remote", "true");
this._browser.setAttribute("remoteType", remoteType);
}
this._browser.changeRemoteness({ remoteType });
this._browser.construct();
}
let loadCompletePromise = new Promise((resolve, reject) => {
// Avoid a hang if page is never loaded for some reason
setTimeout(function () {
reject(new Error("Page never loaded in hidden browser"));
}, 5000);
let { webProgress } = this;
let progressListener = {
@ -189,7 +202,7 @@ class HiddenBrowser {
return;
}
// Ignore the initial about:blank, unless about:blank is requested
if (location.spec == "about:blank" && url != "about:blank") {
if (location.spec == "about:blank" && uri != "about:blank") {
return;
}
progressListeners.delete(progressListener);
@ -208,6 +221,14 @@ class HiddenBrowser {
Ci.nsIWebProgress.NOTIFY_LOCATION
);
});
let loadURISuccess = await this.browsingContext.currentWindowGlobal.getActor("PageData")
.sendQuery("loadURI", { uri });
if (!loadURISuccess) {
Zotero.logError(new Error("Load failed"));
return false;
}
await loadCompletePromise;
}
catch (e) {
Zotero.logError(e);
@ -218,15 +239,15 @@ class HiddenBrowser {
let { channelInfo } = await this.getPageData(['channelInfo']);
if (channelInfo && (channelInfo.responseStatus < 200 || channelInfo.responseStatus >= 400)) {
let response = `${channelInfo.responseStatus} ${channelInfo.responseStatusText}`;
Zotero.debug(`HiddenBrowser.load: ${url} failed with ${response}`, 2);
Zotero.debug(`HiddenBrowser.load: ${uri} failed with ${response}`, 2);
// HiddenBrowser will never get returned so we need to clean it up here
this.destroy()
throw new Zotero.HTTP.UnexpectedStatusException(
{
status: channelInfo.responseStatus
},
url,
`Invalid response ${response} for ${url}`
uri,
`Invalid response ${response} for ${uri}`
);
}
}

View file

@ -2,6 +2,12 @@ var EXPORTED_SYMBOLS = ["PageDataChild"];
class PageDataChild extends JSWindowActorChild {
async receiveMessage(message) {
// Special case for loadURI: don't wait for document to be ready,
// since we haven't loaded anything yet
if (message.name === "loadURI") {
return this.loadURI(message.data.uri);
}
let window = this.contentWindow;
let document = window.document;
@ -40,6 +46,34 @@ class PageDataChild extends JSWindowActorChild {
}
}
loadURI(uri) {
// https://searchfox.org/mozilla-central/rev/e69f323af80c357d287fb6314745e75c62eab92a/toolkit/actors/BackgroundThumbnailsChild.sys.mjs#44-85
let docShell = this.docShell.QueryInterface(Ci.nsIWebNavigation);
// Don't allow downloads/external apps
docShell.allowContentRetargeting = false;
// Get the document to force a content viewer to be created, otherwise
// the first load can fail.
if (!this.document) {
return false;
}
let loadURIOptions = {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
};
try {
docShell.loadURI(
Services.io.newURI(uri),
loadURIOptions
);
return true;
}
catch (e) {
return false;
}
}
// From Mozilla's ScreenshotsComponentChild.jsm
documentIsReady() {
const contentWindow = this.contentWindow;