home *** CD-ROM | disk | FTP | other *** search
/ PC go! 2008 October / PCgo 2008-10 (DVD).iso / interface / contents / vollversionen_6617 / 21733 / files / xulrunner / components / nsHelperAppDlg.js < prev    next >
Encoding:
Text File  |  2008-08-20  |  41.0 KB  |  1,065 lines

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