HiddenBrowser: Block all downloads
This commit is contained in:
parent
0cd868ec87
commit
9b7d3edbb3
2 changed files with 78 additions and 23 deletions
|
@ -144,38 +144,51 @@ class HiddenBrowser {
|
||||||
*/
|
*/
|
||||||
async load(source, options) {
|
async load(source, options) {
|
||||||
await this._createdPromise;
|
await this._createdPromise;
|
||||||
let url;
|
let uri;
|
||||||
if (/^(file|https?|chrome|resource|blob|data):/.test(source)) {
|
if (/^(file|https?|chrome|resource|blob|data):/.test(source)) {
|
||||||
url = source;
|
uri = source;
|
||||||
}
|
}
|
||||||
// Convert string path to file: URL
|
// Convert string path to file: URL
|
||||||
else {
|
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
|
// Next bit adapted from Mozilla's HeadlessShell.jsm
|
||||||
const principal = Services.scriptSecurityManager.getSystemPrincipal();
|
|
||||||
try {
|
try {
|
||||||
await new Promise((resolve, reject) => {
|
// 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 remoteType = E10SUtils.getRemoteTypeForURI(
|
||||||
|
uri,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
E10SUtils.DEFAULT_REMOTE_TYPE,
|
||||||
|
null,
|
||||||
|
oa
|
||||||
|
);
|
||||||
|
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
|
// Avoid a hang if page is never loaded for some reason
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
reject(new Error("Page never loaded in hidden browser"));
|
reject(new Error("Page never loaded in hidden browser"));
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
let oa = E10SUtils.predictOriginAttributes({ browser: this });
|
|
||||||
let loadURIOptions = {
|
|
||||||
triggeringPrincipal: principal,
|
|
||||||
remoteType: E10SUtils.getRemoteTypeForURI(
|
|
||||||
url,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
E10SUtils.DEFAULT_REMOTE_TYPE,
|
|
||||||
null,
|
|
||||||
oa
|
|
||||||
)
|
|
||||||
};
|
|
||||||
this.loadURI(Services.io.newURI(url), loadURIOptions);
|
|
||||||
let { webProgress } = this;
|
let { webProgress } = this;
|
||||||
|
|
||||||
let progressListener = {
|
let progressListener = {
|
||||||
|
@ -189,7 +202,7 @@ class HiddenBrowser {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Ignore the initial about:blank, unless about:blank is requested
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
progressListeners.delete(progressListener);
|
progressListeners.delete(progressListener);
|
||||||
|
@ -208,6 +221,14 @@ class HiddenBrowser {
|
||||||
Ci.nsIWebProgress.NOTIFY_LOCATION
|
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) {
|
catch (e) {
|
||||||
Zotero.logError(e);
|
Zotero.logError(e);
|
||||||
|
@ -218,15 +239,15 @@ class HiddenBrowser {
|
||||||
let { channelInfo } = await this.getPageData(['channelInfo']);
|
let { channelInfo } = await this.getPageData(['channelInfo']);
|
||||||
if (channelInfo && (channelInfo.responseStatus < 200 || channelInfo.responseStatus >= 400)) {
|
if (channelInfo && (channelInfo.responseStatus < 200 || channelInfo.responseStatus >= 400)) {
|
||||||
let response = `${channelInfo.responseStatus} ${channelInfo.responseStatusText}`;
|
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
|
// HiddenBrowser will never get returned so we need to clean it up here
|
||||||
this.destroy()
|
this.destroy()
|
||||||
throw new Zotero.HTTP.UnexpectedStatusException(
|
throw new Zotero.HTTP.UnexpectedStatusException(
|
||||||
{
|
{
|
||||||
status: channelInfo.responseStatus
|
status: channelInfo.responseStatus
|
||||||
},
|
},
|
||||||
url,
|
uri,
|
||||||
`Invalid response ${response} for ${url}`
|
`Invalid response ${response} for ${uri}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,12 @@ var EXPORTED_SYMBOLS = ["PageDataChild"];
|
||||||
|
|
||||||
class PageDataChild extends JSWindowActorChild {
|
class PageDataChild extends JSWindowActorChild {
|
||||||
async receiveMessage(message) {
|
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 window = this.contentWindow;
|
||||||
let document = window.document;
|
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
|
// From Mozilla's ScreenshotsComponentChild.jsm
|
||||||
documentIsReady() {
|
documentIsReady() {
|
||||||
const contentWindow = this.contentWindow;
|
const contentWindow = this.contentWindow;
|
||||||
|
|
Loading…
Reference in a new issue