home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 December / PCWorld_2007-12_cd.bin / audio-video / songbird / Songbird_0.3_windows-i686.exe / xulrunner / components / nsHelperAppDlg.js < prev    next >
Text File  |  2007-10-26  |  41KB  |  1,049 lines

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