1 /* vim:set ts=4 sts=4 sw=4 et cin: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Progress Dialog.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corp.
19 * Portions created by the Initial Developer are Copyright (C) 2002
20 * the Initial Developer. All Rights Reserved.
23 * Bill Law <law@netscape.com>
24 * Aaron Kaluszka <ask@swva.net>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /* This file implements the nsIProgressDialog interface. See nsIProgressDialog.idl
42 * The implementation consists of a JavaScript "class" named nsProgressDialog,
44 * - a JS constructor function
45 * - a prototype providing all the interface methods and implementation stuff
47 * In addition, this file implements an nsIModule object that registers the
48 * nsProgressDialog component.
53 function nsProgressDialog() {
54 // Initialize data properties.
56 this.mOperation
= null;
57 this.mStartTime
= ( new Date() ).getTime();
59 this.mLastUpdate
= Number
.MIN_VALUE
; // To ensure first onProgress causes update.
60 this.mInterval
= 750; // Default to .75 seconds.
63 this.fields
= new Array
;
64 this.strings
= new Array
;
67 this.mTargetFile
= null;
68 this.mMIMEInfo
= null;
70 this.mDisplayName
= null;
73 this.mCompleted
= false;
74 this.mMode
= "normal";
78 this.mCancelDownloadOnClose
= true;
81 const nsIProgressDialog
= Components
.interfaces
.nsIProgressDialog
;
82 const nsIWindowWatcher
= Components
.interfaces
.nsIWindowWatcher
;
83 const nsIWebProgressListener
= Components
.interfaces
.nsIWebProgressListener
;
84 const nsITextToSubURI
= Components
.interfaces
.nsITextToSubURI
;
85 const nsIChannel
= Components
.interfaces
.nsIChannel
;
86 const nsIFileURL
= Components
.interfaces
.nsIFileURL
;
87 const nsIURL
= Components
.interfaces
.nsIURL
;
88 const nsILocalFile
= Components
.interfaces
.nsILocalFile
;
90 nsProgressDialog
.prototype = {
91 // Turn this on to get debugging messages.
94 // Chrome-related constants.
95 dialogChrome
: "chrome://global/content/nsProgressDialog.xul",
96 dialogFeatures
: "chrome,titlebar,minimizable=yes,dialog=no",
99 get saving() { return this.MIMEInfo
== null ||
100 this.MIMEInfo
.preferredAction
== Components
.interfaces
.nsIMIMEInfo
.saveToDisk
; },
101 get parent() { return this.mParent
; },
102 set parent(newval
) { return this.mParent
= newval
; },
103 get operation() { return this.mOperation
; },
104 set operation(newval
) { return this.mOperation
= newval
; },
105 get observer() { return this.mObserver
; },
106 set observer(newval
) { return this.mObserver
= newval
; },
107 get startTime() { return this.mStartTime
; },
108 set startTime(newval
) { return this.mStartTime
= newval
/1000; }, // PR_Now() is
in microseconds
, so we convert
.
109 get lastUpdate() { return this.mLastUpdate
; },
110 set lastUpdate(newval
) { return this.mLastUpdate
= newval
; },
111 get interval() { return this.mInterval
; },
112 set interval(newval
) { return this.mInterval
= newval
; },
113 get elapsed() { return this.mElapsed
; },
114 set elapsed(newval
) { return this.mElapsed
= newval
; },
115 get loaded() { return this.mLoaded
; },
116 set loaded(newval
) { return this.mLoaded
= newval
; },
117 get source() { return this.mSource
; },
118 set source(newval
) { return this.mSource
= newval
; },
119 get target() { return this.mTarget
; },
120 get targetFile() { return this.mTargetFile
; },
121 get MIMEInfo() { return this.mMIMEInfo
; },
122 set MIMEInfo(newval
) { return this.mMIMEInfo
= newval
; },
123 get dialog() { return this.mDialog
; },
124 set dialog(newval
) { return this.mDialog
= newval
; },
125 get displayName() { return this.mDisplayName
; },
126 set displayName(newval
) { return this.mDisplayName
= newval
; },
127 get paused() { return this.mPaused
; },
128 get completed() { return this.mCompleted
; },
129 get mode() { return this.mMode
; },
130 get percent() { return this.mPercent
; },
131 get rate() { return this.mRate
; },
132 get kRate() { return this.mRate
/ 1024; },
133 get cancelDownloadOnClose() { return this.mCancelDownloadOnClose
; },
134 set cancelDownloadOnClose(newval
) { return this.mCancelDownloadOnClose
= newval
; },
137 // If newval references a file on the local filesystem, then grab a
138 // reference to its corresponding nsIFile.
139 if (newval
instanceof nsIFileURL
&& newval
.file
instanceof nsILocalFile
) {
140 this.mTargetFile
= newval
.file
.QueryInterface(nsILocalFile
);
142 this.mTargetFile
= null;
145 return this.mTarget
= newval
;
148 // These setters use functions that update the dialog.
149 set paused(newval
) { return this.setPaused(newval
); },
150 set completed(newval
) { return this.setCompleted(newval
); },
151 set mode(newval
) { return this.setMode(newval
); },
152 set percent(newval
) { return this.setPercent(newval
); },
153 set rate(newval
) { return this.setRate(newval
); },
155 // ---------- nsIProgressDialog methods ----------
157 // open: Store aParentWindow and open the dialog.
158 open: function( aParentWindow
) {
159 // Save parent and "persist" operation.
160 this.parent
= aParentWindow
;
162 // Open dialog using the WindowWatcher service.
163 var ww
= Components
.classes
["@mozilla.org/embedcomp/window-watcher;1"]
164 .getService( nsIWindowWatcher
);
165 this.dialog
= ww
.openWindow( this.parent
,
172 init: function( aSource
, aTarget
, aDisplayName
, aMIMEInfo
, aStartTime
,
173 aTempFile
, aOperation
) {
174 this.source
= aSource
;
175 this.target
= aTarget
;
176 this.displayName
= aDisplayName
;
177 this.MIMEInfo
= aMIMEInfo
;
179 this.startTime
= aStartTime
;
181 this.operation
= aOperation
;
184 // ----- nsIDownloadProgressListener/nsIWebProgressListener methods -----
185 // Take advantage of javascript's function overloading feature to combine
186 // similiar nsIDownloadProgressListener and nsIWebProgressListener methods
187 // in one. For nsIWebProgressListener calls, the aDownload paramater will
188 // always be undefined.
190 // Look for STATE_STOP and update dialog to indicate completion when it happens.
191 onStateChange: function( aWebProgress
, aRequest
, aStateFlags
, aStatus
, aDownload
) {
192 if ( aStateFlags
& nsIWebProgressListener
.STATE_STOP
) {
193 // if we are downloading, then just wait for the first STATE_STOP
194 if ( this.targetFile
!= null ) {
195 // we are done transferring...
196 this.completed
= true;
200 // otherwise, wait for STATE_STOP with aRequest corresponding to
201 // our target. XXX redirects might screw up this logic.
203 var chan
= aRequest
.QueryInterface(nsIChannel
);
204 if (chan
.URI
.equals(this.target
)) {
205 // we are done transferring...
206 this.completed
= true;
214 // Handle progress notifications.
215 onProgressChange: function( aWebProgress
,
222 return this.onProgressChange64(aWebProgress
, aRequest
, aCurSelfProgress
,
223 aMaxSelfProgress
, aCurTotalProgress
, aMaxTotalProgress
, aDownload
);
226 onProgressChange64: function( aWebProgress
,
234 var now
= ( new Date() ).getTime();
236 // If interval hasn't elapsed, ignore it.
237 if ( now
- this.lastUpdate
< this.interval
) {
242 this.lastUpdate
= now
;
244 // Update elapsed time.
245 this.elapsed
= now
- this.startTime
;
247 // Calculate percentage.
248 if ( aMaxTotalProgress
> 0) {
249 this.percent
= Math
.floor( ( aCurTotalProgress
* 100.0 ) / aMaxTotalProgress
);
254 // If dialog not loaded, then don't bother trying to update display.
255 if ( !this.loaded
) {
259 // Update dialog's display of elapsed time.
260 this.setValue( "timeElapsed", this.formatSeconds( this.elapsed
/ 1000 ) );
262 // Now that we've set the progress and the time, update # bytes downloaded...
263 // Update status (nn KB of mm KB at xx.x KB/sec)
264 var status
= this.getString( "progressMsg" );
266 // Insert 1 is the number of kilobytes downloaded so far.
267 status
= this.replaceInsert( status
, 1, parseInt( aCurTotalProgress
/1024 + .5 ) );
269 // Insert 2 is the total number of kilobytes to be downloaded (if known).
270 if ( aMaxTotalProgress
!= "-1" ) {
271 status
= this.replaceInsert( status
, 2, parseInt( aMaxTotalProgress
/1024 + .5 ) );
273 status
= this.replaceInsert( status
, 2, "??" );
276 // Insert 3 is the download rate.
277 if ( this.elapsed
) {
278 // Use the download speed where available, otherwise calculate
279 // rate using current progress and elapsed time.
281 this.rate
= aDownload
.speed
;
283 this.rate
= ( aCurTotalProgress
* 1000 ) / this.elapsed
;
285 status
= this.replaceInsert( status
, 3, this.kRate
.toFixed(1) );
287 // Rate not established, yet.
288 status
= this.replaceInsert( status
, 3, "??.?" );
291 // All 3 inserts are taken care of, now update status msg.
292 this.setValue( "status", status
);
294 // Update time remaining.
295 if ( this.rate
&& ( aMaxTotalProgress
> 0 ) ) {
296 // Calculate how much time to download remaining at this rate.
297 var rem
= Math
.round( ( aMaxTotalProgress
- aCurTotalProgress
) / this.rate
);
298 this.setValue( "timeLeft", this.formatSeconds( rem
) );
300 // We don't know how much time remains.
301 this.setValue( "timeLeft", this.getString( "unknownTime" ) );
305 // Look for error notifications and display alert to user.
306 onStatusChange: function( aWebProgress
, aRequest
, aStatus
, aMessage
, aDownload
) {
307 // Check for error condition (only if dialog is still open).
308 if ( aStatus
!= Components
.results
.NS_OK
) {
310 // Get prompt service.
311 var prompter
= Components
.classes
[ "@mozilla.org/embedcomp/prompt-service;1" ]
312 .getService( Components
.interfaces
.nsIPromptService
);
313 // Display error alert (using text supplied by back-end).
314 var title
= this.getProperty( this.saving
? "savingAlertTitle" : "openingAlertTitle",
317 prompter
.alert( this.dialog
, title
, aMessage
);
320 if ( !this.completed
) {
324 // Error occurred prior to onload even firing.
325 // We can't handle this error until we're done loading, so
326 // defer the handling of this call.
327 this.dialog
.setTimeout( function(obj
,wp
,req
,stat
,msg
){obj
.onStatusChange(wp
,req
,stat
,msg
)},
328 100, this, aWebProgress
, aRequest
, aStatus
, aMessage
);
333 // Ignore onLocationChange and onSecurityChange notifications.
334 onLocationChange: function( aWebProgress
, aRequest
, aLocation
, aDownload
) {
337 onSecurityChange: function( aWebProgress
, aRequest
, aState
, aDownload
) {
340 // ---------- nsIObserver methods ----------
341 observe: function( anObject
, aTopic
, aData
) {
342 // Something of interest occured on the dialog.
343 // Dispatch to corresponding implementation method.
364 // This event comes in when setCompleted needs to be deferred because
365 // the dialog isn't loaded yet.
366 this.completed
= true;
373 // ---------- nsISupports methods ----------
375 // This "class" supports nsIProgressDialog, nsIWebProgressListener (by virtue
376 // of interface inheritance), nsIObserver, and nsISupports.
377 QueryInterface: function (iid
) {
378 if (iid
.equals(Components
.interfaces
.nsIProgressDialog
) ||
379 iid
.equals(Components
.interfaces
.nsIDownload
) ||
380 iid
.equals(Components
.interfaces
.nsITransfer
) ||
381 iid
.equals(Components
.interfaces
.nsIWebProgressListener
) ||
382 iid
.equals(Components
.interfaces
.nsIWebProgressListener2
) ||
383 iid
.equals(Components
.interfaces
.nsIDownloadProgressListener
) ||
384 iid
.equals(Components
.interfaces
.nsIObserver
) ||
385 iid
.equals(Components
.interfaces
.nsIInterfaceRequestor
) ||
386 iid
.equals(Components
.interfaces
.nsISupports
))
389 Components
.returnCode
= Components
.results
.NS_ERROR_NO_INTERFACE
;
393 // ---------- nsIInterfaceRequestor methods ----------
395 getInterface: function(iid
) {
396 if (iid
.equals(Components
.interfaces
.nsIPrompt
) ||
397 iid
.equals(Components
.interfaces
.nsIAuthPrompt
)) {
398 // use the window watcher service to get a nsIPrompt/nsIAuthPrompt impl
399 var ww
= Components
.classes
["@mozilla.org/embedcomp/window-watcher;1"]
400 .getService(Components
.interfaces
.nsIWindowWatcher
);
402 if (iid
.equals(Components
.interfaces
.nsIPrompt
))
403 prompt
= ww
.getNewPrompter(this.parent
);
405 prompt
= ww
.getNewAuthPrompter(this.parent
);
408 Components
.returnCode
= Components
.results
.NS_ERROR_NO_INTERFACE
;
412 // ---------- implementation methods ----------
414 // Initialize the dialog.
416 // Note that onLoad has finished.
423 if ( this.dialog
.opener
) {
424 this.dialog
.moveToAlertPosition();
426 this.dialog
.centerWindowOnScreen();
429 // Set initial focus on "keep open" box. If that box is hidden, or, if
430 // the download is already complete, then focus is on the cancel/close
431 // button. The download may be complete if it was really short and the
432 // dialog took longer to open than to download the data.
433 if ( !this.completed
&& !this.saving
) {
434 this.dialogElement( "keep" ).focus();
436 this.dialogElement( "cancel" ).focus();
440 // load dialog with initial contents
441 loadDialog: function() {
442 // Check whether we're saving versus opening with a helper app.
443 if ( !this.saving
) {
444 // Put proper label on source field.
445 this.setValue( "sourceLabel", this.getString( "openingSource" ) );
447 // Target is the "preferred" application. Hide if empty.
448 if ( this.MIMEInfo
&&
449 this.MIMEInfo
.preferredApplicationHandler
&&
450 this.MIMEInfo
.preferredApplicationHandler
.executable
) {
452 this.MIMEInfo
.preferredApplicationHandler
.executable
.leafName
;
453 if ( appName
== null || appName
.length
== 0 ) {
454 this.hide( "targetRow" );
456 // Use the "with:" label.
457 this.setValue( "targetLabel", this.getString( "openingTarget" ) );
458 // Name of application.
459 this.setValue( "target", appName
);
462 this.hide( "targetRow" );
465 // If target is not a local file, then hide extra dialog controls.
466 if (this.targetFile
!= null) {
467 this.setValue( "target", this.targetFile
.path
);
469 this.setValue( "target", this.target
.spec
);
470 this.hide( "pauseResume" );
471 this.hide( "launch" );
472 this.hide( "reveal" );
477 this.setValue( "source", this.source
.spec
);
479 var now
= ( new Date() ).getTime();
481 // Initialize the elapsed time.
482 if ( !this.elapsed
) {
483 this.elapsed
= now
- this.startTime
;
486 // Update elapsed time display.
487 this.setValue( "timeElapsed", this.formatSeconds( this.elapsed
/ 1000 ) );
488 this.setValue( "timeLeft", this.getString( "unknownTime" ) );
490 // Initialize the "keep open" box. Hide this if we're opening a helper app
491 // or if we are uploading.
492 if ( !this.saving
|| !this.targetFile
) {
493 // Hide this in this case.
496 // Initialize using last-set value from prefs.
497 var prefs
= Components
.classes
[ "@mozilla.org/preferences-service;1" ]
498 .getService( Components
.interfaces
.nsIPrefBranch
);
500 this.dialogElement( "keep" ).checked
= prefs
.getBoolPref( "browser.download.progressDnldDialog.keepAlive" );
508 // Cancel button stops the download (if not completed),
509 // and closes the dialog.
510 onCancel: function() {
511 // Cancel the download, if not completed.
512 if ( !this.completed
) {
513 if ( this.operation
) {
514 const NS_BINDING_ABORTED
= 0x804b0002;
515 this.operation
.cancel(NS_BINDING_ABORTED
);
516 // XXX We're supposed to clean up files/directories.
518 if ( this.observer
) {
519 this.observer
.observe( this, "oncancel", "" );
523 // Test whether the dialog is already closed.
524 // This will be the case if we've come through onUnload.
531 // onunload event means the dialog has closed.
532 // We go through our onCancel logic to stop the download if still in progress.
533 onUnload: function() {
534 // Remember "keep dialog open" setting, if visible.
536 var prefs
= Components
.classes
["@mozilla.org/preferences-service;1"]
537 .getService( Components
.interfaces
.nsIPrefBranch
);
539 prefs
.setBoolPref( "browser.download.progressDnldDialog.keepAlive", this.dialogElement( "keep" ).checked
);
542 this.dialog
= null; // The dialog is history.
543 if ( this.mCancelDownloadOnClose
) {
548 // onpause event means the user pressed the pause/resume button
549 // Toggle the pause/resume state (see the function setPause(), below).i
550 onPause: function() {
551 this.paused
= !this.paused
;
554 // onlaunch event means the user pressed the launch button
555 // Invoke the launch method of the target file.
556 onLaunch: function() {
558 const kDontAskAgainPref
= "browser.download.progressDnlgDialog.dontAskForLaunch";
560 var pref
= Components
.classes
["@mozilla.org/preferences-service;1"]
561 .getService(Components
.interfaces
.nsIPrefBranch
);
562 var dontAskAgain
= pref
.getBoolPref(kDontAskAgainPref
);
564 // we need to ask if we're unsure
565 dontAskAgain
= false;
567 if ( !dontAskAgain
&& this.targetFile
.isExecutable() ) {
569 var promptService
= Components
.classes
["@mozilla.org/embedcomp/prompt-service;1"]
570 .getService( Components
.interfaces
.nsIPromptService
);
572 // getService doesn't return null, it throws
575 var title
= this.getProperty( "openingAlertTitle",
578 var msg
= this.getProperty( "securityAlertMsg",
581 var dontaskmsg
= this.getProperty( "dontAskAgain",
583 var checkbox
= {value
:0};
584 var okToProceed
= promptService
.confirmCheck(this.dialog
, title
, msg
, dontaskmsg
, checkbox
);
586 if (checkbox
.value
!= dontAskAgain
)
587 pref
.setBoolPref(kDontAskAgainPref
, checkbox
.value
);
593 this.targetFile
.launch();
595 } catch ( exception
) {
596 // XXX Need code here to tell user the launch failed!
597 dump( "nsProgressDialog::onLaunch failed: " + exception
+ "\n" );
601 // onreveal event means the user pressed the "reveal location" button
602 // Invoke the reveal method of the target file.
603 onReveal: function() {
605 this.targetFile
.reveal();
607 } catch ( exception
) {
611 // Get filename from the target.
612 fileName: function() {
613 if ( this.targetFile
!= null )
614 return this.targetFile
.leafName
;
616 var escapedFileName
= this.target
.QueryInterface(nsIURL
).fileName
;
617 var textToSubURI
= Components
.classes
["@mozilla.org/intl/texttosuburi;1"]
618 .getService(nsITextToSubURI
);
619 return textToSubURI
.unEscapeURIForUI(this.target
.originCharset
, escapedFileName
);
624 // Set the dialog title.
625 setTitle: function() {
626 // Start with saving/opening template.
627 // If percentage is not known (-1), use alternate template
628 var title
= this.saving
629 ? ( this.percent
!= -1 ? this.getString( "savingTitle" ) : this.getString( "unknownSavingTitle" ) )
630 : ( this.percent
!= -1 ? this.getString( "openingTitle" ) : this.getString( "unknownOpeningTitle" ) );
633 // Use file name as insert 1.
634 title
= this.replaceInsert( title
, 1, this.fileName() );
636 // Use percentage as insert 2 (if known).
637 if ( this.percent
!= -1 ) {
638 title
= this.replaceInsert( title
, 2, this.percent
);
641 // Set dialog's title property.
643 this.dialog
.document
.title
= title
;
647 // Update the dialog to indicate specified percent complete.
648 setPercent: function( percent
) {
649 // Maximum percentage is 100.
650 if ( percent
> 100 ) {
653 // Test if percentage is changing.
654 if ( this.percent
!= percent
) {
655 this.mPercent
= percent
;
657 // If dialog not opened yet, bail early.
658 if ( !this.loaded
) {
659 return this.mPercent
;
662 if ( percent
== -1 ) {
663 // Progress meter needs to be in "undetermined" mode.
664 this.mode
= "undetermined";
666 // Update progress meter percentage text.
667 this.setValue( "progressText", "" );
669 // Progress meter needs to be in normal mode.
670 this.mode
= "normal";
672 // Set progress meter thermometer.
673 this.setValue( "progress", percent
);
675 // Update progress meter percentage text.
676 this.setValue( "progressText", this.replaceInsert( this.getString( "percentMsg" ), 1, percent
) );
682 return this.mPercent
;
685 // Update download rate and dialog display.
686 setRate: function( rate
) {
691 // Handle download completion.
692 setCompleted: function() {
693 // If dialog hasn't loaded yet, defer this.
694 if ( !this.loaded
) {
695 this.dialog
.setTimeout( function(obj
){obj
.setCompleted()}, 100, this );
698 if ( !this.mCompleted
) {
699 this.mCompleted
= true;
701 // If the "keep dialog open" box is checked, then update dialog.
702 if ( this.dialog
&& this.dialogElement( "keep" ).checked
) {
703 // Indicate completion in status area.
704 var string
= this.getString( "completeMsg" );
705 string
= this.replaceInsert( string
,
707 this.formatSeconds( this.elapsed
/1000 ) );
708 string
= this.replaceInsert( string
,
710 this.targetFile
.fileSize
>> 10 );
712 this.setValue( "status", string
);
713 // Put progress meter at 100%.
716 // Set time remaining to 00:00.
717 this.setValue( "timeLeft", this.formatSeconds( 0 ) );
719 // Change Cancel button to Close, and give it focus.
720 var cancelButton
= this.dialogElement( "cancel" );
721 cancelButton
.label
= this.getString( "close" );
722 cancelButton
.focus();
724 // Activate reveal/launch buttons if we enable them.
725 var enableButtons
= true;
727 var prefs
= Components
.classes
[ "@mozilla.org/preferences-service;1" ]
728 .getService( Components
.interfaces
.nsIPrefBranch
);
729 enableButtons
= prefs
.getBoolPref( "browser.download.progressDnldDialog.enable_launch_reveal_buttons" );
733 if ( enableButtons
) {
734 this.enable( "reveal" );
736 if ( this.targetFile
!= null ) {
737 this.enable( "launch" );
743 // Disable the Pause/Resume buttons.
744 this.dialogElement( "pauseResume" ).disabled
= true;
746 // Fix up dialog layout (which gets messed up sometimes).
747 this.dialog
.sizeToContent();
749 // GetAttention to show the user that we're done
750 this.dialog
.getAttention();
751 } else if ( this.dialog
) {
755 return this.mCompleted
;
758 // Set progress meter to given mode ("normal" or "undetermined").
759 setMode: function( newMode
) {
760 if ( this.mode
!= newMode
) {
761 // Need to update progress meter.
762 this.dialogElement( "progress" ).setAttribute( "mode", newMode
);
764 return this.mMode
= newMode
;
767 // Set pause/resume state.
768 setPaused: function( pausing
) {
769 // If state changing, then update stuff.
770 if ( this.paused
!= pausing
) {
771 var string
= pausing
? "resume" : "pause";
772 this.dialogElement( "pauseResume" ).label
= this.getString(string
);
774 // If we have an observer, tell it to suspend/resume
775 if ( this.observer
) {
776 this.observer
.observe( this, pausing
? "onpause" : "onresume" , "" );
779 return this.mPaused
= pausing
;
782 // Format number of seconds in hh:mm:ss form.
783 formatSeconds: function( secs
) {
784 // Round the number of seconds to remove fractions.
785 secs
= parseInt( secs
+ .5 );
786 var hours
= parseInt( secs
/3600 );
788 var mins
= parseInt( secs
/60 );
792 result
= this.getString( "longTimeFormat" );
794 result
= this.getString( "shortTimeFormat" );
803 // Insert hours, minutes, and seconds into result string.
804 result
= this.replaceInsert( result
, 1, hours
);
805 result
= this.replaceInsert( result
, 2, mins
);
806 result
= this.replaceInsert( result
, 3, secs
);
811 // Get dialog element using argument as id.
812 dialogElement: function( id
) {
813 // Check if we've already fetched it.
814 if ( !( id
in this.fields
) ) {
815 // No, then get it from dialog.
817 this.fields
[ id
] = this.dialog
.document
.getElementById( id
);
819 this.fields
[ id
] = {
821 setAttribute: function(id
,val
) {},
822 removeAttribute: function(id
) {}
826 return this.fields
[ id
];
829 // Set dialog element value for given dialog element.
830 setValue: function( id
, val
) {
831 this.dialogElement( id
).value
= val
;
834 // Enable dialog element.
835 enable: function( field
) {
836 this.dialogElement( field
).removeAttribute( "disabled" );
839 // Get localizable string from properties file.
840 getProperty: function( propertyId
, strings
, len
) {
841 if ( !this.mBundle
) {
842 this.mBundle
= Components
.classes
[ "@mozilla.org/intl/stringbundle;1" ]
843 .getService( Components
.interfaces
.nsIStringBundleService
)
844 .createBundle( "chrome://global/locale/nsProgressDialog.properties");
846 return len
? this.mBundle
.formatStringFromName( propertyId
, strings
, len
)
847 : this.mBundle
.getStringFromName( propertyId
);
850 // Get localizable string (from dialog <data> elements).
851 getString: function ( stringId
) {
852 // Check if we've fetched this string already.
853 if ( !( this.strings
&& stringId
in this.strings
) ) {
854 // Presume the string is empty if we can't get it.
855 this.strings
[ stringId
] = "";
858 this.strings
[ stringId
] = this.dialog
.document
.getElementById( "string."+stringId
).childNodes
[0].nodeValue
;
861 return this.strings
[ stringId
];
864 // Replaces insert ("#n") with input text.
865 replaceInsert: function( text
, index
, value
) {
867 var regExp
= new RegExp( "#"+index
);
868 result
= result
.replace( regExp
, value
);
872 // Hide a given dialog field.
873 hide: function( field
) {
874 this.dialogElement( field
).hidden
= true;
876 // Also hide any related separator element...
877 var sep
= this.dialogElement( field
+"Separator" );
882 // Return input in hex, prepended with "0x" and leading zeros (to 8 digits).
884 return "0x" + ("0000000" + Number(x
).toString(16)).slice(-8);
887 // Dump text (if debug is on).
888 dump: function( text
) {
895 // This Component's module implementation. All the code below is used to get this
896 // component registered and accessible via XPCOM.
898 // registerSelf: Register this component.
899 registerSelf: function (compMgr
, fileSpec
, location
, type
) {
900 var compReg
= compMgr
.QueryInterface( Components
.interfaces
.nsIComponentRegistrar
);
901 compReg
.registerFactoryLocation( this.cid
,
902 "Mozilla Download Progress Dialog",
909 // getClassObject: Return this component's factory object.
910 getClassObject: function (compMgr
, cid
, iid
) {
911 if (!cid
.equals(this.cid
))
912 throw Components
.results
.NS_ERROR_NO_INTERFACE
;
914 if (!iid
.equals(Components
.interfaces
.nsIFactory
))
915 throw Components
.results
.NS_ERROR_NOT_IMPLEMENTED
;
920 /* CID for this class */
921 cid
: Components
.ID("{F5D248FD-024C-4f30-B208-F3003B85BC92}"),
923 /* Contract ID for this class */
924 contractId
: "@mozilla.org/progressdialog;1",
928 // createInstance: Return a new nsProgressDialog object.
929 createInstance: function (outer
, iid
) {
931 throw Components
.results
.NS_ERROR_NO_AGGREGATION
;
933 return (new nsProgressDialog()).QueryInterface(iid
);
937 // canUnload: n/a (returns true)
938 canUnload: function(compMgr
) {
943 // NSGetModule: Return the nsIModule object.
944 function NSGetModule(compMgr
, fileSpec
) {