home *** CD-ROM | disk | FTP | other *** search
/ PC Advisor 2010 April / PCA177.iso / ESSENTIALS / Firefox Setup.exe / nonlocalized / components / nsHelperAppDlg.js < prev    next >
Encoding:
Text File  |  2009-07-15  |  41.9 KB  |  1,094 lines

  1. /*
  2. //@line 45 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  3. */
  4.  
  5. /* This file implements the nsIHelperAppLauncherDialog interface.
  6.  *
  7.  * The implementation consists of a JavaScript "class" named nsUnknownContentTypeDialog,
  8.  * comprised of:
  9.  *   - a JS constructor function
  10.  *   - a prototype providing all the interface methods and implementation stuff
  11.  *
  12.  * In addition, this file implements an nsIModule object that registers the
  13.  * nsUnknownContentTypeDialog component.
  14.  */
  15.  
  16. const PREF_BD_USEDOWNLOADDIR = "browser.download.useDownloadDir";
  17. const nsITimer = Components.interfaces.nsITimer;
  18.  
  19. Components.utils.import("resource://gre/modules/DownloadLastDir.jsm");
  20.  
  21. /* ctor
  22.  */
  23. function nsUnknownContentTypeDialog() {
  24.     // Initialize data properties.
  25.     this.mLauncher = null;
  26.     this.mContext  = null;
  27.     this.mSourcePath = null;
  28.     this.chosenApp = null;
  29.     this.givenDefaultApp = false;
  30.     this.updateSelf = true;
  31.     this.mTitle    = "";
  32. }
  33.  
  34. nsUnknownContentTypeDialog.prototype = {
  35.     nsIMIMEInfo  : Components.interfaces.nsIMIMEInfo,
  36.  
  37.     QueryInterface: function (iid) {
  38.         if (!iid.equals(Components.interfaces.nsIHelperAppLauncherDialog) &&
  39.             !iid.equals(Components.interfaces.nsITimerCallback) &&
  40.             !iid.equals(Components.interfaces.nsISupports)) {
  41.             throw Components.results.NS_ERROR_NO_INTERFACE;
  42.         }
  43.         return this;
  44.     },
  45.  
  46.     // ---------- nsIHelperAppLauncherDialog methods ----------
  47.  
  48.     // show: Open XUL dialog using window watcher.  Since the dialog is not
  49.     //       modal, it needs to be a top level window and the way to open
  50.     //       one of those is via that route).
  51.     show: function(aLauncher, aContext, aReason)  {
  52.       this.mLauncher = aLauncher;
  53.       this.mContext  = aContext;
  54.  
  55.       const nsITimer = Components.interfaces.nsITimer;
  56.       this._showTimer = Components.classes["@mozilla.org/timer;1"]
  57.                                   .createInstance(nsITimer);
  58.       this._showTimer.initWithCallback(this, 0, nsITimer.TYPE_ONE_SHOT);
  59.     },
  60.  
  61.     // When opening from new tab, if tab closes while dialog is opening,
  62.     // (which is a race condition on the XUL file being cached and the timer
  63.     // in nsExternalHelperAppService), the dialog gets a blur and doesn't
  64.     // activate the OK button.  So we wait a bit before doing opening it.
  65.     reallyShow: function() {
  66.         try {
  67.           var ir = this.mContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  68.           var dwi = ir.getInterface(Components.interfaces.nsIDOMWindowInternal);
  69.           var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  70.                              .getService(Components.interfaces.nsIWindowWatcher);
  71.           this.mDialog = ww.openWindow(dwi,
  72.                                        "chrome://mozapps/content/downloads/unknownContentType.xul",
  73.                                        null,
  74.                                        "chrome,centerscreen,titlebar,dialog=yes,dependent",
  75.                                        null);
  76.         } catch (ex) {
  77.           // The containing window may have gone away.  Break reference
  78.           // cycles and stop doing the download.
  79.           const NS_BINDING_ABORTED = 0x804b0002;
  80.           this.mLauncher.cancel(NS_BINDING_ABORTED);
  81.           return;
  82.         }
  83.  
  84.         // Hook this object to the dialog.
  85.         this.mDialog.dialog = this;
  86.  
  87.         // Hook up utility functions.
  88.         this.getSpecialFolderKey = this.mDialog.getSpecialFolderKey;
  89.  
  90.         // Watch for error notifications.
  91.         this.progressListener.helperAppDlg = this;
  92.         this.mLauncher.setWebProgressListener(this.progressListener);
  93.     },
  94.  
  95.     // promptForSaveToFile:  Display file picker dialog and return selected file.
  96.     //                       This is called by the External Helper App Service
  97.     //                       after the ucth dialog calls |saveToDisk| with a null
  98.     //                       target filename (no target, therefore user must pick).
  99.     //
  100.     //                       Alternatively, if the user has selected to have all
  101.     //                       files download to a specific location, return that
  102.     //                       location and don't ask via the dialog. 
  103.     //
  104.     // Note - this function is called without a dialog, so it cannot access any part
  105.     // of the dialog XUL as other functions on this object do. 
  106.     promptForSaveToFile: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) {
  107.       var result = null;
  108.       
  109.       this.mLauncher = aLauncher;
  110.  
  111.       let prefs = Components.classes["@mozilla.org/preferences-service;1"]
  112.                             .getService(Components.interfaces.nsIPrefBranch);
  113.  
  114.       if (!aForcePrompt) {
  115.         // Check to see if the user wishes to auto save to the default download
  116.         // folder without prompting. Note that preference might not be set.
  117.         let autodownload = false;
  118.         try {
  119.           autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
  120.         } catch (e) { }
  121.       
  122.         if (autodownload) {
  123.           // Retrieve the user's default download directory
  124.           let dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
  125.                                   .getService(Components.interfaces.nsIDownloadManager);
  126.           let defaultFolder = dnldMgr.userDownloadsDirectory;
  127.           result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
  128.  
  129.           // Check to make sure we have a valid directory, otherwise, prompt
  130.           if (result)
  131.             return result;
  132.         }
  133.       }
  134.       
  135.       // Use file picker to show dialog.
  136.       var nsIFilePicker = Components.interfaces.nsIFilePicker;
  137.       var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  138.  
  139.       var bundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
  140.       bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  141.  
  142.       var windowTitle = bundle.GetStringFromName("saveDialogTitle");
  143.       var parent = aContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowInternal);
  144.       picker.init(parent, windowTitle, nsIFilePicker.modeSave);
  145.       picker.defaultString = aDefaultFile;
  146.  
  147.       if (aSuggestedFileExtension) {
  148.         // aSuggestedFileExtension includes the period, so strip it
  149.         picker.defaultExtension = aSuggestedFileExtension.substring(1);
  150.       } 
  151.       else {
  152.         try {
  153.           picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
  154.         } 
  155.         catch (ex) { }
  156.       }
  157.  
  158.       var wildCardExtension = "*";
  159.       if (aSuggestedFileExtension) {
  160.         wildCardExtension += aSuggestedFileExtension;
  161.         picker.appendFilter(this.mLauncher.MIMEInfo.description, wildCardExtension);
  162.       }
  163.  
  164.       picker.appendFilters( nsIFilePicker.filterAll );
  165.  
  166.       var inPrivateBrowsing = false;
  167.       try {
  168.         var pbs = Components.classes["@mozilla.org/privatebrowsing;1"]
  169.                             .getService(Components.interfaces.nsIPrivateBrowsingService);
  170.         inPrivateBrowsing = pbs.privateBrowsingEnabled;
  171.       }
  172.       catch (e) {
  173.       }
  174.  
  175.       // Default to lastDir if it's valid, use the user's default
  176.       // downloads directory otherwise.
  177.       var dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
  178.                               .getService(Components.interfaces.nsIDownloadManager);
  179.       try {
  180.         var lastDir;
  181.         if (inPrivateBrowsing && gDownloadLastDir.file)
  182.           lastDir = gDownloadLastDir.file;
  183.         else
  184.           lastDir = prefs.getComplexValue("browser.download.lastDir",
  185.                           Components.interfaces.nsILocalFile);
  186.         if (lastDir.exists())
  187.           picker.displayDirectory = lastDir;
  188.         else
  189.           picker.displayDirectory = dnldMgr.userDownloadsDirectory;
  190.       } catch (ex) {
  191.         picker.displayDirectory = dnldMgr.userDownloadsDirectory;
  192.       }
  193.  
  194.       if (picker.show() == nsIFilePicker.returnCancel) {
  195.         // null result means user cancelled.
  196.         return null;
  197.       }
  198.  
  199.       // Be sure to save the directory the user chose through the Save As... 
  200.       // dialog  as the new browser.download.dir since the old one
  201.       // didn't exist.
  202.       result = picker.file;
  203.  
  204.       if (result) {
  205.         try {
  206.           // Remove the file so that it's not there when we ensure non-existence later;
  207.           // this is safe because for the file to exist, the user would have had to
  208.           // confirm that he wanted the file overwritten.
  209.           if (result.exists())
  210.             result.remove(false);
  211.         }
  212.         catch (e) { }
  213.         var newDir = result.parent.QueryInterface(Components.interfaces.nsILocalFile);
  214.  
  215.         // Do not store the last save directory as a pref inside the private browsing mode
  216.         if (inPrivateBrowsing)
  217.           gDownloadLastDir.file = newDir;
  218.         else
  219.           prefs.setComplexValue("browser.download.lastDir", Components.interfaces.nsILocalFile, newDir);
  220.  
  221.         result = this.validateLeafName(newDir, result.leafName, null);
  222.       }
  223.       return result;
  224.     },
  225.  
  226.     /**
  227.      * Ensures that a local folder/file combination does not already exist in
  228.      * the file system (or finds such a combination with a reasonably similar
  229.      * leaf name), creates the corresponding file, and returns it.
  230.      *
  231.      * @param   aLocalFile
  232.      *          the folder where the file resides
  233.      * @param   aLeafName
  234.      *          the string name of the file (may be empty if no name is known,
  235.      *          in which case a name will be chosen)
  236.      * @param   aFileExt
  237.      *          the extension of the file, if one is known; this will be ignored
  238.      *          if aLeafName is non-empty
  239.      * @returns nsILocalFile
  240.      *          the created file
  241.      */
  242.     validateLeafName: function (aLocalFile, aLeafName, aFileExt)
  243.     {
  244.       if (!aLocalFile || !aLocalFile.exists())
  245.         return null;
  246.  
  247.       // Remove any leading periods, since we don't want to save hidden files
  248.       // automatically.
  249.       aLeafName = aLeafName.replace(/^\.+/, "");
  250.  
  251.       if (aLeafName == "")
  252.         aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : "");
  253.       aLocalFile.append(aLeafName);
  254.  
  255.       this.makeFileUnique(aLocalFile);
  256.  
  257. //@line 300 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  258.       let ext;
  259.       try {
  260.         // We can fail here if there's no primary extension set
  261.         ext = "." + this.mLauncher.MIMEInfo.primaryExtension;
  262.       } catch (e) { }
  263.  
  264.       // Append a file extension if it's an executable that doesn't have one
  265.       // but make sure we actually have an extension to add
  266.       let leaf = aLocalFile.leafName;
  267.       if (aLocalFile.isExecutable() && ext &&
  268.           leaf.substring(leaf.length - ext.length) != ext) {
  269.         let f = aLocalFile.clone();
  270.         aLocalFile.leafName = leaf + ext;
  271.  
  272.         f.remove(false);
  273.         this.makeFileUnique(aLocalFile);
  274.       }
  275. //@line 318 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  276.  
  277.       return aLocalFile;
  278.     },
  279.  
  280.     /**
  281.      * Generates and returns a uniquely-named file from aLocalFile.  If
  282.      * aLocalFile does not exist, it will be the file returned; otherwise, a
  283.      * file whose name is similar to that of aLocalFile will be returned.
  284.      */
  285.     makeFileUnique: function (aLocalFile)
  286.     {
  287.       try {
  288.         // Note - this code is identical to that in 
  289.         //   toolkit/content/contentAreaUtils.js.
  290.         // If you are updating this code, update that code too! We can't share code
  291.         // here since this is called in a js component. 
  292.         var collisionCount = 0;
  293.         while (aLocalFile.exists()) {
  294.           collisionCount++;
  295.           if (collisionCount == 1) {
  296.             // Append "(2)" before the last dot in (or at the end of) the filename
  297.             // special case .ext.gz etc files so we don't wind up with .tar(2).gz
  298.             if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i)) {
  299.               aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
  300.             }
  301.             else {
  302.               aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
  303.             }
  304.           }
  305.           else {
  306.             // replace the last (n) in the filename with (n+1)
  307.             aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
  308.           }
  309.         }
  310.         aLocalFile.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);
  311.       }
  312.       catch (e) {
  313.         dump("*** exception in validateLeafName: " + e + "\n");
  314.         if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) {
  315.           aLocalFile.append("unnamed");
  316.           if (aLocalFile.exists())
  317.             aLocalFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);
  318.         }
  319.       }
  320.     },
  321.     
  322.     // ---------- implementation methods ----------
  323.  
  324.     // Web progress listener so we can detect errors while mLauncher is
  325.     // streaming the data to a temporary file.
  326.     progressListener: {
  327.         // Implementation properties.
  328.         helperAppDlg: null,
  329.  
  330.         // nsIWebProgressListener methods.
  331.         // Look for error notifications and display alert to user.
  332.         onStatusChange: function( aWebProgress, aRequest, aStatus, aMessage ) {
  333.             if ( aStatus != Components.results.NS_OK ) {
  334.                 // Get prompt service.
  335.                 var prompter = Components.classes[ "@mozilla.org/embedcomp/prompt-service;1" ]
  336.                                    .getService( Components.interfaces.nsIPromptService );
  337.                 // Display error alert (using text supplied by back-end).
  338.                 prompter.alert( this.dialog, this.helperAppDlg.mTitle, aMessage );
  339.  
  340.                 // Close the dialog.
  341.                 this.helperAppDlg.onCancel();
  342.                 if ( this.helperAppDlg.mDialog ) {
  343.                     this.helperAppDlg.mDialog.close();
  344.                 }
  345.             }
  346.         },
  347.  
  348.         // Ignore onProgressChange, onProgressChange64, onStateChange, onLocationChange, onSecurityChange, and onRefreshAttempted notifications.
  349.         onProgressChange: function( aWebProgress,
  350.                                     aRequest,
  351.                                     aCurSelfProgress,
  352.                                     aMaxSelfProgress,
  353.                                     aCurTotalProgress,
  354.                                     aMaxTotalProgress ) {
  355.         },
  356.  
  357.         onProgressChange64: function( aWebProgress,
  358.                                       aRequest,
  359.                                       aCurSelfProgress,
  360.                                       aMaxSelfProgress,
  361.                                       aCurTotalProgress,
  362.                                       aMaxTotalProgress ) {
  363.         },
  364.  
  365.  
  366.  
  367.         onStateChange: function( aWebProgress, aRequest, aStateFlags, aStatus ) {
  368.         },
  369.  
  370.         onLocationChange: function( aWebProgress, aRequest, aLocation ) {
  371.         },
  372.  
  373.         onSecurityChange: function( aWebProgress, aRequest, state ) {
  374.         },
  375.  
  376.         onRefreshAttempted: function( aWebProgress, aURI, aDelay, aSameURI ) {
  377.           return true;
  378.     }
  379.     },
  380.  
  381.     // initDialog:  Fill various dialog fields with initial content.
  382.     initDialog : function() {
  383.       // Put file name in window title.
  384.       var suggestedFileName = this.mLauncher.suggestedFileName;
  385.  
  386.       // Some URIs do not implement nsIURL, so we can't just QI.
  387.       var url   = this.mLauncher.source;
  388.       var fname = "";
  389.       this.mSourcePath = url.prePath;
  390.       try {
  391.           url = url.QueryInterface( Components.interfaces.nsIURL );
  392.           // A url, use file name from it.
  393.           fname = url.fileName;
  394.           this.mSourcePath += url.directory;
  395.       } catch (ex) {
  396.           // A generic uri, use path.
  397.           fname = url.path;
  398.           this.mSourcePath += url.path;
  399.       }
  400.  
  401.       if (suggestedFileName)
  402.         fname = suggestedFileName;
  403.       
  404.       var displayName = fname.replace(/ +/g, " ");
  405.  
  406.       this.mTitle = this.dialogElement("strings").getFormattedString("title", [displayName]);
  407.       this.mDialog.document.title = this.mTitle;
  408.  
  409.       // Put content type, filename and location into intro.
  410.       this.initIntro(url, fname, displayName);
  411.  
  412.       var iconString = "moz-icon://" + fname + "?size=16&contentType=" + this.mLauncher.MIMEInfo.MIMEType;
  413.       this.dialogElement("contentTypeImage").setAttribute("src", iconString);
  414.  
  415.       // if always-save and is-executable and no-handler
  416.       // then set up simple ui
  417.       var mimeType = this.mLauncher.MIMEInfo.MIMEType;
  418.       var shouldntRememberChoice = (mimeType == "application/octet-stream" || 
  419.                                     mimeType == "application/x-msdownload" ||
  420.                                     this.mLauncher.targetFileIsExecutable);
  421.       if (shouldntRememberChoice && !this.openWithDefaultOK()) {
  422.         // hide featured choice 
  423.         this.dialogElement("normalBox").collapsed = true;
  424.         // show basic choice 
  425.         this.dialogElement("basicBox").collapsed = false;
  426.         // change button labels and icons; use "save" icon for the accept
  427.         // button since it's the only action possible
  428.         let acceptButton = this.mDialog.document.documentElement
  429.                                .getButton("accept");
  430.         acceptButton.label = this.dialogElement("strings")
  431.                                  .getString("unknownAccept.label");
  432.         acceptButton.setAttribute("icon", "save");
  433.         this.mDialog.document.documentElement.getButton("cancel").label = this.dialogElement("strings").getString("unknownCancel.label");
  434.         // hide other handler
  435.         this.dialogElement("openHandler").collapsed = true;
  436.         // set save as the selected option
  437.         this.dialogElement("mode").selectedItem = this.dialogElement("save");
  438.       }
  439.       else {
  440.         this.initAppAndSaveToDiskValues();
  441.  
  442.         // Initialize "always ask me" box. This should always be disabled
  443.         // and set to true for the ambiguous type application/octet-stream.
  444.         // We don't also check for application/x-msdownload here since we
  445.         // want users to be able to autodownload .exe files. 
  446.         var rememberChoice = this.dialogElement("rememberChoice");
  447.  
  448. //@line 509 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  449.         if (shouldntRememberChoice) {
  450.           rememberChoice.checked = false;
  451.           rememberChoice.disabled = true;
  452.         }
  453.         else {
  454.           rememberChoice.checked = !this.mLauncher.MIMEInfo.alwaysAskBeforeHandling;
  455.         }
  456.         this.toggleRememberChoice(rememberChoice);
  457.  
  458.         // XXXben - menulist won't init properly, hack. 
  459.         var openHandler = this.dialogElement("openHandler");
  460.         openHandler.parentNode.removeChild(openHandler);
  461.         var openHandlerBox = this.dialogElement("openHandlerBox");
  462.         openHandlerBox.appendChild(openHandler);
  463.       }
  464.  
  465.       this.mDialog.setTimeout("dialog.postShowCallback()", 0);
  466.       
  467.       this.mDialog.document.documentElement.getButton("accept").disabled = true;
  468.       this._showTimer = Components.classes["@mozilla.org/timer;1"]
  469.                                   .createInstance(nsITimer);
  470.       this._showTimer.initWithCallback(this, 250, nsITimer.TYPE_ONE_SHOT);
  471.     },
  472.  
  473.     notify: function (aTimer) {
  474.       if (aTimer == this._showTimer) {
  475.         if (!this.mDialog) {
  476.           this.reallyShow();
  477.         } else {
  478.           // The user may have already canceled the dialog.
  479.           try {
  480.             if (!this._blurred) {
  481.               this.mDialog.document.documentElement.getButton("accept").disabled = false;
  482.             }
  483.           } catch (ex) {}
  484.           this._delayExpired = true;
  485.         }
  486.         // The timer won't release us, so we have to release it.
  487.         this._showTimer = null;
  488.       }
  489.       else if (aTimer == this._saveToDiskTimer) {
  490.         // Since saveToDisk may open a file picker and therefore block this routine,
  491.         // we should only call it once the dialog is closed.
  492.         this.mLauncher.saveToDisk(null, false);
  493.         this._saveToDiskTimer = null;
  494.       }
  495.     },
  496.  
  497.     postShowCallback: function () {
  498.       this.mDialog.sizeToContent();
  499.  
  500.       // Set initial focus
  501.       this.dialogElement("mode").focus();
  502.     },
  503.  
  504.     // initIntro:
  505.     initIntro: function(url, filename, displayname) {
  506.         this.dialogElement( "location" ).value = displayname;
  507.         this.dialogElement( "location" ).setAttribute("realname", filename);
  508.         this.dialogElement( "location" ).setAttribute("tooltiptext", displayname);
  509.  
  510.         // if mSourcePath is a local file, then let's use the pretty path name instead of an ugly
  511.         // url...
  512.         var pathString = this.mSourcePath;
  513.         try 
  514.         {
  515.           var fileURL = url.QueryInterface(Components.interfaces.nsIFileURL);
  516.           if (fileURL)
  517.           {
  518.             var fileObject = fileURL.file;
  519.             if (fileObject)
  520.             {
  521.               var parentObject = fileObject.parent;
  522.               if (parentObject)
  523.               {
  524.                 pathString = parentObject.path;
  525.               }
  526.             }
  527.           }
  528.         } catch(ex) {}
  529.  
  530.         if (pathString == this.mSourcePath)
  531.         {
  532.           // wasn't a fileURL
  533.           var tmpurl = url.clone(); // don't want to change the real url
  534.           try {
  535.             tmpurl.userPass = "";
  536.           } catch (ex) {}
  537.           pathString = tmpurl.prePath;
  538.         }
  539.  
  540.         // Set the location text, which is separate from the intro text so it can be cropped
  541.         var location = this.dialogElement( "source" );
  542.         location.value = pathString;
  543.         location.setAttribute("tooltiptext", this.mSourcePath);
  544.         
  545.         // Show the type of file. 
  546.         var type = this.dialogElement("type");
  547.         var mimeInfo = this.mLauncher.MIMEInfo;
  548.         
  549.         // 1. Try to use the pretty description of the type, if one is available.
  550.         var typeString = mimeInfo.description;
  551.         
  552.         if (typeString == "") {
  553.           // 2. If there is none, use the extension to identify the file, e.g. "ZIP file"
  554.           var primaryExtension = "";
  555.           try {
  556.             primaryExtension = mimeInfo.primaryExtension;
  557.           }
  558.           catch (ex) {
  559.           }
  560.           if (primaryExtension != "")
  561.             typeString = this.dialogElement("strings").getFormattedString("fileType", [primaryExtension.toUpperCase()]);
  562.           // 3. If we can't even do that, just give up and show the MIME type. 
  563.           else
  564.             typeString = mimeInfo.MIMEType;
  565.         }
  566.         
  567.         type.value = typeString;
  568.     },
  569.     
  570.     _blurred: false,
  571.     _delayExpired: false, 
  572.     onBlur: function(aEvent) {
  573.       this._blurred = true;
  574.       this.mDialog.document.documentElement.getButton("accept").disabled = true;
  575.     },
  576.     
  577.     onFocus: function(aEvent) {
  578.       this._blurred = false;
  579.       if (this._delayExpired) {
  580.         var script = "document.documentElement.getButton('accept').disabled = false";
  581.         this.mDialog.setTimeout(script, 250);
  582.       }
  583.     },
  584.  
  585.     // Returns true if opening the default application makes sense.
  586.     openWithDefaultOK: function() {
  587.         // The checking is different on Windows...
  588. //@line 649 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  589.         // Windows presents some special cases.
  590.         // We need to prevent use of "system default" when the file is
  591.         // executable (so the user doesn't launch nasty programs downloaded
  592.         // from the web), and, enable use of "system default" if it isn't
  593.         // executable (because we will prompt the user for the default app
  594.         // in that case).
  595.         
  596.         //  Default is Ok if the file isn't executable (and vice-versa).
  597.         return !this.mLauncher.targetFileIsExecutable;
  598. //@line 664 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  599.     },
  600.     
  601.     // Set "default" application description field.
  602.     initDefaultApp: function() {
  603.       // Use description, if we can get one.
  604.       var desc = this.mLauncher.MIMEInfo.defaultDescription;
  605.       if (desc) {
  606.         var defaultApp = this.dialogElement("strings").getFormattedString("defaultApp", [desc]);
  607.         this.dialogElement("defaultHandler").label = defaultApp;
  608.       }
  609.       else {
  610.         this.dialogElement("modeDeck").setAttribute("selectedIndex", "1");
  611.         // Hide the default handler item too, in case the user picks a 
  612.         // custom handler at a later date which triggers the menulist to show.
  613.         this.dialogElement("defaultHandler").hidden = true;
  614.       }
  615.     },
  616.  
  617.     // getPath:
  618.     getPath: function (aFile) {
  619. //@line 687 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  620.       return aFile.path;
  621. //@line 689 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  622.     },
  623.  
  624.     // initAppAndSaveToDiskValues:
  625.     initAppAndSaveToDiskValues: function() {
  626.       var modeGroup = this.dialogElement("mode");
  627.  
  628.       // We don't let users open .exe files or random binary data directly 
  629.       // from the browser at the moment because of security concerns. 
  630.       var openWithDefaultOK = this.openWithDefaultOK();
  631.       var mimeType = this.mLauncher.MIMEInfo.MIMEType;
  632.       if (this.mLauncher.targetFileIsExecutable || (
  633.           (mimeType == "application/octet-stream" ||
  634.            mimeType == "application/x-msdownload") && 
  635.            !openWithDefaultOK)) {
  636.         this.dialogElement("open").disabled = true;
  637.         var openHandler = this.dialogElement("openHandler");
  638.         openHandler.disabled = true;
  639.         openHandler.selectedItem = null;
  640.         modeGroup.selectedItem = this.dialogElement("save");
  641.         return;
  642.       }
  643.     
  644.       // Fill in helper app info, if there is any.
  645.       try {
  646.         this.chosenApp =
  647.           this.mLauncher.MIMEInfo.preferredApplicationHandler
  648.               .QueryInterface(Components.interfaces.nsILocalHandlerApp);
  649.       } catch (e) {
  650.         this.chosenApp = null;
  651.       }
  652.       // Initialize "default application" field.
  653.       this.initDefaultApp();
  654.  
  655.       var otherHandler = this.dialogElement("otherHandler");
  656.               
  657.       // Fill application name textbox.
  658.       if (this.chosenApp && this.chosenApp.executable && 
  659.           this.chosenApp.executable.path) {
  660.         otherHandler.setAttribute("path",
  661.                                   this.getPath(this.chosenApp.executable));
  662.  
  663. //@line 734 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  664.         otherHandler.label = this.chosenApp.executable.leafName;
  665. //@line 736 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  666.         otherHandler.hidden = false;
  667.       }
  668.  
  669.       var useDefault = this.dialogElement("useSystemDefault");
  670.       var openHandler = this.dialogElement("openHandler");
  671.       openHandler.selectedIndex = 0;
  672.  
  673.       if (this.mLauncher.MIMEInfo.preferredAction == this.nsIMIMEInfo.useSystemDefault) {
  674.         // Open (using system default).
  675.         modeGroup.selectedItem = this.dialogElement("open");
  676.       } else if (this.mLauncher.MIMEInfo.preferredAction == this.nsIMIMEInfo.useHelperApp) {
  677.         // Open with given helper app.
  678.         modeGroup.selectedItem = this.dialogElement("open");
  679.         openHandler.selectedIndex = 1;
  680.       } else {
  681.         // Save to disk.
  682.         modeGroup.selectedItem = this.dialogElement("save");
  683.       }
  684.       
  685.       // If we don't have a "default app" then disable that choice.
  686.       if (!openWithDefaultOK) {
  687.         var useDefault = this.dialogElement("defaultHandler");
  688.         var isSelected = useDefault.selected;
  689.         
  690.         // Disable that choice.
  691.         useDefault.hidden = true;
  692.         // If that's the default, then switch to "save to disk."
  693.         if (isSelected) {
  694.           openHandler.selectedIndex = 1;
  695.           modeGroup.selectedItem = this.dialogElement("save");
  696.         }
  697.       }
  698.       
  699.       otherHandler.nextSibling.hidden = otherHandler.nextSibling.nextSibling.hidden = false;
  700.       this.updateOKButton();
  701.     },
  702.  
  703.     // Returns the user-selected application
  704.     helperAppChoice: function() {
  705.       return this.chosenApp;
  706.     },
  707.     
  708.     get saveToDisk() {
  709.       return this.dialogElement("save").selected;
  710.     },
  711.     
  712.     get useOtherHandler() {
  713.       return this.dialogElement("open").selected && this.dialogElement("openHandler").selectedIndex == 1;
  714.     },
  715.     
  716.     get useSystemDefault() {
  717.       return this.dialogElement("open").selected && this.dialogElement("openHandler").selectedIndex == 0;
  718.     },
  719.     
  720.     toggleRememberChoice: function (aCheckbox) {
  721.         this.dialogElement("settingsChange").hidden = !aCheckbox.checked;
  722.         this.mDialog.sizeToContent();
  723.     },
  724.     
  725.     openHandlerCommand: function () {
  726.       var openHandler = this.dialogElement("openHandler");
  727.       if (openHandler.selectedItem.id == "choose")
  728.         this.chooseApp();
  729.       else
  730.         openHandler.setAttribute("lastSelectedItemID", openHandler.selectedItem.id);
  731.     },
  732.  
  733.     updateOKButton: function() {
  734.       var ok = false;
  735.       if (this.dialogElement("save").selected) {
  736.         // This is always OK.
  737.         ok = true;
  738.       } 
  739.       else if (this.dialogElement("open").selected) {
  740.         switch (this.dialogElement("openHandler").selectedIndex) {
  741.         case 0:
  742.           // No app need be specified in this case.
  743.           ok = true;
  744.           break;
  745.         case 1:
  746.           // only enable the OK button if we have a default app to use or if 
  747.           // the user chose an app....
  748.           ok = this.chosenApp || /\S/.test(this.dialogElement("otherHandler").getAttribute("path")); 
  749.         break;
  750.         }
  751.       }
  752.  
  753.       // Enable Ok button if ok to press.
  754.       this.mDialog.document.documentElement.getButton("accept").disabled = !ok;
  755.     },
  756.     
  757.     // Returns true iff the user-specified helper app has been modified.
  758.     appChanged: function() {
  759.       return this.helperAppChoice() != this.mLauncher.MIMEInfo.preferredApplicationHandler;
  760.     },
  761.  
  762.     updateMIMEInfo: function() {
  763.       var needUpdate = false;
  764.       // If current selection differs from what's in the mime info object,
  765.       // then we need to update.
  766.       if (this.saveToDisk) {
  767.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.saveToDisk;
  768.         if (needUpdate)
  769.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.saveToDisk;
  770.       } 
  771.       else if (this.useSystemDefault) {
  772.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.useSystemDefault;
  773.         if (needUpdate)
  774.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.useSystemDefault;
  775.       } 
  776.       else {
  777.         // For "open with", we need to check both preferred action and whether the user chose
  778.         // a new app.
  779.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.useHelperApp || this.appChanged();
  780.         if (needUpdate) {
  781.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.useHelperApp;
  782.           // App may have changed - Update application
  783.           var app = this.helperAppChoice();
  784.           this.mLauncher.MIMEInfo.preferredApplicationHandler = app;
  785.         }
  786.       }
  787.       // We will also need to update if the "always ask" flag has changed.
  788.       needUpdate = needUpdate || this.mLauncher.MIMEInfo.alwaysAskBeforeHandling != (!this.dialogElement("rememberChoice").checked);
  789.  
  790.       // One last special case: If the input "always ask" flag was false, then we always
  791.       // update.  In that case we are displaying the helper app dialog for the first
  792.       // time for this mime type and we need to store the user's action in the mimeTypes.rdf
  793.       // data source (whether that action has changed or not; if it didn't change, then we need
  794.       // to store the "always ask" flag so the helper app dialog will or won't display
  795.       // next time, per the user's selection).
  796.       needUpdate = needUpdate || !this.mLauncher.MIMEInfo.alwaysAskBeforeHandling;
  797.  
  798.       // Make sure mime info has updated setting for the "always ask" flag.
  799.       this.mLauncher.MIMEInfo.alwaysAskBeforeHandling = !this.dialogElement("rememberChoice").checked;
  800.  
  801.       return needUpdate;        
  802.     },
  803.     
  804.     // See if the user changed things, and if so, update the
  805.     // mimeTypes.rdf entry for this mime type.
  806.     updateHelperAppPref: function() {
  807.       var ha = new this.mDialog.HelperApps();
  808.       ha.updateTypeInfo(this.mLauncher.MIMEInfo);
  809.       ha.destroy();
  810.     },
  811.     
  812.     // onOK:
  813.     onOK: function() {
  814.       // Verify typed app path, if necessary.
  815.       if (this.useOtherHandler) {
  816.         var helperApp = this.helperAppChoice();
  817.         if (!helperApp || !helperApp.executable ||
  818.             !helperApp.executable.exists()) {
  819.           // Show alert and try again.        
  820.           var bundle = this.dialogElement("strings");                    
  821.           var msg = bundle.getFormattedString("badApp", [this.dialogElement("otherHandler").path]);
  822.           var svc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  823.           svc.alert(this.mDialog, bundle.getString("badApp.title"), msg);
  824.  
  825.           // Disable the OK button.
  826.           this.mDialog.document.documentElement.getButton("accept").disabled = true;
  827.           this.dialogElement("mode").focus();          
  828.  
  829.           // Clear chosen application.
  830.           this.chosenApp = null;
  831.  
  832.           // Leave dialog up.
  833.           return false;
  834.         }
  835.       }
  836.         
  837.       // Remove our web progress listener (a progress dialog will be
  838.       // taking over).
  839.       this.mLauncher.setWebProgressListener(null);
  840.  
  841.       // saveToDisk and launchWithApplication can return errors in 
  842.       // certain circumstances (e.g. The user clicks cancel in the
  843.       // "Save to Disk" dialog. In those cases, we don't want to
  844.       // update the helper application preferences in the RDF file.
  845.       try {
  846.         var needUpdate = this.updateMIMEInfo();
  847.         
  848.         if (this.dialogElement("save").selected) {
  849.           // If we're using a default download location, create a path
  850.           // for the file to be saved to to pass to |saveToDisk| - otherwise
  851.           // we must ask the user to pick a save name.
  852.  
  853. //@line 937 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  854.  
  855.           // see @notify
  856.           // we cannot use opener's setTimeout, see bug 420405
  857.           this._saveToDiskTimer = Components.classes["@mozilla.org/timer;1"]
  858.                                             .createInstance(nsITimer);
  859.           this._saveToDiskTimer.initWithCallback(this, 0,
  860.                                                  nsITimer.TYPE_ONE_SHOT);
  861.         }
  862.         else
  863.           this.mLauncher.launchWithApplication(null, false);
  864.  
  865.         // Update user pref for this mime type (if necessary). We do not
  866.         // store anything in the mime type preferences for the ambiguous
  867.         // type application/octet-stream. We do NOT do this for 
  868.         // application/x-msdownload since we want users to be able to 
  869.         // autodownload these to disk. 
  870.         if (needUpdate && this.mLauncher.MIMEInfo.MIMEType != "application/octet-stream")
  871.           this.updateHelperAppPref();
  872.       } catch(e) { }
  873.  
  874.       // Unhook dialog from this object.
  875.       this.mDialog.dialog = null;
  876.  
  877.       // Close up dialog by returning true.
  878.       return true;
  879.     },
  880.  
  881.     // onCancel:
  882.     onCancel: function() {
  883.       // Remove our web progress listener.
  884.       this.mLauncher.setWebProgressListener(null);
  885.  
  886.       // Cancel app launcher.
  887.       try {
  888.         const NS_BINDING_ABORTED = 0x804b0002;
  889.         this.mLauncher.cancel(NS_BINDING_ABORTED);
  890.       } catch(exception) {
  891.       }
  892.  
  893.       // Unhook dialog from this object.
  894.       this.mDialog.dialog = null;
  895.  
  896.       // Close up dialog by returning true.
  897.       return true;
  898.     },
  899.  
  900.     // dialogElement:  Convenience. 
  901.     dialogElement: function(id) {
  902.       return this.mDialog.document.getElementById(id);
  903.     },
  904.  
  905.     // Retrieve the pretty description from the file
  906.     getFileDisplayName: function getFileDisplayName(file)
  907.     { 
  908. //@line 992 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  909.         if (file instanceof Components.interfaces.nsILocalFileWin) {
  910.           try {
  911.             return file.getVersionInfoField("FileDescription");
  912.           } catch (ex) {
  913.           }
  914.         }
  915. //@line 999 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  916.         return file.leafName;
  917.     },
  918.  
  919.     // chooseApp:  Open file picker and prompt user for application.
  920.     chooseApp: function() {
  921. //@line 1005 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  922.     // Protect against the lack of an extension    
  923.     var fileExtension = "";
  924.     try {
  925.         fileExtension = this.mLauncher.MIMEInfo.primaryExtension;
  926.     } catch(ex) {
  927.     }
  928.  
  929.     // Try to use the pretty description of the type, if one is available.
  930.     var typeString = this.mLauncher.MIMEInfo.description;
  931.  
  932.     if (!typeString) {
  933.       // If there is none, use the extension to 
  934.       // identify the file, e.g. "ZIP file"
  935.       if (fileExtension) {
  936.         typeString =
  937.           this.dialogElement("strings").
  938.           getFormattedString("fileType", [fileExtension.toUpperCase()]);
  939.       } else {
  940.         // If we can't even do that, just give up and show the MIME type.
  941.         typeString = this.mLauncher.MIMEInfo.MIMEType;
  942.       }
  943.     }
  944.  
  945.     var params = {};
  946.     params.title = 
  947.       this.dialogElement("strings").getString("chooseAppFilePickerTitle");
  948.     params.description = typeString;
  949.     params.filename    = this.mLauncher.suggestedFileName;
  950.     params.mimeInfo    = this.mLauncher.MIMEInfo;
  951.     params.handlerApp  = null;
  952.  
  953.     this.mDialog.openDialog("chrome://global/content/appPicker.xul", null,
  954.                             "chrome,modal,centerscreen,titlebar,dialog=yes",
  955.                             params);
  956.  
  957.     if (params.handlerApp &&
  958.         params.handlerApp.executable &&
  959.         params.handlerApp.executable.isFile()) {
  960.         // Show the "handler" menulist since we have a (user-specified) 
  961.         // application now.
  962.         this.dialogElement("modeDeck").setAttribute("selectedIndex", "0");
  963.  
  964.         // Remember the file they chose to run.
  965.         this.chosenApp = params.handlerApp;
  966.  
  967.         // Update dialog
  968.         var otherHandler = this.dialogElement("otherHandler");
  969.         otherHandler.removeAttribute("hidden");
  970.         otherHandler.setAttribute("path",
  971.           this.getPath(this.chosenApp.executable));
  972.         otherHandler.label = 
  973.           this.getFileDisplayName(this.chosenApp.executable);
  974.         this.dialogElement("openHandler").selectedIndex = 1;
  975.         this.dialogElement("openHandler").setAttribute("lastSelectedItemID",
  976.           "otherHandler");
  977.         this.dialogElement("mode").selectedItem = this.dialogElement("open");
  978.     } else {
  979.         var openHandler = this.dialogElement("openHandler");
  980.         var lastSelectedID = openHandler.getAttribute("lastSelectedItemID");
  981.         if (!lastSelectedID)
  982.             lastSelectedID = "defaultHandler";
  983.         openHandler.selectedItem = this.dialogElement(lastSelectedID);
  984.     }
  985.  
  986. //@line 1115 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\downloads\src\nsHelperAppDlg.js.in"
  987.     },
  988.  
  989.     // Turn this on to get debugging messages.
  990.     debug: false,
  991.  
  992.     // Dump text (if debug is on).
  993.     dump: function( text ) {
  994.         if ( this.debug ) {
  995.             dump( text ); 
  996.         }
  997.     },
  998.  
  999.     // dumpInfo:
  1000.     doDebug: function() {
  1001.         const nsIProgressDialog = Components.interfaces.nsIProgressDialog;
  1002.         // Open new progress dialog.
  1003.         var progress = Components.classes[ "@mozilla.org/progressdialog;1" ]
  1004.                          .createInstance( nsIProgressDialog );
  1005.         // Show it.
  1006.         progress.open( this.mDialog );
  1007.     },
  1008.  
  1009.     // dumpObj:
  1010.     dumpObj: function( spec ) {
  1011.          var val = "<undefined>";
  1012.          try {
  1013.              val = eval( "this."+spec ).toString();
  1014.          } catch( exception ) {
  1015.          }
  1016.          this.dump( spec + "=" + val + "\n" );
  1017.     },
  1018.  
  1019.     // dumpObjectProperties
  1020.     dumpObjectProperties: function( desc, obj ) {
  1021.          for( prop in obj ) {
  1022.              this.dump( desc + "." + prop + "=" );
  1023.              var val = "<undefined>";
  1024.              try {
  1025.                  val = obj[ prop ];
  1026.              } catch ( exception ) {
  1027.              }
  1028.              this.dump( val + "\n" );
  1029.          }
  1030.     }
  1031. }
  1032.  
  1033. // This Component's module implementation.  All the code below is used to get this
  1034. // component registered and accessible via XPCOM.
  1035. var module = {
  1036.     firstTime: true,
  1037.  
  1038.     // registerSelf: Register this component.
  1039.     registerSelf: function (compMgr, fileSpec, location, type) {
  1040.         if (this.firstTime) {
  1041.             this.firstTime = false;
  1042.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  1043.         }
  1044.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  1045.  
  1046.         compMgr.registerFactoryLocation( this.cid,
  1047.                                          "Unknown Content Type Dialog",
  1048.                                          this.contractId,
  1049.                                          fileSpec,
  1050.                                          location,
  1051.                                          type );
  1052.     },
  1053.  
  1054.     // getClassObject: Return this component's factory object.
  1055.     getClassObject: function (compMgr, cid, iid) {
  1056.         if (!cid.equals(this.cid)) {
  1057.             throw Components.results.NS_ERROR_NO_INTERFACE;
  1058.         }
  1059.  
  1060.         if (!iid.equals(Components.interfaces.nsIFactory)) {
  1061.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1062.         }
  1063.  
  1064.         return this.factory;
  1065.     },
  1066.  
  1067.     /* CID for this class */
  1068.     cid: Components.ID("{F68578EB-6EC2-4169-AE19-8C6243F0ABE1}"),
  1069.  
  1070.     /* Contract ID for this class */
  1071.     contractId: "@mozilla.org/helperapplauncherdialog;1",
  1072.  
  1073.     /* factory object */
  1074.     factory: {
  1075.         // createInstance: Return a new nsProgressDialog object.
  1076.         createInstance: function (outer, iid) {
  1077.             if (outer != null)
  1078.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  1079.  
  1080.             return (new nsUnknownContentTypeDialog()).QueryInterface(iid);
  1081.         }
  1082.     },
  1083.  
  1084.     // canUnload: n/a (returns true)
  1085.     canUnload: function(compMgr) {
  1086.         return true;
  1087.     }
  1088. };
  1089.  
  1090. // NSGetModule: Return the nsIModule object.
  1091. function NSGetModule(compMgr, fileSpec) {
  1092.     return module;
  1093. }
  1094.