mirror of
https://github.com/zotero/zotero.git
synced 2026-10-08 03:08:19 +00:00
Add support for taking snapshots of files handled by plugins (e.g. Flash, QuickTime, PDFs) with importDocument()
Apparently nsIWebBrowserPersist's nsIWebProgressListener never reaches STOP_STOP and STATE_IS_NETWORK in onStateChange() for plugin-handled files, so the import process wasn't completing. I'm now using onProgressChange() for such files. Also: - Moved DB commits out of callbacks so that transactions aren't left open if the progress listener never completes - Blocked attempts to call snapshots on plugin files with file:// URLs, since they're one case where the listener doesn't work properly - Added Zotero.MIME.hasNativeHandler() to be able to distinguish between natively handled files and plugin-handled files. - Fixed a couple bugs that might have made prevented snapshots from working properly
This commit is contained in:
parent
9bc8d1356a
commit
be5ddb1505
3 changed files with 125 additions and 25 deletions
|
|
@ -224,12 +224,36 @@ Zotero.Attachments = new function(){
|
|||
file.append(fileName);
|
||||
|
||||
wbp.progressListener = new Zotero.WebProgressFinishListener(function(){
|
||||
_addToDB(file, url, title, Zotero.Attachments.LINK_MODE_IMPORTED_URL,
|
||||
mimeType, null, sourceItemID, itemID);
|
||||
|
||||
Zotero.DB.commitTransaction();
|
||||
try {
|
||||
_addToDB(file, url, title, Zotero.Attachments.LINK_MODE_IMPORTED_URL,
|
||||
mimeType, null, sourceItemID, itemID);
|
||||
}
|
||||
catch (e) {
|
||||
// Clean up
|
||||
if (itemID) {
|
||||
var item = Zotero.Items.get(itemID);
|
||||
if (item) {
|
||||
item.erase();
|
||||
}
|
||||
|
||||
try {
|
||||
var destDir = Zotero.getStorageDirectory();
|
||||
destDir.append(itemID);
|
||||
if (destDir.exists()) {
|
||||
destDir.remove(true);
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
|
||||
throw (e);
|
||||
}
|
||||
});
|
||||
|
||||
// The attachment is still incomplete here, but we can't risk
|
||||
// leaving the transaction open if the callback never triggers
|
||||
Zotero.DB.commitTransaction();
|
||||
|
||||
wbp.saveURI(nsIURL, null, null, null, null, file);
|
||||
}
|
||||
catch (e){
|
||||
|
|
@ -282,7 +306,7 @@ Zotero.Attachments = new function(){
|
|||
function linkFromDocument(document, sourceItemID, parentCollectionIDs){
|
||||
Zotero.debug('Linking attachment from document');
|
||||
|
||||
var url = document.location;
|
||||
var url = document.location.href;
|
||||
var title = document.title; // TODO: don't use Mozilla-generated title for images, etc.
|
||||
var mimeType = document.contentType;
|
||||
var charsetID = Zotero.CharacterSets.getID(document.characterSet);
|
||||
|
|
@ -312,10 +336,20 @@ Zotero.Attachments = new function(){
|
|||
function importFromDocument(document, sourceItemID, forceTitle, parentCollectionIDs){
|
||||
Zotero.debug('Importing attachment from document');
|
||||
|
||||
var url = document.location;
|
||||
var url = document.location.href;
|
||||
var title = forceTitle ? forceTitle : document.title;
|
||||
var mimeType = document.contentType;
|
||||
var charsetID = Zotero.CharacterSets.getID(document.characterSet);
|
||||
var hasNativeHandler = Zotero.MIME.hasNativeHandler(mimeType, _getExtensionFromURL(url))
|
||||
|
||||
// TODO: make this work -- with local plugin files, onStateChange in the
|
||||
// nsIWebBrowserPersist's nsIWebProgressListener never completes and
|
||||
// onProgressChange returns -1 for maxTotal, which prevents it from
|
||||
// triggering the callback.
|
||||
if (!hasNativeHandler && url.substr(0, 4) == 'file') {
|
||||
Zotero.debug('Import of loaded files from plugins is not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
|
||||
var wbp = Components
|
||||
|
|
@ -360,6 +394,8 @@ Zotero.Attachments = new function(){
|
|||
|
||||
wbp.progressListener = new Zotero.WebProgressFinishListener(function(){
|
||||
try {
|
||||
Zotero.DB.beginTransaction();
|
||||
|
||||
_addToDB(file, url, title, Zotero.Attachments.LINK_MODE_IMPORTED_URL, mimeType,
|
||||
charsetID, sourceItemID, itemID);
|
||||
|
||||
|
|
@ -377,23 +413,32 @@ Zotero.Attachments = new function(){
|
|||
catch (e) {
|
||||
Zotero.DB.rollbackTransaction();
|
||||
|
||||
try {
|
||||
// Clean up
|
||||
if (itemID) {
|
||||
// Clean up
|
||||
if (itemID) {
|
||||
var item = Zotero.Items.get(itemID);
|
||||
if (item) {
|
||||
item.erase();
|
||||
}
|
||||
|
||||
try {
|
||||
var destDir = Zotero.getStorageDirectory();
|
||||
destDir.append(itemID);
|
||||
if (destDir.exists()) {
|
||||
destDir.remove(true);
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
throw (e);
|
||||
}
|
||||
|
||||
Zotero.Fulltext.indexDocument(document, itemID);
|
||||
});
|
||||
}, !hasNativeHandler);
|
||||
|
||||
// The attachment is still incomplete here, but we can't risk
|
||||
// leaving the transaction open if the callback never triggers
|
||||
Zotero.DB.commitTransaction();
|
||||
|
||||
wbp.saveDocument(document, file, destDir, mimeType, encodingFlags, false);
|
||||
}
|
||||
|
|
@ -430,7 +475,7 @@ Zotero.Attachments = new function(){
|
|||
try {
|
||||
var ext = Components.classes["@mozilla.org/mime;1"]
|
||||
.getService(Components.interfaces.nsIMIMEService)
|
||||
.getPrimaryExtension(mimeType, nsIURL.fileExt ? nsIURL.fileExt : null);
|
||||
.getPrimaryExtension(mimeType, nsIURL.fileExtension);
|
||||
}
|
||||
// getPrimaryExtension doesn't work on Linux
|
||||
catch (e) {}
|
||||
|
|
@ -440,6 +485,14 @@ Zotero.Attachments = new function(){
|
|||
}
|
||||
|
||||
|
||||
function _getExtensionFromURL(url) {
|
||||
var nsIURL = Components.classes["@mozilla.org/network/standard-url;1"]
|
||||
.createInstance(Components.interfaces.nsIURL);
|
||||
nsIURL.spec = url;
|
||||
return nsIURL.fileExtension;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new item of type 'attachment' and add to the itemAttachments table
|
||||
*
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ Zotero.MIME = new function(){
|
|||
this.sniffForBinary = sniffForBinary;
|
||||
this.getMIMETypeFromData = getMIMETypeFromData;
|
||||
this.getMIMETypeFromFile = getMIMETypeFromFile;
|
||||
this.hasNativeHandler = hasNativeHandler;
|
||||
this.hasInternalHandler = hasInternalHandler;
|
||||
this.fileHasInternalHandler = fileHasInternalHandler;
|
||||
|
||||
|
|
@ -134,8 +135,8 @@ Zotero.MIME = new function(){
|
|||
|
||||
|
||||
/*
|
||||
* Determine if a MIME type can be handled internally (natively or with plugins)
|
||||
* or if it needs to be passed off to an external helper app
|
||||
* Determine if a MIME type can be handled natively
|
||||
* or if it needs to be passed off to a plugin or external helper app
|
||||
*
|
||||
* ext is an optional extension hint (only needed for text/plain files
|
||||
* that should be forced to open externally)
|
||||
|
|
@ -146,7 +147,7 @@ Zotero.MIME = new function(){
|
|||
* Note: nsIMIMEInfo provides a hasDefaultHandler() method, but it doesn't
|
||||
* do what we need
|
||||
*/
|
||||
function hasInternalHandler(mimeType, ext){
|
||||
function hasNativeHandler(mimeType, ext) {
|
||||
if (mimeType=='text/plain'){
|
||||
if (this.isExternalTextExtension(ext)){
|
||||
Zotero.debug('text/plain file has extension that should be handled externally');
|
||||
|
|
@ -160,6 +161,22 @@ Zotero.MIME = new function(){
|
|||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Determine if a MIME type can be handled internally
|
||||
* or if it needs to be passed off to an external helper app
|
||||
*
|
||||
* Similar to hasNativeHandler() but also includes plugins
|
||||
*/
|
||||
function hasInternalHandler(mimeType, ext) {
|
||||
var isNative = hasNativeHandler(mimeType, ext);
|
||||
if (isNative !== null) {
|
||||
return isNative;
|
||||
}
|
||||
|
||||
// Is there a better way to get to navigator?
|
||||
var types = Components.classes["@mozilla.org/appshell/appShellService;1"]
|
||||
.getService(Components.interfaces.nsIAppShellService)
|
||||
|
|
|
|||
|
|
@ -1148,16 +1148,46 @@ Zotero.Browser = new function() {
|
|||
}
|
||||
|
||||
|
||||
Zotero.WebProgressFinishListener = function(onFinish){
|
||||
this.onStateChange = function(wp, req, stateFlags, status){
|
||||
if ((stateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP)
|
||||
&& (stateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_NETWORK)){
|
||||
onFinish();
|
||||
/*
|
||||
* Implements nsIWebProgressListener
|
||||
*
|
||||
* For plugin content, onStateChange doesn't seem to be called after the document
|
||||
* finishes loading, so the useProgress flag can be used to run onFinish()
|
||||
* when all the content of the request has been loaded -- this should only be
|
||||
* used for single file requests (generally, things handled by plugins)
|
||||
*/
|
||||
Zotero.WebProgressFinishListener = function(onFinish, useProgress, wbp) {
|
||||
var _finished = false;
|
||||
|
||||
this.onStateChange = function(wp, req, stateFlags, status) {
|
||||
if ((stateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP)
|
||||
&& (stateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_NETWORK)) {
|
||||
if (useProgress) {
|
||||
Zotero.debug('WebProgressFinishListener: useProgress set but STOP_STOP and STATE_IS_NETWORK were reached', 2);
|
||||
if (_finished) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
_finished = true;
|
||||
}
|
||||
}
|
||||
onFinish();
|
||||
}
|
||||
|
||||
this.onLocationChange = function(){}
|
||||
this.onProgressChange = function(){}
|
||||
this.onSecurityChange = function(){}
|
||||
this.onStatusChange = function(){}
|
||||
}
|
||||
|
||||
this.onLocationChange = function() {}
|
||||
|
||||
this.onProgressChange = function(wp, req, cur, max, curTotal, maxTotal) {
|
||||
// DEBUG: This will never complete if the file size (maxTotal) isn't
|
||||
// available, which seems to be the case with local files and is
|
||||
// presumably the case with remote servers that don't send the file size.
|
||||
//Zotero.debug('Current total: ' + curTotal + ' Max total: ' + maxTotal);
|
||||
if (!_finished && useProgress && (curTotal == maxTotal)) {
|
||||
_finished = true;
|
||||
onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
this.onSecurityChange = function() {}
|
||||
this.onStatusChange = function(wp, req, st, msg) {}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue