home *** CD-ROM | disk | FTP | other *** search
/ PC World 2003 May / PCWorld_2003-05_cd.bin / Komunik / phoenix / chrome / browser.jar / content / browser / contentAreaUtils.js < prev    next >
Text File  |  2002-11-12  |  26KB  |  782 lines

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