home *** CD-ROM | disk | FTP | other *** search
/ PC World 2006 December / PCWorld_2006-12_cd.bin / komunikace / netscape / nsb-install-8-1-2.exe / components / nsHelperAppDlg.js < prev    next >
Text File  |  2006-01-06  |  36KB  |  935 lines

  1. /*
  2. */
  3.  
  4. /* This file implements the nsIHelperAppLauncherDialog interface.
  5.  *
  6.  * The implementation consists of a JavaScript "class" named nsUnknownContentTypeDialog,
  7.  * comprised of:
  8.  *   - a JS constructor function
  9.  *   - a prototype providing all the interface methods and implementation stuff
  10.  *
  11.  * In addition, this file implements an nsIModule object that registers the
  12.  * nsUnknownContentTypeDialog component.
  13.  */
  14.  
  15.  
  16. /* ctor
  17.  */
  18. function nsUnknownContentTypeDialog() {
  19.     // Initialize data properties.
  20.     this.mLauncher = null;
  21.     this.mContext  = null;
  22.     this.mSourcePath = null;
  23.     this.chosenApp = null;
  24.     this.givenDefaultApp = false;
  25.     this.updateSelf = true;
  26.     this.mTitle    = "";
  27. }
  28.  
  29. nsUnknownContentTypeDialog.prototype = {
  30.     nsIMIMEInfo  : Components.interfaces.nsIMIMEInfo,
  31.  
  32.     // This "class" supports nsIHelperAppLauncherDialog, and nsISupports.
  33.     QueryInterface: function (iid) {
  34.         if (!iid.equals(Components.interfaces.nsIHelperAppLauncherDialog) &&
  35.             !iid.equals(Components.interfaces.nsIHelperAppLauncherDialogExtension) &&
  36.             !iid.equals(Components.interfaces.nsISupports)) {
  37.             throw Components.results.NS_ERROR_NO_INTERFACE;
  38.         }
  39.         return this;
  40.     },
  41.  
  42.     // ---------- nsIHelperAppLauncherDialog methods ----------
  43.  
  44.     // show: Open XUL dialog using window watcher.  Since the dialog is not
  45.     //       modal, it needs to be a top level window and the way to open
  46.     //       one of those is via that route).
  47.     show: function(aLauncher, aContext)  {  
  48.       this.mLauncher = aLauncher;
  49.       this.mContext  = aContext;
  50.       // Display the dialog using the Window Watcher interface.
  51.       
  52.       var ir = aContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  53.       var dwi = ir.getInterface(Components.interfaces.nsIDOMWindowInternal);
  54.       var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  55.                 .getService(Components.interfaces.nsIWindowWatcher);
  56.       this.mDialog = ww.openWindow(dwi,
  57.                                    "chrome://mozapps/content/downloads/unknownContentType.xul",
  58.                                    null,
  59.                                    "chrome,centerscreen,titlebar,dialog=yes,dependent",
  60.                                    null);
  61.       // Hook this object to the dialog.
  62.       this.mDialog.dialog = this;
  63.       
  64.       // Hook up utility functions. 
  65.       this.getSpecialFolderKey = this.mDialog.getSpecialFolderKey;
  66.       
  67.       // Watch for error notifications.
  68.       this.progressListener.helperAppDlg = this;
  69.       this.mLauncher.setWebProgressListener(this.progressListener);
  70.     },
  71.  
  72.     // promptForSaveToFile:  Display file picker dialog and return selected file.
  73.     //                       This is called by the External Helper App Service
  74.     //                       after the ucth dialog calls |saveToDisk| with a null
  75.     //                       target filename (no target, therefore user must pick).
  76.     //
  77.     //                       Alternatively, if the user has selected to have all
  78.     //                       files download to a specific location, return that
  79.     //                       location and don't ask via the dialog. 
  80.     //
  81.     // Note - this function is called without a dialog, so it cannot access any part
  82.     // of the dialog XUL as other functions on this object do. 
  83.     promptForSaveToFile: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension) {
  84.       var result = "";
  85.       
  86.       this.mLauncher = aLauncher;
  87.  
  88.       // If the user is always downloading to the same location, the default download
  89.       // folder is stored in preferences. If a value is found stored, use that 
  90.       // automatically and don't ask via a dialog. 
  91.       const kDownloadFolderPref = "browser.download.defaultFolder";
  92.       var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  93.       try {
  94.         result = prefs.getComplexValue(kDownloadFolderPref, Components.interfaces.nsILocalFile);
  95.         result = this.validateLeafName(result, aDefaultFile, aSuggestedFileExtension);
  96.       }
  97.       catch (e) { 
  98.         // If we get here, it's because we have a new profile and the user has never configured download
  99.         // options, so "browser.download.defaultFolder" is not set yet. If the default is autodownload, 
  100.         // we need to discover the default save location. 
  101.         var autodownload = prefs.getBoolPref("browser.download.useDownloadDir");
  102.         if (autodownload) {
  103.           function getSpecialFolderKey(aFolderType) 
  104.           {
  105.             return aFolderType == "Desktop" ? "DeskP" : "Pers";
  106.             return "Home";
  107.           }
  108.           
  109.           function getDownloadsFolder(aFolder)
  110.           {
  111.             var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
  112.  
  113.             var dir = fileLocator.get(getSpecialFolderKey(aFolder), Components.interfaces.nsILocalFile);
  114.             
  115.             var bundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
  116.             bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  117.  
  118.             var description = bundle.GetStringFromName("myDownloads");
  119.             if (aFolder != "Desktop")
  120.               dir.append(description);
  121.               
  122.             return dir;
  123.           }
  124.  
  125.           var defaultFolder = null;
  126.           switch (prefs.getIntPref("browser.download.folderList")) {
  127.           case 0:
  128.             defaultFolder = getDownloadsFolder("Desktop")
  129.             break;
  130.           case 1:
  131.             defaultFolder = getDownloadsFolder("Downloads");
  132.             break;
  133.           case 2:
  134.             defaultFolder = prefs.getComplexValue("browser.download.dir", Components.interfaces.nsILocalFile);
  135.             break;
  136.           }
  137.           
  138.           // While we're here, set the pref too so that we don't keep coming back into this less efficient
  139.           // code block. 
  140.           prefs.setComplexValue("browser.download.defaultFolder", Components.interfaces.nsILocalFile, defaultFolder);
  141.           
  142.           result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
  143.         }
  144.       }
  145.       
  146.       if (!result) {
  147.         // Use file picker to show dialog.
  148.         var nsIFilePicker = Components.interfaces.nsIFilePicker;
  149.         var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  150.  
  151.         var bundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
  152.         bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  153.  
  154.         var windowTitle = bundle.GetStringFromName("saveDialogTitle");
  155.         var parent = aContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowInternal);
  156.         picker.init(parent, windowTitle, nsIFilePicker.modeSave);
  157.         picker.defaultString = aDefaultFile;
  158.  
  159.         if (aSuggestedFileExtension) {
  160.           // aSuggestedFileExtension includes the period, so strip it
  161.           picker.defaultExtension = aSuggestedFileExtension.substring(1);
  162.         } 
  163.         else {
  164.           try {
  165.             picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
  166.           } 
  167.           catch (ex) { }
  168.         }
  169.  
  170.         var wildCardExtension = "*";
  171.         if (aSuggestedFileExtension) {
  172.           wildCardExtension += aSuggestedFileExtension;
  173.           picker.appendFilter(this.mLauncher.MIMEInfo.Description, wildCardExtension);
  174.         }
  175.  
  176.         picker.appendFilters( nsIFilePicker.filterAll );
  177.  
  178.         // Pull in the user's preferences and get the default download directory.
  179.         var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  180.         try {
  181.           var startDir = prefs.getComplexValue("browser.download.dir", Components.interfaces.nsILocalFile);
  182.           if (startDir.exists()) {
  183.             picker.displayDirectory = startDir;
  184.           }
  185.         } 
  186.         catch(exception) { }
  187.  
  188.         var dlgResult = picker.show();
  189.  
  190.         if (dlgResult == nsIFilePicker.returnCancel) {
  191.           // null result means user cancelled.
  192.           return null;
  193.         }
  194.  
  195.  
  196.         // Be sure to save the directory the user chose through the Save As... 
  197.         // dialog  as the new browser.download.dir
  198.         result = picker.file;
  199.  
  200.         if (result) {
  201.           var newDir = result.parent;
  202.           prefs.setComplexValue("browser.download.dir", Components.interfaces.nsILocalFile, newDir);
  203.         }
  204.       }
  205.       return result;
  206.     },
  207.     
  208.     validateLeafName: function (aLocalFile, aLeafName, aFileExt)
  209.     {
  210.       if (aLeafName == "")
  211.         aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : "");
  212.       aLocalFile.append(aLeafName);
  213.  
  214.       this.makeFileUnique(aLocalFile);
  215.  
  216.       if (aLocalFile.isExecutable() && !this.mLauncher.targetFile.isExecutable()) {
  217.         var f = aLocalFile.clone();
  218.         aLocalFile.leafName = aLocalFile.leafName + "." + this.mLauncher.MIMEInfo.primaryExtension; 
  219.  
  220.         f.remove(false);
  221.         this.makeFileUnique(aLocalFile);
  222.       }
  223.       return aLocalFile;
  224.     },
  225.  
  226.     makeFileUnique: function (aLocalFile)
  227.     {
  228.       try {
  229.         // Since we're automatically downloading, we don't get the file picker's 
  230.         // logic to check for existing files, so we need to do that here.
  231.         //
  232.         // Note - this code is identical to that in 
  233.         //   browser/base/content/contentAreaUtils.js. 
  234.         // If you are updating this code, update that code too! We can't share code
  235.         // here since this is called in a js component. 
  236.         while (aLocalFile.exists()) {
  237.           var parts = /.+-(\d+)(\..*)?$/.exec(aLocalFile.leafName);
  238.           if (parts) {
  239.             aLocalFile.leafName = aLocalFile.leafName.replace(/((\d+)\.)|((\d+)$)/,
  240.                                                               function (str, dot, dotNum, noDot, noDotNum, pos, s) {
  241.                                                                 return (parseInt(str) + 1) + (dot ? "." : "");
  242.                                                               });
  243.           }
  244.           else {
  245.             aLocalFile.leafName = aLocalFile.leafName.replace(/\.|$/, "-1$&");
  246.           }
  247.         }
  248.         aLocalFile.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);
  249.       }
  250.       catch (e) {
  251.         dump("*** exception in validateLeafName: " + e + "\n");
  252.         if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) {
  253.           aLocalFile.append("unnamed");
  254.           if (aLocalFile.exists())
  255.             aLocalFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);
  256.         }
  257.       }
  258.     },
  259.     
  260.     // ---------- nsIHelperAppLauncherDialog methods ----------
  261.     
  262.     // MERC: AAM (BLT 196088 and 196516)
  263.     // abort - allow the dialog to be aborted and closed
  264.     // This is important for spyware file download processing.  If the file
  265.     // is detected as spyware and the file is then deleted, we have to force
  266.     // this dialog to be closed.
  267.     abort : function() {
  268.       this.onCancel();
  269.       if ( this.mDialog ) {
  270.           this.mDialog.close();
  271.       }
  272.     },
  273.     
  274.     // ---------- implementation methods ----------
  275.  
  276.     // Web progress listener so we can detect errors while mLauncher is
  277.     // streaming the data to a temporary file.
  278.     progressListener: {
  279.         // Implementation properties.
  280.         NS_ERROR_FILE_SPYWARE_DETECTED : 0x80520016,
  281.         helperAppDlg: null,
  282.  
  283.         // nsIWebProgressListener methods.
  284.         // Look for error notifications and display alert to user.
  285.         onStatusChange: function( aWebProgress, aRequest, aStatus, aMessage ) {
  286.             if ( (aStatus != Components.results.NS_OK) && (aStatus != this.NS_ERROR_FILE_SPYWARE_DETECTED) ) {
  287.                 // Get prompt service.
  288.                 var prompter = Components.classes[ "@mozilla.org/embedcomp/prompt-service;1" ]
  289.                                    .getService( Components.interfaces.nsIPromptService );
  290.                 // Display error alert (using text supplied by back-end).
  291.                 prompter.alert( this.dialog, this.helperAppDlg.mTitle, aMessage );
  292.  
  293.                 // Close the dialog.
  294.                 this.helperAppDlg.onCancel();
  295.                 if ( this.helperAppDlg.mDialog ) {
  296.                     this.helperAppDlg.mDialog.close();
  297.                 }
  298.             }
  299.         },
  300.  
  301.         // Ignore onProgressChange, onStateChange, onLocationChange, and onSecurityChange notifications.
  302.         onProgressChange: function( aWebProgress,
  303.                                     aRequest,
  304.                                     aCurSelfProgress,
  305.                                     aMaxSelfProgress,
  306.                                     aCurTotalProgress,
  307.                                     aMaxTotalProgress ) {
  308.         },
  309.  
  310.         onStateChange: function( aWebProgress, aRequest, aStateFlags, aStatus ) {
  311.         },
  312.  
  313.         onLocationChange: function( aWebProgress, aRequest, aLocation ) {
  314.         },
  315.  
  316.         onSecurityChange: function( aWebProgress, aRequest, state ) {
  317.         }
  318.     },
  319.  
  320.     // initDialog:  Fill various dialog fields with initial content.
  321.     initDialog : function() {
  322.       // Put file name in window title.
  323.       var win   = this.dialogElement( "unknownContentType" );
  324.       var suggestedFileName = this.mLauncher.suggestedFileName;
  325.  
  326.       // Some URIs do not implement nsIURL, so we can't just QI.
  327.       var url   = this.mLauncher.source;
  328.       var fname = "";
  329.       this.mSourcePath = url.prePath;
  330.       try {
  331.           url = url.QueryInterface( Components.interfaces.nsIURL );
  332.           // A url, use file name from it.
  333.           fname = url.fileName;
  334.           this.mSourcePath += url.directory;
  335.       } catch (ex) {
  336.           // A generic uri, use path.
  337.           fname = url.path;
  338.           this.mSourcePath += url.path;
  339.       }
  340.  
  341.       if (suggestedFileName)
  342.         fname = suggestedFileName;
  343.       
  344.       var displayName = fname.replace(/ +/g, " ");
  345.  
  346.       this.mTitle = this.dialogElement("strings").getFormattedString("title", [displayName]);
  347.       win.setAttribute( "title", this.mTitle );
  348.  
  349.       // Put content type, filename and location into intro.
  350.       this.initIntro(url, fname, displayName);
  351.  
  352.       var iconString = "moz-icon://" + fname + "?size=16&contentType=" + this.mLauncher.MIMEInfo.MIMEType;
  353.       this.dialogElement("contentTypeImage").setAttribute("src", iconString);
  354.  
  355.       this.initAppAndSaveToDiskValues();
  356.  
  357.       // Initialize "always ask me" box. This should always be disabled
  358.       // and set to true for the ambiguous type application/octet-stream.
  359.       // We don't also check for application/x-msdownload here since we
  360.       // want users to be able to autodownload .exe files. 
  361.       var rememberChoice = this.dialogElement("rememberChoice");
  362.  
  363.       var mimeType = this.mLauncher.MIMEInfo.MIMEType;
  364.       if (mimeType == "application/octet-stream" || 
  365.           mimeType == "application/x-msdownload" ||
  366.           this.mLauncher.targetFile.isExecutable()) {
  367.         rememberChoice.checked = false;
  368.         rememberChoice.disabled = true;
  369.       }
  370.       else {
  371.         rememberChoice.checked = !this.mLauncher.MIMEInfo.alwaysAskBeforeHandling;
  372.       }
  373.       this.toggleRememberChoice(rememberChoice);
  374.  
  375.       // XXXben - menulist won't init properly, hack. 
  376.       var openHandler = this.dialogElement("openHandler");
  377.       openHandler.parentNode.removeChild(openHandler);
  378.       var openHandlerBox = this.dialogElement("openHandlerBox");
  379.       openHandlerBox.appendChild(openHandler);
  380.  
  381.       this.mDialog.setTimeout("dialog.postShowCallback()", 0);
  382.       
  383.       this.mDialog.document.documentElement.getButton("accept").disabled = true;
  384.       const nsITimer = Components.interfaces.nsITimer;
  385.       this._timer = Components.classes["@mozilla.org/timer;1"]
  386.                               .createInstance(nsITimer);
  387.       this._timer.initWithCallback(this, 250, nsITimer.TYPE_ONE_SHOT);
  388.     },
  389.     
  390.     _timer: null,
  391.     notify: function (aTimer) {
  392.       if (!this._blurred)
  393.         this.mDialog.document.documentElement.getButton('accept').disabled = false;
  394.       this._delayExpired = true;
  395.     },
  396.     
  397.     postShowCallback: function () {
  398.       this.mDialog.sizeToContent();
  399.  
  400.       // Set initial focus
  401.       this.dialogElement("mode").focus();
  402.     },
  403.  
  404.     // initIntro:
  405.     initIntro: function(url, filename, displayname) {
  406.         this.dialogElement( "location" ).value = displayname;
  407.         this.dialogElement( "location" ).setAttribute("realname", filename);
  408.         this.dialogElement( "location" ).setAttribute("tooltiptext", displayname);
  409.  
  410.         // if mSourcePath is a local file, then let's use the pretty path name instead of an ugly
  411.         // url...
  412.         var pathString = this.mSourcePath;
  413.         try 
  414.         {
  415.           var fileURL = url.QueryInterface(Components.interfaces.nsIFileURL);
  416.           if (fileURL)
  417.           {
  418.             var fileObject = fileURL.file;
  419.             if (fileObject)
  420.             {
  421.               var parentObject = fileObject.parent;
  422.               if (parentObject)
  423.               {
  424.                 pathString = parentObject.path;
  425.               }
  426.             }
  427.           }
  428.         } catch(ex) {}
  429.  
  430.         if (pathString == this.mSourcePath)
  431.         {
  432.           // wasn't a fileURL
  433.           var tmpurl = url.clone(); // don't want to change the real url
  434.           tmpurl.userPass = "";
  435.           pathString = tmpurl.prePath;
  436.         }
  437.  
  438.         // Set the location text, which is separate from the intro text so it can be cropped
  439.         var location = this.dialogElement( "source" );
  440.         location.value = pathString;
  441.         location.setAttribute("tooltiptext", this.mSourcePath);
  442.         
  443.         // Show the type of file. 
  444.         var type = this.dialogElement("type");
  445.         var mimeInfo = this.mLauncher.MIMEInfo;
  446.         
  447.         // 1. Try to use the pretty description of the type, if one is available.
  448.         var typeString = mimeInfo.Description;
  449.         
  450.         if (typeString == "") {
  451.           // 2. If there is none, use the extension to identify the file, e.g. "ZIP file"
  452.           var primaryExtension = "";
  453.           try {
  454.             primaryExtension = mimeInfo.primaryExtension;
  455.           }
  456.           catch (ex) {
  457.           }
  458.           if (primaryExtension != "")
  459.             typeString = primaryExtension.toUpperCase() + " file";
  460.           // 3. If we can't even do that, just give up and show the MIME type. 
  461.           else
  462.             typeString = mimeInfo.MIMEType;
  463.         }
  464.         
  465.         type.value = typeString;
  466.     },
  467.     
  468.     _blurred: false,
  469.     _delayExpired: false, 
  470.     onBlur: function(aEvent) {
  471.       if (aEvent.target != this.mDialog.document)
  472.         return;
  473.       this._blurred = true;
  474.       this.mDialog.document.documentElement.getButton("accept").disabled = true;
  475.     },
  476.     
  477.     onFocus: function(aEvent) {
  478.       if (aEvent.target != this.mDialog.document)
  479.         return;
  480.       this._blurred = false;
  481.       if (this._delayExpired) {
  482.         var script = "document.documentElement.getButton('accept').disabled = false";
  483.         this.mDialog.setTimeout(script, 250);
  484.       }
  485.     },
  486.  
  487.     // Returns true if opening the default application makes sense.
  488.     openWithDefaultOK: function() {
  489.         var result;
  490.  
  491.         // The checking is different on Windows...
  492.         // Windows presents some special cases.
  493.         // We need to prevent use of "system default" when the file is
  494.         // executable (so the user doesn't launch nasty programs downloaded
  495.         // from the web), and, enable use of "system default" if it isn't
  496.         // executable (because we will prompt the user for the default app
  497.         // in that case).
  498.         
  499.         // Need to get temporary file and check for executable-ness.
  500.         var tmpFile = this.mLauncher.targetFile;
  501.         
  502.         //  Default is Ok if the file isn't executable (and vice-versa).
  503.         return !tmpFile.isExecutable();
  504.     },
  505.     
  506.     // Set "default" application description field.
  507.     initDefaultApp: function() {
  508.       // Use description, if we can get one.
  509.       var desc = this.mLauncher.MIMEInfo.defaultDescription;
  510.       if (desc) {
  511.         var defaultApp = this.dialogElement("strings").getFormattedString("defaultApp", [desc]);
  512.         this.dialogElement("defaultHandler").label = defaultApp;
  513.       }
  514.       else {
  515.         this.dialogElement("modeDeck").setAttribute("selectedIndex", "1");
  516.         // Hide the default handler item too, in case the user picks a 
  517.         // custom handler at a later date which triggers the menulist to show.
  518.         this.dialogElement("defaultHandler").hidden = true;
  519.       }
  520.     },
  521.  
  522.     // getPath:
  523.     getPath: function (aFile) {
  524.       return aFile.path;
  525.     },
  526.  
  527.     // initAppAndSaveToDiskValues:
  528.     initAppAndSaveToDiskValues: function() {
  529.       var modeGroup = this.dialogElement("mode");
  530.  
  531.       // We don't let users open .exe files or random binary data directly 
  532.       // from the browser at the moment because of security concerns. 
  533.       var openWithDefaultOK = this.openWithDefaultOK();
  534.       var mimeType = this.mLauncher.MIMEInfo.MIMEType;
  535.       if (this.mLauncher.targetFile.isExecutable() || (
  536.           (mimeType == "application/octet-stream" ||
  537.            mimeType == "application/x-msdownload") && 
  538.            !openWithDefaultOK)) {
  539.         this.dialogElement("open").disabled = true;
  540.         var openHandler = this.dialogElement("openHandler");
  541.         openHandler.disabled = true;
  542.         openHandler.label = "";
  543.         modeGroup.selectedItem = this.dialogElement("save");
  544.         return;
  545.       }
  546.     
  547.       // Fill in helper app info, if there is any.
  548.       this.chosenApp = this.mLauncher.MIMEInfo.preferredApplicationHandler;
  549.       // Initialize "default application" field.
  550.       this.initDefaultApp();
  551.  
  552.       var otherHandler = this.dialogElement("otherHandler");
  553.               
  554.       // Fill application name textbox.
  555.       if (this.chosenApp && this.chosenApp.path) {
  556.         otherHandler.setAttribute("path", this.getPath(this.chosenApp));
  557.         otherHandler.label = this.chosenApp.leafName;
  558.         otherHandler.hidden = false;
  559.       }
  560.  
  561.       var useDefault = this.dialogElement("useSystemDefault");
  562.       var openHandler = this.dialogElement("openHandler");
  563.       openHandler.selectedIndex = 0;
  564.  
  565.       if (this.mLauncher.MIMEInfo.preferredAction == this.nsIMIMEInfo.useSystemDefault) {
  566.         // Open (using system default).
  567.         modeGroup.selectedItem = this.dialogElement("open");
  568.       } else if (this.mLauncher.MIMEInfo.preferredAction == this.nsIMIMEInfo.useHelperApp) {
  569.         // Open with given helper app.
  570.         modeGroup.selectedItem = this.dialogElement("open");
  571.         openHandler.selectedIndex = 1;
  572.       } else {
  573.         // Save to disk.
  574.         modeGroup.selectedItem = this.dialogElement("save");
  575.       }
  576.       
  577.       // If we don't have a "default app" then disable that choice.
  578.       if (!openWithDefaultOK) {
  579.         var useDefault = this.dialogElement("defaultHandler");
  580.         var isSelected = useDefault.selected;
  581.         
  582.         // Disable that choice.
  583.         useDefault.hidden = true;
  584.         // If that's the default, then switch to "save to disk."
  585.         if (isSelected) {
  586.           openHandler.selectedIndex = 1;
  587.           modeGroup.selectedItem = this.dialogElement("save");
  588.         }
  589.       }
  590.       
  591.       // otherHandler is always disabled on Mac
  592.       otherHandler.nextSibling.hidden = otherHandler.nextSibling.nextSibling.hidden = false;
  593.       this.updateOKButton();
  594.     },
  595.  
  596.     // Returns the user-selected application
  597.     helperAppChoice: function() {
  598.       return this.chosenApp;
  599.     },
  600.     
  601.     get saveToDisk() {
  602.       return this.dialogElement("save").selected;
  603.     },
  604.     
  605.     get useOtherHandler() {
  606.       return this.dialogElement("open").selected && this.dialogElement("openHandler").selectedIndex == 1;
  607.     },
  608.     
  609.     get useSystemDefault() {
  610.       return this.dialogElement("open").selected && this.dialogElement("openHandler").selectedIndex == 0;
  611.     },
  612.     
  613.     toggleRememberChoice: function (aCheckbox) {
  614.         this.dialogElement("settingsChange").hidden = !aCheckbox.checked;
  615.         this.mDialog.sizeToContent();
  616.     },
  617.     
  618.     openHandlerCommand: function () {
  619.       var openHandler = this.dialogElement("openHandler");
  620.       if (openHandler.selectedItem.id == "choose")
  621.         this.chooseApp();
  622.       else
  623.         openHandler.setAttribute("lastSelectedItemID", openHandler.selectedItem.id);
  624.     },
  625.  
  626.     updateOKButton: function() {
  627.       var ok = false;
  628.       if (this.dialogElement("save").selected) {
  629.         // This is always OK.
  630.         ok = true;
  631.       } 
  632.       else if (this.dialogElement("open").selected) {
  633.         switch (this.dialogElement("openHandler").selectedIndex) {
  634.         case 0:
  635.           // No app need be specified in this case.
  636.           ok = true;
  637.           break;
  638.         case 1:
  639.           // only enable the OK button if we have a default app to use or if 
  640.           // the user chose an app....
  641.           ok = this.chosenApp || /\S/.test(this.dialogElement("otherHandler").getAttribute("path")); 
  642.         break;
  643.         }
  644.       }
  645.  
  646.       // Enable Ok button if ok to press.
  647.       this.mDialog.document.documentElement.getButton("accept").disabled = !ok;
  648.     },
  649.     
  650.     // Returns true iff the user-specified helper app has been modified.
  651.     appChanged: function() {
  652.       return this.helperAppChoice() != this.mLauncher.MIMEInfo.preferredApplicationHandler;
  653.     },
  654.  
  655.     updateMIMEInfo: function() {
  656.       var needUpdate = false;
  657.       // If current selection differs from what's in the mime info object,
  658.       // then we need to update.
  659.       if (this.saveToDisk) {
  660.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.saveToDisk;
  661.         if (needUpdate)
  662.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.saveToDisk;
  663.       } 
  664.       else if (this.useSystemDefault) {
  665.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.useSystemDefault;
  666.         if (needUpdate)
  667.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.useSystemDefault;
  668.       } 
  669.       else {
  670.         // For "open with", we need to check both preferred action and whether the user chose
  671.         // a new app.
  672.         needUpdate = this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.useHelperApp || this.appChanged();
  673.         if (needUpdate) {
  674.           this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.useHelperApp;
  675.           // App may have changed - Update application and description
  676.           var app = this.helperAppChoice();
  677.           this.mLauncher.MIMEInfo.preferredApplicationHandler = app;
  678.           this.mLauncher.MIMEInfo.applicationDescription = "";
  679.         }
  680.       }
  681.       // We will also need to update if the "always ask" flag has changed.
  682.       needUpdate = needUpdate || this.mLauncher.MIMEInfo.alwaysAskBeforeHandling != (!this.dialogElement("rememberChoice").checked);
  683.  
  684.       // One last special case: If the input "always ask" flag was false, then we always
  685.       // update.  In that case we are displaying the helper app dialog for the first
  686.       // time for this mime type and we need to store the user's action in the mimeTypes.rdf
  687.       // data source (whether that action has changed or not; if it didn't change, then we need
  688.       // to store the "always ask" flag so the helper app dialog will or won't display
  689.       // next time, per the user's selection).
  690.       needUpdate = needUpdate || !this.mLauncher.MIMEInfo.alwaysAskBeforeHandling;
  691.  
  692.       // Make sure mime info has updated setting for the "always ask" flag.
  693.       this.mLauncher.MIMEInfo.alwaysAskBeforeHandling = !this.dialogElement("rememberChoice").checked;
  694.  
  695.       return needUpdate;        
  696.     },
  697.     
  698.     // See if the user changed things, and if so, update the
  699.     // mimeTypes.rdf entry for this mime type.
  700.     updateHelperAppPref: function() {
  701.       var ha = new this.mDialog.HelperApps();
  702.       ha.updateTypeInfo(this.mLauncher.MIMEInfo);
  703.     },
  704.     
  705.     // onOK:
  706.     onOK: function() {
  707.       // Verify typed app path, if necessary.
  708.       if (this.useOtherHandler) {
  709.         var helperApp = this.helperAppChoice();
  710.         if (!helperApp || !helperApp.exists()) {
  711.           // Show alert and try again.        
  712.           var bundle = this.dialogElement("strings");                    
  713.           var msg = bundle.getFormattedString("badApp", [this.dialogElement("otherHandler").path]);
  714.           var svc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  715.           svc.alert(this.mDialog, bundle.getString("badApp.title"), msg);
  716.  
  717.           // Disable the OK button.
  718.           this.mDialog.document.documentElement.getButton("accept").disabled = true;
  719.           this.dialogElement("mode").focus();          
  720.  
  721.           // Clear chosen application.
  722.           this.chosenApp = null;
  723.  
  724.           // Leave dialog up.
  725.           return false;
  726.         }
  727.       }
  728.         
  729.       // Remove our web progress listener (a progress dialog will be
  730.       // taking over).
  731.       this.mLauncher.setWebProgressListener(null);
  732.       
  733.       // saveToDisk and launchWithApplication can return errors in 
  734.       // certain circumstances (e.g. The user clicks cancel in the
  735.       // "Save to Disk" dialog. In those cases, we don't want to
  736.       // update the helper application preferences in the RDF file.
  737.       try {
  738.         var needUpdate = this.updateMIMEInfo();
  739.         
  740.         if (this.dialogElement("save").selected) {
  741.           // If we're using a default download location, create a path
  742.           // for the file to be saved to to pass to |saveToDisk| - otherwise
  743.           // we must ask the user to pick a save name.
  744.  
  745.           this.mLauncher.saveToDisk(null, false);
  746.         }
  747.         else
  748.         {
  749.             //Merc BH: fix for blt#202269: When opening a Word file through the browser, it opens beneath the browser instead of giving it top focus
  750.                     //this changes the app lauch context to the parent window to prevent the parent from taking focus when the dialog
  751.                     //closes after the app launches.  This isn't just for Word files but for any file opened in an external app
  752.             this.mDialog.opener.setTimeout(function(lnchr){lnchr.launchWithApplication(null, false);},0,this.mLauncher);
  753.         }
  754.  
  755.         // Update user pref for this mime type (if necessary). We do not
  756.         // store anything in the mime type preferences for the ambiguous
  757.         // type application/octet-stream. We do NOT do this for 
  758.         // application/x-msdownload since we want users to be able to 
  759.         // autodownload these to disk. 
  760.         if (needUpdate && this.mLauncher.MIMEInfo.MIMEType != "application/octet-stream")
  761.           this.updateHelperAppPref();
  762.       } catch(e) { }
  763.  
  764.       // Unhook dialog from this object.
  765.       this.mDialog.dialog = null;
  766.  
  767.       // Close up dialog by returning true.
  768.       return true;
  769.     },
  770.  
  771.     // onCancel:
  772.     onCancel: function() {
  773.       // Remove our web progress listener.
  774.       this.mLauncher.setWebProgressListener(null);
  775.  
  776.       // Cancel app launcher.
  777.       try {
  778.         this.mLauncher.Cancel();
  779.       } catch(exception) {
  780.       }
  781.  
  782.       // Unhook dialog from this object.
  783.       this.mDialog.dialog = null;
  784.  
  785.       // Close up dialog by returning true.
  786.       return true;
  787.     },
  788.  
  789.     // dialogElement:  Convenience. 
  790.     dialogElement: function(id) {
  791.       return this.mDialog.document.getElementById(id);
  792.     },
  793.  
  794.     // chooseApp:  Open file picker and prompt user for application.
  795.     chooseApp: function() {
  796.       var nsIFilePicker = Components.interfaces.nsIFilePicker;
  797.       var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  798.       fp.init(this.mDialog,
  799.               this.dialogElement("strings").getString("chooseAppFilePickerTitle"),
  800.               nsIFilePicker.modeOpen);
  801.  
  802.       fp.appendFilters(nsIFilePicker.filterApps);
  803.  
  804.       if (fp.show() == nsIFilePicker.returnOK && fp.file) {
  805.         // Show the "handler" menulist since we have a (user-specified) 
  806.         // application now.
  807.         this.dialogElement("modeDeck").setAttribute("selectedIndex", "0");
  808.         
  809.         // Remember the file they chose to run.
  810.         this.chosenApp = fp.file;
  811.         // Update dialog.
  812.         var otherHandler = this.dialogElement("otherHandler");
  813.         otherHandler.removeAttribute("hidden");
  814.         otherHandler.setAttribute("path", this.getPath(this.chosenApp));
  815.         otherHandler.label = this.chosenApp.leafName;
  816.         this.dialogElement("openHandler").selectedIndex = 1;
  817.         this.dialogElement("openHandler").setAttribute("lastSelectedItemID", "otherHandler");
  818.         
  819.         this.dialogElement("mode").selectedItem = this.dialogElement("open");
  820.       }
  821.       else {
  822.         var openHandler = this.dialogElement("openHandler");
  823.         var lastSelectedID = openHandler.getAttribute("lastSelectedItemID");
  824.         if (!lastSelectedID)
  825.           lastSelectedID = "defaultHandler";
  826.         openHandler.selectedItem = this.dialogElement(lastSelectedID);
  827.       }
  828.     },
  829.  
  830.     // Turn this on to get debugging messages.
  831.     debug: false,
  832.  
  833.     // Dump text (if debug is on).
  834.     dump: function( text ) {
  835.         if ( this.debug ) {
  836.             dump( text ); 
  837.         }
  838.     },
  839.  
  840.     // dumpInfo:
  841.     doDebug: function() {
  842.         const nsIProgressDialog = Components.interfaces.nsIProgressDialog;
  843.         // Open new progress dialog.
  844.         var progress = Components.classes[ "@mozilla.org/progressdialog;1" ]
  845.                          .createInstance( nsIProgressDialog );
  846.         // Show it.
  847.         progress.open( this.mDialog );
  848.     },
  849.  
  850.     // dumpObj:
  851.     dumpObj: function( spec ) {
  852.          var val = "<undefined>";
  853.          try {
  854.              val = eval( "this."+spec ).toString();
  855.          } catch( exception ) {
  856.          }
  857.          this.dump( spec + "=" + val + "\n" );
  858.     },
  859.  
  860.     // dumpObjectProperties
  861.     dumpObjectProperties: function( desc, obj ) {
  862.          for( prop in obj ) {
  863.              this.dump( desc + "." + prop + "=" );
  864.              var val = "<undefined>";
  865.              try {
  866.                  val = obj[ prop ];
  867.              } catch ( exception ) {
  868.              }
  869.              this.dump( val + "\n" );
  870.          }
  871.     }
  872. }
  873.  
  874. // This Component's module implementation.  All the code below is used to get this
  875. // component registered and accessible via XPCOM.
  876. var module = {
  877.     firstTime: true,
  878.  
  879.     // registerSelf: Register this component.
  880.     registerSelf: function (compMgr, fileSpec, location, type) {
  881.         if (this.firstTime) {
  882.             this.firstTime = false;
  883.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  884.         }
  885.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  886.  
  887.         compMgr.registerFactoryLocation( this.cid,
  888.                                          "Unknown Content Type Dialog",
  889.                                          this.contractId,
  890.                                          fileSpec,
  891.                                          location,
  892.                                          type );
  893.     },
  894.  
  895.     // getClassObject: Return this component's factory object.
  896.     getClassObject: function (compMgr, cid, iid) {
  897.         if (!cid.equals(this.cid)) {
  898.             throw Components.results.NS_ERROR_NO_INTERFACE;
  899.         }
  900.  
  901.         if (!iid.equals(Components.interfaces.nsIFactory)) {
  902.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  903.         }
  904.  
  905.         return this.factory;
  906.     },
  907.  
  908.     /* CID for this class */
  909.     cid: Components.ID("{F68578EB-6EC2-4169-AE19-8C6243F0ABE1}"),
  910.  
  911.     /* Contract ID for this class */
  912.     contractId: "@mozilla.org/helperapplauncherdialog;1",
  913.  
  914.     /* factory object */
  915.     factory: {
  916.         // createInstance: Return a new nsProgressDialog object.
  917.         createInstance: function (outer, iid) {
  918.             if (outer != null)
  919.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  920.  
  921.             return (new nsUnknownContentTypeDialog()).QueryInterface(iid);
  922.         }
  923.     },
  924.  
  925.     // canUnload: n/a (returns true)
  926.     canUnload: function(compMgr) {
  927.         return true;
  928.     }
  929. };
  930.  
  931. // NSGetModule: Return the nsIModule object.
  932. function NSGetModule(compMgr, fileSpec) {
  933.     return module;
  934. }
  935.