home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 October / maximum-cd-2009-10.iso / DiscContents / Firefox Setup 3.5.exe / nonlocalized / chrome / toolkit.jar / content / global / contentAreaUtils.js < prev    next >
Encoding:
JavaScript  |  2009-06-24  |  33.7 KB  |  974 lines

  1. //@line 41 "e:\builds\moz2_slave\win32_build\build\toolkit\content\contentAreaUtils.js"
  2.  
  3. /**
  4.  * urlSecurityCheck: JavaScript wrapper for checkLoadURIWithPrincipal
  5.  * and checkLoadURIStrWithPrincipal.
  6.  * If |aPrincipal| is not allowed to link to |aURL|, this function throws with
  7.  * an error message.
  8.  *
  9.  * @param aURL
  10.  *        The URL a page has linked to. This could be passed either as a string
  11.  *        or as a nsIURI object.
  12.  * @param aPrincipal
  13.  *        The principal of the document from which aURL came.
  14.  * @param aFlags
  15.  *        Flags to be passed to checkLoadURIStr. If undefined,
  16.  *        nsIScriptSecurityManager.STANDARD will be passed.
  17.  */
  18. function urlSecurityCheck(aURL, aPrincipal, aFlags)
  19. {
  20.   const nsIScriptSecurityManager =
  21.     Components.interfaces.nsIScriptSecurityManager;
  22.   var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
  23.                          .getService(nsIScriptSecurityManager);
  24.   if (aFlags === undefined)
  25.     aFlags = nsIScriptSecurityManager.STANDARD;
  26.  
  27.   try {
  28.     if (aURL instanceof Components.interfaces.nsIURI)
  29.       secMan.checkLoadURIWithPrincipal(aPrincipal, aURL, aFlags);
  30.     else
  31.       secMan.checkLoadURIStrWithPrincipal(aPrincipal, aURL, aFlags);
  32.   } catch (e) {
  33.     // XXXmano: dump the principal url here too
  34.     throw "Load of " + aURL + " denied.";
  35.   }
  36. }
  37.  
  38. /**
  39.  * Determine whether or not a given focused DOMWindow is in the content area.
  40.  **/
  41. function isContentFrame(aFocusedWindow)
  42. {
  43.   if (!aFocusedWindow)
  44.     return false;
  45.  
  46.   return (aFocusedWindow.top == window.content);
  47. }
  48.  
  49.  
  50. // Clientelle: (Make sure you don't break any of these)
  51. //  - File    ->  Save Page/Frame As...
  52. //  - Context ->  Save Page/Frame As...
  53. //  - Context ->  Save Link As...
  54. //  - Alt-Click links in web pages
  55. //  - Alt-Click links in the UI
  56. //
  57. // Try saving each of these types:
  58. // - A complete webpage using File->Save Page As, and Context->Save Page As
  59. // - A webpage as HTML only using the above methods
  60. // - A webpage as Text only using the above methods
  61. // - An image with an extension (e.g. .jpg) in its file name, using
  62. //   Context->Save Image As...
  63. // - An image without an extension (e.g. a banner ad on cnn.com) using
  64. //   the above method.
  65. // - A linked document using Save Link As...
  66. // - A linked document using Alt-click Save Link As...
  67. //
  68. function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
  69.                  aSkipPrompt, aReferrer)
  70. {
  71.   internalSave(aURL, null, aFileName, null, null, aShouldBypassCache,
  72.                aFilePickerTitleKey, null, aReferrer, aSkipPrompt);
  73. }
  74.  
  75. // Just like saveURL, but will get some info off the image before
  76. // calling internalSave
  77. // Clientelle: (Make sure you don't break any of these)
  78. //  - Context ->  Save Image As...
  79. const imgICache = Components.interfaces.imgICache;
  80. const nsISupportsCString = Components.interfaces.nsISupportsCString;
  81.  
  82. function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
  83.                       aSkipPrompt, aReferrer)
  84. {
  85.   var contentType = null;
  86.   var contentDisposition = null;
  87.   if (!aShouldBypassCache) {
  88.     try {
  89.       var imageCache = Components.classes["@mozilla.org/image/cache;1"]
  90.                                  .getService(imgICache);
  91.       var props =
  92.         imageCache.findEntryProperties(makeURI(aURL, getCharsetforSave(null)));
  93.       if (props) {
  94.         contentType = props.get("type", nsISupportsCString);
  95.         contentDisposition = props.get("content-disposition",
  96.                                        nsISupportsCString);
  97.       }
  98.     } catch (e) {
  99.       // Failure to get type and content-disposition off the image is non-fatal
  100.     }
  101.   }
  102.   internalSave(aURL, null, aFileName, contentDisposition, contentType,
  103.                aShouldBypassCache, aFilePickerTitleKey, null, aReferrer, aSkipPrompt);
  104. }
  105.  
  106. function saveFrameDocument()
  107. {
  108.   var focusedWindow = document.commandDispatcher.focusedWindow;
  109.   if (isContentFrame(focusedWindow))
  110.     saveDocument(focusedWindow.document);
  111. }
  112.  
  113. function saveDocument(aDocument, aSkipPrompt)
  114. {
  115.   if (!aDocument)
  116.     throw "Must have a document when calling saveDocument";
  117.  
  118.   // We want to use cached data because the document is currently visible.
  119.   var contentDisposition = null;
  120.   try {
  121.     contentDisposition =
  122.       aDocument.defaultView
  123.                .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  124.                .getInterface(Components.interfaces.nsIDOMWindowUtils)
  125.                .getDocumentMetadata("content-disposition");
  126.   } catch (ex) {
  127.     // Failure to get a content-disposition is ok
  128.   }
  129.   internalSave(aDocument.location.href, aDocument, null, contentDisposition,
  130.                aDocument.contentType, false, null, null,
  131.                aDocument.referrer ? makeURI(aDocument.referrer) : null,
  132.                aSkipPrompt);
  133. }
  134.  
  135. function DownloadListener(win, transfer) {
  136.   function makeClosure(name) {
  137.     return function() {
  138.       transfer[name].apply(transfer, arguments);
  139.     }
  140.   }
  141.  
  142.   this.window = win;
  143.  
  144.   // Now... we need to forward all calls to our transfer
  145.   for (var i in transfer) {
  146.     if (i != "QueryInterface")
  147.       this[i] = makeClosure(i);
  148.   }
  149. }
  150.  
  151. DownloadListener.prototype = {
  152.   QueryInterface: function dl_qi(aIID)
  153.   {
  154.     if (aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
  155.         aIID.equals(Components.interfaces.nsIWebProgressListener) ||
  156.         aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
  157.         aIID.equals(Components.interfaces.nsISupports)) {
  158.       return this;
  159.     }
  160.     throw Components.results.NS_ERROR_NO_INTERFACE;
  161.   },
  162.  
  163.   getInterface: function dl_gi(aIID)
  164.   {
  165.     if (aIID.equals(Components.interfaces.nsIAuthPrompt) ||
  166.         aIID.equals(Components.interfaces.nsIAuthPrompt2)) {
  167.       var ww =
  168.         Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  169.                   .getService(Components.interfaces.nsIPromptFactory);
  170.       return ww.getPrompt(this.window, aIID);
  171.     }
  172.  
  173.     throw Components.results.NS_ERROR_NO_INTERFACE;
  174.   }
  175. }
  176.  
  177. const kSaveAsType_Complete = 0; // Save document with attached objects.
  178. // const kSaveAsType_URL      = 1; // Save document or URL by itself.
  179. const kSaveAsType_Text     = 2; // Save document, converting to plain text.
  180.  
  181. /**
  182.  * internalSave: Used when saving a document or URL. This method:
  183.  *  - Determines a local target filename to use (unless parameter
  184.  *    aChosenData is non-null)
  185.  *  - Determines content-type if possible
  186.  *  - Prompts the user to confirm the destination filename and save mode
  187.  *    (content-type affects this)
  188.  *  - Creates a 'Persist' object (which will perform the saving in the
  189.  *    background) and then starts it.
  190.  *
  191.  * @param aURL
  192.  *        The String representation of the URL of the document being saved
  193.  * @param aDocument
  194.  *        The document to be saved
  195.  * @param aDefaultFileName
  196.  *        The caller-provided suggested filename if we don't 
  197.  *        find a better one
  198.  * @param aContentDisposition
  199.  *        The caller-provided content-disposition header to use.
  200.  * @param aContentType
  201.  *        The caller-provided content-type to use
  202.  * @param aShouldBypassCache
  203.  *        If true, the document will always be refetched from the server
  204.  * @param aFilePickerTitleKey
  205.  *        Alternate title for the file picker
  206.  * @param aChosenData
  207.  *        If non-null this contains an instance of object AutoChosen (see below)
  208.  *        which holds pre-determined data so that the user does not need to be
  209.  *        prompted for a target filename.
  210.  * @param aReferrer
  211.  *        the referrer URI object (not URL string) to use, or null
  212.  *        if no referrer should be sent.
  213.  * @param aSkipPrompt [optional]
  214.  *        If set to true, we will attempt to save the file to the
  215.  *        default downloads folder without prompting.
  216.  */
  217. function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
  218.                       aContentType, aShouldBypassCache, aFilePickerTitleKey,
  219.                       aChosenData, aReferrer, aSkipPrompt)
  220. {
  221.   if (aSkipPrompt == undefined)
  222.     aSkipPrompt = false;
  223.  
  224.   // Note: aDocument == null when this code is used by save-link-as...
  225.   var saveMode = GetSaveModeForContentType(aContentType);
  226.   var isDocument = aDocument != null && saveMode != SAVEMODE_FILEONLY;
  227.   var saveAsType = kSaveAsType_Complete;
  228.  
  229.   var file, fileURL;
  230.   // Find the URI object for aURL and the FileName/Extension to use when saving.
  231.   // FileName/Extension will be ignored if aChosenData supplied.
  232.   var fileInfo = new FileInfo(aDefaultFileName);
  233.   if (aChosenData)
  234.     file = aChosenData.file;
  235.   else {
  236.     var charset = null;
  237.     if (aDocument)
  238.       charset = aDocument.characterSet;
  239.     else if (aReferrer)
  240.       charset = aReferrer.originCharset;
  241.     initFileInfo(fileInfo, aURL, charset, aDocument,
  242.                  aContentType, aContentDisposition);
  243.     var fpParams = {
  244.       fpTitleKey: aFilePickerTitleKey,
  245.       isDocument: isDocument,
  246.       fileInfo: fileInfo,
  247.       contentType: aContentType,
  248.       saveMode: saveMode,
  249.       saveAsType: saveAsType,
  250.       file: file,
  251.       fileURL: fileURL
  252.     };
  253.  
  254.     if (!getTargetFile(fpParams, aSkipPrompt))
  255.       // If the method returned false this is because the user cancelled from
  256.       // the save file picker dialog.
  257.       return;
  258.  
  259.     saveAsType = fpParams.saveAsType;
  260.     saveMode = fpParams.saveMode;
  261.     file = fpParams.file;
  262.     fileURL = fpParams.fileURL;
  263.   }
  264.  
  265.   if (!fileURL)
  266.     fileURL = makeFileURI(file);
  267.  
  268.   // XXX We depend on the following holding true in appendFiltersForContentType():
  269.   // If we should save as a complete page, the saveAsType is kSaveAsType_Complete.
  270.   // If we should save as text, the saveAsType is kSaveAsType_Text.
  271.   var useSaveDocument = isDocument &&
  272.                         (((saveMode & SAVEMODE_COMPLETE_DOM) && (saveAsType == kSaveAsType_Complete)) ||
  273.                          ((saveMode & SAVEMODE_COMPLETE_TEXT) && (saveAsType == kSaveAsType_Text)));
  274.   // If we're saving a document, and are saving either in complete mode or
  275.   // as converted text, pass the document to the web browser persist component.
  276.   // If we're just saving the HTML (second option in the list), send only the URI.
  277.   var source = useSaveDocument ? aDocument : fileInfo.uri;
  278.   var persistArgs = {
  279.     source      : source,
  280.     contentType : (!aChosenData && useSaveDocument &&
  281.                    saveAsType == kSaveAsType_Text) ?
  282.                   "text/plain" : null,
  283.     target      : fileURL,
  284.     postData    : isDocument ? getPostData(aDocument) : null,
  285.     bypassCache : aShouldBypassCache
  286.   };
  287.  
  288.   var persist = makeWebBrowserPersist();
  289.  
  290.   // Calculate persist flags.
  291.   const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
  292.   const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
  293.   if (aShouldBypassCache)
  294.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
  295.   else
  296.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
  297.  
  298.   // Leave it to WebBrowserPersist to discover the encoding type (or lack thereof):
  299.   persist.persistFlags |= nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
  300.  
  301.   // Create download and initiate it (below)
  302.   var tr = Components.classes["@mozilla.org/transfer;1"].createInstance(Components.interfaces.nsITransfer);
  303.  
  304.   if (useSaveDocument) {
  305.     // Saving a Document, not a URI:
  306.     var filesFolder = null;
  307.     if (persistArgs.contentType != "text/plain") {
  308.       // Create the local directory into which to save associated files.
  309.       filesFolder = file.clone();
  310.  
  311.       var nameWithoutExtension = getFileBaseName(filesFolder.leafName);
  312.       var filesFolderLeafName = getStringBundle().formatStringFromName("filesFolder",
  313.                                                                        [nameWithoutExtension],
  314.                                                                        1);
  315.  
  316.       filesFolder.leafName = filesFolderLeafName;
  317.     }
  318.  
  319.     var encodingFlags = 0;
  320.     if (persistArgs.contentType == "text/plain") {
  321.       encodingFlags |= nsIWBP.ENCODE_FLAGS_FORMATTED;
  322.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
  323.       encodingFlags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT;
  324.     }
  325.     else {
  326.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
  327.     }
  328.  
  329.     const kWrapColumn = 80;
  330.     tr.init((aChosenData ? aChosenData.uri : fileInfo.uri),
  331.             persistArgs.target, "", null, null, null, persist);
  332.     persist.progressListener = new DownloadListener(window, tr);
  333.     persist.saveDocument(persistArgs.source, persistArgs.target, filesFolder,
  334.                          persistArgs.contentType, encodingFlags, kWrapColumn);
  335.   } else {
  336.     tr.init((aChosenData ? aChosenData.uri : source),
  337.             persistArgs.target, "", null, null, null, persist);
  338.     persist.progressListener = new DownloadListener(window, tr);
  339.     persist.saveURI((aChosenData ? aChosenData.uri : source),
  340.                     null, aReferrer, persistArgs.postData, null,
  341.                     persistArgs.target);
  342.   }
  343. }
  344.  
  345. /**
  346.  * Structure for holding info about automatically supplied parameters for
  347.  * internalSave(...). This allows parameters to be supplied so the user does not
  348.  * need to be prompted for file info.
  349.  * @param aFileAutoChosen This is an nsILocalFile object that has been
  350.  *        pre-determined as the filename for the target to save to
  351.  * @param aUriAutoChosen  This is the nsIURI object for the target
  352.  */
  353. function AutoChosen(aFileAutoChosen, aUriAutoChosen) {
  354.   this.file = aFileAutoChosen;
  355.   this.uri  = aUriAutoChosen;
  356. }
  357.  
  358. /**
  359.  * Structure for holding info about a URL and the target filename it should be
  360.  * saved to. This structure is populated by initFileInfo(...).
  361.  * @param aSuggestedFileName This is used by initFileInfo(...) when it
  362.  *        cannot 'discover' the filename from the url 
  363.  * @param aFileName The target filename
  364.  * @param aFileBaseName The filename without the file extension
  365.  * @param aFileExt The extension of the filename
  366.  * @param aUri An nsIURI object for the url that is being saved
  367.  */
  368. function FileInfo(aSuggestedFileName, aFileName, aFileBaseName, aFileExt, aUri) {
  369.   this.suggestedFileName = aSuggestedFileName;
  370.   this.fileName = aFileName;
  371.   this.fileBaseName = aFileBaseName;
  372.   this.fileExt = aFileExt;
  373.   this.uri = aUri;
  374. }
  375.  
  376. /**
  377.  * Determine what the 'default' filename string is, its file extension and the
  378.  * filename without the extension. This filename is used when prompting the user
  379.  * for confirmation in the file picker dialog.
  380.  * @param aFI A FileInfo structure into which we'll put the results of this method.
  381.  * @param aURL The String representation of the URL of the document being saved
  382.  * @param aURLCharset The charset of aURL.
  383.  * @param aDocument The document to be saved
  384.  * @param aContentType The content type we're saving, if it could be
  385.  *        determined by the caller.
  386.  * @param aContentDisposition The content-disposition header for the object
  387.  *        we're saving, if it could be determined by the caller.
  388.  */
  389. function initFileInfo(aFI, aURL, aURLCharset, aDocument,
  390.                       aContentType, aContentDisposition)
  391. {
  392.   try {
  393.     // Get an nsIURI object from aURL if possible:
  394.     try {
  395.       aFI.uri = makeURI(aURL, aURLCharset);
  396.       // Assuming nsiUri is valid, calling QueryInterface(...) on it will
  397.       // populate extra object fields (eg filename and file extension).
  398.       var url = aFI.uri.QueryInterface(Components.interfaces.nsIURL);
  399.       aFI.fileExt = url.fileExtension;
  400.     } catch (e) {
  401.     }
  402.  
  403.     // Get the default filename:
  404.     aFI.fileName = getDefaultFileName((aFI.suggestedFileName || aFI.fileName),
  405.                                       aFI.uri, aDocument, aContentDisposition);
  406.     // If aFI.fileExt is still blank, consider: aFI.suggestedFileName is supplied
  407.     // if saveURL(...) was the original caller (hence both aContentType and
  408.     // aDocument are blank). If they were saving a link to a website then make
  409.     // the extension .htm .
  410.     if (!aFI.fileExt && !aDocument && !aContentType && (/^http(s?):\/\//i.test(aURL))) {
  411.       aFI.fileExt = "htm";
  412.       aFI.fileBaseName = aFI.fileName;
  413.     } else {
  414.       aFI.fileExt = getDefaultExtension(aFI.fileName, aFI.uri, aContentType);
  415.       aFI.fileBaseName = getFileBaseName(aFI.fileName);
  416.     }
  417.   } catch (e) {
  418.   }
  419. }
  420.  
  421. Components.utils.import("resource://gre/modules/DownloadLastDir.jsm");
  422.  
  423. function getTargetFile(aFpP, /* optional */ aSkipPrompt)
  424. {
  425.   const prefSvcContractID = "@mozilla.org/preferences-service;1";
  426.   const prefSvcIID = Components.interfaces.nsIPrefService;                              
  427.   var prefs = Components.classes[prefSvcContractID]
  428.                         .getService(prefSvcIID).getBranch("browser.download.");
  429.  
  430.   const nsILocalFile = Components.interfaces.nsILocalFile;
  431.  
  432.   var inPrivateBrowsing = false;
  433.   try {
  434.     var pbs = Components.classes["@mozilla.org/privatebrowsing;1"]
  435.                         .getService(Components.interfaces.nsIPrivateBrowsingService);
  436.     inPrivateBrowsing = pbs.privateBrowsingEnabled;
  437.   }
  438.   catch (e) {
  439.   }
  440.  
  441.   // For information on download folder preferences, see
  442.   // mozilla/browser/components/preferences/main.js
  443.   
  444.   var useDownloadDir = prefs.getBoolPref("useDownloadDir");
  445.   var dir = null;
  446.   
  447.   // Default to lastDir if useDownloadDir is false, and lastDir
  448.   // is configured and valid. Otherwise, use the user's default
  449.   // downloads directory configured through download prefs.
  450.   var dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
  451.                           .getService(Components.interfaces.nsIDownloadManager);
  452.   try {                          
  453.     var lastDir;
  454.     if (inPrivateBrowsing && gDownloadLastDir.file)
  455.       lastDir = gDownloadLastDir.file;
  456.     else
  457.       lastDir = prefs.getComplexValue("lastDir", nsILocalFile);
  458.     if ((!aSkipPrompt || !useDownloadDir) && lastDir.exists())
  459.       dir = lastDir;
  460.     else
  461.       dir = dnldMgr.userDownloadsDirectory;
  462.   } catch(ex) {
  463.     dir = dnldMgr.userDownloadsDirectory;
  464.   }
  465.  
  466.   if (!aSkipPrompt || !useDownloadDir || !dir || (dir && !dir.exists())) {
  467.     if (!dir || (dir && !dir.exists())) {
  468.       // Default to desktop.
  469.       var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
  470.                                   .getService(Components.interfaces.nsIProperties);
  471.       dir = fileLocator.get("Desk", nsILocalFile);
  472.     }
  473.  
  474.     var fp = makeFilePicker();
  475.     var titleKey = aFpP.fpTitleKey || "SaveLinkTitle";
  476.     var bundle = getStringBundle();
  477.     fp.init(window, bundle.GetStringFromName(titleKey), 
  478.             Components.interfaces.nsIFilePicker.modeSave);
  479.     
  480.     fp.defaultExtension = aFpP.fileInfo.fileExt;
  481.     fp.defaultString = getNormalizedLeafName(aFpP.fileInfo.fileName,
  482.                                              aFpP.fileInfo.fileExt);
  483.     appendFiltersForContentType(fp, aFpP.contentType, aFpP.fileInfo.fileExt,
  484.                                 aFpP.saveMode);
  485.  
  486.     if (dir)
  487.       fp.displayDirectory = dir;
  488.     
  489.     if (aFpP.isDocument) {
  490.       try {
  491.         fp.filterIndex = prefs.getIntPref("save_converter_index");
  492.       }
  493.       catch (e) {
  494.       }
  495.     }
  496.  
  497.     if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
  498.       return false;
  499.  
  500.     // Do not store the last save directory as a pref inside the private browsing mode
  501.     var directory = fp.file.parent.QueryInterface(nsILocalFile);
  502.     if (inPrivateBrowsing)
  503.       gDownloadLastDir.file = directory;
  504.     else
  505.       prefs.setComplexValue("lastDir", nsILocalFile, directory);
  506.  
  507.     fp.file.leafName = validateFileName(fp.file.leafName);
  508.     aFpP.saveAsType = fp.filterIndex;
  509.     aFpP.file = fp.file;
  510.     aFpP.fileURL = fp.fileURL;
  511.  
  512.     if (aFpP.isDocument)
  513.       prefs.setIntPref("save_converter_index", aFpP.saveAsType);
  514.   }
  515.   else {
  516.     dir.append(getNormalizedLeafName(aFpP.fileInfo.fileName,
  517.                                      aFpP.fileInfo.fileExt));
  518.     var file = dir;
  519.     
  520.     // Since we're automatically downloading, we don't get the file picker's 
  521.     // logic to check for existing files, so we need to do that here.
  522.     //
  523.     // Note - this code is identical to that in
  524.     //   mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
  525.     // If you are updating this code, update that code too! We can't share code
  526.     // here since that code is called in a js component.
  527.     var collisionCount = 0;
  528.     while (file.exists()) {
  529.       collisionCount++;
  530.       if (collisionCount == 1) {
  531.         // Append "(2)" before the last dot in (or at the end of) the filename
  532.         // special case .ext.gz etc files so we don't wind up with .tar(2).gz
  533.         if (file.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i))
  534.           file.leafName = file.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
  535.         else
  536.           file.leafName = file.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
  537.       }
  538.       else {
  539.         // replace the last (n) in the filename with (n+1)
  540.         file.leafName = file.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
  541.       }
  542.     }
  543.     aFpP.file = file;
  544.   }
  545.  
  546.   return true;
  547. }
  548.  
  549. // We have no DOM, and can only save the URL as is.
  550. const SAVEMODE_FILEONLY      = 0x00;
  551. // We have a DOM and can save as complete.
  552. const SAVEMODE_COMPLETE_DOM  = 0x01;
  553. // We have a DOM which we can serialize as text.
  554. const SAVEMODE_COMPLETE_TEXT = 0x02;
  555.  
  556. // If we are able to save a complete DOM, the 'save as complete' filter
  557. // must be the first filter appended.  The 'save page only' counterpart
  558. // must be the second filter appended.  And the 'save as complete text'
  559. // filter must be the third filter appended.
  560. function appendFiltersForContentType(aFilePicker, aContentType, aFileExtension, aSaveMode)
  561. {
  562.   var bundle = getStringBundle();
  563.   // The bundle name for saving only a specific content type.
  564.   var bundleName;
  565.   // The corresponding filter string for a specific content type.
  566.   var filterString;
  567.  
  568.   // XXX all the cases that are handled explicitly here MUST be handled
  569.   // in GetSaveModeForContentType to return a non-fileonly filter.
  570.   switch (aContentType) {
  571.   case "text/html":
  572.     bundleName   = "WebPageHTMLOnlyFilter";
  573.     filterString = "*.htm; *.html";
  574.     break;
  575.  
  576.   case "application/xhtml+xml":
  577.     bundleName   = "WebPageXHTMLOnlyFilter";
  578.     filterString = "*.xht; *.xhtml";
  579.     break;
  580.  
  581.   case "image/svg+xml":
  582.     bundleName   = "WebPageSVGOnlyFilter";
  583.     filterString = "*.svg; *.svgz";
  584.     break;
  585.  
  586.   case "text/xml":
  587.   case "application/xml":
  588.     bundleName   = "WebPageXMLOnlyFilter";
  589.     filterString = "*.xml";
  590.     break;
  591.  
  592.   default:
  593.     if (aSaveMode != SAVEMODE_FILEONLY)
  594.       throw "Invalid save mode for type '" + aContentType + "'";
  595.  
  596.     var mimeInfo = getMIMEInfoForType(aContentType, aFileExtension);
  597.     if (mimeInfo) {
  598.  
  599.       var extEnumerator = mimeInfo.getFileExtensions();
  600.  
  601.       var extString = "";
  602.       while (extEnumerator.hasMore()) {
  603.         var extension = extEnumerator.getNext();
  604.         if (extString)
  605.           extString += "; ";    // If adding more than one extension,
  606.                                 // separate by semi-colon
  607.         extString += "*." + extension;
  608.       }
  609.  
  610.       if (extString)
  611.         aFilePicker.appendFilter(mimeInfo.description, extString);
  612.     }
  613.  
  614.     break;
  615.   }
  616.  
  617.   if (aSaveMode & SAVEMODE_COMPLETE_DOM) {
  618.     aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), filterString);
  619.     // We should always offer a choice to save document only if
  620.     // we allow saving as complete.
  621.     aFilePicker.appendFilter(bundle.GetStringFromName(bundleName), filterString);
  622.   }
  623.  
  624.   if (aSaveMode & SAVEMODE_COMPLETE_TEXT)
  625.     aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterText);
  626.  
  627.   // Always append the all files (*) filter
  628.   aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
  629. }
  630.  
  631. function getPostData(aDocument)
  632. {
  633.   try {
  634.     var sessionHistory = aDocument.defaultView
  635.                                   .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  636.                                   .getInterface(Components.interfaces.nsIWebNavigation)
  637.                                   .sessionHistory;
  638.     return sessionHistory.getEntryAtIndex(sessionHistory.index, false)
  639.                          .QueryInterface(Components.interfaces.nsISHEntry)
  640.                          .postData;
  641.   }
  642.   catch (e) {
  643.   }
  644.   return null;
  645. }
  646.  
  647. function getStringBundle()
  648. {
  649.   return Components.classes["@mozilla.org/intl/stringbundle;1"]
  650.                    .getService(Components.interfaces.nsIStringBundleService)
  651.                    .createBundle("chrome://global/locale/contentAreaCommands.properties");
  652. }
  653.  
  654. function makeWebBrowserPersist()
  655. {
  656.   const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
  657.   const persistIID = Components.interfaces.nsIWebBrowserPersist;
  658.   return Components.classes[persistContractID].createInstance(persistIID);
  659. }
  660.  
  661. /**
  662.  * Constructs a new URI, using nsIIOService.
  663.  * @param aURL The URI spec.
  664.  * @param aOriginCharset The charset of the URI.
  665.  * @param aBaseURI Base URI to resolve aURL, or null.
  666.  * @return an nsIURI object based on aURL.
  667.  */
  668. function makeURI(aURL, aOriginCharset, aBaseURI)
  669. {
  670.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  671.                             .getService(Components.interfaces.nsIIOService);
  672.   return ioService.newURI(aURL, aOriginCharset, aBaseURI);
  673. }
  674.  
  675. function makeFileURI(aFile)
  676. {
  677.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  678.                             .getService(Components.interfaces.nsIIOService);
  679.   return ioService.newFileURI(aFile);
  680. }
  681.  
  682. function makeFilePicker()
  683. {
  684.   const fpContractID = "@mozilla.org/filepicker;1";
  685.   const fpIID = Components.interfaces.nsIFilePicker;
  686.   return Components.classes[fpContractID].createInstance(fpIID);
  687. }
  688.  
  689. function getMIMEService()
  690. {
  691.   const mimeSvcContractID = "@mozilla.org/mime;1";
  692.   const mimeSvcIID = Components.interfaces.nsIMIMEService;
  693.   const mimeSvc = Components.classes[mimeSvcContractID].getService(mimeSvcIID);
  694.   return mimeSvc;
  695. }
  696.  
  697. // Given aFileName, find the fileName without the extension on the end.
  698. function getFileBaseName(aFileName)
  699. {
  700.   // Remove the file extension from aFileName:
  701.   return aFileName.replace(/\.[^.]*$/, "");
  702. }
  703.  
  704. function getMIMETypeForURI(aURI)
  705. {
  706.   try {
  707.     return getMIMEService().getTypeFromURI(aURI);
  708.   }
  709.   catch (e) {
  710.   }
  711.   return null;
  712. }
  713.  
  714. function getMIMEInfoForType(aMIMEType, aExtension)
  715. {
  716.   if (aMIMEType || aExtension) {
  717.     try {
  718.       return getMIMEService().getFromTypeAndExtension(aMIMEType, aExtension);
  719.     }
  720.     catch (e) {
  721.     }
  722.   }
  723.   return null;
  724. }
  725.  
  726. function getDefaultFileName(aDefaultFileName, aURI, aDocument,
  727.                             aContentDisposition)
  728. {
  729.   // 1) look for a filename in the content-disposition header, if any
  730.   if (aContentDisposition) {
  731.     const mhpContractID = "@mozilla.org/network/mime-hdrparam;1";
  732.     const mhpIID = Components.interfaces.nsIMIMEHeaderParam;
  733.     const mhp = Components.classes[mhpContractID].getService(mhpIID);
  734.     var dummy = { value: null };  // Need an out param...
  735.     var charset = getCharsetforSave(aDocument);
  736.  
  737.     var fileName = null;
  738.     try {
  739.       fileName = mhp.getParameter(aContentDisposition, "filename", charset,
  740.                                   true, dummy);
  741.     }
  742.     catch (e) {
  743.       try {
  744.         fileName = mhp.getParameter(aContentDisposition, "name", charset, true,
  745.                                     dummy);
  746.       }
  747.       catch (e) {
  748.       }
  749.     }
  750.     if (fileName)
  751.       return fileName;
  752.   }
  753.  
  754.   try {
  755.     var url = aURI.QueryInterface(Components.interfaces.nsIURL);
  756.     if (url.fileName != "") {
  757.       // 2) Use the actual file name, if present
  758.       var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  759.                                    .getService(Components.interfaces.nsITextToSubURI);
  760.       return validateFileName(textToSubURI.unEscapeURIForUI(url.originCharset || "UTF-8", url.fileName));
  761.     }
  762.   } catch (e) {
  763.     // This is something like a data: and so forth URI... no filename here.
  764.   }
  765.  
  766.   if (aDocument) {
  767.     var docTitle = validateFileName(aDocument.title).replace(/^\s+|\s+$/g, "");
  768.     if (docTitle) {
  769.       // 3) Use the document title
  770.       return docTitle;
  771.     }
  772.   }
  773.  
  774.   if (aDefaultFileName)
  775.     // 4) Use the caller-provided name, if any
  776.     return validateFileName(aDefaultFileName);
  777.  
  778.   // 5) If this is a directory, use the last directory name
  779.   var path = aURI.path.match(/\/([^\/]+)\/$/);
  780.   if (path && path.length > 1)
  781.     return validateFileName(path[1]);
  782.  
  783.   try {
  784.     if (aURI.host)
  785.       // 6) Use the host.
  786.       return aURI.host;
  787.   } catch (e) {
  788.     // Some files have no information at all, like Javascript generated pages
  789.   }
  790.   try {
  791.     // 7) Use the default file name
  792.     return getStringBundle().GetStringFromName("DefaultSaveFileName");
  793.   } catch (e) {
  794.     //in case localized string cannot be found
  795.   }
  796.   // 8) If all else fails, use "index"
  797.   return "index";
  798. }
  799.  
  800. function validateFileName(aFileName)
  801. {
  802.   var re = /[\/]+/g;
  803.   if (navigator.appVersion.indexOf("Windows") != -1) {
  804.     re = /[\\\/\|]+/g;
  805.     aFileName = aFileName.replace(/[\"]+/g, "'");
  806.     aFileName = aFileName.replace(/[\*\:\?]+/g, " ");
  807.     aFileName = aFileName.replace(/[\<]+/g, "(");
  808.     aFileName = aFileName.replace(/[\>]+/g, ")");
  809.   }
  810.   else if (navigator.appVersion.indexOf("Macintosh") != -1)
  811.     re = /[\:\/]+/g;
  812.   
  813.   return aFileName.replace(re, "_");
  814. }
  815.  
  816. function getNormalizedLeafName(aFile, aDefaultExtension)
  817. {
  818.   if (!aDefaultExtension)
  819.     return aFile;
  820.  
  821. //@line 861 "e:\builds\moz2_slave\win32_build\build\toolkit\content\contentAreaUtils.js"
  822.   // Remove trailing dots and spaces on windows
  823.   aFile = aFile.replace(/[\s.]+$/, "");
  824. //@line 864 "e:\builds\moz2_slave\win32_build\build\toolkit\content\contentAreaUtils.js"
  825.  
  826.   // Remove leading dots
  827.   aFile = aFile.replace(/^\.+/, "");
  828.  
  829.   // Fix up the file name we're saving to to include the default extension
  830.   var i = aFile.lastIndexOf(".");
  831.   if (aFile.substr(i + 1) != aDefaultExtension)
  832.     return aFile + "." + aDefaultExtension;
  833.  
  834.   return aFile;
  835. }
  836.  
  837. function getDefaultExtension(aFilename, aURI, aContentType)
  838. {
  839.   if (aContentType == "text/plain" || aContentType == "application/octet-stream" || aURI.scheme == "ftp")
  840.     return "";   // temporary fix for bug 120327
  841.  
  842.   // First try the extension from the filename
  843.   const stdURLContractID = "@mozilla.org/network/standard-url;1";
  844.   const stdURLIID = Components.interfaces.nsIURL;
  845.   var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
  846.   url.filePath = aFilename;
  847.  
  848.   var ext = url.fileExtension;
  849.  
  850.   // This mirrors some code in nsExternalHelperAppService::DoContent
  851.   // Use the filename first and then the URI if that fails
  852.  
  853.   var mimeInfo = getMIMEInfoForType(aContentType, ext);
  854.  
  855.   if (ext && mimeInfo && mimeInfo.extensionExists(ext))
  856.     return ext;
  857.  
  858.   // Well, that failed.  Now try the extension from the URI
  859.   var urlext;
  860.   try {
  861.     url = aURI.QueryInterface(Components.interfaces.nsIURL);
  862.     urlext = url.fileExtension;
  863.   } catch (e) {
  864.   }
  865.  
  866.   if (urlext && mimeInfo && mimeInfo.extensionExists(urlext)) {
  867.     return urlext;
  868.   }
  869.   else {
  870.     try {
  871.       if (mimeInfo)
  872.         return mimeInfo.primaryExtension;
  873.     }
  874.     catch (e) {
  875.     }
  876.     // Fall back on the extensions in the filename and URI for lack
  877.     // of anything better.
  878.     return ext || urlext;
  879.   }
  880. }
  881.  
  882. function GetSaveModeForContentType(aContentType)
  883. {
  884.   var saveMode = SAVEMODE_FILEONLY;
  885.   switch (aContentType) {
  886.   case "text/html":
  887.   case "application/xhtml+xml":
  888.   case "image/svg+xml":
  889.     saveMode |= SAVEMODE_COMPLETE_TEXT;
  890.     // Fall through
  891.   case "text/xml":
  892.   case "application/xml":
  893.     saveMode |= SAVEMODE_COMPLETE_DOM;
  894.     break;
  895.   }
  896.  
  897.   return saveMode;
  898. }
  899.  
  900. function getCharsetforSave(aDocument)
  901. {
  902.   if (aDocument)
  903.     return aDocument.characterSet;
  904.  
  905.   if (document.commandDispatcher.focusedWindow)
  906.     return document.commandDispatcher.focusedWindow.document.characterSet;
  907.  
  908.   return window.content.document.characterSet;
  909. }
  910.  
  911. /**
  912.  * Open a URL from chrome, determining if we can handle it internally or need to
  913.  *  launch an external application to handle it.
  914.  * @param aURL The URL to be opened
  915.  */
  916. function openURL(aURL)
  917. {
  918.   var ios = Components.classes["@mozilla.org/network/io-service;1"]
  919.                       .getService(Components.interfaces.nsIIOService);
  920.   var uri = ios.newURI(aURL, null, null);
  921.  
  922.   var protocolSvc = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
  923.                               .getService(Components.interfaces.nsIExternalProtocolService);
  924.  
  925.   if (!protocolSvc.isExposedProtocol(uri.scheme)) {
  926.     // If we're not a browser, use the external protocol service to load the URI.
  927.     protocolSvc.loadUrl(uri);
  928.   }
  929.   else {
  930.     var loadgroup = Components.classes["@mozilla.org/network/load-group;1"]
  931.                               .createInstance(Components.interfaces.nsILoadGroup);
  932.     var appstartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
  933.                                .getService(Components.interfaces.nsIAppStartup);
  934.  
  935.     var loadListener = {
  936.       onStartRequest: function ll_start(aRequest, aContext) {
  937.         appstartup.enterLastWindowClosingSurvivalArea();
  938.       },
  939.       onStopRequest: function ll_stop(aRequest, aContext, aStatusCode) {
  940.         appstartup.exitLastWindowClosingSurvivalArea();
  941.       },
  942.       QueryInterface: function ll_QI(iid) {
  943.         if (iid.equals(Components.interfaces.nsISupports) ||
  944.             iid.equals(Components.interfaces.nsIRequestObserver) ||
  945.             iid.equals(Components.interfaces.nsISupportsWeakReference))
  946.           return this;
  947.         throw Components.results.NS_ERROR_NO_INTERFACE;
  948.       }
  949.     }
  950.     loadgroup.groupObserver = loadListener;
  951.  
  952.     var uriListener = {
  953.       onStartURIOpen: function(uri) { return false; },
  954.       doContent: function(ctype, preferred, request, handler) { return false; },
  955.       isPreferred: function(ctype, desired) { return false; },
  956.       canHandleContent: function(ctype, preferred, desired) { return false; },
  957.       loadCookie: null,
  958.       parentContentListener: null,
  959.       getInterface: function(iid) {
  960.         if (iid.equals(Components.interfaces.nsIURIContentListener))
  961.           return this;
  962.         if (iid.equals(Components.interfaces.nsILoadGroup))
  963.           return loadgroup;
  964.         throw Components.results.NS_ERROR_NO_INTERFACE;
  965.       }
  966.     }
  967.  
  968.     var channel = ios.newChannelFromURI(uri);
  969.     var uriLoader = Components.classes["@mozilla.org/uriloader;1"]
  970.                               .getService(Components.interfaces.nsIURILoader);
  971.     uriLoader.openURI(channel, true, uriListener);
  972.   }
  973. }
  974.