commit 89acdf101ca35514e4282339ff4c34fca89619f4
parent 3ce672756c4f0954b3429ec05c43849c5b43cc4d
Author: Dan Stillman <dstillman@zotero.org>
Date: Sat, 19 Aug 2006 20:51:01 +0000
Fix #191, calling Scholar.Attachments.importFromURL on a PDF without PDF plug-in installed results in a prompt to save the file to the disk
Attachments.importFromURL() now first does a HEAD request to get the MIME type and passes that through Scholar.MIME.hasInternalHandler() (now abstracted from Scholar.File, along with the other MIME functions) -- if it can handle the MIME type, it uses a hidden browser; otherwise, it use a remote web page persist to save the file directly
Diffstat:
5 files changed, 272 insertions(+), 156 deletions(-)
diff --git a/chrome/chromeFiles/content/scholar/overlay.js b/chrome/chromeFiles/content/scholar/overlay.js
@@ -530,8 +530,7 @@ var ScholarPane = new function()
if(attachment.getAttachmentLinkMode() != Scholar.Attachments.LINK_MODE_LINKED_URL)
{
var file = attachment.getFile();
- if (attachment.getAttachmentLinkMode() == Scholar.Attachments.LINK_MODE_IMPORTED_URL
- || Scholar.File.hasInternalHandler(file))
+ if (Scholar.MIME.fileHasInternalHandler(file))
{
window.loadURI(attachment.getLocalFileURL());
}
diff --git a/chrome/chromeFiles/content/scholar/xpcom/data_access.js b/chrome/chromeFiles/content/scholar/xpcom/data_access.js
@@ -1978,13 +1978,70 @@ Scholar.Attachments = new function(){
function importFromURL(url, sourceItemID){
- var browser = Scholar.Browser.createHiddenBrowser();
- browser.addEventListener("pageshow", function(){
- Scholar.Attachments.importFromDocument(browser.contentDocument, sourceItemID);
- browser.removeEventListener("pageshow", arguments.callee, true);
- Scholar.Browser.deleteHiddenBrowser(browser);
- }, true);
- browser.loadURI(url, null, null, null, null);
+ Scholar.Utilities.HTTP.doHead(url, function(obj){
+ var mimeType = obj.channel.contentType;
+
+ var nsIURL = Components.classes["@mozilla.org/network/standard-url;1"]
+ .createInstance(Components.interfaces.nsIURL);
+ nsIURL.spec = url;
+ var ext = nsIURL.fileExtension;
+
+ // If we can load this internally, use a hidden browser (so we can
+ // get the charset and title)
+ if (Scholar.MIME.hasInternalHandler(mimeType, ext)){
+ var browser = Scholar.Browser.createHiddenBrowser();
+ browser.addEventListener("pageshow", function(){
+ Scholar.Attachments.importFromDocument(browser.contentDocument, sourceItemID);
+ browser.removeEventListener("pageshow", arguments.callee, true);
+ Scholar.Browser.deleteHiddenBrowser(browser);
+ }, true);
+ browser.loadURI(url, null, null, null, null);
+ }
+
+ // Otherwise use a remote web page persist
+ else {
+ var fileName = _getFileNameFromURL(url, mimeType);
+ var title = fileName;
+
+ const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
+ var wbp = Components
+ .classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ //wbp.persistFlags = nsIWBP.PERSIST_FLAGS...;
+ var encodingFlags = false;
+
+ Scholar.DB.beginTransaction();
+
+ try {
+ // Create a new attachment
+ var attachmentItem = Scholar.Items.getNewItemByType(Scholar.ItemTypes.getID('attachment'));
+ attachmentItem.setField('title', title);
+ attachmentItem.save();
+ var itemID = attachmentItem.getID();
+
+ // Create a new folder for this item in the storage directory
+ var destDir = Scholar.getStorageDirectory();
+ destDir.append(itemID);
+ destDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0644);
+
+ var file = Components.classes["@mozilla.org/file/local;1"].
+ createInstance(Components.interfaces.nsILocalFile);
+ file.initWithFile(destDir);
+ file.append(fileName);
+
+ wbp.saveURI(nsIURL, null, null, null, null, file);
+
+ _addToDB(file, url, title, Scholar.Attachments.LINK_MODE_IMPORTED_URL,
+ mimeType, null, sourceItemID, itemID);
+
+ Scholar.DB.commitTransaction();
+ }
+ catch (e){
+ Scholar.DB.rollbackTransaction();
+ throw (e);
+ }
+ }
+ });
}
diff --git a/chrome/chromeFiles/content/scholar/xpcom/file.js b/chrome/chromeFiles/content/scholar/xpcom/file.js
@@ -1,38 +1,6 @@
Scholar.File = new function(){
this.getExtension = getExtension;
- this.isExternalTextExtension = isExternalTextExtension;
this.getSample = getSample;
- this.sniffForMIMEType = sniffForMIMEType;
- this.sniffForBinary = sniffForBinary;
- this.getMIMETypeFromFile = getMIMETypeFromFile;
- this.hasInternalHandler = hasInternalHandler;
-
- // Magic numbers
- var _snifferEntries = [
- ["%PDF-", "application/pdf"],
- ["%!PS-Adobe-", 'application/postscript'],
- ["%! PS-Adobe-", 'application/postscript'],
- ["From", 'text/plain'],
- [">From", 'text/plain'],
- ["#!", 'text/plain'],
- ["<?xml", 'text/xml']
- ];
-
- // MIME types handled natively by Gecko
- // DEBUG: There's definitely a better way of getting these
- var _nativeMIMETypes = {
- 'text/html': true,
- 'image/jpeg': true,
- 'image/gif': true,
- 'text/xml': true,
- 'text/plain': true,
- 'application/x-javascript': true
- };
-
- // Extensions of text files (generally XML) to force to be external
- var _externalTextExtensions = {
- 'graffle': true
- };
function getExtension(file){
@@ -42,14 +10,6 @@ Scholar.File = new function(){
/*
- * Check if file extension should be forced to open externally
- */
- function isExternalTextExtension(ext){
- return typeof _externalTextExtensions['ext'] != 'undefined';
- }
-
-
- /*
* Get the first 128 bytes of the file as a string (multibyte-safe)
*/
function getSample(file){
@@ -69,113 +29,31 @@ Scholar.File = new function(){
return str.value;
}
- /*
- * Searches file for magic numbers
- */
- function sniffForMIMEType(file){
- var str = this.getSample(file);
-
- for (var i in _snifferEntries){
- if (str.indexOf(_snifferEntries[i][0])==0){
- return _snifferEntries[i][1];
- }
- }
-
- return false;
- }
-
-
- /*
- * Searches file for embedded nulls
- */
- function sniffForBinary(file){
- var str = this.getSample(file);
-
- for (var i=0; i<str.length; i++){
- if (!_isTextCharacter(str.charAt(i))){
- return 'application/octet-stream';
- }
- }
- return 'text/plain';
- }
-
- /*
- * Try to determine the MIME type of the file, trying a few different
- * techniques
- */
- function getMIMETypeFromFile(file){
- var mimeType = this.sniffForMIMEType(file);
- if (mimeType){
- Scholar.debug('Detected MIME type ' + mimeType);
- return mimeType;
- }
+ function getCharsetFromFile(file){
+ var browser = Scholar.Browser.createHiddenBrowser();
+ var url = Components.classes["@mozilla.org/network/protocol;1?name=file"]
+ .getService(Components.interfaces.nsIFileProtocolHandler)
+ .getURLSpecFromFile(file);
+
+ var prefService = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+ var oldPref = prefService.getCharPref('intl.charset.detector');
- try {
- var mimeType = Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"]
- .getService(Components.interfaces.nsIMIMEService).getTypeFromFile(file);
- Scholar.debug('Got MIME type ' + mimeType + ' from extension');
- return mimeType;
- }
- catch (e){
- var mimeType = this.sniffForBinary(file);
- Scholar.debug('Cannot determine MIME type -- settling for ' + mimeType);
- return mimeType;
- }
- }
-
-
- /*
- * Determine if file can be handled internally (natively or with plugins)
- * or if it needs to be passed off to an external helper app
- *
- * Note: it certainly seems there should be a more native way of doing this
- * without replicating all the Mozilla functionality
- */
- function hasInternalHandler(file){
- var mimeType = this.getMIMETypeFromFile(file);
+ browser.addEventListener("load", function(){
+ var charset = browser.contentDocument.characterSet;
+ Scholar.debug('Resetting character detector to ' + oldPref);
+ prefService.setCharPref('intl.charset.detector', oldPref);
+ Scholar.Browser.deleteHiddenBrowser(browser);
+
+ }, true);
- if (mimeType=='text/plain'){
- if (this.isExternalTextExtension(this.getExtension(file))){
- Scholar.debug('text/plain file has extension that should be handled externally');
- return false;
- }
- return true;
+ var newPref = 'universal_charset_detector';
+ if (oldPref!=newPref){
+ Scholar.debug('Setting character detector to universal_charset_detector');
+ prefService.setCharPref('intl.charset.detector', 'universal_charset_detector'); // universal_charset_detector
}
- if (_nativeMIMETypes[mimeType]){
- Scholar.debug('MIME type ' + mimeType + ' can be handled natively');
- return true;
- }
-
- // Is there a better way to get to navigator?
- var types = Components.classes["@mozilla.org/appshell/appShellService;1"]
- .getService(Components.interfaces.nsIAppShellService)
- .hiddenDOMWindow.navigator.mimeTypes;
-
- for (var i in types){
- if (types[i].type==mimeType){
- Scholar.debug('MIME type ' + mimeType + ' can be handled by plugins');
- return true;
- }
- }
-
- Scholar.debug('MIME type ' + mimeType + ' cannot be handled natively');
- return false;
- }
-
-
- /*
- * Detect whether a character is text
- *
- * Based on RFC 2046 Section 4.1.2. Treat any char 0-31
- * except the 9-13 range (\t, \n, \v, \f, \r) and char 27 (used by
- * encodings like Shift_JIS) as non-text
- *
- * This is the logic used by the Mozilla sniffer.
- */
- function _isTextCharacter(chr){
- var chr = chr.charCodeAt(0);
- return chr > 31 || (9 <= chr && chr <=13 ) || chr == 27;
+ browser.loadURI(url, Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY);
}
}
diff --git a/chrome/chromeFiles/content/scholar/xpcom/mime.js b/chrome/chromeFiles/content/scholar/xpcom/mime.js
@@ -0,0 +1,178 @@
+Scholar.MIME = new function(){
+ this.isExternalTextExtension = isExternalTextExtension;
+ this.sniffForMIMEType = sniffForMIMEType;
+ this.sniffForBinary = sniffForBinary;
+ this.getMIMETypeFromData = getMIMETypeFromData;
+ this.getMIMETypeFromFile = getMIMETypeFromFile;
+ this.hasInternalHandler = hasInternalHandler;
+ this.fileHasInternalHandler = fileHasInternalHandler;
+
+ // Magic numbers
+ var _snifferEntries = [
+ ["%PDF-", "application/pdf"],
+ ["%!PS-Adobe-", 'application/postscript'],
+ ["%! PS-Adobe-", 'application/postscript'],
+ ["From", 'text/plain'],
+ [">From", 'text/plain'],
+ ["#!", 'text/plain'],
+ ["<?xml", 'text/xml']
+ ];
+
+ // MIME types handled natively by Gecko
+ // DEBUG: There's definitely a better way of getting these
+ var _nativeMIMETypes = {
+ 'text/html': true,
+ 'image/jpeg': true,
+ 'image/gif': true,
+ 'text/xml': true,
+ 'text/plain': true,
+ 'application/x-javascript': true
+ };
+
+ // Extensions of text files (generally XML) to force to be external
+ var _externalTextExtensions = {
+ 'graffle': true
+ };
+
+
+ /*
+ * Check if file extension should be forced to open externally
+ */
+ function isExternalTextExtension(ext){
+ return typeof _externalTextExtensions['ext'] != 'undefined';
+ }
+
+
+ /*
+ * Searches string for magic numbers
+ */
+ function sniffForMIMEType(str){
+ for (var i in _snifferEntries){
+ if (str.indexOf(_snifferEntries[i][0])==0){
+ return _snifferEntries[i][1];
+ }
+ }
+
+ return false;
+ }
+
+
+ /*
+ * Searches string for embedded nulls
+ *
+ * Returns 'application/octet-stream' or 'text/plain'
+ */
+ function sniffForBinary(str){
+ for (var i=0; i<str.length; i++){
+ if (!_isTextCharacter(str.charAt(i))){
+ return 'application/octet-stream';
+ }
+ }
+ return 'text/plain';
+ }
+
+
+ /*
+ * Try to determine the MIME type of a string, using a few different
+ * techniques
+ *
+ * ext is an optional file extension hint if data sniffing is unsuccessful
+ */
+ function getMIMETypeFromData(str, ext){
+ var mimeType = this.sniffForMIMEType(str);
+ if (mimeType){
+ Scholar.debug('Detected MIME type ' + mimeType);
+ return mimeType;
+ }
+
+ try {
+ var mimeType = Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"]
+ .getService(Components.interfaces.nsIMIMEService).getTypeFromExtension(ext);
+ Scholar.debug('Got MIME type ' + mimeType + ' from extension');
+ return mimeType;
+ }
+ catch (e){
+ var mimeType = this.sniffForBinary(str);
+ Scholar.debug('Cannot determine MIME type -- settling for ' + mimeType);
+ return mimeType;
+ }
+ }
+
+
+ /*
+ * Try to determine the MIME type of the file, using a few different
+ * techniques
+ */
+ function getMIMETypeFromFile(file){
+ var str = Scholar.File.getSample(file);
+ var ext = Scholar.File.getExtension(file);
+
+ return this.getMIMETypeFromData(str, ext);
+ }
+
+
+ /*
+ * 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
+ *
+ * ext is an optional extension hint (only needed for text/plain files
+ * that should be forced to open externally)
+ *
+ * Note: it certainly seems there should be a more native way of doing this
+ * without replicating all the Mozilla functionality
+ *
+ * Note: nsIMIMEInfo provides a hasDefaultHandler() method, but it doesn't
+ * do what we need
+ */
+ function hasInternalHandler(mimeType, ext){
+ if (mimeType=='text/plain'){
+ if (this.isExternalTextExtension(ext)){
+ Scholar.debug('text/plain file has extension that should be handled externally');
+ return false;
+ }
+ return true;
+ }
+
+ if (_nativeMIMETypes[mimeType]){
+ Scholar.debug('MIME type ' + mimeType + ' can be handled natively');
+ return true;
+ }
+
+ // Is there a better way to get to navigator?
+ var types = Components.classes["@mozilla.org/appshell/appShellService;1"]
+ .getService(Components.interfaces.nsIAppShellService)
+ .hiddenDOMWindow.navigator.mimeTypes;
+
+ for (var i in types){
+ if (types[i].type==mimeType){
+ Scholar.debug('MIME type ' + mimeType + ' can be handled by plugins');
+ return true;
+ }
+ }
+
+ Scholar.debug('MIME type ' + mimeType + ' cannot be handled natively');
+ return false;
+ }
+
+
+ function fileHasInternalHandler(file){
+ var mimeType = this.getMIMETypeFromFile(file);
+ var ext = Scholar.File.getExtension(file);
+ return this.hasInternalHandler(mimeType, ext);
+ }
+
+
+ /*
+ * Detect whether a character is text
+ *
+ * Based on RFC 2046 Section 4.1.2. Treat any char 0-31
+ * except the 9-13 range (\t, \n, \v, \f, \r) and char 27 (used by
+ * encodings like Shift_JIS) as non-text
+ *
+ * This is the logic used by the Mozilla sniffer.
+ */
+ function _isTextCharacter(chr){
+ var chr = chr.charCodeAt(0);
+ return chr > 31 || (9 <= chr && chr <=13 ) || chr == 27;
+ }
+}
diff --git a/components/chnmIScholarService.js b/components/chnmIScholarService.js
@@ -32,10 +32,6 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"]
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
- .loadSubScript("chrome://scholar/content/xpcom/file.js");
-
-Cc["@mozilla.org/moz/jssubscript-loader;1"]
- .getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://scholar/content/xpcom/notifier.js");
Cc["@mozilla.org/moz/jssubscript-loader;1"]
@@ -62,6 +58,14 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://scholar/content/xpcom/utilities.js");
+Cc["@mozilla.org/moz/jssubscript-loader;1"]
+ .getService(Ci.mozIJSSubScriptLoader)
+ .loadSubScript("chrome://scholar/content/xpcom/file.js");
+
+Cc["@mozilla.org/moz/jssubscript-loader;1"]
+ .getService(Ci.mozIJSSubScriptLoader)
+ .loadSubScript("chrome://scholar/content/xpcom/mime.js");
+
/********************************************************************/