releases-comm-central/suite/common/contentAreaUtils.js

1062 строки
36 KiB
JavaScript

/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Save File)
* Justin Wood <Callek@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const kExistingWindow = Components.interfaces.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW;
const kNewWindow = Components.interfaces.nsIBrowserDOMWindow.OPEN_NEWWINDOW;
const kNewTab = Components.interfaces.nsIBrowserDOMWindow.OPEN_NEWTAB;
/**
* Determine whether or not a given focused DOMWindow is in the content area.
**/
function isContentFrame(aFocusedWindow)
{
if (!aFocusedWindow)
return false;
return (aFocusedWindow.top == window.content);
}
/**
* urlSecurityCheck: JavaScript wrapper for checkLoadURIWithPrincipal
* and checkLoadURIStrWithPrincipal.
* If |aPrincipal| is not allowed to link to |aURL|, this function throws with
* an error message.
*
* @param aURI
* The URL a page has linked to. This could be passed either as a string
* or as an nsIURI object.
* @param aPrincipal
* The principal of the node from which aURL came.
* @param aFlags
* Flags to be passed to checkLoadURIStrWithPrincipal.
* nsIScriptSecurityManager.STANDARD is the default value.
*/
function urlSecurityCheck(aURI, aPrincipal, aFlags)
{
// URL Loading Security Check
const nsIScriptSecurityManager =
Components.interfaces.nsIScriptSecurityManager;
var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(nsIScriptSecurityManager);
try {
if (aURI instanceof Components.interfaces.nsIURI)
secMan.checkLoadURIWithPrincipal(aPrincipal, aURI, aFlags);
else
secMan.checkLoadURIStrWithPrincipal(aPrincipal, aURI, aFlags);
} catch (e) {
throw "Load of " + aURI + " denied.";
}
}
function getContentFrameURI(aFocusedWindow)
{
var contentFrame = isContentFrame(aFocusedWindow) ? aFocusedWindow : window.content;
return contentFrame.location.href;
}
function getContentFrameDocument(aFocusedWindow)
{
var contentFrame = isContentFrame(aFocusedWindow) ?
aFocusedWindow : window.content;
return contentFrame.document;
}
function openAsExternal(aURL)
{
var loadType = pref.getIntPref("browser.link.open_external");
var loadInBackground = pref.getBoolPref("browser.tabs.loadDivertedInBackground");
openNewTabWindowOrExistingWith(loadType, aURL, null, loadInBackground);
}
function openNewWindowWith(aURL, aDoc)
{
openNewTabWindowOrExistingWith(kNewWindow, aURL, aDoc, false);
}
function openNewTabWith(aURL, aDoc, aReverseBackgroundPref)
{
var loadInBackground = false;
if (pref) {
loadInBackground = pref.getBoolPref("browser.tabs.loadInBackground");
if (aReverseBackgroundPref)
loadInBackground = !loadInBackground;
}
openNewTabWindowOrExistingWith(kNewTab, aURL, aDoc, loadInBackground);
}
function openNewTabWindowOrExistingWith(aType, aURL, aDoc, aLoadInBackground)
{
// Make sure we are allowed to open this url
if (aDoc)
urlSecurityCheck(aURL, aDoc.nodePrincipal,
Components.interfaces.nsIScriptSecurityManager.STANDARD);
// get referrer, if as external should be null
var referrer = aDoc ? aDoc.documentURIObject : null;
var browserWin;
// if we're not opening a new window, try and find existing window
if (aType != kNewWindow)
browserWin = getTopWin();
// Where appropiate we want to pass the charset of the
// current document over to a new tab / window.
var originCharset = null;
if (aType != kExistingWindow) {
var wintype = document.documentElement.getAttribute('windowtype');
if (wintype == "navigator:browser")
originCharset = window.content.document.characterSet;
}
// We want to open in a new window or no existing window can be found.
if (!browserWin) {
var charsetArg = null;
if (originCharset)
charsetArg = "charset=" + originCharset;
window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no",
aURL, charsetArg, referrer);
return;
}
// Get the existing browser object
var browser = browserWin.getBrowser();
// Open link in an existing window.
if (aType == kExistingWindow) {
browser.loadURI(aURL);
browserWin.content.focus();
return;
}
// open link in new tab
browser.addTab(aURL, referrer, originCharset, !aLoadInBackground);
if (!aLoadInBackground)
browserWin.content.focus();
}
// Clientelle: (Make sure you don't break any of these)
// - File -> Save Page/Frame As...
// - Context -> Save Page/Frame As...
// - Context -> Save Link As...
// - Shift-Click Save Link As
//
// Try saving each of these types:
// - A complete webpage using File->Save Page As, and Context->Save Page As
// - A webpage as HTML only using the above methods
// - A webpage as Text only using the above methods
// - An image with an extension (e.g. .jpg) in its file name, using
// Context->Save Image As...
// - An image without an extension (e.g. a banner ad on cnn.com) using
// the above method.
// - A linked document using Save Link As...
// - A linked document using shift-click Save Link As...
//
function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
aReferrer)
{
internalSave(aURL, null, aFileName, null, null, aShouldBypassCache,
aFilePickerTitleKey, null, aReferrer, true, null);
}
// Just like saveURL, but will get some info off the image before
// calling internalSave
// Clientelle: (Make sure you don't break any of these)
// - Context -> Save Image As...
const imgICache = Components.interfaces.imgICache;
const nsISupportsCString = Components.interfaces.nsISupportsCString;
function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
aReferrer)
{
var contentType = null;
var contentDisposition = null;
if (!aShouldBypassCache) {
try {
var imageCache = Components.classes["@mozilla.org/image/cache;1"]
.getService(imgICache);
var props =
imageCache.findEntryProperties(makeURI(aURL, getCharsetforSave(null)));
if (props) {
contentType = props.get("type", nsISupportsCString);
contentDisposition = props.get("content-disposition",
nsISupportsCString);
}
} catch (e) {
// Failure to get type and content-disposition off the image is non-fatal
}
}
internalSave(aURL, null, aFileName, contentDisposition, contentType,
aShouldBypassCache, aFilePickerTitleKey, null, aReferrer,
true, null);
}
function saveFrameDocument()
{
var focusedWindow = document.commandDispatcher.focusedWindow;
if (isContentFrame(focusedWindow))
saveDocument(focusedWindow.document);
}
function saveDocument(aDocument)
{
if (!aDocument)
throw "Must have a document when calling saveDocument";
// We want to use cached data because the document is currently visible.
var ifreq =
aDocument.defaultView
.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
var contentDisposition = null;
try {
contentDisposition =
ifreq.getInterface(Components.interfaces.nsIDOMWindowUtils)
.getDocumentMetadata("content-disposition");
} catch (ex) {
// Failure to get a content-disposition is ok
}
var cacheKey = null;
try {
cacheKey = ifreq.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIWebPageDescriptor)
.currentDescriptor
.QueryInterface(Components.interfaces.nsISHEntry).cacheKey;
} catch (ex) {
// We might not find it in the cache. Oh, well.
}
internalSave(aDocument.location.href, aDocument, null, contentDisposition,
aDocument.contentType, false, null, null,
aDocument.referrer ? makeURI(aDocument.referrer) : null,
true, cacheKey);
}
function DownloadListener(win, transfer) {
function makeClosure(name) {
return function() {
transfer[name].apply(transfer, arguments);
}
}
this.window = win;
// Now... we need to forward all calls to our transfer
for (var i in transfer) {
if (i != "QueryInterface")
this[i] = makeClosure(i);
}
}
DownloadListener.prototype = {
QueryInterface: function dl_qi(aIID)
{
if (aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
aIID.equals(Components.interfaces.nsISupports)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
},
getInterface: function dl_gi(aIID)
{
if (aIID.equals(Components.interfaces.nsIAuthPrompt) ||
aIID.equals(Components.interfaces.nsIAuthPrompt2)) {
var ww =
Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIPromptFactory);
return ww.getPrompt(this.window, aIID);
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
}
const kSaveAsType_Complete = 0; // Save document with attached objects.
// const kSaveAsType_URL = 1; // Save document or URL by itself.
const kSaveAsType_Text = 2; // Save document, converting to plain text.
/**
* internalSave: Used when saving a document or URL. This method:
* - Determines a local target filename to use (unless parameter
* aChosenData is non-null)
* - Determines content-type if possible
* - Prompts the user to confirm the destination filename and save mode
* (content-type affects this)
* - Creates a 'Persist' object (which will perform the saving in the
* background) and then starts it.
*
* @param aURL The String representation of the URL of the document being saved
* @param aDocument The document to be saved
* @param aDefaultFileName The caller-provided suggested filename if we don't
* find a better one
* @param aContentDisposition The caller-provided content-disposition header
* to use.
* @param aContentType The caller-provided content-type to use
* @param aShouldBypassCache If true, the document will always be refetched
* from the server
* @param aFilePickerTitleKey Alternate title for the file picker
* @param aChosenData If non-null this contains an instance of object AutoChosen
* (see below) which holds pre-determined data so that the user does not
* need to be prompted for a target filename.
* @param aReferrer the referrer URI object (not URL string) to use, or null
* if no referrer should be sent.
* @param aUsePref [ignored] Only here to be compatible with toolkit, it uses
* the param name aSkipPrompt but it does not mean what it says.
* @param aCacheKey [optional] If set will be passed to saveURI.
* See nsIWebBrowserPersist for allowed values.
*/
function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
aContentType, aShouldBypassCache, aFilePickerTitleKey,
aChosenData, aReferrer, aUsePref, aCacheKey)
{
// Note: aDocument == null when this code is used by save-link-as...
var saveMode = GetSaveModeForContentType(aContentType);
var isDocument = aDocument != null && saveMode != SAVEMODE_FILEONLY;
var saveAsType = kSaveAsType_Complete;
var file, fileURL;
// Find the URI object for aURL and the FileName/Extension to use when saving.
// FileName/Extension will be ignored if aChosenData supplied.
var fileInfo = new FileInfo(aDefaultFileName);
if (aChosenData)
file = aChosenData.file;
else {
var charset = null;
if (aDocument)
charset = aDocument.characterSet;
else if (aReferrer)
charset = aReferrer.originCharset;
initFileInfo(fileInfo, aURL, charset, aDocument,
aContentType, aContentDisposition);
var fpParams = {
fpTitleKey: aFilePickerTitleKey,
isDocument: isDocument,
fileInfo: fileInfo,
contentType: aContentType,
saveMode: saveMode,
saveAsType: saveAsType,
file: file,
fileURL: fileURL
};
if (!getTargetFile(fpParams))
// If the method returned false this is because the user cancelled from
// the save file picker dialog.
return;
saveAsType = fpParams.saveAsType;
saveMode = fpParams.saveMode;
file = fpParams.file;
fileURL = fpParams.fileURL;
}
if (!fileURL)
fileURL = makeFileURI(file);
// XXX We depend on the following holding true in appendFiltersForContentType():
// If we should save as a complete page, the saveAsType is kSaveAsType_Complete.
// If we should save as text, the saveAsType is kSaveAsType_Text.
var useSaveDocument = isDocument &&
(((saveMode & SAVEMODE_COMPLETE_DOM) && (saveAsType == kSaveAsType_Complete)) ||
((saveMode & SAVEMODE_COMPLETE_TEXT) && (saveAsType == kSaveAsType_Text)));
// If we're saving a document, and are saving either in complete mode or
// as converted text, pass the document to the web browser persist component.
// If we're just saving the HTML (second option in the list), send only the URI.
var source = useSaveDocument ? aDocument : fileInfo.uri;
var persistArgs = {
source : source,
contentType : (!aChosenData && useSaveDocument &&
saveAsType == kSaveAsType_Text) ?
"text/plain" : null,
target : fileURL,
postData : isDocument ? getPostData(aDocument) : null,
bypassCache : aShouldBypassCache
};
var persist = makeWebBrowserPersist();
// Calculate persist flags.
const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
if (aShouldBypassCache)
persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
else
persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
// Leave it to WebBrowserPersist to discover the encoding type (or lack thereof):
persist.persistFlags |= nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
// Create download and initiate it (below)
var tr = Components.classes["@mozilla.org/transfer;1"].createInstance(Components.interfaces.nsITransfer);
if (useSaveDocument) {
// Saving a Document, not a URI:
var filesFolder = null;
if (persistArgs.contentType != "text/plain") {
// Create the local directory into which to save associated files.
filesFolder = file.clone();
var nameWithoutExtension = getFileBaseName(filesFolder.leafName);
filesFolder.leafName =
document.getElementById("contentAreaCommandsBundle")
.getFormattedString("filesFolder", [nameWithoutExtension]);
}
var encodingFlags = 0;
if (persistArgs.contentType == "text/plain") {
encodingFlags |= nsIWBP.ENCODE_FLAGS_FORMATTED;
encodingFlags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
encodingFlags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT;
}
else {
encodingFlags |= nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
}
const kWrapColumn = 80;
tr.init((aChosenData ? aChosenData.uri : fileInfo.uri),
persistArgs.target, "", null, null, null, persist);
persist.progressListener = new DownloadListener(window, tr);
persist.saveDocument(persistArgs.source, persistArgs.target, filesFolder,
persistArgs.contentType, encodingFlags, kWrapColumn);
} else {
tr.init((aChosenData ? aChosenData.uri : source),
persistArgs.target, "", null, null, null, persist);
persist.progressListener = new DownloadListener(window, tr);
persist.saveURI((aChosenData ? aChosenData.uri : source),
aCacheKey, aReferrer, persistArgs.postData, null,
persistArgs.target);
}
}
/**
* Structure for holding info about automatically supplied parameters for
* internalSave(...). This allows parameters to be supplied so the user does not
* need to be prompted for file info.
* @param aFileAutoChosen This is an nsILocalFile object that has been
* pre-determined as the filename for the target to save to
* @param aUriAutoChosen This is the nsIURI object for the target
*/
function AutoChosen(aFileAutoChosen, aUriAutoChosen) {
this.file = aFileAutoChosen;
this.uri = aUriAutoChosen;
}
/**
* Structure for holding info about a URL and the target filename it should be
* saved to. This structure is populated by initFileInfo(...).
* @param aSuggestedFileName This is used by initFileInfo(...) when it
* cannot 'discover' the filename from the url
* @param aFileName The target filename
* @param aFileBaseName The filename without the file extension
* @param aFileExt The extension of the filename
* @param aUri An nsIURI object for the url that is being saved
*/
function FileInfo(aSuggestedFileName, aFileName, aFileBaseName, aFileExt, aUri) {
this.suggestedFileName = aSuggestedFileName;
this.fileName = aFileName;
this.fileBaseName = aFileBaseName;
this.fileExt = aFileExt;
this.uri = aUri;
}
/**
* Determine what the 'default' filename string is, its file extension and the
* filename without the extension. This filename is used when prompting the user
* for confirmation in the file picker dialog.
* @param aFI A FileInfo structure into which we'll put the results of this method.
* @param aURL The String representation of the URL of the document being saved
* @param aURLCharset The charset of aURL.
* @param aDocument The document to be saved
* @param aContentType The content type we're saving, if it could be
* determined by the caller.
* @param aContentDisposition The content-disposition header for the object
* we're saving, if it could be determined by the caller.
*/
function initFileInfo(aFI, aURL, aURLCharset, aDocument,
aContentType, aContentDisposition)
{
try {
// Get an nsIURI object from aURL if possible:
try {
aFI.uri = makeURI(aURL, aURLCharset);
// Assuming nsiUri is valid, calling QueryInterface(...) on it will
// populate extra object fields (eg filename and file extension).
var url = aFI.uri.QueryInterface(Components.interfaces.nsIURL);
aFI.fileExt = url.fileExtension;
} catch (e) {
}
// Get the default filename:
aFI.fileName = getDefaultFileName((aFI.suggestedFileName || aFI.fileName),
aFI.uri, aDocument, aContentDisposition);
// If aFI.fileExt is still blank, consider: aFI.suggestedFileName is supplied
// if saveURL(...) was the original caller (hence both aContentType and
// aDocument are blank). If they were saving a link to a website then make
// the extension .htm .
if (!aFI.fileExt && !aDocument && !aContentType && (/^http(s?):\/\//i.test(aURL))) {
aFI.fileExt = "htm";
aFI.fileBaseName = aFI.fileName;
} else {
aFI.fileExt = getDefaultExtension(aFI.fileName, aFI.uri, aContentType);
aFI.fileBaseName = getFileBaseName(aFI.fileName);
}
} catch (e) {
}
}
/**
* Given the Filepicker Parameters (aFpP), show the file picker dialog,
* prompting the user to confirm (or change) the fileName.
* @param aFpP a structure (see definition in internalSave(...) method)
* containing all the data used within this method.
* @return true if the user confirmed a filename in the picker; false if they
* dismissed the picker.
*/
function getTargetFile(aFpP)
{
var prefs = getPrefsBrowserDownload("browser.download.");
var useDownloadDir = prefs.getBoolPref("useDownloadDir");
const nsILocalFile = Components.interfaces.nsILocalFile;
// Default to the user's default downloads directory configured
// through download prefs.
var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
.getService(Components.interfaces.nsIDownloadManager);
var dir = dlMgr.userDownloadsDirectory;
var dirExists = dir && dir.exists();
if (useDownloadDir && dirExists) {
dir.append(getNormalizedLeafName(aFpP.fileInfo.fileName,
aFpP.fileInfo.fileExt));
aFpP.file = uniqueFile(dir);
return true;
}
// Must Prompt
// Default to lastDir if we must prompt, and lastDir
// is configured and valid. Otherwise, keep the default of
// the user's default downloads directory.
if (!useDownloadDir) try {
var lastDir = prefs.getComplexValue("lastDir", nsILocalFile);
if (lastDir.exists()) {
dir = lastDir;
dirExists = true;
}
} catch(e) {}
if (!dirExists) {
// Default to desktop.
var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
dir = fileLocator.get("Desk", nsILocalFile);
}
var fp = makeFilePicker();
var titleKey = aFpP.fpTitleKey || "SaveLinkTitle";
fp.init(window,
document.getElementById("contentAreaCommandsBundle")
.getString(titleKey),
Components.interfaces.nsIFilePicker.modeSave);
fp.displayDirectory = dir;
fp.defaultExtension = aFpP.fileInfo.fileExt;
fp.defaultString = getNormalizedLeafName(aFpP.fileInfo.fileName,
aFpP.fileInfo.fileExt);
appendFiltersForContentType(fp, aFpP.contentType, aFpP.fileInfo.fileExt,
aFpP.saveMode);
if (aFpP.isDocument) {
try {
fp.filterIndex = prefs.getIntPref("save_converter_index");
}
catch (e) {
}
}
if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
return false;
if (aFpP.isDocument)
prefs.setIntPref("save_converter_index", fp.filterIndex);
var directory = fp.file.parent.QueryInterface(nsILocalFile);
prefs.setComplexValue("lastDir", nsILocalFile, directory);
fp.file.leafName = validateFileName(fp.file.leafName);
aFpP.saveAsType = fp.filterIndex;
aFpP.file = fp.file;
aFpP.fileURL = fp.fileURL;
return true;
}
// Since we're automatically downloading, we don't get the file picker's
// logic to check for existing files, so we need to do that here.
//
// Note - this code is identical to that in
// mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
// If you are updating this code, update that code too! We can't share code
// here since that code is called in a js component.
function uniqueFile(aLocalFile)
{
var collisionCount = 0;
while (aLocalFile.exists()) {
collisionCount++;
if (collisionCount == 1) {
// Append "(2)" before the last dot in (or at the end of) the filename
// special case .ext.gz etc files so we don't wind up with .tar(2).gz
if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i))
aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
else
aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
}
else {
// replace the last (n) in the filename with (n+1)
aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount + 1) + ")");
}
}
return aLocalFile;
}
// We have no DOM, and can only save the URL as is.
const SAVEMODE_FILEONLY = 0x00;
// We have a DOM and can save as complete.
const SAVEMODE_COMPLETE_DOM = 0x01;
// We have a DOM which we can serialize as text.
const SAVEMODE_COMPLETE_TEXT = 0x02;
// If we are able to save a complete DOM, the 'save as complete' filter
// must be the first filter appended. The 'save page only' counterpart
// must be the second filter appended. And the 'save as complete text'
// filter must be the third filter appended.
function appendFiltersForContentType(aFilePicker, aContentType, aFileExtension, aSaveMode)
{
// The bundle name for saving only a specific content type.
var bundleName;
// The corresponding filter string for a specific content type.
var filterString;
// XXX all the cases that are handled explicitly here MUST be handled
// in GetSaveModeForContentType to return a non-fileonly filter.
switch (aContentType) {
case "text/html":
bundleName = "WebPageHTMLOnlyFilter";
filterString = "*.htm; *.html";
break;
case "application/xhtml+xml":
bundleName = "WebPageXHTMLOnlyFilter";
filterString = "*.xht; *.xhtml";
break;
case "image/svg+xml":
bundleName = "WebPageSVGOnlyFilter";
filterString = "*.svg; *.svgz";
break;
case "text/xml":
case "application/xml":
bundleName = "WebPageXMLOnlyFilter";
filterString = "*.xml";
break;
default:
if (aSaveMode != SAVEMODE_FILEONLY)
throw "Invalid save mode for type '" + aContentType + "'";
var mimeInfo = getMIMEInfoForType(aContentType, aFileExtension);
if (mimeInfo) {
var extEnumerator = mimeInfo.getFileExtensions();
var extString = "";
while (extEnumerator.hasMore()) {
var extension = extEnumerator.getNext();
if (extString)
extString += "; "; // If adding more than one extension,
// separate by semi-colon
extString += "*." + extension;
}
if (extString)
aFilePicker.appendFilter(mimeInfo.description, extString);
}
break;
}
if (aSaveMode & SAVEMODE_COMPLETE_DOM) {
const bundle = document.getElementById("contentAreaCommandsBundle");
aFilePicker.appendFilter(bundle.getString("WebPageCompleteFilter"), filterString);
// We should always offer a choice to save document only if
// we allow saving as complete.
aFilePicker.appendFilter(bundle.getString(bundleName), filterString);
}
if (aSaveMode & SAVEMODE_COMPLETE_TEXT)
aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterText);
// Always append the all files (*) filter
aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
}
function getPostData(aDocument)
{
try {
var sessionHistory = aDocument.defaultView
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.sessionHistory;
return sessionHistory.getEntryAtIndex(sessionHistory.index, false)
.QueryInterface(Components.interfaces.nsISHEntry)
.postData;
}
catch (e) {
}
return null;
}
// Get the preferences branch ("browser.download." for normal 'save' mode)...
function getPrefsBrowserDownload(branch)
{
const prefSvcContractID = "@mozilla.org/preferences-service;1";
const prefSvcIID = Components.interfaces.nsIPrefService;
return Components.classes[prefSvcContractID].getService(prefSvcIID).getBranch(branch);
}
function makeWebBrowserPersist()
{
const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
const persistIID = Components.interfaces.nsIWebBrowserPersist;
return Components.classes[persistContractID].createInstance(persistIID);
}
/**
* Constructs a new URI, using nsIIOService.
* @param aURL The URI spec.
* @param aOriginCharset The charset of the URI.
* @return an nsIURI object based on aURL.
*/
function makeURI(aURL, aOriginCharset)
{
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
return ioService.newURI(aURL, aOriginCharset, null);
}
function makeFileURI(aFile)
{
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
return ioService.newFileURI(aFile);
}
function makeFilePicker()
{
const fpContractID = "@mozilla.org/filepicker;1";
const fpIID = Components.interfaces.nsIFilePicker;
return Components.classes[fpContractID].createInstance(fpIID);
}
function getMIMEService()
{
const mimeSvcContractID = "@mozilla.org/mime;1";
const mimeSvcIID = Components.interfaces.nsIMIMEService;
const mimeSvc = Components.classes[mimeSvcContractID].getService(mimeSvcIID);
return mimeSvc;
}
// Given aFileName, find the fileName without the extension on the end.
function getFileBaseName(aFileName)
{
// Remove the file extension from aFileName:
return aFileName.replace(/\.[^.]*$/, "");
}
function getMIMETypeForURI(aURI)
{
try {
return getMIMEService().getTypeFromURI(aURI);
}
catch (e) {
}
return null;
}
function getMIMEInfoForType(aMIMEType, aExtension)
{
if (aMIMEType || aExtension) {
try {
return getMIMEService().getFromTypeAndExtension(aMIMEType, aExtension);
}
catch (e) {
}
}
return null;
}
function getDefaultFileName(aDefaultFileName, aURI, aDocument,
aContentDisposition)
{
// 1) look for a filename in the content-disposition header, if any
if (aContentDisposition) {
const mhpContractID = "@mozilla.org/network/mime-hdrparam;1";
const mhpIID = Components.interfaces.nsIMIMEHeaderParam;
const mhp = Components.classes[mhpContractID].getService(mhpIID);
var dummy = { value: null }; // Need an out param...
var charset = getCharsetforSave(aDocument);
var fileName = null;
try {
fileName = mhp.getParameter(aContentDisposition, "filename", charset,
true, dummy);
}
catch (e) {
try {
fileName = mhp.getParameter(aContentDisposition, "name", charset, true,
dummy);
}
catch (e) {
}
}
if (fileName)
return fileName;
}
try {
var url = aURI.QueryInterface(Components.interfaces.nsIURL);
if (url.fileName != "") {
// 2) Use the actual file name, if present
var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(Components.interfaces.nsITextToSubURI);
return validateFileName(textToSubURI.unEscapeURIForUI(url.originCharset || "UTF-8", url.fileName));
}
} catch (e) {
// This is something like a data: and so forth URI... no filename here.
}
if (aDocument) {
var docTitle = GenerateValidFilename(aDocument.title, "");
if (docTitle) {
// 3) Use the document title
return docTitle;
}
}
if (aDefaultFileName)
// 4) Use the caller-provided name, if any
return validateFileName(aDefaultFileName);
// 5) If this is a directory, use the last directory name
var path = aURI.path.match(/\/([^\/]+)\/$/);
if (path && path.length > 1)
return validateFileName(path[1]);
try {
if (aURI.host)
// 6) Use the host.
return aURI.host;
} catch (e) {
// Some files have no information at all, like Javascript generated pages
}
try {
// 7) Use the default file name
return document.getElementById("contentAreaCommandsBundle")
.getString("DefaultSaveFileName");
} catch (e) {
//in case localized string cannot be found
}
// 8) If all else fails, use "index"
return "index";
}
function getNormalizedLeafName(aFile, aDefaultExtension)
{
if (!aDefaultExtension)
return aFile;
// Remove trailing dots and spaces on windows
if (/Win/.test(navigator.platform))
aFile = aFile.replace(/[\s.]+$/, "");
// Remove leading dots
aFile = aFile.replace(/^\.+/, "");
// Fix up the file name we're saving to to include the default extension
var i = aFile.lastIndexOf(".");
if (aFile.substr(i + 1) != aDefaultExtension)
return aFile + "." + aDefaultExtension;
return aFile;
}
function getDefaultExtension(aFilename, aURI, aContentType)
{
if (aContentType == "text/plain" || aContentType == "application/octet-stream" || aURI.scheme == "ftp")
return ""; // temporary fix for bug 120327
// First try the extension from the filename
const stdURLContractID = "@mozilla.org/network/standard-url;1";
const stdURLIID = Components.interfaces.nsIURL;
var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
url.filePath = aFilename;
var ext = url.fileExtension;
// This mirrors some code in nsExternalHelperAppService::DoContent
// Use the filename first and then the URI if that fails
var mimeInfo = getMIMEInfoForType(aContentType, ext);
if (ext && mimeInfo && mimeInfo.extensionExists(ext))
return ext;
// Well, that failed. Now try the extension from the URI
var urlext;
try {
url = aURI.QueryInterface(Components.interfaces.nsIURL);
urlext = url.fileExtension;
} catch (e) {
}
if (urlext && mimeInfo && mimeInfo.extensionExists(urlext)) {
return urlext;
}
else {
try {
if (mimeInfo)
return mimeInfo.primaryExtension;
}
catch (e) {
}
// Fall back on the extensions in the filename and URI for lack
// of anything better.
return ext || urlext;
}
}
function GetSaveModeForContentType(aContentType)
{
var saveMode = SAVEMODE_FILEONLY;
switch (aContentType) {
case "text/html":
case "application/xhtml+xml":
case "image/svg+xml":
saveMode |= SAVEMODE_COMPLETE_TEXT;
// Fall through
case "text/xml":
case "application/xml":
saveMode |= SAVEMODE_COMPLETE_DOM;
break;
}
return saveMode;
}
function getCharsetforSave(aDocument)
{
if (aDocument)
return aDocument.characterSet;
if (document.commandDispatcher.focusedWindow)
return document.commandDispatcher.focusedWindow.document.characterSet;
return window.content.document.characterSet;
}
/**
* Content area tooltip.
* XXX - this must move into XBL binding/equiv! Do not want to pollute
* navigator.js with functionality that can be encapsulated into
* browser widget. TEMPORARY!
*
* NOTE: Any changes to this routine need to be mirrored in ChromeListener::FindTitleText()
* (located in mozilla/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp)
* which performs the same function, but for embedded clients that
* don't use a XUL/JS layer. It is important that the logic of
* these two routines be kept more or less in sync.
* (pinkerton)
**/
function FillInHTMLTooltip(tipElement)
{
var retVal = false;
if (tipElement.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")
return retVal;
const XLinkNS = "http://www.w3.org/1999/xlink";
var titleText = null;
var XLinkTitleText = null;
var direction = tipElement.ownerDocument.dir;
while (!titleText && !XLinkTitleText && tipElement) {
if (tipElement.nodeType == Node.ELEMENT_NODE) {
titleText = tipElement.getAttribute("title");
XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
var defView = tipElement.ownerDocument.defaultView;
// XXX Work around bug 350679:
// "Tooltips can be fired in documents with no view".
if (!defView)
return retVal;
direction = defView.getComputedStyle(tipElement, "")
.getPropertyValue("direction");
}
tipElement = tipElement.parentNode;
}
var texts = [titleText, XLinkTitleText];
var tipNode = document.getElementById("aHTMLTooltip");
tipNode.style.direction = direction;
for (var i = 0; i < texts.length; ++i) {
var t = texts[i];
if (t && t.search(/\S/) >= 0) {
tipNode.setAttribute("label", t);
retVal = true;
}
}
return retVal;
}