blob: 660f9b3eef9960581fe5ca4746eac732129c01f0 [file] [log] [blame]
/* vim:set ts=4 sts=4 sw=4 et cin: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Progress Dialog.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corp.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bill Law <law@netscape.com>
* Aaron Kaluszka <ask@swva.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* This file implements the nsIProgressDialog interface. See nsIProgressDialog.idl
*
* The implementation consists of a JavaScript "class" named nsProgressDialog,
* comprised of:
* - a JS constructor function
* - a prototype providing all the interface methods and implementation stuff
*
* In addition, this file implements an nsIModule object that registers the
* nsProgressDialog component.
*/
/* ctor
*/
function nsProgressDialog() {
// Initialize data properties.
this.mParent = null;
this.mOperation = null;
this.mStartTime = ( new Date() ).getTime();
this.observer = null;
this.mLastUpdate = Number.MIN_VALUE; // To ensure first onProgress causes update.
this.mInterval = 750; // Default to .75 seconds.
this.mElapsed = 0;
this.mLoaded = false;
this.fields = new Array;
this.strings = new Array;
this.mSource = null;
this.mTarget = null;
this.mTargetFile = null;
this.mMIMEInfo = null;
this.mDialog = null;
this.mDisplayName = null;
this.mPaused = false;
this.mRequest = null;
this.mCompleted = false;
this.mMode = "normal";
this.mPercent = -1;
this.mRate = 0;
this.mBundle = null;
this.mCancelDownloadOnClose = true;
}
const nsIProgressDialog = Components.interfaces.nsIProgressDialog;
const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher;
const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
const nsITextToSubURI = Components.interfaces.nsITextToSubURI;
const nsIChannel = Components.interfaces.nsIChannel;
const nsIFileURL = Components.interfaces.nsIFileURL;
const nsIURL = Components.interfaces.nsIURL;
const nsILocalFile = Components.interfaces.nsILocalFile;
nsProgressDialog.prototype = {
// Turn this on to get debugging messages.
debug: false,
// Chrome-related constants.
dialogChrome: "chrome://global/content/nsProgressDialog.xul",
dialogFeatures: "chrome,titlebar,minimizable=yes,dialog=no",
// getters/setters
get saving() { return this.MIMEInfo == null ||
this.MIMEInfo.preferredAction == Components.interfaces.nsIMIMEInfo.saveToDisk; },
get parent() { return this.mParent; },
set parent(newval) { return this.mParent = newval; },
get operation() { return this.mOperation; },
set operation(newval) { return this.mOperation = newval; },
get observer() { return this.mObserver; },
set observer(newval) { return this.mObserver = newval; },
get startTime() { return this.mStartTime; },
set startTime(newval) { return this.mStartTime = newval/1000; }, // PR_Now() is in microseconds, so we convert.
get lastUpdate() { return this.mLastUpdate; },
set lastUpdate(newval) { return this.mLastUpdate = newval; },
get interval() { return this.mInterval; },
set interval(newval) { return this.mInterval = newval; },
get elapsed() { return this.mElapsed; },
set elapsed(newval) { return this.mElapsed = newval; },
get loaded() { return this.mLoaded; },
set loaded(newval) { return this.mLoaded = newval; },
get source() { return this.mSource; },
set source(newval) { return this.mSource = newval; },
get target() { return this.mTarget; },
get targetFile() { return this.mTargetFile; },
get MIMEInfo() { return this.mMIMEInfo; },
set MIMEInfo(newval) { return this.mMIMEInfo = newval; },
get dialog() { return this.mDialog; },
set dialog(newval) { return this.mDialog = newval; },
get displayName() { return this.mDisplayName; },
set displayName(newval) { return this.mDisplayName = newval; },
get paused() { return this.mPaused; },
get completed() { return this.mCompleted; },
get mode() { return this.mMode; },
get percent() { return this.mPercent; },
get rate() { return this.mRate; },
get kRate() { return this.mRate / 1024; },
get cancelDownloadOnClose() { return this.mCancelDownloadOnClose; },
set cancelDownloadOnClose(newval) { return this.mCancelDownloadOnClose = newval; },
set target(newval) {
// If newval references a file on the local filesystem, then grab a
// reference to its corresponding nsIFile.
if (newval instanceof nsIFileURL && newval.file instanceof nsILocalFile) {
this.mTargetFile = newval.file.QueryInterface(nsILocalFile);
} else {
this.mTargetFile = null;
}
return this.mTarget = newval;
},
// These setters use functions that update the dialog.
set paused(newval) { return this.setPaused(newval); },
set completed(newval) { return this.setCompleted(newval); },
set mode(newval) { return this.setMode(newval); },
set percent(newval) { return this.setPercent(newval); },
set rate(newval) { return this.setRate(newval); },
// ---------- nsIProgressDialog methods ----------
// open: Store aParentWindow and open the dialog.
open: function( aParentWindow ) {
// Save parent and "persist" operation.
this.parent = aParentWindow;
// Open dialog using the WindowWatcher service.
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService( nsIWindowWatcher );
this.dialog = ww.openWindow( this.parent,
this.dialogChrome,
null,
this.dialogFeatures,
this );
},
init: function( aSource, aTarget, aDisplayName, aMIMEInfo, aStartTime,
aTempFile, aOperation ) {
this.source = aSource;
this.target = aTarget;
this.displayName = aDisplayName;
this.MIMEInfo = aMIMEInfo;
if ( aStartTime ) {
this.startTime = aStartTime;
}
this.operation = aOperation;
},
// ----- nsIDownloadProgressListener/nsIWebProgressListener methods -----
// Take advantage of javascript's function overloading feature to combine
// similiar nsIDownloadProgressListener and nsIWebProgressListener methods
// in one. For nsIWebProgressListener calls, the aDownload paramater will
// always be undefined.
// Look for STATE_STOP and update dialog to indicate completion when it happens.
onStateChange: function( aWebProgress, aRequest, aStateFlags, aStatus, aDownload ) {
if ( aStateFlags & nsIWebProgressListener.STATE_STOP ) {
// if we are downloading, then just wait for the first STATE_STOP
if ( this.targetFile != null ) {
// we are done transferring...
this.completed = true;
return;
}
// otherwise, wait for STATE_STOP with aRequest corresponding to
// our target. XXX redirects might screw up this logic.
try {
var chan = aRequest.QueryInterface(nsIChannel);
if (chan.URI.equals(this.target)) {
// we are done transferring...
this.completed = true;
}
}
catch (e) {
}
}
},
// Handle progress notifications.
onProgressChange: function( aWebProgress,
aRequest,
aCurSelfProgress,
aMaxSelfProgress,
aCurTotalProgress,
aMaxTotalProgress,
aDownload ) {
return this.onProgressChange64(aWebProgress, aRequest, aCurSelfProgress,
aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload);
},
onProgressChange64: function( aWebProgress,
aRequest,
aCurSelfProgress,
aMaxSelfProgress,
aCurTotalProgress,
aMaxTotalProgress,
aDownload ) {
// Get current time.
var now = ( new Date() ).getTime();
// If interval hasn't elapsed, ignore it.
if ( now - this.lastUpdate < this.interval ) {
return;
}
// Update this time.
this.lastUpdate = now;
// Update elapsed time.
this.elapsed = now - this.startTime;
// Calculate percentage.
if ( aMaxTotalProgress > 0) {
this.percent = Math.floor( ( aCurTotalProgress * 100.0 ) / aMaxTotalProgress );
} else {
this.percent = -1;
}
// If dialog not loaded, then don't bother trying to update display.
if ( !this.loaded ) {
return;
}
// Update dialog's display of elapsed time.
this.setValue( "timeElapsed", this.formatSeconds( this.elapsed / 1000 ) );
// Now that we've set the progress and the time, update # bytes downloaded...
// Update status (nn KB of mm KB at xx.x KB/sec)
var status = this.getString( "progressMsg" );
// Insert 1 is the number of kilobytes downloaded so far.
status = this.replaceInsert( status, 1, parseInt( aCurTotalProgress/1024 + .5 ) );
// Insert 2 is the total number of kilobytes to be downloaded (if known).
if ( aMaxTotalProgress != "-1" ) {
status = this.replaceInsert( status, 2, parseInt( aMaxTotalProgress/1024 + .5 ) );
} else {
status = this.replaceInsert( status, 2, "??" );
}
// Insert 3 is the download rate.
if ( this.elapsed ) {
// Use the download speed where available, otherwise calculate
// rate using current progress and elapsed time.
if ( aDownload ) {
this.rate = aDownload.speed;
} else {
this.rate = ( aCurTotalProgress * 1000 ) / this.elapsed;
}
status = this.replaceInsert( status, 3, this.kRate.toFixed(1) );
} else {
// Rate not established, yet.
status = this.replaceInsert( status, 3, "??.?" );
}
// All 3 inserts are taken care of, now update status msg.
this.setValue( "status", status );
// Update time remaining.
if ( this.rate && ( aMaxTotalProgress > 0 ) ) {
// Calculate how much time to download remaining at this rate.
var rem = Math.round( ( aMaxTotalProgress - aCurTotalProgress ) / this.rate );
this.setValue( "timeLeft", this.formatSeconds( rem ) );
} else {
// We don't know how much time remains.
this.setValue( "timeLeft", this.getString( "unknownTime" ) );
}
},
// Look for error notifications and display alert to user.
onStatusChange: function( aWebProgress, aRequest, aStatus, aMessage, aDownload ) {
// Check for error condition (only if dialog is still open).
if ( aStatus != Components.results.NS_OK ) {
if ( this.loaded ) {
// Get prompt service.
var prompter = Components.classes[ "@mozilla.org/embedcomp/prompt-service;1" ]
.getService( Components.interfaces.nsIPromptService );
// Display error alert (using text supplied by back-end).
var title = this.getProperty( this.saving ? "savingAlertTitle" : "openingAlertTitle",
[ this.fileName() ],
1 );
prompter.alert( this.dialog, title, aMessage );
// Close the dialog.
if ( !this.completed ) {
this.onCancel();
}
} else {
// Error occurred prior to onload even firing.
// We can't handle this error until we're done loading, so
// defer the handling of this call.
this.dialog.setTimeout( function(obj,wp,req,stat,msg){obj.onStatusChange(wp,req,stat,msg)},
100, this, aWebProgress, aRequest, aStatus, aMessage );
}
}
},
// Ignore onLocationChange and onSecurityChange notifications.
onLocationChange: function( aWebProgress, aRequest, aLocation, aDownload ) {
},
onSecurityChange: function( aWebProgress, aRequest, aState, aDownload ) {
},
// ---------- nsIObserver methods ----------
observe: function( anObject, aTopic, aData ) {
// Something of interest occured on the dialog.
// Dispatch to corresponding implementation method.
switch ( aTopic ) {
case "onload":
this.onLoad();
break;
case "oncancel":
this.onCancel();
break;
case "onpause":
this.onPause();
break;
case "onlaunch":
this.onLaunch();
break;
case "onreveal":
this.onReveal();
break;
case "onunload":
this.onUnload();
break;
case "oncompleted":
// This event comes in when setCompleted needs to be deferred because
// the dialog isn't loaded yet.
this.completed = true;
break;
default:
break;
}
},
// ---------- nsISupports methods ----------
// This "class" supports nsIProgressDialog, nsIWebProgressListener (by virtue
// of interface inheritance), nsIObserver, and nsISupports.
QueryInterface: function (iid) {
if (iid.equals(Components.interfaces.nsIProgressDialog) ||
iid.equals(Components.interfaces.nsIDownload) ||
iid.equals(Components.interfaces.nsITransfer) ||
iid.equals(Components.interfaces.nsIWebProgressListener) ||
iid.equals(Components.interfaces.nsIWebProgressListener2) ||
iid.equals(Components.interfaces.nsIDownloadProgressListener) ||
iid.equals(Components.interfaces.nsIObserver) ||
iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
iid.equals(Components.interfaces.nsISupports))
return this;
Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
return null;
},
// ---------- nsIInterfaceRequestor methods ----------
getInterface: function(iid) {
if (iid.equals(Components.interfaces.nsIPrompt) ||
iid.equals(Components.interfaces.nsIAuthPrompt)) {
// use the window watcher service to get a nsIPrompt/nsIAuthPrompt impl
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
var prompt;
if (iid.equals(Components.interfaces.nsIPrompt))
prompt = ww.getNewPrompter(this.parent);
else
prompt = ww.getNewAuthPrompter(this.parent);
return prompt;
}
Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
return null;
},
// ---------- implementation methods ----------
// Initialize the dialog.
onLoad: function() {
// Note that onLoad has finished.
this.loaded = true;
// Fill dialog.
this.loadDialog();
// Position dialog.
if ( this.dialog.opener ) {
this.dialog.moveToAlertPosition();
} else {
this.dialog.centerWindowOnScreen();
}
// Set initial focus on "keep open" box. If that box is hidden, or, if
// the download is already complete, then focus is on the cancel/close
// button. The download may be complete if it was really short and the
// dialog took longer to open than to download the data.
if ( !this.completed && !this.saving ) {
this.dialogElement( "keep" ).focus();
} else {
this.dialogElement( "cancel" ).focus();
}
},
// load dialog with initial contents
loadDialog: function() {
// Check whether we're saving versus opening with a helper app.
if ( !this.saving ) {
// Put proper label on source field.
this.setValue( "sourceLabel", this.getString( "openingSource" ) );
// Target is the "preferred" application. Hide if empty.
if ( this.MIMEInfo &&
this.MIMEInfo.preferredApplicationHandler &&
this.MIMEInfo.preferredApplicationHandler.executable ) {
var appName =
this.MIMEInfo.preferredApplicationHandler.executable.leafName;
if ( appName == null || appName.length == 0 ) {
this.hide( "targetRow" );
} else {
// Use the "with:" label.
this.setValue( "targetLabel", this.getString( "openingTarget" ) );
// Name of application.
this.setValue( "target", appName );
}
} else {
this.hide( "targetRow" );
}
} else {
// If target is not a local file, then hide extra dialog controls.
if (this.targetFile != null) {
this.setValue( "target", this.targetFile.path );
} else {
this.setValue( "target", this.target.spec );
this.hide( "pauseResume" );
this.hide( "launch" );
this.hide( "reveal" );
}
}
// Set source field.
this.setValue( "source", this.source.spec );
var now = ( new Date() ).getTime();
// Initialize the elapsed time.
if ( !this.elapsed ) {
this.elapsed = now - this.startTime;
}
// Update elapsed time display.
this.setValue( "timeElapsed", this.formatSeconds( this.elapsed / 1000 ) );
this.setValue( "timeLeft", this.getString( "unknownTime" ) );
// Initialize the "keep open" box. Hide this if we're opening a helper app
// or if we are uploading.
if ( !this.saving || !this.targetFile ) {
// Hide this in this case.
this.hide( "keep" );
} else {
// Initialize using last-set value from prefs.
var prefs = Components.classes[ "@mozilla.org/preferences-service;1" ]
.getService( Components.interfaces.nsIPrefBranch );
if ( prefs ) {
this.dialogElement( "keep" ).checked = prefs.getBoolPref( "browser.download.progressDnldDialog.keepAlive" );
}
}
// Initialize title.
this.setTitle();
},
// Cancel button stops the download (if not completed),
// and closes the dialog.
onCancel: function() {
// Cancel the download, if not completed.
if ( !this.completed ) {
if ( this.operation ) {
const NS_BINDING_ABORTED = 0x804b0002;
this.operation.cancel(NS_BINDING_ABORTED);
// XXX We're supposed to clean up files/directories.
}
if ( this.observer ) {
this.observer.observe( this, "oncancel", "" );
}
this.paused = false;
}
// Test whether the dialog is already closed.
// This will be the case if we've come through onUnload.
if ( this.dialog ) {
// Close the dialog.
this.dialog.close();
}
},
// onunload event means the dialog has closed.
// We go through our onCancel logic to stop the download if still in progress.
onUnload: function() {
// Remember "keep dialog open" setting, if visible.
if ( this.saving ) {
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService( Components.interfaces.nsIPrefBranch );
if ( prefs ) {
prefs.setBoolPref( "browser.download.progressDnldDialog.keepAlive", this.dialogElement( "keep" ).checked );
}
}
this.dialog = null; // The dialog is history.
if ( this.mCancelDownloadOnClose ) {
this.onCancel();
}
},
// onpause event means the user pressed the pause/resume button
// Toggle the pause/resume state (see the function setPause(), below).i
onPause: function() {
this.paused = !this.paused;
},
// onlaunch event means the user pressed the launch button
// Invoke the launch method of the target file.
onLaunch: function() {
try {
const kDontAskAgainPref = "browser.download.progressDnlgDialog.dontAskForLaunch";
try {
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var dontAskAgain = pref.getBoolPref(kDontAskAgainPref);
} catch (e) {
// we need to ask if we're unsure
dontAskAgain = false;
}
if ( !dontAskAgain && this.targetFile.isExecutable() ) {
try {
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService( Components.interfaces.nsIPromptService );
} catch (ex) {
// getService doesn't return null, it throws
return;
}
var title = this.getProperty( "openingAlertTitle",
[ this.fileName() ],
1 );
var msg = this.getProperty( "securityAlertMsg",
[ this.fileName() ],
1 );
var dontaskmsg = this.getProperty( "dontAskAgain",
[ ], 0 );
var checkbox = {value:0};
var okToProceed = promptService.confirmCheck(this.dialog, title, msg, dontaskmsg, checkbox);
try {
if (checkbox.value != dontAskAgain)
pref.setBoolPref(kDontAskAgainPref, checkbox.value);
} catch (ex) {}
if ( !okToProceed )
return;
}
this.targetFile.launch();
this.dialog.close();
} catch ( exception ) {
// XXX Need code here to tell user the launch failed!
dump( "nsProgressDialog::onLaunch failed: " + exception + "\n" );
}
},
// onreveal event means the user pressed the "reveal location" button
// Invoke the reveal method of the target file.
onReveal: function() {
try {
this.targetFile.reveal();
this.dialog.close();
} catch ( exception ) {
}
},
// Get filename from the target.
fileName: function() {
if ( this.targetFile != null )
return this.targetFile.leafName;
try {
var escapedFileName = this.target.QueryInterface(nsIURL).fileName;
var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(nsITextToSubURI);
return textToSubURI.unEscapeURIForUI(this.target.originCharset, escapedFileName);
} catch (e) {}
return "";
},
// Set the dialog title.
setTitle: function() {
// Start with saving/opening template.
// If percentage is not known (-1), use alternate template
var title = this.saving
? ( this.percent != -1 ? this.getString( "savingTitle" ) : this.getString( "unknownSavingTitle" ) )
: ( this.percent != -1 ? this.getString( "openingTitle" ) : this.getString( "unknownOpeningTitle" ) );
// Use file name as insert 1.
title = this.replaceInsert( title, 1, this.fileName() );
// Use percentage as insert 2 (if known).
if ( this.percent != -1 ) {
title = this.replaceInsert( title, 2, this.percent );
}
// Set dialog's title property.
if ( this.dialog ) {
this.dialog.document.title = title;
}
},
// Update the dialog to indicate specified percent complete.
setPercent: function( percent ) {
// Maximum percentage is 100.
if ( percent > 100 ) {
percent = 100;
}
// Test if percentage is changing.
if ( this.percent != percent ) {
this.mPercent = percent;
// If dialog not opened yet, bail early.
if ( !this.loaded ) {
return this.mPercent;
}
if ( percent == -1 ) {
// Progress meter needs to be in "undetermined" mode.
this.mode = "undetermined";
// Update progress meter percentage text.
this.setValue( "progressText", "" );
} else {
// Progress meter needs to be in normal mode.
this.mode = "normal";
// Set progress meter thermometer.
this.setValue( "progress", percent );
// Update progress meter percentage text.
this.setValue( "progressText", this.replaceInsert( this.getString( "percentMsg" ), 1, percent ) );
}
// Update title.
this.setTitle();
}
return this.mPercent;
},
// Update download rate and dialog display.
setRate: function( rate ) {
this.mRate = rate;
return this.mRate;
},
// Handle download completion.
setCompleted: function() {
// If dialog hasn't loaded yet, defer this.
if ( !this.loaded ) {
this.dialog.setTimeout( function(obj){obj.setCompleted()}, 100, this );
return false;
}
if ( !this.mCompleted ) {
this.mCompleted = true;
// If the "keep dialog open" box is checked, then update dialog.
if ( this.dialog && this.dialogElement( "keep" ).checked ) {
// Indicate completion in status area.
var string = this.getString( "completeMsg" );
string = this.replaceInsert( string,
1,
this.formatSeconds( this.elapsed/1000 ) );
string = this.replaceInsert( string,
2,
this.targetFile.fileSize >> 10 );
this.setValue( "status", string);
// Put progress meter at 100%.
this.percent = 100;
// Set time remaining to 00:00.
this.setValue( "timeLeft", this.formatSeconds( 0 ) );
// Change Cancel button to Close, and give it focus.
var cancelButton = this.dialogElement( "cancel" );
cancelButton.label = this.getString( "close" );
cancelButton.focus();
// Activate reveal/launch buttons if we enable them.
var enableButtons = true;
try {
var prefs = Components.classes[ "@mozilla.org/preferences-service;1" ]
.getService( Components.interfaces.nsIPrefBranch );
enableButtons = prefs.getBoolPref( "browser.download.progressDnldDialog.enable_launch_reveal_buttons" );
} catch ( e ) {
}
if ( enableButtons ) {
this.enable( "reveal" );
try {
if ( this.targetFile != null ) {
this.enable( "launch" );
}
} catch(e) {
}
}
// Disable the Pause/Resume buttons.
this.dialogElement( "pauseResume" ).disabled = true;
// Fix up dialog layout (which gets messed up sometimes).
this.dialog.sizeToContent();
// GetAttention to show the user that we're done
this.dialog.getAttention();
} else if ( this.dialog ) {
this.dialog.close();
}
}
return this.mCompleted;
},
// Set progress meter to given mode ("normal" or "undetermined").
setMode: function( newMode ) {
if ( this.mode != newMode ) {
// Need to update progress meter.
this.dialogElement( "progress" ).setAttribute( "mode", newMode );
}
return this.mMode = newMode;
},
// Set pause/resume state.
setPaused: function( pausing ) {
// If state changing, then update stuff.
if ( this.paused != pausing ) {
var string = pausing ? "resume" : "pause";
this.dialogElement( "pauseResume" ).label = this.getString(string);
// If we have an observer, tell it to suspend/resume
if ( this.observer ) {
this.observer.observe( this, pausing ? "onpause" : "onresume" , "" );
}
}
return this.mPaused = pausing;
},
// Format number of seconds in hh:mm:ss form.
formatSeconds: function( secs ) {
// Round the number of seconds to remove fractions.
secs = parseInt( secs + .5 );
var hours = parseInt( secs/3600 );
secs -= hours*3600;
var mins = parseInt( secs/60 );
secs -= mins*60;
var result;
if ( hours )
result = this.getString( "longTimeFormat" );
else
result = this.getString( "shortTimeFormat" );
if ( hours < 10 )
hours = "0" + hours;
if ( mins < 10 )
mins = "0" + mins;
if ( secs < 10 )
secs = "0" + secs;
// Insert hours, minutes, and seconds into result string.
result = this.replaceInsert( result, 1, hours );
result = this.replaceInsert( result, 2, mins );
result = this.replaceInsert( result, 3, secs );
return result;
},
// Get dialog element using argument as id.
dialogElement: function( id ) {
// Check if we've already fetched it.
if ( !( id in this.fields ) ) {
// No, then get it from dialog.
try {
this.fields[ id ] = this.dialog.document.getElementById( id );
} catch(e) {
this.fields[ id ] = {
value: "",
setAttribute: function(id,val) {},
removeAttribute: function(id) {}
}
}
}
return this.fields[ id ];
},
// Set dialog element value for given dialog element.
setValue: function( id, val ) {
this.dialogElement( id ).value = val;
},
// Enable dialog element.
enable: function( field ) {
this.dialogElement( field ).removeAttribute( "disabled" );
},
// Get localizable string from properties file.
getProperty: function( propertyId, strings, len ) {
if ( !this.mBundle ) {
this.mBundle = Components.classes[ "@mozilla.org/intl/stringbundle;1" ]
.getService( Components.interfaces.nsIStringBundleService )
.createBundle( "chrome://global/locale/nsProgressDialog.properties");
}
return len ? this.mBundle.formatStringFromName( propertyId, strings, len )
: this.mBundle.getStringFromName( propertyId );
},
// Get localizable string (from dialog <data> elements).
getString: function ( stringId ) {
// Check if we've fetched this string already.
if ( !( this.strings && stringId in this.strings ) ) {
// Presume the string is empty if we can't get it.
this.strings[ stringId ] = "";
// Try to get it.
try {
this.strings[ stringId ] = this.dialog.document.getElementById( "string."+stringId ).childNodes[0].nodeValue;
} catch (e) {}
}
return this.strings[ stringId ];
},
// Replaces insert ("#n") with input text.
replaceInsert: function( text, index, value ) {
var result = text;
var regExp = new RegExp( "#"+index );
result = result.replace( regExp, value );
return result;
},
// Hide a given dialog field.
hide: function( field ) {
this.dialogElement( field ).hidden = true;
// Also hide any related separator element...
var sep = this.dialogElement( field+"Separator" );
if (sep)
sep.hidden = true;
},
// Return input in hex, prepended with "0x" and leading zeros (to 8 digits).
hex: function( x ) {
return "0x" + ("0000000" + Number(x).toString(16)).slice(-8);
},
// Dump text (if debug is on).
dump: function( text ) {
if ( this.debug ) {
dump( text );
}
}
}
// This Component's module implementation. All the code below is used to get this
// component registered and accessible via XPCOM.
var module = {
// registerSelf: Register this component.
registerSelf: function (compMgr, fileSpec, location, type) {
var compReg = compMgr.QueryInterface( Components.interfaces.nsIComponentRegistrar );
compReg.registerFactoryLocation( this.cid,
"Mozilla Download Progress Dialog",
this.contractId,
fileSpec,
location,
type );
},
// getClassObject: Return this component's factory object.
getClassObject: function (compMgr, cid, iid) {
if (!cid.equals(this.cid))
throw Components.results.NS_ERROR_NO_INTERFACE;
if (!iid.equals(Components.interfaces.nsIFactory))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
return this.factory;
},
/* CID for this class */
cid: Components.ID("{F5D248FD-024C-4f30-B208-F3003B85BC92}"),
/* Contract ID for this class */
contractId: "@mozilla.org/progressdialog;1",
/* factory object */
factory: {
// createInstance: Return a new nsProgressDialog object.
createInstance: function (outer, iid) {
if (outer != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return (new nsProgressDialog()).QueryInterface(iid);
}
},
// canUnload: n/a (returns true)
canUnload: function(compMgr) {
return true;
}
};
// NSGetModule: Return the nsIModule object.
function NSGetModule(compMgr, fileSpec) {
return module;
}