www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

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:
Mchrome/chromeFiles/content/scholar/overlay.js | 3+--
Mchrome/chromeFiles/content/scholar/xpcom/data_access.js | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mchrome/chromeFiles/content/scholar/xpcom/file.js | 164++++++++++---------------------------------------------------------------------
Achrome/chromeFiles/content/scholar/xpcom/mime.js | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcomponents/chnmIScholarService.js | 12++++++++----
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"); + /********************************************************************/