home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2005 October / Gamestar_77_2005-10_dvd.iso / Programy / nsb-install-8-0.exe / chrome / comm.jar / content / communicator / contentAreaUtils.js < prev    next >
Encoding:
JavaScript  |  2005-07-29  |  29.3 KB  |  857 lines

  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public License
  6.  * Version 1.1 (the "License"); you may not use this file except in
  7.  * compliance with the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is 
  18.  * Netscape Communications Corporation.
  19.  * Portions created by the Initial Developer are Copyright (C) 1998
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Ben Goodger <ben@netscape.com> (Save File)
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the NPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the NPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. /**
  40.  * Determine whether or not a given focused DOMWindow is in the content
  41.  * area.
  42.  **/
  43. function isContentFrame(aFocusedWindow)
  44. {
  45.   if (!aFocusedWindow)
  46.     return false;
  47.  
  48.   var focusedTop = Components.lookupMethod(aFocusedWindow, 'top')
  49.                              .call(aFocusedWindow);
  50.  
  51.   return (focusedTop == window.content);
  52. }
  53.  
  54. function urlSecurityCheck(url, doc) 
  55. {
  56.   // URL Loading Security Check
  57.   var focusedWindow = doc.commandDispatcher.focusedWindow;
  58.   var sourceURL = getContentFrameURI(focusedWindow);
  59.   const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
  60.   var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
  61.                          .getService(nsIScriptSecurityManager);
  62.   try {
  63.     secMan.checkLoadURIStr(sourceURL, url, nsIScriptSecurityManager.STANDARD);
  64.   } catch (e) {
  65.     throw "Load of " + url + " denied.";
  66.   }
  67. }
  68.  
  69. function getContentFrameURI(aFocusedWindow)
  70. {
  71.   var contentFrame = isContentFrame(aFocusedWindow) ? aFocusedWindow : window.content;
  72.   return Components.lookupMethod(contentFrame, 'location').call(contentFrame).href;
  73. }
  74.  
  75. function getReferrer(doc)
  76. {
  77.   var focusedWindow = doc.commandDispatcher.focusedWindow;
  78.   var sourceURL = getContentFrameURI(focusedWindow);
  79.  
  80.   try {
  81.     return makeURL(sourceURL);
  82.   } catch (e) {
  83.     return null;
  84.   }
  85. }
  86.  
  87. function openNewWindowWith(url, sendReferrer) 
  88. {
  89.   urlSecurityCheck(url, document);
  90.  
  91.   // if and only if the current window is a browser window and it has a document with a character
  92.   // set, then extract the current charset menu setting from the current document and use it to
  93.   // initialize the new browser window...
  94.   var charsetArg = null;
  95.   var wintype = document.firstChild.getAttribute('windowtype');
  96.   if (wintype == "navigator:browser")
  97.     charsetArg = "charset=" + window._content.document.characterSet;
  98.  
  99.   var referrer = sendReferrer ? getReferrer(document) : null;
  100.   window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", url, charsetArg, referrer);
  101. }
  102.  
  103. function openTopBrowserWith(url)
  104. {
  105.   urlSecurityCheck(url, document);
  106.  
  107.   var windowMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
  108.   var browserWin = windowMediator.getMostRecentWindow("navigator:browser");
  109.  
  110.   // if there's an existing browser window, open this url in one
  111.   if (browserWin) {
  112.     browserWin.getBrowser().loadURI(url); // Just do a normal load.
  113.     browserWin.content.focus();
  114.   }
  115.   else
  116.     window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", url, null, null);
  117. }
  118.  
  119. function openNewTabWith(url, sendReferrer, reverseBackgroundPref) 
  120. {
  121.   var browser;
  122.   try {
  123.     // if we're running in a browser window, this should work
  124.     //
  125.     browser = getBrowser();
  126.  
  127.   } catch (ex if ex instanceof ReferenceError) {
  128.  
  129.     // must be running somewhere else (eg mailnews message pane); need to
  130.     // find a browser window first
  131.     //
  132.     var windowMediator =
  133.       Components.classes["@mozilla.org/appshell/window-mediator;1"]
  134.       .getService(Components.interfaces.nsIWindowMediator);
  135.  
  136.     var browserWin = windowMediator.getMostRecentWindow("navigator:browser");
  137.  
  138.     // if there's no existing browser window, then, as long as
  139.     // we are allowed to, open this url in one and return
  140.     //
  141.     if (!browserWin) {
  142.       urlSecurityCheck(url, document);
  143.       window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", 
  144.                         url, null, referrer);
  145.       return;
  146.     }
  147.  
  148.     // otherwise, get the existing browser object
  149.     //
  150.     browser = browserWin.getBrowser();
  151.   }
  152.  
  153.   // Get the XUL document that the browser is actually contained in.
  154.   // This is needed if we are trying to load a URL from a non-navigator
  155.   // window such as the JS Console.
  156.   var browserDocument = browser.ownerDocument;
  157.  
  158.   urlSecurityCheck(url, browserDocument);
  159.  
  160.   var referrer = sendReferrer ? getReferrer(browserDocument) : null;
  161.  
  162.   // As in openNewWindowWith(), we want to pass the charset of the
  163.   // current document over to a new tab. 
  164.   var wintype = browserDocument.firstChild.getAttribute('windowtype');
  165.   var originCharset;
  166.   if (wintype == "navigator:browser") {
  167.     originCharset = window._content.document.characterSet;
  168.   }
  169.  
  170.   // open link in new tab
  171.   var tab = browser.addTab(url, referrer, originCharset); 
  172.   if (pref) {
  173.     var loadInBackground = pref.getBoolPref("browser.tabs.loadInBackground");
  174.     if (reverseBackgroundPref)
  175.       loadInBackground = !loadInBackground;
  176.  
  177.     if (!loadInBackground)
  178.       browser.selectedTab = tab;
  179.   }
  180. }
  181.  
  182. // Clientelle: (Make sure you don't break any of these)
  183. //  - File    ->  Save Page/Frame As...
  184. //  - Context ->  Save Page/Frame As...
  185. //  - Context ->  Save Link As...
  186. //  - Context ->  Save Image As...
  187. //  - Shift-Click Save Link As
  188. //
  189. // Try saving each of these types:
  190. // - A complete webpage using File->Save Page As, and Context->Save Page As
  191. // - A webpage as HTML only using the above methods
  192. // - A webpage as Text only using the above methods
  193. // - An image with an extension (e.g. .jpg) in its file name, using
  194. //   Context->Save Image As...
  195. // - An image without an extension (e.g. a banner ad on cnn.com) using
  196. //   the above method. 
  197. // - A linked document using Save Link As...
  198. // - A linked document using shift-click Save Link As...
  199. //
  200. function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache)
  201. {
  202.   saveInternal(aURL, null, aFileName, aFilePickerTitleKey, aShouldBypassCache);
  203. }
  204.  
  205. function saveFrameDocument()
  206. {
  207.   var focusedWindow = document.commandDispatcher.focusedWindow;
  208.   if (isContentFrame(focusedWindow))
  209.     saveDocument(focusedWindow.document);
  210. }
  211.  
  212. function saveDocument(aDocument)
  213. {
  214.   // In both cases here, we want to use cached data because the 
  215.   // document is currently visible. 
  216.   if (aDocument) 
  217.     saveInternal(aDocument.location.href, aDocument, false);
  218.   else
  219.     saveInternal(_content.location.href, null, false);
  220. }
  221.  
  222. function saveInternal(aURL, aDocument, 
  223.                       aFileName, aFilePickerTitleKey,
  224.                       aShouldBypassCache)
  225. {
  226.   var data = {
  227.     url: aURL,
  228.     fileName: aFileName,
  229.     filePickerTitle: aFilePickerTitleKey,
  230.     document: aDocument,
  231.     bypassCache: aShouldBypassCache,
  232.     window: window
  233.   };
  234.   var sniffer = new nsHeaderSniffer(aURL, foundHeaderInfo, data);
  235. }
  236.  
  237. function foundHeaderInfo(aSniffer, aData)
  238. {
  239.   var contentType = aSniffer.contentType;
  240.   var contentEncodingType = aSniffer.contentEncodingType;
  241.  
  242.   var shouldDecode = false;
  243.   var urlExt = null;
  244.   // Are we allowed to decode?
  245.   try {
  246.     const helperAppService =
  247.       Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].
  248.         getService(Components.interfaces.nsIExternalHelperAppService);
  249.     var url = aSniffer.uri.QueryInterface(Components.interfaces.nsIURL);
  250.     urlExt = url.fileExtension;
  251.     if (contentEncodingType &&
  252.         helperAppService.applyDecodingForExtension(urlExt,
  253.                                                    contentEncodingType)) {
  254.       shouldDecode = true;
  255.     }
  256.   }
  257.   catch (e) {
  258.   }
  259.  
  260.   var fp = makeFilePicker();
  261.   var titleKey = aData.filePickerTitle || "SaveLinkTitle";
  262.   var bundle = getStringBundle();
  263.   fp.init(window, bundle.GetStringFromName(titleKey), 
  264.           Components.interfaces.nsIFilePicker.modeSave);
  265.  
  266.   var saveMode = GetSaveModeForContentType(contentType);
  267.   var isDocument = aData.document != null && saveMode;
  268.   if (!isDocument && !shouldDecode && contentEncodingType) {
  269.     // The data is encoded, we are not going to decode it, and this is not a
  270.     // document save so we won't be doing a "save as, complete" (which would
  271.     // break if we reset the type here).  So just set our content type to
  272.     // correspond to the outermost encoding so we get extensions and the like
  273.     // right.
  274.     contentType = contentEncodingType;
  275.   }
  276.  
  277.   appendFiltersForContentType(fp, contentType, urlExt,
  278.                               isDocument ? saveMode : SAVEMODE_FILEONLY);
  279.  
  280.   const prefSvcContractID = "@mozilla.org/preferences-service;1";
  281.   const prefSvcIID = Components.interfaces.nsIPrefService;                              
  282.   var prefs = Components.classes[prefSvcContractID].getService(prefSvcIID).getBranch("browser.download.");
  283.   
  284.   const nsILocalFile = Components.interfaces.nsILocalFile;
  285.   try {
  286.     fp.displayDirectory = prefs.getComplexValue("dir", nsILocalFile);
  287.   }
  288.   catch (e) {
  289.   }
  290.  
  291.   if (isDocument) {
  292.     try {
  293.       fp.filterIndex = prefs.getIntPref("save_converter_index");
  294.     }
  295.     catch (e) {
  296.     }
  297.   }
  298.   
  299.   // Determine what the 'default' string to display in the File Picker dialog 
  300.   // should be. 
  301.   var defaultFileName = getDefaultFileName(aData.fileName, 
  302.                                            aSniffer.suggestedFileName, 
  303.                                            aSniffer.uri,
  304.                                            aData.document);
  305.   var defaultExtension = getDefaultExtension(defaultFileName, aSniffer.uri, contentType);
  306.   fp.defaultExtension = defaultExtension;
  307.   fp.defaultString = getNormalizedLeafName(defaultFileName, defaultExtension);
  308.   
  309.   if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
  310.     return;
  311.  
  312.   if (isDocument) 
  313.     prefs.setIntPref("save_converter_index", fp.filterIndex);
  314.   var directory = fp.file.parent.QueryInterface(nsILocalFile);
  315.   prefs.setComplexValue("dir", nsILocalFile, directory);
  316.  
  317.   fp.file.leafName = validateFileName(fp.file.leafName);
  318.  
  319.   // XXX We depend on the following holding true in appendFiltersForContentType():
  320.   // If we should save as a complete page, the filterIndex is 0.
  321.   // If we should save as text, the filterIndex is 2.
  322.   var useSaveDocument = isDocument &&
  323.                         ((saveMode & SAVEMODE_COMPLETE_DOM && fp.filterIndex == 0) ||
  324.                          (saveMode & SAVEMODE_COMPLETE_TEXT && fp.filterIndex == 2));
  325.  
  326.   // If we're saving a document, and are saving either in complete mode or 
  327.   // as converted text, pass the document to the web browser persist component.
  328.   // If we're just saving the HTML (second option in the list), send only the URI.
  329.   var source = useSaveDocument ? aData.document : aSniffer.uri;
  330.   var persistArgs = {
  331.     source      : source,
  332.     contentType : (useSaveDocument && fp.filterIndex == 2) ? "text/plain" : contentType,
  333.     target      : makeFileURL(fp.file),
  334.     postData    : isDocument ? getPostData() : null,
  335.     bypassCache : aData.bypassCache
  336.   };
  337.   
  338.   var persist = makeWebBrowserPersist();
  339.  
  340.   // Calculate persist flags.
  341.   const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
  342.   const flags = nsIWBP.PERSIST_FLAGS_NO_CONVERSION | nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
  343.   if (aData.bypassCache)
  344.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
  345.   else 
  346.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
  347.  
  348.   if (shouldDecode)
  349.     persist.persistFlags &= ~nsIWBP.PERSIST_FLAGS_NO_CONVERSION;
  350.     
  351.   // Create download and initiate it (below)
  352.   var dl = Components.classes["@mozilla.org/download;1"].createInstance(Components.interfaces.nsIDownload);
  353.  
  354.   if (useSaveDocument) {
  355.     // Saving a Document, not a URI:
  356.     var filesFolder = null;
  357.     if (persistArgs.contentType != "text/plain") {
  358.       // Create the local directory into which to save associated files. 
  359.       filesFolder = fp.file.clone();
  360.       
  361.       var nameWithoutExtension = filesFolder.leafName.replace(/\.[^.]*$/, "");
  362.       var filesFolderLeafName = getStringBundle().formatStringFromName("filesFolder",
  363.                                                                        [nameWithoutExtension],
  364.                                                                        1);
  365.  
  366.       filesFolder.leafName = filesFolderLeafName;
  367.     }
  368.       
  369.     var encodingFlags = 0;
  370.     if (persistArgs.contentType == "text/plain") {
  371.       encodingFlags |= nsIWBP.ENCODE_FLAGS_FORMATTED;
  372.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
  373.       encodingFlags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT;        
  374.     }
  375.     else {
  376.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
  377.     }
  378.  
  379.     const kWrapColumn = 80;
  380.     dl.init(aSniffer.uri, persistArgs.target, null, null, null, persist);
  381.     persist.saveDocument(persistArgs.source, persistArgs.target, filesFolder, 
  382.                          persistArgs.contentType, encodingFlags, kWrapColumn);
  383.   } else {
  384.     dl.init(source, persistArgs.target, null, null, null, persist);
  385.     var referer = getReferrer(document);
  386.     persist.saveURI(source, null, referer, persistArgs.postData, null, persistArgs.target);
  387.   }
  388. }
  389.  
  390. function nsHeaderSniffer(aURL, aCallback, aData)
  391. {
  392.   this.mCallback = aCallback;
  393.   this.mData = aData;
  394.   
  395.   this.uri = makeURL(aURL);
  396.   
  397.   this.linkChecker = Components.classes["@mozilla.org/network/urichecker;1"]
  398.     .createInstance(Components.interfaces.nsIURIChecker);
  399.   this.linkChecker.init(this.uri);
  400.  
  401.   var flags;
  402.   if (aData.bypassCache) {
  403.     flags = Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
  404.   } else {
  405.     flags = Components.interfaces.nsIRequest.LOAD_FROM_CACHE;
  406.   }
  407.   this.linkChecker.loadFlags = flags;
  408.  
  409.   // Set referrer, ignore errors
  410.   try {
  411.     var referrer = getReferrer(document);
  412.     this.linkChecker.baseChannel.QueryInterface(Components.interfaces.nsIHttpChannel).referrer = referrer;
  413.   }
  414.   catch (ex) { }
  415.  
  416.   this.linkChecker.asyncCheck(this, null);
  417. }
  418.  
  419. nsHeaderSniffer.prototype = {
  420.  
  421.   // ---------- nsISupports methods ----------
  422.   QueryInterface: function (iid) {
  423.     if (!iid.equals(Components.interfaces.nsIRequestObserver) &&
  424.         !iid.equals(Components.interfaces.nsISupports) &&
  425.         !iid.equals(Components.interfaces.nsIInterfaceRequestor)) {
  426.       throw Components.results.NS_ERROR_NO_INTERFACE;
  427.     }
  428.     return this;
  429.   },
  430.  
  431.   // ---------- nsIInterfaceRequestor methods ----------
  432.   getInterface : function(iid) {
  433.     if (iid.equals(Components.interfaces.nsIAuthPrompt)) {
  434.       // use the window watcher service to get a nsIAuthPrompt impl
  435.       var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  436.                          .getService(Components.interfaces.nsIWindowWatcher);
  437.       return ww.getNewAuthPrompter(window);
  438.     }
  439.     Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  440.     return null;
  441.   },
  442.  
  443.   // ---------- nsIRequestObserver methods ----------
  444.   onStartRequest: function (aRequest, aContext) { },
  445.   
  446.   onStopRequest: function (aRequest, aContext, aStatus) {
  447.     try {
  448.       if (aStatus == 0) { // NS_BINDING_SUCCEEDED, so there's something there
  449.         var linkChecker = aRequest.QueryInterface(Components.interfaces.nsIURIChecker);
  450.         var channel = linkChecker.baseChannel;
  451.         this.contentType = channel.contentType;
  452.         try {
  453.           var httpChannel = channel.QueryInterface(Components.interfaces.nsIHttpChannel);
  454.           var encodedChannel = channel.QueryInterface(Components.interfaces.nsIEncodedChannel);
  455.           this.contentEncodingType = null;
  456.           // There may be content-encodings on the channel.  Multiple content
  457.           // encodings are allowed, eg "Content-Encoding: gzip, uuencode".  This
  458.           // header would mean that the content was first gzipped and then
  459.           // uuencoded.  The encoding enumerator returns MIME types
  460.           // corresponding to each encoding starting from the end, so the first
  461.           // thing it returns corresponds to the outermost encoding.
  462.           var encodingEnumerator = encodedChannel.contentEncodings;
  463.           if (encodingEnumerator && encodingEnumerator.hasMore()) {
  464.             try {
  465.               this.contentEncodingType = encodingEnumerator.getNext();
  466.             } catch (e) {
  467.             }
  468.           }
  469.           this.mContentDisposition = httpChannel.getResponseHeader("content-disposition");
  470.         }
  471.         catch (e) {
  472.         }
  473.         if (!this.contentType || this.contentType == "application/x-unknown-content-type") {
  474.           // We didn't get a type from the server.  Fall back on other type detection mechanisms
  475.           throw "Unknown Type";
  476.         }
  477.       }
  478.       else {
  479.         dump("Error saving link aStatus = 0x" + aStatus.toString(16) + "\n");
  480.         var bundle = getStringBundle();
  481.         var errorTitle = bundle.GetStringFromName("saveLinkErrorTitle");
  482.         var errorMsg = bundle.GetStringFromName("saveLinkErrorMsg");
  483.         const promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  484.         promptService.alert(this.mData.window, errorTitle, errorMsg);
  485.         return;
  486.       }
  487.     }
  488.     catch (e) {
  489.       if (this.mData.document) {
  490.         this.contentType = this.mData.document.contentType;
  491.       } else {
  492.         var type = getMIMETypeForURI(this.uri);
  493.         if (type)
  494.           this.contentType = type;
  495.       }
  496.     }
  497.     this.mCallback(this, this.mData);
  498.   },
  499.  
  500.   // ------------------------------------------------
  501.  
  502.   get promptService()
  503.   {
  504.     var promptSvc;
  505.     try {
  506.       promptSvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
  507.       promptSvc = promptSvc.QueryInterface(Components.interfaces.nsIPromptService);
  508.     }
  509.     catch (e) {}
  510.     return promptSvc;
  511.   },
  512.  
  513.   get suggestedFileName()
  514.   {
  515.     var fileName = "";
  516.  
  517.     if ("mContentDisposition" in this) {
  518.       const mhpContractID = "@mozilla.org/network/mime-hdrparam;1"
  519.       const mhpIID = Components.interfaces.nsIMIMEHeaderParam;
  520.       const mhp = Components.classes[mhpContractID].getService(mhpIID);
  521.       var dummy = { value: null }; // To make JS engine happy.
  522.       var charset = getCharsetforSave(null);
  523.  
  524.       try {
  525.         fileName = mhp.getParameter(this.mContentDisposition, "filename", charset, true, dummy);
  526.       } 
  527.       catch (e) {
  528.         try {
  529.           fileName = mhp.getParameter(this.mContentDisposition, "name", charset, true, dummy);
  530.         }
  531.         catch (e) {
  532.         }
  533.       }
  534.     }
  535.  
  536.     fileName = fileName.replace(/^"|"$/g, "");
  537.     return fileName;
  538.   }
  539. };
  540.  
  541. // We have no DOM, and can only save the URL as is.
  542. const SAVEMODE_FILEONLY      = 0x00;
  543. // We have a DOM and can save as complete.
  544. const SAVEMODE_COMPLETE_DOM  = 0x01;
  545. // We have a DOM which we can serialize as text.
  546. const SAVEMODE_COMPLETE_TEXT = 0x02;
  547.  
  548. // If we are able to save a complete DOM, the 'save as complete' filter
  549. // must be the first filter appended.  The 'save page only' counterpart
  550. // must be the second filter appended.  And the 'save as complete text'
  551. // filter must be the third filter appended.
  552. function appendFiltersForContentType(aFilePicker, aContentType, aFileExtension, aSaveMode)
  553. {
  554.   var bundle = getStringBundle();
  555.   // The bundle name for saving only a specific content type.
  556.   var bundleName;
  557.   // The corresponding filter string for a specific content type.
  558.   var filterString;
  559.  
  560.   // XXX all the cases that are handled explicitly here MUST be handled
  561.   // in GetSaveModeForContentType to return a non-fileonly filter.
  562.   switch (aContentType) {
  563.   case "text/html":
  564.     bundleName   = "WebPageHTMLOnlyFilter";
  565.     filterString = "*.htm; *.html";
  566.     break;
  567.  
  568.   case "application/xhtml+xml":
  569.     bundleName   = "WebPageXHTMLOnlyFilter";
  570.     filterString = "*.xht; *.xhtml";
  571.     break;
  572.  
  573.   case "text/xml":
  574.   case "application/xml":
  575.     bundleName   = "WebPageXMLOnlyFilter";
  576.     filterString = "*.xml";
  577.     break;
  578.  
  579.   default:
  580.     if (aSaveMode != SAVEMODE_FILEONLY) {
  581.       throw "Invalid save mode for type '" + aContentType + "'";
  582.     }
  583.  
  584.     var mimeInfo = getMIMEInfoForType(aContentType, aFileExtension);
  585.     if (mimeInfo) {
  586.       
  587.       var extEnumerator = mimeInfo.getFileExtensions();
  588.  
  589.       var extString = "";
  590.       while (extEnumerator.hasMore()) {
  591.         var extension = extEnumerator.getNext();
  592.         if (extString)
  593.           extString += "; ";    // If adding more than one extension,
  594.                                 // separate by semi-colon
  595.         extString += "*." + extension;
  596.       }
  597.         
  598.       if (extString) {
  599.         aFilePicker.appendFilter(mimeInfo.Description, extString);
  600.       }
  601.     }
  602.  
  603.     break;
  604.   }
  605.  
  606.   if (aSaveMode & SAVEMODE_COMPLETE_DOM) {
  607.     aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), filterString);
  608.     // We should always offer a choice to save document only if
  609.     // we allow saving as complete.
  610.     aFilePicker.appendFilter(bundle.GetStringFromName(bundleName), filterString);
  611.   }
  612.  
  613.   if (aSaveMode & SAVEMODE_COMPLETE_TEXT) {
  614.     aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterText);
  615.   }
  616.  
  617.   // Always append the all files (*) filter
  618.   aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
  619.  
  620. function getPostData()
  621. {
  622.   try {
  623.     var sessionHistory = getWebNavigation().sessionHistory;
  624.     return sessionHistory.getEntryAtIndex(sessionHistory.index, false)
  625.                          .QueryInterface(Components.interfaces.nsISHEntry)
  626.                          .postData;
  627.   }
  628.   catch (e) {
  629.   }
  630.   return null;
  631. }
  632.  
  633. function getStringBundle()
  634. {
  635.   const bundleURL = "chrome://communicator/locale/contentAreaCommands.properties";
  636.   
  637.   const sbsContractID = "@mozilla.org/intl/stringbundle;1";
  638.   const sbsIID = Components.interfaces.nsIStringBundleService;
  639.   const sbs = Components.classes[sbsContractID].getService(sbsIID);
  640.   
  641.   const lsContractID = "@mozilla.org/intl/nslocaleservice;1";
  642.   const lsIID = Components.interfaces.nsILocaleService;
  643.   const ls = Components.classes[lsContractID].getService(lsIID);
  644.   var appLocale = ls.getApplicationLocale();
  645.   return sbs.createBundle(bundleURL, appLocale);    
  646. }
  647.  
  648. function makeWebBrowserPersist()
  649. {
  650.   const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
  651.   const persistIID = Components.interfaces.nsIWebBrowserPersist;
  652.   return Components.classes[persistContractID].createInstance(persistIID);
  653. }
  654.  
  655. function makeURL(aURL)
  656. {
  657.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  658.                 .getService(Components.interfaces.nsIIOService);
  659.   return ioService.newURI(aURL, null, null);
  660. }
  661.  
  662. function makeFileURL(aFile)
  663. {
  664.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  665.                 .getService(Components.interfaces.nsIIOService);
  666.   return ioService.newFileURI(aFile);
  667. }
  668.  
  669. function makeFilePicker()
  670. {
  671.   const fpContractID = "@mozilla.org/filepicker;1";
  672.   const fpIID = Components.interfaces.nsIFilePicker;
  673.   return Components.classes[fpContractID].createInstance(fpIID);
  674. }
  675.  
  676. function getMIMEService()
  677. {
  678.   const mimeSvcContractID = "@mozilla.org/mime;1";
  679.   const mimeSvcIID = Components.interfaces.nsIMIMEService;
  680.   const mimeSvc = Components.classes[mimeSvcContractID].getService(mimeSvcIID);
  681.   return mimeSvc;
  682. }
  683.  
  684. function getMIMETypeForURI(aURI)
  685. {
  686.   try {  
  687.     return getMIMEService().getTypeFromURI(aURI);
  688.   }
  689.   catch (e) {
  690.   }
  691.   return null;
  692. }
  693.  
  694. function getMIMEInfoForType(aMIMEType, aExtension)
  695. {
  696.   try {  
  697.     return getMIMEService().getFromTypeAndExtension(aMIMEType, aExtension);
  698.   }
  699.   catch (e) {
  700.   }
  701.   return null;
  702. }
  703.  
  704. function getDefaultFileName(aDefaultFileName, aNameFromHeaders, aDocumentURI, aDocument)
  705. {
  706.   if (aNameFromHeaders)
  707.     // 1) Use the name suggested by the HTTP headers
  708.     return validateFileName(aNameFromHeaders);
  709.  
  710.   try {
  711.     var url = aDocumentURI.QueryInterface(Components.interfaces.nsIURL);
  712.     if (url.fileName != "") {
  713.       // 2) Use the actual file name, if present
  714.       return validateFileName(decodeURIComponent(url.fileName));
  715.     }
  716.   } catch (e) {
  717.     try {
  718.       // the file name might be non ASCII
  719.       // try unescape again with a characterSet
  720.       var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  721.                                    .getService(Components.interfaces.nsITextToSubURI);
  722.       var charset = getCharsetforSave(aDocument);
  723.       return validateFileName(textToSubURI.unEscapeURIForUI(charset, url.fileName));
  724.     } catch (e) {
  725.       // This is something like a wyciwyg:, data:, and so forth
  726.       // URI... no usable filename here.
  727.     }
  728.   }
  729.   
  730.   if (aDocument) {
  731.     var docTitle = GenerateValidFilename(aDocument.title, "");
  732.  
  733.     if (docTitle) {
  734.       // 3) Use the document title
  735.       return docTitle;
  736.     }
  737.   }
  738.   
  739.   if (aDefaultFileName)
  740.     // 4) Use the caller-provided name, if any
  741.     return validateFileName(aDefaultFileName);
  742.  
  743.   // 5) If this is a directory, use the last directory name
  744.   var path = aDocumentURI.path.match(/\/([^\/]+)\/$/);
  745.   if (path && path.length > 1) {
  746.       return validateFileName(path[1]);
  747.   }
  748.  
  749.   try {
  750.     if (aDocumentURI.host)
  751.       // 6) Use the host.
  752.       return aDocumentURI.host;
  753.   } catch (e) {
  754.     // Some files have no information at all, like Javascript generated pages
  755.   }
  756.   try {
  757.     // 7) Use the default file name
  758.     return getStringBundle().GetStringFromName("DefaultSaveFileName");
  759.   } catch (e) {
  760.     //in case localized string cannot be found
  761.   }
  762.   // 8) If all else fails, use "index"
  763.   return "index";
  764. }
  765.  
  766. function getNormalizedLeafName(aFile, aDefaultExtension)
  767. {
  768.   if (!aDefaultExtension)
  769.     return aFile;
  770.   
  771.   // Fix up the file name we're saving to to include the default extension
  772.   const stdURLContractID = "@mozilla.org/network/standard-url;1";
  773.   const stdURLIID = Components.interfaces.nsIURL;
  774.   var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
  775.   url.filePath = aFile;
  776.   
  777.   if (url.fileExtension != aDefaultExtension) {
  778.     return aFile + "." + aDefaultExtension;
  779.   }
  780.  
  781.   return aFile;
  782. }
  783.  
  784. function getDefaultExtension(aFilename, aURI, aContentType)
  785. {
  786.   if (aContentType == "text/plain" || aContentType == "application/octet-stream" || aURI.scheme == "ftp")
  787.     return "";   // temporary fix for bug 120327
  788.  
  789.   // First try the extension from the filename
  790.   const stdURLContractID = "@mozilla.org/network/standard-url;1";
  791.   const stdURLIID = Components.interfaces.nsIURL;
  792.   var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
  793.   url.filePath = aFilename;
  794.  
  795.   var ext = url.fileExtension;
  796.  
  797.   // This mirrors some code in nsExternalHelperAppService::DoContent
  798.   // Use the filename first and then the URI if that fails
  799.   
  800.   var mimeInfo = getMIMEInfoForType(aContentType, ext);
  801.  
  802.   if (ext && mimeInfo && mimeInfo.ExtensionExists(ext)) {
  803.     return ext;
  804.   }
  805.   
  806.   // Well, that failed.  Now try the extension from the URI
  807.   var urlext;
  808.   try {
  809.     url = aURI.QueryInterface(Components.interfaces.nsIURL);
  810.     urlext = url.fileExtension;
  811.   } catch (e) {
  812.   }
  813.  
  814.   if (urlext && mimeInfo && mimeInfo.ExtensionExists(urlext)) {
  815.     return urlext;
  816.   }
  817.   else {
  818.     try {
  819.       return mimeInfo.primaryExtension;
  820.     }
  821.     catch (e) {
  822.       // Fall back on the extensions in the filename and URI for lack
  823.       // of anything better.
  824.       return ext || urlext;
  825.     }
  826.   }
  827. }
  828.  
  829. function GetSaveModeForContentType(aContentType)
  830. {
  831.   var saveMode = SAVEMODE_FILEONLY;
  832.   switch (aContentType) {
  833.   case "text/html":
  834.   case "application/xhtml+xml":
  835.     saveMode |= SAVEMODE_COMPLETE_TEXT;
  836.     // Fall through
  837.   case "text/xml":
  838.   case "application/xml":
  839.     saveMode |= SAVEMODE_COMPLETE_DOM;
  840.     break;
  841.   }
  842.  
  843.   return saveMode;
  844. }
  845.  
  846. function getCharsetforSave(aDocument)
  847. {
  848.   if (aDocument)
  849.     return aDocument.characterSet;
  850.  
  851.   if (document.commandDispatcher.focusedWindow)
  852.     return document.commandDispatcher.focusedWindow.document.characterSet;
  853.  
  854.   return  window._content.document.characterSet;
  855. }
  856.