Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Gracefully handle dependency download failures.

Refs #1458
  • Loading branch information...
commit 8ab2168dde1abe103c5784dbb942ac4f1c3783e6 1 parent f5fae6f
@arantius arantius authored
View
15 components/greasemonkey.js
@@ -158,7 +158,7 @@ function installDialog(aUri, aBrowser, aService) {
var params = [rs, aScript];
params.wrappedJSObject = params;
// TODO: Find a better fix than this sloppy workaround.
- // Apparently this version of .openWindow() blocks; and as called by the
+ // Apparently this version of .openWindow() blocks; and called by the
// "script meta data available" callback as this is, blocks the further
// download of the script!
var curriedOpenWindow = GM_util.hitch(
@@ -173,13 +173,12 @@ function installDialog(aUri, aBrowser, aService) {
rs.download(function(aSuccess, aType) {
if (!aSuccess) {
- // Failure downloading script; browse to it.
- aService.ignoreNextScript();
- aBrowser.loadURI(aUri.spec, /* aReferrer */ null, /* aCharset */ null);
-// } else if (aSuccess && 'script' == aType) {
-// dump('script is downloaded.\n');
-// } else if (aSuccess && 'dependencies' == aType) {
-// dump('script and all dependencies downloaded.\n');
+ if ('script' == aType) {
+ // Failure downloading script; browse to it.
+ aService.ignoreNextScript();
+ // TODO: Test this in Firefox 3.
+ aBrowser.loadURI(aUri.spec, /* aReferrer */ null, /* aCharset */ null);
+ }
}
});
}
View
22 content/install.js
@@ -76,8 +76,18 @@ function onOk() {
function onProgress(aRemoteScript, aEventType, aData) {
if (!document) return; // lingering download after window cancel
gProgress = Math.floor(100 * aData);
- if (1 == aData) {
+ if (gRemoteScript.done) {
document.getElementById('loading').style.display = 'none';
+ if (gRemoteScript.errorMessage) {
+ document.documentElement.getButton('extra1').disabled = true;
+ document.getElementById('dialogContentBox').style.display = 'none';
+ document.getElementById('errorContentBox').style.display = '-moz-box';
+ document.getElementById('errorMessage')
+ .textContent = gRemoteScript.errorMessage;
+ stopTimer();
+ updateLabel(false);
+ return;
+ }
} else {
document.getElementById('progressmeter').setAttribute('value', gProgress);
}
@@ -118,16 +128,22 @@ function startTimer() {
function stopTimer() {
if (gTimer) window.clearInterval(gTimer);
+ gCurrentDelay = 0;
}
-function updateLabel() {
+function updateLabel(aOkAllowed) {
+ if ('undefined' == typeof aOkAllowed) aOkAllowed = true;
+
if (gCurrentDelay > 0) {
gAcceptButton.focus();
gAcceptButton.label = gAcceptButton.baseLabel + ' (' + gCurrentDelay + ')';
} else {
gAcceptButton.label = gAcceptButton.baseLabel;
}
- gAcceptButton.disabled = (gCurrentDelay > 0) || (gProgress < 100);
+
+ gAcceptButton.disabled = aOkAllowed
+ ? ((gCurrentDelay > 0) || (gProgress < 100))
+ : true;
}
// See: closewindow.xul .
View
11 content/install.xul
@@ -26,9 +26,7 @@
<script type="application/x-javascript" src="chrome://greasemonkey/content/install.js" />
- <vbox id="dialogContentBox"
- flex="1"
- >
+ <vbox id="dialogContentBox" flex="1">
<description id="heading"
style="margin-bottom:1em" />
<vbox id="itemBox"
@@ -82,4 +80,11 @@
</vbox>
</vbox>
+ <vbox id="errorContentBox" flex="1" style="display: none">
+ <description style="margin-bottom: 1em; font-weight: bold;">
+ <!-- TODO: Localize. -->
+ Download Error
+ </description>
+ <description id="errorMessage" style="white-space: pre-line" />
+ </vbox>
</dialog>
View
8 locale/en-US/greasemonkey.properties
@@ -1,5 +1,9 @@
extensions.{e4a8a97b-f2ed-450b-b12d-ee082ba24781}.description=A User Script Manager for Firefox
-error.matchPattern.parse=@match: Could not parse the pattern.
-error.matchPattern.scheme=@match: Invalid scheme specified.
+error.downloadingUrl=Error downloading URL:
error.matchPattern.host=@match: Invalid host specified.
+error.matchPattern.parse=@match: Could not parse the pattern.
error.matchPattern.path=@match: Invalid path specified.
+error.matchPattern.scheme=@match: Invalid scheme specified.
+error.parsingScript=Could not parse script:
+error.serverReturned=Server returned
+error.unknown=Unknown error.
View
55 modules/remoteScript.js
@@ -17,6 +17,11 @@ var GM_config = GM_util.getService().config;
var ioService = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
+var stringBundle = Components
+ .classes["@mozilla.org/intl/stringbundle;1"]
+ .getService(Components.interfaces.nsIStringBundleService)
+ .createBundle("chrome://greasemonkey/locale/greasemonkey.properties");
+
/////////////////////////////// Private Helpers ////////////////////////////////
function assertIsFunction(aFunc, aMessage) {
@@ -48,10 +53,11 @@ function filenameFromUri(aUri, aDefault) {
////////////////////////// Private Progress Listener ///////////////////////////
function ProgressListener(
- aRemoteScript, aCompletionCallback, aProgressCallback) {
- this._remoteScript = aRemoteScript;
+ aRemoteScript, aUri, aCompletionCallback, aProgressCallback) {
this._completionCallback = aCompletionCallback || function() {};
this._progressCallback = aProgressCallback || function() {};
+ this._remoteScript = aRemoteScript;
+ this._uri = aUri;
}
ProgressListener.prototype.onLocationChange = function(
@@ -67,7 +73,7 @@ ProgressListener.prototype.onProgressChange = function(
if (!this._progressCallback(progress)) {
// The progress callback is where we check for HTML type, and return false
// if so. In such a case, immediately complete as a failure.
- this._completionCallback(false, 'any');
+ this._completionCallback(false, 'script');
}
};
@@ -83,10 +89,14 @@ ProgressListener.prototype.onStateChange = function(
return;
}
var error = aStatus !== 0;
+ var errorMessage = stringBundle.GetStringFromName('error.unknown');
try {
var httpChannel = aRequest.QueryInterface(Ci.nsIHttpChannel);
error |= !httpChannel.requestSucceeded;
error |= httpChannel.responseStatus >= 400;
+ errorMessage = stringBundle.GetStringFromName('error.serverReturned')
+ + ' ' + httpChannel.responseStatus + ' '
+ + httpChannel.responseStatusText + '.';
} catch (e) {
try {
aRequest.QueryInterface(Ci.nsIFileChannel);
@@ -104,9 +114,12 @@ ProgressListener.prototype.onStateChange = function(
}
}
- // Indicate final progress (complete).
+ if (error) {
+ errorMessage = stringBundle.GetStringFromName('error.downloadingUrl')
+ + '\n' + this._uri.spec + '\n\n' + errorMessage;
+ this._remoteScript.cleanup(errorMessage);
+ }
this._progressCallback(1);
- // Call back with that found error state.
this._completionCallback(!error);
};
@@ -138,15 +151,21 @@ function RemoteScript(aUrl) {
this._url = aUrl;
this.done = false;
+ this.errorMessage = null;
this.script = null;
}
-/** Clean up all temporary files. */
-RemoteScript.prototype.cleanup = function() {
+/** Clean up all temporary files, stop all actions. */
+RemoteScript.prototype.cleanup = function(aErrorMessage) {
+ this.errorMessage = aErrorMessage || null;
+ this.done = true;
+
if (this._wbp) this._wbp.cancelSave();
if (this._tempDir && this._tempDir.exists()) {
this._tempDir.remove(true);
}
+
+ this._dispatchCallbacks('progress', 1);
};
/** Download the entire script, starting from the .user.js itself. */
@@ -193,7 +212,7 @@ RemoteScript.prototype.install = function(aOldScript) {
while (file.exists()) {
suffix++;
file = GM_util.scriptDir();
- file = append(this._baseName + '-' + suffix);
+ file.append(this._baseName + '-' + suffix);
}
this._baseName = file.leafName;
@@ -234,6 +253,8 @@ RemoteScript.prototype._dispatchCallbacks = function(aType, aData) {
/** Download any dependencies (@icon, @require, @resource). */
RemoteScript.prototype._downloadDependencies = function(aCompletionCallback) {
+ if (this.done) return;
+
this._progressIndex++;
if (this._progressIndex > this._dependencies.length) {
this.done = true;
@@ -248,7 +269,11 @@ RemoteScript.prototype._downloadDependencies = function(aCompletionCallback) {
var file = GM_util.getTempFile(this._tempDir, filenameFromUri(uri));
dependency.setFilename(file);
- function dependencyDownloadComplete(aChannel) {
+ function dependencyDownloadComplete(aChannel, aSuccess) {
+ if (!aSuccess) {
+ aCompletionCallback(aSuccess, 'dependency');
+ return;
+ }
if (dependency.setMimetype) {
dependency.setMimetype(aChannel.contentType);
}
@@ -271,6 +296,11 @@ RemoteScript.prototype._downloadFile = function(
assertIsFunction(aCompletionCallback,
'_downloadFile() completion callback is not a function.');
+ if (!GM_util.isGreasemonkeyable(aUri.spec)) {
+ this.cleanup('Will not download unsafe URL:\n' + aUri.spec);
+ return;
+ }
+
// Dangerous semi-global state: The web browser persist object is stored
// in the object, so that it can be canceled. Parallel downloads would need
// to be handled differently.
@@ -283,7 +313,7 @@ RemoteScript.prototype._downloadFile = function(
Ci.nsIWebBrowserPersist.PERSIST_FLAGS_CLEANUP_ON_FAILURE |
Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FORCE_ALLOW_COOKIES;
this._wbp.progressListener = new ProgressListener(
- this,
+ this, aUri,
GM_util.hitch(null, aCompletionCallback, channel),
GM_util.hitch(this, this._downloadFileProgress, channel));
this._wbp.saveChannel(channel, aFile);
@@ -364,9 +394,8 @@ RemoteScript.prototype._parseScriptFile = function(aForce) {
try {
var script = GM_config.parse(source, this._uri);
} catch (e) {
- dump('RemoteScript._parseScriptFile error: ' + e + '\n');
- // TODO: Surface this error? How?
- // TODO: In case of parse error, stop download?
+ this.cleanup(
+ stringBundle.GetStringFromName('error.parsingScript') + ':\n' + e);
return null;
}
this._baseName = cleanFilename(script.name, 'gm-script');
Please sign in to comment.
Something went wrong with that request. Please try again.