home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 December / Chip Aralık 2001.iso / prog / internet / netscape / browser.xpi / bin / components / nsHelperAppDlg.js < prev    next >
Encoding:
JavaScript  |  2001-09-01  |  23.2 KB  |  624 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.1 (the "License"); you may not use this file except in
  5.  * compliance with the License.  You may obtain a copy of the License at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10.  * for the specific language governing rights and limitations under the
  11.  * License.
  12.  *
  13.  * The Original Code is the Mozilla browser.
  14.  *
  15.  * The Initial Developer of the Original Code is Netscape Communications 
  16.  * Corporation.  Portions created by Netscape are 
  17.  * Copyright (C) 2001 Netscape Communications Corporation.  All Rights
  18.  * Reserved.
  19.  *
  20.  * Contributors:
  21.  *  Bill Law    <law@netscape.com>
  22.  *  Scott MacGregor <mscott@netscape.com>
  23.  */
  24.  
  25. /* This file implements the nsIHelperAppLauncherDialog interface.
  26.  *
  27.  * The implementation consists of a JavaScript "class" named nsHelperAppDialog,
  28.  * comprised of:
  29.  *   - a JS constructor function
  30.  *   - a prototype providing all the interface methods and implementation stuff
  31.  *
  32.  * In addition, this file implements an nsIModule object that registers the
  33.  * nsHelperAppDialog component.
  34.  */
  35.  
  36.  
  37. /* ctor
  38.  */
  39. function nsHelperAppDialog() {
  40.     // Initialize data properties.
  41.     this.mLauncher = null;
  42.     this.mContext  = null;
  43.     this.mSourcePath = null;
  44.     this.choseApp  = false;
  45.     this.chosenApp = null;
  46.     this.givenDefaultApp = false;
  47.     this.strings   = new Array;
  48.     this.elements  = new Array;
  49. }
  50.  
  51. nsHelperAppDialog.prototype = {
  52.     // Turn this on to get debugging messages.
  53.     debug: false,
  54.  
  55.     nsIMIMEInfo  : Components.interfaces.nsIMIMEInfo,
  56.  
  57.     // Dump text (if debug is on).
  58.     dump: function( text ) {
  59.         if ( this.debug ) {
  60.             dump( text ); 
  61.         }
  62.     },
  63.  
  64.     // This "class" supports nsIHelperAppLauncherDialog, and nsISupports.
  65.     QueryInterface: function (iid) {
  66.         if (!iid.equals(Components.interfaces.nsIHelperAppLauncherDialog) &&
  67.             !iid.equals(Components.interfaces.nsISupports)) {
  68.             throw Components.results.NS_ERROR_NO_INTERFACE;
  69.         }
  70.         return this;
  71.     },
  72.  
  73.     // ---------- nsIHelperAppLauncherDialog methods ----------
  74.  
  75.     // show: Open XUL dialog using window watcher.  Since the dialog is not
  76.     //       modal, it needs to be a top level window and the way to open
  77.     //       one of those is via that route).
  78.     show: function(aLauncher, aContext)  {
  79.          this.mLauncher = aLauncher;
  80.          this.mContext  = aContext;
  81.          // Display the dialog using the Window Watcher interface.
  82.          var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  83.                     .getService( Components.interfaces.nsIWindowWatcher );
  84.          this.mDialog = ww.openWindow( null, // no parent
  85.                                        "chrome://global/content/nsHelperAppDlg.xul",
  86.                                        null,
  87.                                        "chrome,titlebar,dialog=yes",
  88.                                        null );
  89.          // Hook this object to the dialog.
  90.          this.mDialog.dialog = this;
  91.     },
  92.  
  93.     // promptForSaveToFile:  Display file picker dialog and return selected file.
  94.     promptForSaveToFile: function(aContext, aDefaultFile, aSuggestedFileExtension) {
  95.         var result = "";
  96.  
  97.         // Use file picker to show dialog.
  98.         var nsIFilePicker = Components.interfaces.nsIFilePicker;
  99.         var picker = Components.classes[ "@mozilla.org/filepicker;1" ]
  100.                        .createInstance( nsIFilePicker );
  101.         var bundle = Components.classes[ "@mozilla.org/intl/stringbundle;1" ]
  102.                        .getService( Components.interfaces.nsIStringBundleService )
  103.                            .createBundle( "chrome://global/locale/nsHelperAppDlg.properties");
  104.  
  105.         var windowTitle = bundle.GetStringFromName( "saveDialogTitle" );
  106.         
  107.         var parent = aContext
  108.                         .QueryInterface( Components.interfaces.nsIInterfaceRequestor )
  109.                         .getInterface( Components.interfaces.nsIDOMWindowInternal );
  110.         picker.init( parent, windowTitle, nsIFilePicker.modeSave );
  111.         picker.defaultString = aDefaultFile;
  112.  
  113.         var wildCardExtension = "*";
  114.         if ( aSuggestedFileExtension ) {
  115.             wildCardExtension += aSuggestedFileExtension;
  116.             picker.appendFilter( wildCardExtension, wildCardExtension );
  117.         }
  118.  
  119.         picker.appendFilters( nsIFilePicker.filterAll );
  120.  
  121.         // Pull in the user's preferences and get the default download directory.
  122.         var prefs = Components.classes[ "@mozilla.org/preferences;1" ]
  123.                         .getService( Components.interfaces.nsIPref );
  124.         try {
  125.             var startDir = prefs.getFileXPref( "browser.download.dir" );
  126.             if ( startDir.exists() ) {
  127.                 picker.displayDirectory = startDir;
  128.             }
  129.         } catch( exception ) {
  130.         }
  131.  
  132.         var dlgResult = picker.show();
  133.  
  134.         if ( dlgResult == nsIFilePicker.returnCancel ) {
  135.             throw Components.results.NS_ERROR_FAILURE;
  136.         }
  137.  
  138.  
  139.         // be sure to save the directory the user chose as the new browser.download.dir
  140.         result = picker.file;
  141.  
  142.         if ( result ) {
  143.             var newDir = result.parent;
  144.             prefs.setFileXPref( "browser.download.dir", newDir );
  145.         }
  146.         return result;
  147.     },
  148.     
  149.     // showProgressDialog:  For now, use old dialog.  At some point, the caller should be
  150.     //                      converted to use the new generic progress dialog (when it's
  151.     //                      finished).
  152.     showProgressDialog: function(aLauncher, aContext) {
  153.          // Display the dialog using the Window Watcher interface.
  154.          var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  155.                     .getService( Components.interfaces.nsIWindowWatcher );
  156.          ww.openWindow( null, // no parent
  157.                         "chrome://global/content/helperAppDldProgress.xul",
  158.                         null,
  159.                         "chrome,titlebar,minimizable,dialog=yes",
  160.                         aLauncher );
  161.     },
  162.     
  163.     // ---------- implementation methods ----------
  164.  
  165.     // initDialog:  Fill various dialog fields with initial content.
  166.     initDialog : function() {
  167.          // Check if file is executable (in which case, we will go straight to
  168.          // "save to disk").
  169.          var ignore1 = new Object;
  170.          var ignore2 = new Object;
  171.          var tmpFile = this.mLauncher.getDownloadInfo( ignore1, ignore2 );
  172.          if ( tmpFile.isExecutable() ) {
  173.              this.mLauncher.saveToDisk( null, false );
  174.              this.mDialog.close();
  175.              return;
  176.          }
  177.  
  178.          // Put product brand short name in prompt.
  179.          var prompt = this.dialogElement( "prompt" );
  180.          var modified = this.replaceInsert( prompt.firstChild.nodeValue, 1, this.getString( "brandShortName" ) );
  181.          prompt.firstChild.nodeValue = modified;
  182.  
  183.          // Put file name in window title.
  184.          var win   = this.dialogElement( "nsHelperAppDlg" );
  185.          var url   = this.mLauncher.source.QueryInterface( Components.interfaces.nsIURL );
  186.          var fname = "";
  187.          this.mSourcePath = url.prePath;
  188.          if ( url ) {
  189.              // A url, use file name from it.
  190.              fname = url.fileName;
  191.              this.mSourcePath += url.directory;
  192.          } else {
  193.              // A generic uri, use path.
  194.              fname = this.mLauncher.source.path;
  195.              this.mSourcePath += url.path;
  196.          }
  197.  
  198.          var title = this.replaceInsert( win.getAttribute( "title" ), 1, fname);
  199.          win.setAttribute( "title", title );
  200.  
  201.          // Put content type and location into intro.
  202.          this.initIntro(url);
  203.  
  204.          var iconString = "moz-icon://" + fname + "?size=32&contentType=" + this.mLauncher.MIMEInfo.MIMEType;
  205.  
  206.          this.dialogElement("contentTypeImage").setAttribute("src", iconString);
  207.  
  208.          this.initAppAndSaveToDiskValues();
  209.  
  210.          // always make sure the window starts off with this checked....
  211.          this.dialogElement( "alwaysAskMe" ).checked = true;
  212.  
  213.          // Add special debug hook.
  214.          if ( this.debug ) {
  215.              var prompt = this.dialogElement( "prompt" );
  216.              prompt.setAttribute( "onclick", "dialog.doDebug()" );
  217.          }
  218.  
  219.          // Set up dialog button callbacks.
  220.          var object = this; // "this.onOK()" doesn't work!
  221.          this.mDialog.doSetOKCancel( function () { return object.onOK(); },
  222.                                      function () { return object.onCancel(); } );
  223.  
  224.          // Position it.
  225.          if ( this.mDialog.opener ) {
  226.              this.mDialog.moveToAlertPosition();
  227.          } else {
  228.              this.mDialog.centerWindowOnScreen();
  229.          }
  230.     },
  231.  
  232.     // initIntro:
  233.     initIntro: function(url) {
  234.         var intro = this.dialogElement( "intro" );
  235.         var desc = this.mLauncher.MIMEInfo.Description;
  236.         var modified;
  237.         if ( desc != "" ) 
  238.         {
  239.           // Use intro with descriptive text.
  240.           modified = this.replaceInsert( this.getString( "intro.withDesc" ), 1, this.mLauncher.MIMEInfo.Description );
  241.         } 
  242.         else 
  243.         {
  244.           // Use intro without descriptive text.
  245.           modified = this.getString( "intro.noDesc" );
  246.         }
  247.  
  248.         modified = this.replaceInsert( modified, 2, this.mLauncher.MIMEInfo.MIMEType );
  249.  
  250.         // if mSourcePath is a local file, then let's use the pretty path name instead of an ugly
  251.         // url...
  252.         var pathString = this.mSourcePath;
  253.         try 
  254.         {
  255.           var fileURL = url.QueryInterface( Components.interfaces.nsIFileURL);
  256.           if (fileURL)
  257.           {
  258.              var fileObject = fileURL.file;
  259.              if (fileObject)
  260.              {
  261.                var parentObject = fileObject.parent;
  262.                if (parentObject)
  263.                {
  264.                  pathString = parentObject.unicodePath;
  265.                }
  266.              }
  267.           }
  268.         } catch(ex) {}
  269.  
  270.  
  271.         intro.firstChild.nodeValue = "";
  272.         intro.firstChild.nodeValue = modified;
  273.  
  274.         // Set the location text, which is separate from the intro text so it can be cropped
  275.         var location = this.dialogElement( "location" );
  276.         location.value = pathString;
  277.     },
  278.  
  279.     // initAppAndSaveToDiskValues:
  280.     initAppAndSaveToDiskValues: function() {
  281.  
  282.         // Pre-select the choice the user made last time.
  283.         this.chosenApp = this.mLauncher.MIMEInfo.preferredApplicationHandler;
  284.         var applicationDescription = this.mLauncher.MIMEInfo.applicationDescription;
  285.  
  286.         if (applicationDescription != "")
  287.         {
  288.           this.updateApplicationName(applicationDescription); 
  289.           this.givenDefaultApp = true;
  290.         }
  291.         else if (this.chosenApp && this.chosenApp.unicodePath)
  292.         {
  293.           // If a user-chosen application, show its path.
  294.           this.updateApplicationName(this.chosenApp.unicodePath);
  295.           this.choseApp = true;
  296.         }
  297.         else
  298.          this.updateApplicationName(this.getString("noApplicationSpecified"));
  299.  
  300.         if ( (applicationDescription || this.choseApp) && this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.saveToDisk ) 
  301.         {
  302.           this.dialogElement( "openUsing" ).checked = true;
  303.           this.dialogElement( "saveToDisk" ).checked = false;         
  304.         }
  305.         else 
  306.         {
  307.           // Save to disk.
  308.           this.dialogElement( "saveToDisk" ).checked = true;
  309.           this.dialogElement( "openUsing" ).checked = false;
  310.           // Disable choose app button.
  311.           this.dialogElement( "chooseApp" ).setAttribute( "disabled", "true" );
  312.         }
  313.     },
  314.  
  315.     updateApplicationName: function(newValue)
  316.     {
  317.       var applicationText = this.getString( "openUsingString" );
  318.       applicationText = this.replaceInsert( applicationText, 1, newValue );
  319.       var expl = this.dialogElement( "openUsing" );
  320.       expl.label = applicationText;
  321.     },
  322.  
  323.     // Enable pick app button if the user chooses that option.
  324.     toggleChoice : function () {
  325.         // See what option is checked.
  326.         if ( this.dialogElement( "openUsing" ).checked ) {
  327.             // We can enable the pick app button.
  328.             this.dialogElement( "chooseApp" ).removeAttribute( "disabled" );
  329.         } else {
  330.             // We can disable the pick app button.
  331.             this.dialogElement( "chooseApp" ).setAttribute( "disabled", "true" );
  332.         }
  333.  
  334.        this.updateOKButton();
  335.     },
  336.  
  337.     processAlwaysAskState : function () 
  338.     {
  339.       // if the user deselected the always ask checkbox, then store that on the mime object for future use...
  340.       if (!this.dialogElement( "alwaysAskMe" ).checked)
  341.       {
  342.         // we first need to rest the user action if the user selected save to disk instead of open...
  343.         // reset the preferred action in this case...we need to do this b4 setting the always ask before handling state
  344.  
  345.         if (!this.dialogElement( "openUsing" ).checked)
  346.         this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.saveToDisk;
  347.          
  348.  
  349.         this.mLauncher.MIMEInfo.alwaysAskBeforeHandling = false;
  350.       }
  351.     },
  352.     updateOKButton: function() {
  353.         var ok = false;
  354.         if ( this.dialogElement( "saveToDisk" ).checked ) 
  355.         {
  356.             // This is always OK.
  357.             ok = true;
  358.         } 
  359.         else 
  360.         {
  361.           // only enable the OK button if we have a default app to use or if 
  362.           // the user chose an app....
  363.           if ((this.choseApp && this.chosenApp.unicodePath) || this.givenDefaultApp)
  364.             ok = true;
  365.         }
  366.         
  367.         // Enable Ok button if ok to press.
  368.         this.dialogElement( "ok" ).disabled = !ok;
  369.     },
  370.  
  371.     // onOK:
  372.     onOK: function() {
  373.  
  374.       this.processAlwaysAskState(); 
  375.  
  376.       if ( this.dialogElement( "openUsing" ).checked ) 
  377.       {
  378.          // If no app "chosen" then convert input string to file.
  379.          if (this.chosenApp)
  380.            this.mLauncher.launchWithApplication( this.chosenApp, false );
  381.           else 
  382.            this.mLauncher.launchWithApplication( null, false );
  383.       }
  384.       else
  385.         this.mLauncher.saveToDisk( null, false );
  386.         
  387.       // Unhook dialog from this object.
  388.       this.mDialog.dialog = null;
  389.  
  390.       // Close up dialog by returning true.
  391.       return true;
  392.      //this.mDialog.close();
  393.     },
  394.  
  395.     // onCancel:
  396.     onCancel: function() {
  397.         // Cancel app launcher.
  398.         try {
  399.             this.mLauncher.Cancel();
  400.         } catch( exception ) {
  401.         }
  402.         
  403.         // Unhook dialog from this object.
  404.         this.mDialog.dialog = null;
  405.  
  406.         // Close up dialog by returning true.
  407.         return true;
  408.     },
  409.  
  410.     // dialogElement:  Try cache; obtain from document if not there.
  411.     dialogElement: function( id ) {
  412.          // Check if we've already fetched it.
  413.          if ( !( id in this.elements ) ) {
  414.              // No, then get it from dialog.
  415.              this.elements[ id ] = this.mDialog.document.getElementById( id );
  416.          }
  417.          return this.elements[ id ];
  418.     },
  419.  
  420.     // chooseApp:  Open file picker and prompt user for application.
  421.     chooseApp: function() {
  422.         var nsIFilePicker = Components.interfaces.nsIFilePicker;
  423.         var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance( nsIFilePicker );
  424.         fp.init( this.mDialog,
  425.                  this.getString( "chooseAppFilePickerTitle" ),
  426.                  nsIFilePicker.modeOpen );
  427.  
  428.         // XXX - We want to say nsIFilePicker.filterExecutable or something
  429.         fp.appendFilters( nsIFilePicker.filterAll );
  430.         
  431.         if ( fp.show() == nsIFilePicker.returnOK && fp.file ) {
  432.             // Remember the file they chose to run.
  433.             this.choseApp = true;
  434.             this.chosenApp    = fp.file;
  435.             // Update dialog.
  436.  
  437.             this.updateApplicationName(this.chosenApp.unicodePath);
  438.         }
  439.     },
  440.  
  441.     // setDefault:  Open "edit MIMEInfo" dialog (borrowed from prefs).
  442.     setDefault: function() {
  443.         // Get RDF service.
  444.         var rdf = Components.classes[ "@mozilla.org/rdf/rdf-service;1" ]
  445.                     .getService( Components.interfaces.nsIRDFService );
  446.         // Now ask if it knows about this mime type.
  447.         var exists = false;
  448.         var fileLocator = Components.classes[ "@mozilla.org/file/directory_service;1" ]
  449.                             .getService( Components.interfaces.nsIProperties );
  450.         var file        = fileLocator.get( "UMimTyp", Components.interfaces.nsIFile );
  451.         
  452.         // We must try creating a fresh remote DS in order to avoid accidentally
  453.         // having GetDataSource trigger an asych load.
  454.         var ds = Components.classes[ "@mozilla.org/rdf/datasource;1?name=xml-datasource" ].createInstance( Components.interfaces.nsIRDFDataSource );
  455.         try {
  456.             // Initialize it.  This will fail if the uriloader (or anybody else)
  457.             // has already loaded/registered this data source.
  458.             var remoteDS = ds.QueryInterface( Components.interfaces.nsIRDFRemoteDataSource );
  459.             remoteDS.Init( file.URL );
  460.             remoteDS.Refresh( true );
  461.         } catch ( all ) {
  462.             // OK then, presume it was already registered; get it.
  463.             ds = rdf.GetDataSource( file.URL );
  464.         }
  465.  
  466.         // Now check if this mimetype is really in there;
  467.         // This is done by seeing if there's a "value" arc from the mimetype resource
  468.         // to the mimetype literal string.
  469.         var mimeRes       = rdf.GetResource( "urn:mimetype:" + this.mLauncher.MIMEInfo.MIMEType );
  470.         var valueProperty = rdf.GetResource( "http://home.netscape.com/NC-rdf#value" );
  471.         var mimeLiteral   = rdf.GetLiteral( this.mLauncher.MIMEInfo.MIMEType );
  472.         exists =  ds.HasAssertion( mimeRes, valueProperty, mimeLiteral, true );
  473.  
  474.         var dlgUrl;
  475.         if ( exists ) {
  476.             // Open "edit mime type" dialog.
  477.             dlgUrl = "chrome://communicator/content/pref/pref-applications-edit.xul";
  478.         } else {
  479.             // Open "add mime type" dialog.
  480.             dlgUrl = "chrome://communicator/content/pref/pref-applications-new.xul";
  481.         }
  482.  
  483.         // Open whichever dialog is appropriate, passing this dialog object as argument.
  484.         this.mDialog.openDialog( dlgUrl,
  485.                                  "_blank",
  486.                                  "chrome,modal=yes,resizable=no",
  487.                                  this );
  488.  
  489.         // Refresh dialog with updated info about the default action.
  490.         this.initIntro();
  491.         this.initAppAndSaveToDiskValues();
  492.     },
  493.  
  494.     // updateMIMEInfo:  This is called from the pref-applications-edit dialog when the user
  495.     //                  presses OK.  Take the updated MIMEInfo and have the helper app service
  496.     //                  "write" it back out to the RDF datasource.
  497.     updateMIMEInfo: function() {
  498.         this.dumpObjectProperties( "\tMIMEInfo", this.mLauncher.MIMEInfo );
  499.     },
  500.  
  501.     // dumpInfo:
  502.     doDebug: function() {
  503.         const nsIProgressDialog = Components.interfaces.nsIProgressDialog;
  504.         // Open new progress dialog.
  505.         var progress = Components.classes[ "@mozilla.org/progressdialog;1" ]
  506.                          .createInstance( nsIProgressDialog );
  507.         // Show it.
  508.         progress.open( this.mDialog );
  509.     },
  510.  
  511.     // dumpObj:
  512.     dumpObj: function( spec ) {
  513.          var val = "<undefined>";
  514.          try {
  515.              val = eval( "this."+spec ).toString();
  516.          } catch( exception ) {
  517.          }
  518.          this.dump( spec + "=" + val + "\n" );
  519.     },
  520.  
  521.     // dumpObjectProperties
  522.     dumpObjectProperties: function( desc, obj ) {
  523.          for( prop in obj ) {
  524.              this.dump( desc + "." + prop + "=" );
  525.              var val = "<undefined>";
  526.              try {
  527.                  val = obj[ prop ];
  528.              } catch ( exception ) {
  529.              }
  530.              this.dump( val + "\n" );
  531.          }
  532.     },
  533.  
  534.     // getString: Fetch data string from dialog content (and cache it).
  535.     getString: function( id ) {
  536.         // Check if we've fetched this string already.
  537.         if ( !( id in this.strings ) ) {
  538.             // Try to get it.
  539.             var elem = this.mDialog.document.getElementById( id );
  540.             if ( elem
  541.                  &&
  542.                  elem.firstChild
  543.                  &&
  544.                  elem.firstChild.nodeValue ) {
  545.                 this.strings[ id ] = elem.firstChild.nodeValue;
  546.             } else {
  547.                 // If unable to fetch string, use an empty string.
  548.                 this.strings[ id ] = "";
  549.             }
  550.         }
  551.         return this.strings[ id ];
  552.     },
  553.  
  554.     // replaceInsert: Replace given insert with replacement text and return the result.
  555.     replaceInsert: function( text, insertNo, replacementText ) {
  556.         var result = text;
  557.         var regExp = new RegExp("#"+insertNo);
  558.         result = result.replace( regExp, replacementText );
  559.         return result;
  560.     }
  561. }
  562.  
  563. // This Component's module implementation.  All the code below is used to get this
  564. // component registered and accessible via XPCOM.
  565. var module = {
  566.     firstTime: true,
  567.  
  568.     // registerSelf: Register this component.
  569.     registerSelf: function (compMgr, fileSpec, location, type) {
  570.         if (this.firstTime) {
  571.             this.firstTime = false;
  572.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  573.         }
  574.         compMgr.registerComponentWithType( this.cid,
  575.                                            "Mozilla Helper App Launcher Dialog",
  576.                                            this.contractId,
  577.                                            fileSpec,
  578.                                            location,
  579.                                            true,
  580.                                            true,
  581.                                            type );
  582.     },
  583.  
  584.     // getClassObject: Return this component's factory object.
  585.     getClassObject: function (compMgr, cid, iid) {
  586.         if (!cid.equals(this.cid)) {
  587.             throw Components.results.NS_ERROR_NO_INTERFACE;
  588.         }
  589.  
  590.         if (!iid.equals(Components.interfaces.nsIFactory)) {
  591.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  592.         }
  593.  
  594.         return this.factory;
  595.     },
  596.  
  597.     /* CID for this class */
  598.     cid: Components.ID("{F68578EB-6EC2-4169-AE19-8C6243F0ABE1}"),
  599.  
  600.     /* Contract ID for this class */
  601.     contractId: "@mozilla.org/helperapplauncherdialog;1",
  602.  
  603.     /* factory object */
  604.     factory: {
  605.         // createInstance: Return a new nsProgressDialog object.
  606.         createInstance: function (outer, iid) {
  607.             if (outer != null)
  608.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  609.  
  610.             return (new nsHelperAppDialog()).QueryInterface(iid);
  611.         }
  612.     },
  613.  
  614.     // canUnload: n/a (returns true)
  615.     canUnload: function(compMgr) {
  616.         return true;
  617.     }
  618. };
  619.  
  620. // NSGetModule: Return the nsIModule object.
  621. function NSGetModule(compMgr, fileSpec) {
  622.     return module;
  623. }
  624.