home *** CD-ROM | disk | FTP | other *** search
/ PC World 2003 May / PCWorld_2003-05_cd.bin / Komunik / phoenix / chrome / comm.jar / content / communicator / contentAreaUtils.js < prev    next >
Encoding:
JavaScript  |  2002-11-20  |  28.6 KB  |  858 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.     var uri = Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIURI);
  82.     uri.spec = sourceURL;
  83.     return uri;
  84.   } catch (e) {
  85.     return null;
  86.   }
  87. }
  88.  
  89. function openNewWindowWith(url, sendReferrer) 
  90. {
  91.   urlSecurityCheck(url, document);
  92.  
  93.   // if and only if the current window is a browser window and it has a document with a character
  94.   // set, then extract the current charset menu setting from the current document and use it to
  95.   // initialize the new browser window...
  96.   var charsetArg = null;
  97.   var wintype = document.firstChild.getAttribute('windowtype');
  98.   if (wintype == "navigator:browser")
  99.     charsetArg = "charset=" + window._content.document.characterSet;
  100.  
  101.   var referrer = sendReferrer ? getReferrer(document) : null;
  102.   window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", url, charsetArg, referrer);
  103. }
  104.  
  105. function openNewTabWith(url, sendReferrer, reverseBackgroundPref) 
  106. {
  107.   var browser;
  108.   try {
  109.     // if we're running in a browser window, this should work
  110.     //
  111.     browser = getBrowser();
  112.  
  113.   } catch (ex if ex instanceof ReferenceError) {
  114.  
  115.     // must be running somewhere else (eg mailnews message pane); need to
  116.     // find a browser window first
  117.     //
  118.     var windowMediator =
  119.       Components.classes["@mozilla.org/appshell/window-mediator;1"]
  120.       .getService(Components.interfaces.nsIWindowMediator);
  121.  
  122.     var browserWin = windowMediator.getMostRecentWindow("navigator:browser");
  123.  
  124.     // if there's no existing browser window, open this url in one, and
  125.     // return
  126.     //
  127.     if (!browserWin) {
  128.       window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", 
  129.                         url, null, referrer);
  130.       return;
  131.     }
  132.  
  133.     // otherwise, get the existing browser object
  134.     //
  135.     browser = browserWin.getBrowser();
  136.   }
  137.  
  138.   // Get the XUL document that the browser is actually contained in.
  139.   // This is needed if we are trying to load a URL from a non-navigator
  140.   // window such as the JS Console.
  141.   var browserDocument = browser.ownerDocument;
  142.  
  143.   urlSecurityCheck(url, browserDocument);
  144.  
  145.   var referrer = sendReferrer ? getReferrer(browserDocument) : null;
  146.  
  147.   var tab = browser.addTab(url, referrer); // open link in new tab
  148.   if (pref) {
  149.     var loadInBackground = pref.getBoolPref("browser.tabs.loadInBackground");
  150.     if (reverseBackgroundPref)
  151.       loadInBackground = !loadInBackground;
  152.  
  153.     if (!loadInBackground)
  154.       browser.selectedTab = tab;
  155.   }
  156. }
  157.  
  158. function findParentNode(node, parentNode)
  159. {
  160.   if (node && node.nodeType == Node.TEXT_NODE) {
  161.     node = node.parentNode;
  162.   }
  163.   while (node) {
  164.     var nodeName = node.localName;
  165.     if (!nodeName)
  166.       return null;
  167.     nodeName = nodeName.toLowerCase();
  168.     if (nodeName == "body" || nodeName == "html" ||
  169.         nodeName == "#document") {
  170.       return null;
  171.     }
  172.     if (nodeName == parentNode)
  173.       return node;
  174.     node = node.parentNode;
  175.   }
  176.   return null;
  177. }
  178.  
  179. // Clientelle: (Make sure you don't break any of these)
  180. //  - File    ->  Save Page/Frame As...
  181. //  - Context ->  Save Page/Frame As...
  182. //  - Context ->  Save Link As...
  183. //  - Context ->  Save Image As...
  184. //  - Shift-Click Save Link As
  185. //
  186. // Try saving each of these types:
  187. // - A complete webpage using File->Save Page As, and Context->Save Page As
  188. // - A webpage as HTML only using the above methods
  189. // - A webpage as Text only using the above methods
  190. // - An image with an extension (e.g. .jpg) in its file name, using
  191. //   Context->Save Image As...
  192. // - An image without an extension (e.g. a banner ad on cnn.com) using
  193. //   the above method. 
  194. // - A linked document using Save Link As...
  195. // - A linked document using shift-click Save Link As...
  196. //
  197. function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache)
  198. {
  199.   saveInternal(aURL, null, aFileName, aFilePickerTitleKey, aShouldBypassCache);
  200. }
  201.  
  202. function saveFrameDocument()
  203. {
  204.   var focusedWindow = document.commandDispatcher.focusedWindow;
  205.   if (isContentFrame(focusedWindow))
  206.     saveDocument(focusedWindow.document);
  207. }
  208.  
  209. function saveDocument(aDocument)
  210. {
  211.   // In both cases here, we want to use cached data because the 
  212.   // document is currently visible. 
  213.   if (aDocument) 
  214.     saveInternal(aDocument.location.href, aDocument, false);
  215.   else
  216.     saveInternal(_content.location.href, null, false);
  217. }
  218.  
  219. function saveInternal(aURL, aDocument, 
  220.                       aFileName, aFilePickerTitleKey,
  221.                       aShouldBypassCache)
  222. {
  223.   var data = {
  224.     url: aURL,
  225.     fileName: aFileName,
  226.     filePickerTitle: aFilePickerTitleKey,
  227.     document: aDocument,
  228.     bypassCache: aShouldBypassCache,
  229.     window: window
  230.   };
  231.   var sniffer = new nsHeaderSniffer(aURL, foundHeaderInfo, data);
  232. }
  233.  
  234. function foundHeaderInfo(aSniffer, aData)
  235. {
  236.   var contentType = aSniffer.contentType;
  237.   var contentEncodingType = aSniffer.contentEncodingType;
  238.  
  239.   var shouldDecode = false;
  240.   // Are we allowed to decode?
  241.   try {
  242.     const helperAppService =
  243.       Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].
  244.         getService(Components.interfaces.nsIExternalHelperAppService);
  245.     var url = aSniffer.uri.QueryInterface(Components.interfaces.nsIURL);
  246.     var urlExt = url.fileExtension;
  247.     if (helperAppService.applyDecodingForType(contentType) &&
  248.         (!urlExt || helperAppService.applyDecodingForExtension(urlExt))) {
  249.       shouldDecode = true;
  250.     }
  251.   }
  252.   catch (e) {
  253.   }
  254.  
  255.   var fp = makeFilePicker();
  256.   var titleKey = aData.filePickerTitle || "SaveLinkTitle";
  257.   var bundle = getStringBundle();
  258.   fp.init(window, bundle.GetStringFromName(titleKey), 
  259.           Components.interfaces.nsIFilePicker.modeSave);
  260.  
  261.  
  262.   var isDocument = aData.document != null && isDocumentType(contentType);
  263.   if (!isDocument && !shouldDecode && contentEncodingType) {
  264.     // The data is encoded, we are not going to decode it, and this is not a
  265.     // document save so we won't be doing a "save as, complete" (which would
  266.     // break if we reset the type here).  So just set our content type to
  267.     // correspond to the outermost encoding so we get extensions and the like
  268.     // right.
  269.     contentType = contentEncodingType;
  270.   }
  271.     
  272.   appendFiltersForContentType(fp, contentType,
  273.                               isDocument ? MODE_COMPLETE : MODE_FILEONLY);  
  274.  
  275.   const prefSvcContractID = "@mozilla.org/preferences-service;1";
  276.   const prefSvcIID = Components.interfaces.nsIPrefService;                              
  277.   var prefs = Components.classes[prefSvcContractID].getService(prefSvcIID).getBranch("browser.download.");
  278.   
  279.   const nsILocalFile = Components.interfaces.nsILocalFile;
  280.   try {
  281.     fp.displayDirectory = prefs.getComplexValue("dir", nsILocalFile);
  282.   }
  283.   catch (e) {
  284.   }
  285.  
  286.   if (isDocument) {
  287.     try {
  288.       fp.filterIndex = prefs.getIntPref("save_converter_index");
  289.     }
  290.     catch (e) {
  291.     }
  292.   }
  293.   
  294.   // Determine what the 'default' string to display in the File Picker dialog 
  295.   // should be. 
  296.   var defaultFileName = getDefaultFileName(aData.fileName, 
  297.                                            aSniffer.suggestedFileName, 
  298.                                            aSniffer.uri,
  299.                                            aData.document);
  300.   var defaultExtension = getDefaultExtension(defaultFileName, aSniffer.uri, contentType);
  301.   fp.defaultExtension = defaultExtension;
  302.   fp.defaultString = getNormalizedLeafName(defaultFileName, defaultExtension);
  303.   
  304.   if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
  305.     return;
  306.     
  307.   if (isDocument) 
  308.     prefs.setIntPref("save_converter_index", fp.filterIndex);
  309.   var directory = fp.file.parent.QueryInterface(nsILocalFile);
  310.   prefs.setComplexValue("dir", nsILocalFile, directory);
  311.     
  312.   fp.file.leafName = validateFileName(fp.file.leafName);
  313.   
  314.   // If we're saving a document, and are saving either in complete mode or 
  315.   // as converted text, pass the document to the web browser persist component.
  316.   // If we're just saving the HTML (second option in the list), send only the URI.
  317.   var source = (isDocument && fp.filterIndex != 1) ? aData.document : aSniffer.uri;
  318.   var persistArgs = {
  319.     source      : source,
  320.     contentType : (isDocument && fp.filterIndex == 2) ? "text/plain" : contentType,
  321.     target      : fp.file,
  322.     postData    : isDocument ? getPostData() : null,
  323.     bypassCache : aData.bypassCache
  324.   };
  325.   
  326.   var persist = makeWebBrowserPersist();
  327.  
  328.   // Calculate persist flags.
  329.   const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
  330.   const flags = nsIWBP.PERSIST_FLAGS_NO_CONVERSION | nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
  331.   if (aData.bypassCache)
  332.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
  333.   else 
  334.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
  335.  
  336.   if (shouldDecode)
  337.     persist.persistFlags &= ~nsIWBP.PERSIST_FLAGS_NO_CONVERSION;
  338.     
  339.   // Create download and initiate it (below)
  340.   var dl = Components.classes["@mozilla.org/download;1"].createInstance(Components.interfaces.nsIDownload);
  341.  
  342.   if (isDocument && fp.filterIndex != 1) {
  343.     // Saving a Document, not a URI:
  344.     var filesFolder = null;
  345.     if (persistArgs.contentType != "text/plain") {
  346.       // Create the local directory into which to save associated files. 
  347.       const lfContractID = "@mozilla.org/file/local;1";
  348.       const lfIID = Components.interfaces.nsILocalFile;
  349.       filesFolder = Components .classes[lfContractID].createInstance(lfIID);
  350.       filesFolder.initWithPath(persistArgs.target.path);
  351.       
  352.       var nameWithoutExtension = filesFolder.leafName;
  353.       nameWithoutExtension = nameWithoutExtension.substring(0, nameWithoutExtension.lastIndexOf("."));
  354.       var filesFolderLeafName = getStringBundle().formatStringFromName("filesFolder",
  355.                                                                        [nameWithoutExtension],
  356.                                                                        1);
  357.  
  358.       filesFolder.leafName = filesFolderLeafName;
  359.     }
  360.       
  361.     var encodingFlags = 0;
  362.     if (persistArgs.contentType == "text/plain") {
  363.       encodingFlags |= nsIWBP.ENCODE_FLAGS_FORMATTED;
  364.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
  365.       encodingFlags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT;        
  366.     }
  367.     
  368.     const kWrapColumn = 80;
  369.     dl.init(aSniffer.uri, persistArgs.target, null, null, null, persist);
  370.     persist.saveDocument(persistArgs.source, persistArgs.target, filesFolder, 
  371.                          persistArgs.contentType, encodingFlags, kWrapColumn);
  372.   } else {
  373.     dl.init(source, persistArgs.target, null, null, null, persist);
  374.     persist.saveURI(source, persistArgs.postData, persistArgs.target);
  375.   }
  376. }
  377.  
  378. function nsHeaderSniffer(aURL, aCallback, aData)
  379. {
  380.   this.mCallback = aCallback;
  381.   this.mData = aData;
  382.   
  383.   this.uri = makeURL(aURL);
  384.   
  385.   this.linkChecker = Components.classes["@mozilla.org/network/urichecker;1"]
  386.     .createInstance().QueryInterface(Components.interfaces.nsIURIChecker);
  387.  
  388.   var flags;
  389.   if (aData.bypassCache) {
  390.     flags = Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
  391.   } else {
  392.     flags = Components.interfaces.nsIRequest.LOAD_FROM_CACHE;
  393.   }
  394.  
  395.   this.linkChecker.asyncCheckURI(aURL, this, null, flags);
  396. }
  397.  
  398. nsHeaderSniffer.prototype = {
  399.  
  400.   // ---------- nsISupports methods ----------
  401.   QueryInterface: function (iid) {
  402.     if (!iid.equals(Components.interfaces.nsIRequestObserver) &&
  403.         !iid.equals(Components.interfaces.nsISupports) &&
  404.         !iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
  405.         !iid.equals(Components.interfaces.nsIAuthPrompt)) {
  406.       throw Components.results.NS_ERROR_NO_INTERFACE;
  407.     }
  408.     return this;
  409.   },
  410.  
  411.   // ---------- nsIInterfaceRequestor methods ----------
  412.   getInterface : function(iid) {
  413.     return this.QueryInterface(iid);
  414.   },
  415.  
  416.   // ---------- nsIAuthPrompt methods ----------
  417.   prompt : function(dlgTitle, text, pwrealm, savePW, defaultText, result)
  418.   {
  419.     dump("authprompt prompt! pwrealm="+pwrealm+"\n");
  420.     var promptServ = this.promptService;
  421.     if (!promptServ)
  422.       return false;
  423.     var saveCheck = {value:savePW};
  424.     return promptServ.prompt(window, dlgTitle, text, defaultText, pwrealm, saveCheck);
  425.   },
  426.   promptUsernameAndPassword : function(dlgTitle, text, pwrealm, savePW, user, pw)
  427.   {
  428.     dump("authprompt promptUsernameAndPassword!  "+dlgTitle+" "+text+", pwrealm="+pwrealm+"\n");
  429.     var promptServ = this.promptService;
  430.     if (!promptServ)
  431.       return false;
  432.     var saveCheck = {value:savePW};
  433.     return promptServ.promptUsernameAndPassword(window, dlgTitle, text, user, pw, pwrealm, saveCheck);
  434.   },
  435.   promptPassword : function(dlgTitle, text, pwrealm, savePW, pw)
  436.   {
  437.     dump("auth promptPassword!  "+dlgTitle+" "+text+", pwrealm="+pwrealm+"\n");
  438.     var promptServ = this.promptService;
  439.     if (!promptServ)
  440.       return false;
  441.  
  442.     var saveCheck = {value:savePW};
  443.     return promptServ.promptPassword(window, dlgTitle, text, pw, pwrealm, saveCheck);
  444.   },
  445.  
  446.   // ---------- nsIRequestObserver methods ----------
  447.   onStartRequest: function (aRequest, aContext) { },
  448.   
  449.   onStopRequest: function (aRequest, aContext, aStatus) {
  450.     try {
  451.       if (aStatus == 0) { // NS_BINDING_SUCCEEDED, so there's something there
  452.         var linkChecker = aRequest.QueryInterface(Components.interfaces.nsIURIChecker);
  453.         var channel = linkChecker.baseRequest.QueryInterface(Components.interfaces.nsIChannel);
  454.         this.contentType = channel.contentType;
  455.         try {
  456.           var httpChannel = channel.QueryInterface(Components.interfaces.nsIHttpChannel);
  457.           var encodedChannel = channel.QueryInterface(Components.interfaces.nsIEncodedChannel);
  458.           this.contentEncodingType = null;
  459.           // There may be content-encodings on the channel.  Multiple content
  460.           // encodings are allowed, eg "Content-Encoding: gzip, uuencode".  This
  461.           // header would mean that the content was first gzipped and then
  462.           // uuencoded.  The encoding enumerator returns MIME types
  463.           // corresponding to each encoding starting from the end, so the first
  464.           // thing it returns corresponds to the outermost encoding.
  465.           var encodingEnumerator = encodedChannel.contentEncodings;
  466.           if (encodingEnumerator && encodingEnumerator.hasMoreElements()) {
  467.             try {
  468.               this.contentEncodingType =
  469.                 encodingEnumerator.getNext().
  470.                   QueryInterface(Components.interfaces.nsISupportsCString).data;
  471.             } catch (e) {
  472.             }
  473.           }
  474.           this.mContentDisposition = httpChannel.getResponseHeader("content-disposition");
  475.         }
  476.         catch (e) {
  477.         }
  478.         if (!this.contentType || this.contentType == "application/x-unknown-content-type") {
  479.           // We didn't get a type from the server.  Fall back on other type detection mechanisms
  480.           throw "Unknown Type";
  481.         }
  482.       }
  483.       else {
  484.         dump("Error saving link aStatus = 0x" + aStatus.toString(16) + "\n");
  485.         var bundle = getStringBundle();
  486.         var errorTitle = bundle.GetStringFromName("saveLinkErrorTitle");
  487.         var errorMsg = bundle.GetStringFromName("saveLinkErrorMsg");
  488.         const promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  489.         promptService.alert(this.mData.window, errorTitle, errorMsg);
  490.         return;
  491.       }
  492.     }
  493.     catch (e) {
  494.       if (this.mData.document) {
  495.         this.contentType = this.mData.document.contentType;
  496.       } else {
  497.         try {
  498.           var url = this.uri.QueryInterface(Components.interfaces.nsIURL);
  499.           var ext = url.fileExtension;
  500.           if (ext) {
  501.             var mimeInfo = getMIMEInfoForExtension(ext);
  502.             if (mimeInfo)
  503.               this.contentType = mimeInfo.MIMEType;
  504.           }
  505.         }
  506.         catch (e) {
  507.           // Not much we can do here.  Give up.
  508.         }
  509.       }
  510.     }
  511.     this.mCallback(this, this.mData);
  512.   },
  513.  
  514.   // ------------------------------------------------
  515.  
  516.   get promptService()
  517.   {
  518.     var promptSvc;
  519.     try {
  520.       promptSvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
  521.       promptSvc = promptSvc.QueryInterface(Components.interfaces.nsIPromptService);
  522.     }
  523.     catch (e) {}
  524.     return promptSvc;
  525.   },
  526.  
  527.   get suggestedFileName()
  528.   {
  529.     var filename = "";
  530.     var name = this.mContentDisposition;
  531.     if (name) {
  532.       const filenamePrefix = "filename=";
  533.       var ix = name.indexOf(filenamePrefix);
  534.       if (ix >= 0) {
  535.         // Adjust ix to point to start of actual name
  536.         ix += filenamePrefix.length;
  537.         filename = name.substr(ix, name.length);
  538.         if (filename != "") {
  539.           ix = filename.lastIndexOf(";");
  540.           if (ix > 0)
  541.             filename = filename.substr(0, ix);
  542.           
  543.           filename = filename.replace(/^"|"$/g, "");
  544.         }
  545.       }
  546.     }
  547.     return filename;
  548.   }  
  549.  
  550. };
  551.  
  552. const MODE_COMPLETE = 0;
  553. const MODE_FILEONLY = 1;
  554.  
  555. function appendFiltersForContentType(aFilePicker, aContentType, aSaveMode)
  556. {
  557.   var bundle = getStringBundle();
  558.     
  559.   switch (aContentType) {
  560.   case "text/html":
  561.     if (aSaveMode == MODE_COMPLETE)
  562.       aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), "*.htm; *.html");
  563.     aFilePicker.appendFilter(bundle.GetStringFromName("WebPageHTMLOnlyFilter"), "*.htm; *.html");
  564.     if (aSaveMode == MODE_COMPLETE)
  565.       aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterText);
  566.     break;
  567.  
  568.   case "application/xhtml+xml":
  569.     if (aSaveMode == MODE_COMPLETE)
  570.       aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), "*.xht; *.xhtml");
  571.     aFilePicker.appendFilter(bundle.GetStringFromName("WebPageXHTMLOnlyFilter"), "*.xht; *.xhtml");
  572.     if (aSaveMode == MODE_COMPLETE)
  573.       aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterText);
  574.     break;
  575.  
  576.   case "text/xml":
  577.   case "application/xml":
  578.     if (aSaveMode == MODE_COMPLETE)
  579.       aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), "*.xml");
  580.     aFilePicker.appendFilter(bundle.GetStringFromName("WebPageXMLOnlyFilter"), "*.xml");
  581.     // It does not make sense to save as text because we have no idea how to format text only
  582.     break;
  583.  
  584.   default:
  585.     var mimeInfo = getMIMEInfoForType(aContentType);
  586.     if (mimeInfo) {
  587.       var extCount = { };
  588.       var extList = { };
  589.       mimeInfo.GetFileExtensions(extCount, extList);
  590.       
  591.       var extString = "";
  592.       for (var i = 0; i < extCount.value; ++i) {
  593.         if (i > 0) 
  594.           extString += "; "; // If adding more than one extension, separate by semi-colon
  595.         extString += "*." + extList.value[i];
  596.       }
  597.       
  598.       if (extCount.value > 0) {
  599.         aFilePicker.appendFilter(mimeInfo.Description, extString);
  600.       } else {
  601.         aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
  602.       }        
  603.     }
  604.     else
  605.       aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
  606.     break;
  607.   }
  608.  
  609. function getPostData()
  610. {
  611.   try {
  612.     var sessionHistory = getWebNavigation().sessionHistory;
  613.     entry = sessionHistory.getEntryAtIndex(sessionHistory.index, false);
  614.     entry = entry.QueryInterface(Components.interfaces.nsISHEntry);
  615.     return entry.postData;
  616.   }
  617.   catch (e) {
  618.   }
  619.   return null;
  620. }
  621.  
  622. function getStringBundle()
  623. {
  624.   const bundleURL = "chrome://communicator/locale/contentAreaCommands.properties";
  625.   
  626.   const sbsContractID = "@mozilla.org/intl/stringbundle;1";
  627.   const sbsIID = Components.interfaces.nsIStringBundleService;
  628.   const sbs = Components.classes[sbsContractID].getService(sbsIID);
  629.   
  630.   const lsContractID = "@mozilla.org/intl/nslocaleservice;1";
  631.   const lsIID = Components.interfaces.nsILocaleService;
  632.   const ls = Components.classes[lsContractID].getService(lsIID);
  633.   var appLocale = ls.GetApplicationLocale();
  634.   return sbs.createBundle(bundleURL, appLocale);    
  635. }
  636.  
  637. function makeWebBrowserPersist()
  638. {
  639.   const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
  640.   const persistIID = Components.interfaces.nsIWebBrowserPersist;
  641.   return Components.classes[persistContractID].createInstance(persistIID);
  642. }
  643.  
  644. function makeProgressDialog()
  645. {
  646.   const progressDialogContractID = "@mozilla.org/progressdialog;1";
  647.   const progressDialogIID = Components.interfaces.nsIProgressDialog;
  648.   return Components.classes[progressDialogContractID].createInstance(progressDialogIID);
  649. }
  650.  
  651. function makeURL(aURL)
  652. {
  653.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  654.                 .getService(Components.interfaces.nsIIOService);
  655.   return ioService.newURI(aURL, null, null);
  656.   
  657. }
  658.  
  659. function makeFilePicker()
  660. {
  661.   const fpContractID = "@mozilla.org/filepicker;1";
  662.   const fpIID = Components.interfaces.nsIFilePicker;
  663.   return Components.classes[fpContractID].createInstance(fpIID);
  664. }
  665.  
  666. function makeTempFile()
  667. {
  668.   const mimeTypes = "TmpD";
  669.   const flContractID = "@mozilla.org/file/directory_service;1";
  670.   const flIID = Components.interfaces.nsIProperties;
  671.   var fileLocator = Components.classes[flContractID].getService(flIID);
  672.   var tempFile = fileLocator.get(mimeTypes, Components.interfaces.nsIFile);
  673.   tempFile.append("~sav" + Math.floor(Math.random() * 1000) + ".tmp");
  674.   return tempFile;
  675. }
  676.  
  677. function getMIMEService()
  678. {
  679.   const mimeSvcContractID = "@mozilla.org/mime;1";
  680.   const mimeSvcIID = Components.interfaces.nsIMIMEService;
  681.   const mimeSvc = Components.classes[mimeSvcContractID].getService(mimeSvcIID);
  682.   return mimeSvc;
  683. }
  684.  
  685. function getMIMEInfoForExtension(aExtension)
  686. {
  687.   try {  
  688.     return getMIMEService().GetFromExtension(aExtension);
  689.   }
  690.   catch (e) {
  691.   }
  692.   return null;
  693. }
  694.  
  695. function getMIMEInfoForType(aMIMEType)
  696. {
  697.   try {  
  698.     return getMIMEService().GetFromMIMEType(aMIMEType);
  699.   }
  700.   catch (e) {
  701.   }
  702.   return null;
  703. }
  704.  
  705. function getDefaultFileName(aDefaultFileName, aNameFromHeaders, aDocumentURI, aDocument)
  706. {
  707.   if (aNameFromHeaders)
  708.     // 1) Use the name suggested by the HTTP headers
  709.     return validateFileName(aNameFromHeaders);
  710.  
  711.   try {
  712.     var url = aDocumentURI.QueryInterface(Components.interfaces.nsIURL);
  713.     if (url.fileName != "") {
  714.       // 2) Use the actual file name, if present
  715.       return unescape(url.fileName);
  716.     }
  717.   } catch (e) {
  718.     try {
  719.       // the file name might be non ASCII
  720.       // try unescape again with a characterSet
  721.       var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  722.                                    .getService(Components.interfaces.nsITextToSubURI);
  723.       var charset;
  724.       if (aDocument)
  725.         charset = aDocument.characterSet;
  726.       else if (document.commandDispatcher.focusedWindow)
  727.         charset = document.commandDispatcher.focusedWindow.document.characterSet;
  728.       else
  729.         charset = window._content.document.characterSet;
  730.       return textToSubURI.unEscapeURIForUI(charset, url.fileName);
  731.     } catch (e) {
  732.       // This is something like a wyciwyg:, data:, and so forth
  733.       // URI... no usable filename here.
  734.     }
  735.   }
  736.   
  737.   if (aDocument) {
  738.     var docTitle = validateFileName(aDocument.title).replace(/^\s+|\s+$/g, "");
  739.  
  740.     if (docTitle != "") {
  741.       // 3) Use the document title
  742.       return docTitle;
  743.     }
  744.   }
  745.   
  746.   if (aDefaultFileName)
  747.     // 4) Use the caller-provided name, if any
  748.     return validateFileName(aDefaultFileName);
  749.  
  750.   try {
  751.     if (aDocumentURI.host)
  752.       // 5) Use the host.
  753.       return aDocumentURI.host;
  754.   } catch (e) {
  755.     // Some files have no information at all, like Javascript generated pages
  756.   }
  757.   try {
  758.     // 6) Use the default file name
  759.     return getStringBundle().GetStringFromName("DefaultSaveFileName");
  760.   } catch (e) {
  761.     //in case localized string cannot be found
  762.   }
  763.   // 7) If all else fails, use "index"
  764.   return "index";
  765. }
  766.  
  767. function validateFileName(aFileName)
  768. {
  769.   var re = /[\/]+/g;
  770.   if (navigator.appVersion.indexOf("Windows") != -1) {
  771.     re = /[\\\/\|]+/g;
  772.     aFileName = aFileName.replace(/[\"]+/g, "'");
  773.     aFileName = aFileName.replace(/[\*\:\?]+/g, " ");
  774.     aFileName = aFileName.replace(/[\<]+/g, "(");
  775.     aFileName = aFileName.replace(/[\>]+/g, ")");
  776.   }
  777.   else if (navigator.appVersion.indexOf("Macintosh") != -1)
  778.     re = /[\:\/]+/g;
  779.   
  780.   return aFileName.replace(re, "_");
  781. }
  782.  
  783. function getNormalizedLeafName(aFile, aDefaultExtension)
  784. {
  785.   if (!aDefaultExtension)
  786.     return aFile;
  787.   
  788.   // Fix up the file name we're saving to to include the default extension
  789.   const stdURLContractID = "@mozilla.org/network/standard-url;1";
  790.   const stdURLIID = Components.interfaces.nsIURL;
  791.   var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
  792.   url.filePath = aFile;
  793.   
  794.   if (url.fileExtension != aDefaultExtension) {
  795.     return aFile + "." + aDefaultExtension;
  796.   }
  797.  
  798.   return aFile;
  799. }
  800.  
  801. function getDefaultExtension(aFilename, aURI, aContentType)
  802. {
  803.   if (aContentType == "text/plain" || aContentType == "application/octet-stream" || aURI.scheme == "ftp")
  804.     return "";   // temporary fix for bug 120327
  805.  
  806.   // This mirrors some code in nsExternalHelperAppService::DoContent
  807.   // Use the filename first and then the URI if that fails
  808.   
  809.   var mimeInfo = getMIMEInfoForType(aContentType);
  810.  
  811.   // First try the extension from the filename
  812.   const stdURLContractID = "@mozilla.org/network/standard-url;1";
  813.   const stdURLIID = Components.interfaces.nsIURL;
  814.   var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
  815.   url.filePath = aFilename;
  816.  
  817.   var ext = url.fileExtension;
  818.  
  819.   if (ext && mimeInfo && mimeInfo.ExtensionExists(ext)) {
  820.     return ext;
  821.   }
  822.   
  823.   // Well, that failed.  Now try the extension from the URI
  824.   var urlext;
  825.   try {
  826.     url = aURI.QueryInterface(Components.interfaces.nsIURL);
  827.     urlext = url.fileExtension;
  828.   } catch (e) {
  829.   }
  830.  
  831.   if (urlext && mimeInfo && mimeInfo.ExtensionExists(urlext)) {
  832.     return urlext;
  833.   }
  834.   else {
  835.     try {
  836.       return mimeInfo.primaryExtension;
  837.     }
  838.     catch (e) {
  839.       // Fall back on the extensions in the filename and URI for lack
  840.       // of anything better.
  841.       return ext || urlext;
  842.     }
  843.   }
  844. }
  845.  
  846. function isDocumentType(aContentType)
  847. {
  848.   switch (aContentType) {
  849.   case "text/html":
  850.   case "text/xml":
  851.   case "application/xhtml+xml":
  852.   case "application/xml":
  853.     return true;
  854.   }
  855.   return false;
  856. }
  857.