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
This commit is contained in:
parent
3ce672756c
commit
89acdf101c
|
@ -530,8 +530,7 @@ var ScholarPane = new function()
|
||||||
if(attachment.getAttachmentLinkMode() != Scholar.Attachments.LINK_MODE_LINKED_URL)
|
if(attachment.getAttachmentLinkMode() != Scholar.Attachments.LINK_MODE_LINKED_URL)
|
||||||
{
|
{
|
||||||
var file = attachment.getFile();
|
var file = attachment.getFile();
|
||||||
if (attachment.getAttachmentLinkMode() == Scholar.Attachments.LINK_MODE_IMPORTED_URL
|
if (Scholar.MIME.fileHasInternalHandler(file))
|
||||||
|| Scholar.File.hasInternalHandler(file))
|
|
||||||
{
|
{
|
||||||
window.loadURI(attachment.getLocalFileURL());
|
window.loadURI(attachment.getLocalFileURL());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1978,13 +1978,70 @@ Scholar.Attachments = new function(){
|
||||||
|
|
||||||
|
|
||||||
function importFromURL(url, sourceItemID){
|
function importFromURL(url, sourceItemID){
|
||||||
var browser = Scholar.Browser.createHiddenBrowser();
|
Scholar.Utilities.HTTP.doHead(url, function(obj){
|
||||||
browser.addEventListener("pageshow", function(){
|
var mimeType = obj.channel.contentType;
|
||||||
Scholar.Attachments.importFromDocument(browser.contentDocument, sourceItemID);
|
|
||||||
browser.removeEventListener("pageshow", arguments.callee, true);
|
var nsIURL = Components.classes["@mozilla.org/network/standard-url;1"]
|
||||||
Scholar.Browser.deleteHiddenBrowser(browser);
|
.createInstance(Components.interfaces.nsIURL);
|
||||||
}, true);
|
nsIURL.spec = url;
|
||||||
browser.loadURI(url, null, null, null, null);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,6 @@
|
||||||
Scholar.File = new function(){
|
Scholar.File = new function(){
|
||||||
this.getExtension = getExtension;
|
this.getExtension = getExtension;
|
||||||
this.isExternalTextExtension = isExternalTextExtension;
|
|
||||||
this.getSample = getSample;
|
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){
|
function getExtension(file){
|
||||||
|
@ -41,14 +9,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)
|
* Get the first 128 bytes of the file as a string (multibyte-safe)
|
||||||
*/
|
*/
|
||||||
|
@ -69,113 +29,31 @@ Scholar.File = new function(){
|
||||||
return str.value;
|
return str.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Searches file for magic numbers
|
function getCharsetFromFile(file){
|
||||||
*/
|
var browser = Scholar.Browser.createHiddenBrowser();
|
||||||
function sniffForMIMEType(file){
|
var url = Components.classes["@mozilla.org/network/protocol;1?name=file"]
|
||||||
var str = this.getSample(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');
|
||||||
|
|
||||||
for (var i in _snifferEntries){
|
browser.addEventListener("load", function(){
|
||||||
if (str.indexOf(_snifferEntries[i][0])==0){
|
var charset = browser.contentDocument.characterSet;
|
||||||
return _snifferEntries[i][1];
|
Scholar.debug('Resetting character detector to ' + oldPref);
|
||||||
}
|
prefService.setCharPref('intl.charset.detector', oldPref);
|
||||||
|
Scholar.Browser.deleteHiddenBrowser(browser);
|
||||||
|
|
||||||
|
}, 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
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
browser.loadURI(url, Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
178
chrome/chromeFiles/content/scholar/xpcom/mime.js
Normal file
178
chrome/chromeFiles/content/scholar/xpcom/mime.js
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,10 +30,6 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||||
.getService(Ci.mozIJSSubScriptLoader)
|
.getService(Ci.mozIJSSubScriptLoader)
|
||||||
.loadSubScript("chrome://scholar/content/xpcom/data_access.js");
|
.loadSubScript("chrome://scholar/content/xpcom/data_access.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"]
|
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||||
.getService(Ci.mozIJSSubScriptLoader)
|
.getService(Ci.mozIJSSubScriptLoader)
|
||||||
.loadSubScript("chrome://scholar/content/xpcom/notifier.js");
|
.loadSubScript("chrome://scholar/content/xpcom/notifier.js");
|
||||||
|
@ -62,6 +58,14 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||||
.getService(Ci.mozIJSSubScriptLoader)
|
.getService(Ci.mozIJSSubScriptLoader)
|
||||||
.loadSubScript("chrome://scholar/content/xpcom/utilities.js");
|
.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");
|
||||||
|
|
||||||
/********************************************************************/
|
/********************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user