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:
Dan Stillman 2006-12-05 23:23:01 +00:00
parent 9bc8d1356a
commit be5ddb1505
3 changed files with 125 additions and 25 deletions

View file

@ -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
*

View file

@ -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)

View file

@ -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) {}
}