Do not load-timeout on browser PDF download during content load

Closes #2726
This commit is contained in:
Adomas Venčkauskas 2022-07-26 13:48:31 +03:00
parent 6733f2edca
commit fc0f6157d0
2 changed files with 91 additions and 60 deletions

View file

@ -1151,43 +1151,45 @@ Zotero.Attachments = new function(){
let hiddenBrowserPDFFoundDeferred = Zotero.Promise.defer();
let isOurPDF = false;
var pdfMIMETypeHandler = async (blob, name, _, channel) => {
Zotero.debug(`downloadPDFViaBrowser: Sniffing a PDF loaded at ${name}`);
// try the browser
try {
channelBrowser = channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
}
catch (e) {}
if (channelBrowser) {
isOurPDF = hiddenBrowser === channelBrowser;
}
else {
// try the document for the load group
var pdfMIMETypeHandler = {
onStartRequest: function (name, _, channel) {
Zotero.debug(`downloadPDFViaBrowser: Sniffing a PDF loaded at ${name}`);
// try the browser
try {
channelBrowser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
channelBrowser = channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
}
catch(e) {}
catch (e) {}
if (channelBrowser) {
isOurPDF = hiddenBrowser === channelBrowser;
}
}
if (isOurPDF) {
Zotero.debug(`downloadPDFViaBrowser: Found our PDF at ${name}`);
await Zotero.File.putContentsAsync(path, blob);
hiddenBrowserPDFFoundDeferred.resolve();
return true;
}
else {
Zotero.debug(`downloadPDFViaBrowser: Not our PDF at ${name}`);
return false;
else {
// try the document for the load group
try {
channelBrowser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
}
catch(e) {}
if (channelBrowser) {
isOurPDF = hiddenBrowser === channelBrowser;
}
}
},
onContent: async (blob, name, _, channel) => {
if (isOurPDF) {
Zotero.debug(`downloadPDFViaBrowser: Found our PDF at ${name}`);
await Zotero.File.putContentsAsync(path, blob);
hiddenBrowserPDFFoundDeferred.resolve();
return true;
}
else {
Zotero.debug(`downloadPDFViaBrowser: Not our PDF at ${name}`);
return false;
}
}
};
try {
Zotero.MIMETypeHandler.addHandler("application/pdf", pdfMIMETypeHandler, true);
Zotero.MIMETypeHandler.addHandlers("application/pdf", pdfMIMETypeHandler, true);
function noop() {};
hiddenBrowser = Zotero.HTTP.loadDocuments([url], noop, noop, noop, true, options.cookieSandbox);
let onLoadTimeoutDeferred = Zotero.Promise.defer();
@ -1200,7 +1202,7 @@ Zotero.Attachments = new function(){
onStatusChange: noop,
onSecurityChange: noop,
async onLocationChange() {
let url = hiddenBrowser.contentDocument.location.href
let url = hiddenBrowser.contentDocument.location.href;
if (currentUrl) {
Zotero.debug(`downloadPDFViaBrowser: A JS redirect occurred to ${hiddenBrowser.contentDocument.location.href}`);
}
@ -1227,13 +1229,13 @@ Zotero.Attachments = new function(){
try {
await OS.File.remove(path, { ignoreAbsent: true });
}
catch (e) {
Zotero.logError(e);
catch (err) {
Zotero.logError(err);
}
throw e;
}
finally {
Zotero.MIMETypeHandler.removeHandler('application/pdf', pdfMIMETypeHandler);
Zotero.MIMETypeHandler.removeHandlers('application/pdf', pdfMIMETypeHandler);
if (hiddenBrowser) {
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
}

View file

@ -55,20 +55,22 @@ Zotero.MIMETypeHandler = new function () {
_observers = [];
// Install styles from the Cite preferences
this.addHandler("application/vnd.citationstyles.style+xml", async function (blob, origin) {
let win = Services.wm.getMostRecentWindow("zotero:basicViewer");
var data = await Zotero.Utilities.Internal.blobToText(blob);
try {
await Zotero.Styles.install(data, origin, true);
}
catch (e) {
Zotero.logError(e);
(new Zotero.Exception.Alert("styles.install.unexpectedError",
origin, "styles.install.title", e)).present();
}
// Close styles page in basic viewer after installing a style
if (win) {
win.close();
this.addHandlers("application/vnd.citationstyles.style+xml", {
onContent: async function (blob, origin) {
let win = Services.wm.getMostRecentWindow("zotero:basicViewer");
var data = await Zotero.Utilities.Internal.blobToText(blob);
try {
await Zotero.Styles.install(data, origin, true);
}
catch (e) {
Zotero.logError(e);
(new Zotero.Exception.Alert("styles.install.unexpectedError",
origin, "styles.install.title", e)).present();
}
// Close styles page in basic viewer after installing a style
if (win) {
win.close();
}
}
}, true);
};
@ -76,17 +78,25 @@ Zotero.MIMETypeHandler = new function () {
/**
* Adds a handler to handle a specific MIME type
* @param {String} type MIME type to handle
* @param {Function} fn Function to call to handle type - fn(string, uri)
* @param {Object} handlers
* - handlers.onContent - function to call when content is received
* - handlers.onStartRequest - function to call when response for content type is first received
* @param {Boolean} ignoreContentDisposition If true, ignores the Content-Disposition header,
* which is often used to force a file to download rather than let it be handled by the web
* browser
*/
this.addHandler = function (type, fn, ignoreContentDisposition) {
this.addHandlers = function (type, handlers, ignoreContentDisposition) {
if (typeof handlers == 'function') {
handlers = {
onContent: handlers
};
Zotero.debug('MIMETypeHandler.addHandler: second parameter function is deprecated. Pass an object', 1)
}
if (_typeHandlers[type]) {
_typeHandlers[type].push(fn);
_typeHandlers[type].push(handlers);
}
else {
_typeHandlers[type] = [fn];
_typeHandlers[type] = [handlers];
}
if (ignoreContentDisposition) {
_ignoreContentDispositionTypes.add(type);
@ -96,17 +106,17 @@ Zotero.MIMETypeHandler = new function () {
/**
* Removes a handler for a specific MIME type
* @param {String} type MIME type to handle
* @param {Function} handler Function handler to remove
* @param {Object} handlers Function handlers to remove
*/
this.removeHandler = function (type, handler) {
this.removeHandlers = function (type, handlers) {
// If no handler specified or this is the last handler for the type
// stop monitoring the content type completely.
if (!handler || _typeHandlers[type] && _typeHandlers[type].length <= 1) {
if (!handlers || _typeHandlers[type] && _typeHandlers[type].length <= 1) {
delete _typeHandlers[type];
_ignoreContentDispositionTypes.delete(type);
}
else if (_typeHandlers[type]) {
var i = _typeHandlers[type].indexOf(handler);
var i = _typeHandlers[type].indexOf(handlers);
if (i != -1) {
_typeHandlers[type].splice(i, 1);
}
@ -214,10 +224,27 @@ Zotero.MIMETypeHandler = new function () {
}
/**
* Called when the request is started; we ignore this
* Called when the request is started
*/
_StreamListener.prototype.onStartRequest = function(channel, context) {}
_StreamListener.prototype.onStartRequest = async function(channel, context) {
try {
if (!_typeHandlers[this._contentType]) return;
for (let handlers of _typeHandlers[this._contentType]) {
if (!handlers.onStartRequest) continue;
let maybePromise = handlers.onStartRequest(
this._request.name ? this._request.name : null,
this._contentType,
channel
);
if (maybePromise && maybePromise.then) {
maybePromise = await maybePromise;
}
}
}
catch (e) {
Zotero.logError(e);
}
}
/**
* Called when there's data available; we collect this data and keep it until the request is
@ -259,14 +286,16 @@ Zotero.MIMETypeHandler = new function () {
var handled = false;
try {
for (let handler of _typeHandlers[this._contentType]) {
let maybePromise = handler(
if (!_typeHandlers[this._contentType]) return;
for (let handlers of _typeHandlers[this._contentType]) {
if (!handlers.onContent) continue;
let maybePromise = handlers.onContent(
blob,
this._request.name ? this._request.name : null,
this._contentType,
channel
);
if (maybePromise.then) {
if (maybePromise && maybePromise.then) {
maybePromise = await maybePromise;
}
handled = handled || maybePromise;