Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Completely refactor Config.parse() into a standalone module.

Roundabout way of fixing #1467
  • Loading branch information...
commit 236607559f1c0bddb1a444e8e006eee375a33039 1 parent 30eda66
@arantius arantius authored
View
4 components/greasemonkey.js
@@ -265,10 +265,6 @@ function startup() {
.getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://global/content/XPCNativeWrapper.js");
loader.loadSubScript("chrome://greasemonkey/content/config.js");
- loader.loadSubScript("chrome://greasemonkey/content/script.js");
- loader.loadSubScript("chrome://greasemonkey/content/scriptrequire.js");
- loader.loadSubScript("chrome://greasemonkey/content/scriptresource.js");
- loader.loadSubScript("chrome://greasemonkey/content/scripticon.js");
loader.loadSubScript("chrome://greasemonkey/content/miscapis.js");
loader.loadSubScript("chrome://greasemonkey/content/xmlhttprequester.js");
loader.loadSubScript("chrome://greasemonkey/content/third-party/mpl-utils.js");
View
181 content/config.js
@@ -1,5 +1,6 @@
Components.utils.import('resource://greasemonkey/constants.js');
Components.utils.import('resource://greasemonkey/prefmanager.js');
+Components.utils.import('resource://greasemonkey/script.js');
Components.utils.import('resource://greasemonkey/third-party/MatchPattern.js');
Components.utils.import('resource://greasemonkey/util.js');
@@ -124,179 +125,6 @@ Config.prototype._save = function(saveNow) {
GM_util.writeToFile(domSerializer.serializeToString(doc), this._configFile);
};
-Config.prototype.parse = function(source, uri, updateScript) {
- var script = new Script();
-
- if (uri) script._downloadURL = uri.spec;
-
- // read one line at a time looking for start meta delimiter or EOF
- var lines = source.match(/.+/g);
- var lnIdx = 0;
- var result = {};
- var foundMeta = false;
-
- while ((result = lines[lnIdx++])) {
- if (result.indexOf("// ==UserScript==") == 0) {
- foundMeta = true;
- break;
- }
- }
-
- // gather up meta lines
- if (foundMeta) {
- // used for duplicate resource name detection
- var previousResourceNames = {};
- script._rawMeta = "";
-
- while ((result = lines[lnIdx++])) {
- if (result.indexOf("// ==/UserScript==") == 0) {
- break;
- }
-
- var match = result.match(/\/\/ \@(\S+)(?:\s+([^\n]+))?/);
- if (match === null) continue;
-
- var header = match[1];
- var value = (match[2] && match[2].replace(/\s+$/, '')) || null;
-
- if (!value) {
- switch (header) {
- case "unwrap":
- script._unwrap = true;
- break;
- default:
- continue;
- }
- }
-
- switch (header) {
- case "name":
- case "namespace":
- case "description":
- case "version":
- case "updateURL":
- script["_" + header] = value;
- break;
- case "installURL":
- script._downloadURL = value;
- case "include":
- script._includes.push(value);
- break;
- case "userInclude":
- script._userIncludes.push(value);
- break;
- case "exclude":
- script._excludes.push(value);
- break;
- case "userExclude":
- script._userExcludes.push(value);
- break;
- case "match":
- try {
- var match = new MatchPattern(value);
- script._matches.push(match);
- } catch (e) {
- GM_util.logError("Ignoring @match pattern " + value + " because:\n" + e);
- }
- break;
- case "icon":
- script._rawMeta += header + '\0' + value + '\0';
- try {
- script.icon.metaVal = value;
- } catch (e) {
- if (updateScript) {
- script._dependFail = true;
- } else if (script.icon.dataUriError) {
- throw new Error(e.message);
- } else {
- throw new Error('Failed to get @icon '+ value);
- }
- }
- break;
- case "require":
- try {
- var reqUri = GM_util.uriFromUrl(value, uri);
- var scriptRequire = new ScriptRequire(script);
- scriptRequire._downloadURL = reqUri.spec;
- script._requires.push(scriptRequire);
- script._rawMeta += header + '\0' + value + '\0';
- } catch (e) {
- if (updateScript) {
- script._dependFail = true;
- } else {
- throw new Error('Failed to @require '+ value);
- }
- }
- break;
- case "resource":
- var res = value.match(/(\S+)\s+(.*)/);
- if (res === null) {
- // NOTE: Unlocalized strings
- throw new Error("Invalid syntax for @resource declaration '" +
- value + "'. Resources are declared like: " +
- "@resource <name> <url>.");
- }
-
- var resName = res[1];
- if (previousResourceNames[resName]) {
- throw new Error("Duplicate resource name '" + resName + "' " +
- "detected. Each resource must have a unique " +
- "name.");
- } else {
- previousResourceNames[resName] = true;
- }
-
- try {
- var resUri = GM_util.uriFromUrl(res[2], uri);
- var scriptResource = new ScriptResource(script);
- scriptResource._name = resName;
- scriptResource._downloadURL = resUri.spec;
- script._resources.push(scriptResource);
- script._rawMeta += header + '\0' + resName + '\0' + resUri.spec + '\0';
- } catch (e) {
- if (updateScript) {
- script._dependFail = true;
- } else {
- throw new Error('Failed to get @resource '+ resName +' from '+
- res[2]);
- }
- }
- break;
- case "run-at":
- script._runAt = value;
- break;
- }
- }
- }
-
- if (!script.updateURL && script._downloadURL) {
- script.updateURL = script._downloadURL;
- }
-
- // if no meta info, default to reasonable values
- if (!script._name) {
- var name = (uri && uri.spec) || (updateScript && updateScript.filename);
- if (name) {
- name = name.substring(0, name.indexOf(".user.js"));
- name = name.substring(name.lastIndexOf("/") + 1);
- script._name = name;
- } else {
- script._name = 'user-script';
- }
- }
- if (!script._namespace && uri) script._namespace = uri.host;
- if (!script._description) script._description = "";
- if (!script._version) script._version = "";
- if ("document-start" != script._runAt && "document-end" != script._runAt) {
- script._runAt = "document-end";
- }
- if (script._includes.length == 0 && script._matches.length == 0) {
- script._includes.push("*");
- }
-
- return script;
-};
-
Config.prototype.install = function(script, oldScript) {
var existingIndex = this._find(oldScript || script);
if (!oldScript) oldScript = this.scripts[existingIndex];
@@ -402,8 +230,11 @@ Config.prototype.updateModifiedScripts = function(aWhen, aSafeWin, aChromeWin) {
for (var i = 0, script; script = scripts[i]; i++) {
if (0 == script.pendingExec.length) {
var oldScriptId = new String(script.id);
- var parsedScript = this.parse(
- script.textContent, GM_util.uriFromUrl(script._downloadURL), !!script);
+ var scope = {};
+ Components.utils.import('resource://greasemonkey/parseScript.js', scope);
+ var parsedScript = scope.parse(
+ script.textContent, GM_util.uriFromUrl(script._downloadURL));
+ // TODO: Show PopupNotifications about parse error(s)?
script.updateFromNewScript(parsedScript, aSafeWin, aChromeWin);
this._changed(script, "modified", oldScriptId, true);
} else {
View
4 content/newscript.js
@@ -28,7 +28,9 @@ function doInstall() {
var config = GM_util.getService().config;
// Create a script object with parsed metadata, and ...
- var script = config.parse(scriptSrc);
+ var scope = {};
+ Components.utils.import('resource://greasemonkey/parseScript.js', scope);
+ var script = scope.parse(scriptSrc);
// ... make sure entered details will not ruin an existing file.
if (config.installIsUpdate(script)) {
var overwrite = confirm(bundle.getString("newscript.exists"));
View
142 modules/parseScript.js
@@ -0,0 +1,142 @@
+var EXPORTED_SYMBOLS = ['parse'];
+
+Components.utils.import('resource://greasemonkey/script.js');
+Components.utils.import('resource://greasemonkey/scriptIcon.js');
+Components.utils.import('resource://greasemonkey/scriptRequire.js');
+Components.utils.import('resource://greasemonkey/scriptResource.js');
+Components.utils.import('resource://greasemonkey/third-party/MatchPattern.js');
+Components.utils.import('resource://greasemonkey/util.js');
+
+var gLineSplitRegexp = /.+/g;
+var gAllMetaRegexp = new RegExp(
+ '^// ==UserScript==([\\s\\S]*?)// ==/UserScript==', 'm');
+var gMetaLineRegexp = new RegExp('// @(\\S+)(?:\\s+(.*))?');
+
+/** Parse the source of a script; produce Script object. */
+function parse(aSource, aUri) {
+ var script = new Script();
+
+ if (aUri) script._downloadURL = aUri.spec;
+ if (aUri && aUri.spec) {
+ var name = aUri.spec;
+ name = name.substring(0, name.indexOf(".user.js"));
+ name = name.substring(name.lastIndexOf("/") + 1);
+ script._name = name;
+ }
+ if (aUri) script._namespace = aUri.host;
+
+ var meta = aSource.match(gAllMetaRegexp);
+ meta = meta && meta[1] || '';
+ meta = meta.match(gLineSplitRegexp);
+
+ var resourceNames = {};
+ for (var i = 0, metaLine = ''; metaLine = meta[i]; i++) {
+ metaLine = metaLine.replace(/\s+$/, '');
+
+ var match = metaLine.match(gMetaLineRegexp);
+ if (!match) continue;
+
+ var header = match[1];
+ var value = match[2] || null;
+
+ switch (header) {
+ case 'description':
+ case 'name':
+ case 'namespace':
+ case 'updateURL':
+ case 'version':
+ script['_' + header] = value;
+ break;
+
+ case 'downloadURL':
+ case 'installURL':
+ script._downloadURL = value;
+ break;
+ case 'exclude':
+ script._excludes.push(value);
+ break;
+ case 'icon':
+ try {
+ script.icon.setMetaVal(value);
+ script._rawMeta += header + '\0' + value + '\0';
+ } catch (e) {
+ script.parseErrors.push(e.message);
+ }
+ break;
+ case 'include':
+ script._includes.push(value);
+ break;
+ case 'match':
+ try {
+ var match = new MatchPattern(value);
+ script._matches.push(match);
+ } catch (e) {
+ // TODO: Localize.
+ script.parseErrors.push(
+ 'Ignoring @match pattern ' + value + ' because:\n' + e);
+ }
+ break;
+ case 'require':
+ try {
+ var reqUri = GM_util.uriFromUrl(value, aUri);
+ var scriptRequire = new ScriptRequire(script);
+ scriptRequire._downloadURL = reqUri.spec;
+ script._requires.push(scriptRequire);
+ script._rawMeta += header + '\0' + value + '\0';
+ } catch (e) {
+ // TODO: Localize.
+ script.parseErrors.push('Failed to @require URL: '+ value);
+ }
+ break;
+ case 'resource':
+ var res = value.match(/(\S+)\s+(.*)/);
+ if (res === null) {
+ // TODO: Localize.
+ script.parseErrors.push(
+ 'Invalid syntax for @resource declaration "' + value
+ + '". Resources are declared like "@resource <name> <url>".');
+ break;
+ }
+
+ var resName = res[1];
+ if (resourceNames[resName]) {
+ script.parseErrors.push(
+ 'Duplicate resource name "' + resName + '" detected. '
+ + 'Each resource must have a unique name.');
+ break;
+ }
+ resourceNames[resName] = true;
+
+ try {
+ var resUri = GM_util.uriFromUrl(res[2], aUri);
+ var scriptResource = new ScriptResource(script);
+ scriptResource._name = resName;
+ scriptResource._downloadURL = resUri.spec;
+ script._resources.push(scriptResource);
+ script._rawMeta += header + '\0' + resName + '\0' + resUri.spec + '\0';
+ } catch (e) {
+ script.parseErrors.push(
+ 'Failed to get @resource '+ resName +' from ' + res[2]);
+ }
+ break;
+ case 'run-at':
+ script._runAt = value;
+ break;
+ case 'unwrap':
+ script._unwrap = true;
+ break;
+ }
+ }
+
+ if (!script.updateURL && script._downloadURL) {
+ script.updateURL = script._downloadURL;
+ }
+ if ('document-start' != script._runAt && 'document-end' != script._runAt) {
+ script._runAt = 'document-end';
+ }
+ if (script._includes.length == 0 && script._matches.length == 0) {
+ script._includes.push('*');
+ }
+
+ return script;
+}
View
45 modules/remoteScript.js
@@ -3,16 +3,9 @@ var EXPORTED_SYMBOLS = ['RemoteScript'];
var Cc = Components.classes;
var Ci = Components.interfaces;
+Components.utils.import('resource://greasemonkey/script.js');
Components.utils.import('resource://greasemonkey/util.js');
-// Load in the Script objects, not yet module-ized.
-var loader = Cc['@mozilla.org/moz/jssubscript-loader;1']
- .getService(Ci.mozIJSSubScriptLoader);
-loader.loadSubScript('chrome://greasemonkey/content/script.js');
-loader.loadSubScript('chrome://greasemonkey/content/scriptrequire.js');
-loader.loadSubScript('chrome://greasemonkey/content/scriptresource.js');
-loader.loadSubScript('chrome://greasemonkey/content/scripticon.js');
-
var GM_config = GM_util.getService().config;
var ioService = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
@@ -447,27 +440,25 @@ RemoteScript.prototype._downloadScriptCb = function(
};
/** Produce a Script object from the contents of this._scriptFile. */
-RemoteScript.prototype._metadataRegExp = new RegExp(
- '^// ==UserScript==([\\s\\S]*?)^// ==/UserScript==', 'm');
-RemoteScript.prototype._parseScriptFile = function(aForce) {
- var content = GM_util.getContents(this._scriptFile);
- var meta = content.match(this._metadataRegExp);
-
- var source = (meta && meta[0]) || (aForce && content) || null;
- if (source) {
- try {
- var script = GM_config.parse(source, this._uri);
- } catch (e) {
- this.cleanup(
- stringBundle.GetStringFromName('error.parsingScript') + ':\n' + e);
- return null;
- }
- this._baseName = cleanFilename(script.name, 'gm-script');
- this._dispatchCallbacks('scriptMeta', script);
- return script;
+RemoteScript.prototype._parseScriptFile = function() {
+ if (this.errorMessage) return;
+
+ var source = GM_util.getContents(this._scriptFile);
+ if (!source) return null;
+
+ var scope = {};
+ Components.utils.import('resource://greasemonkey/parseScript.js', scope);
+ var script = scope.parse(source, this._uri);
+ if (script.parseErrors.length) {
+ this.cleanup(
+ stringBundle.GetStringFromName('error.parsingScript')
+ + '\n' + script.parseErrors);
+ return null;
}
+ this._baseName = cleanFilename(script.name, 'gm-script');
+ this._dispatchCallbacks('scriptMeta', script);
- return null;
+ return script;
};
RemoteScript.prototype._postParseScriptFile = function() {
View
62 content/script.js → modules/script.js
@@ -1,5 +1,10 @@
+var EXPORTED_SYMBOLS = ['Script'];
+
Components.utils.import('resource://greasemonkey/constants.js');
Components.utils.import('resource://greasemonkey/prefmanager.js');
+Components.utils.import('resource://greasemonkey/scriptIcon.js');
+Components.utils.import('resource://greasemonkey/scriptRequire.js');
+Components.utils.import('resource://greasemonkey/scriptResource.js');
Components.utils.import('resource://greasemonkey/third-party/MatchPattern.js');
Components.utils.import('resource://greasemonkey/third-party/convert2RegExp.js');
Components.utils.import('resource://greasemonkey/util.js');
@@ -7,39 +12,40 @@ Components.utils.import('resource://greasemonkey/util.js');
function Script(configNode) {
this._observers = [];
- this._downloadURL = null;
- this._updateURL = null;
- this._tempFile = null;
this._basedir = null;
- this._filename = null;
- this._modified = null;
+ this._dependFail = false;
this._dependhash = null;
-
- this._name = null;
- this._namespace = "";
- this._id = null;
- this._prefroot = null;
- this._description = null;
- this._version = null;
- this._icon = new ScriptIcon(this);
+ this._description = '';
+ this._downloadURL = null;
this._enabled = true;
- this.needsUninstall = false;
- this._includes = [];
- this._userIncludes = [];
this._excludes = [];
- this._userExcludes = [];
+ this._filename = null;
+ this._icon = new ScriptIcon(this);
+ this._id = null;
+ this._includes = [];
+ this._lastUpdateCheck = null;
this._matches = [];
+ this._modified = null;
+ this._name = 'user-script';
+ this._namespace = '';
+ this._prefroot = null;
+ this._rawMeta = '';
this._requires = [];
this._resources = [];
- this._unwrap = false;
- this._dependFail = false;
this._runAt = null;
- this._rawMeta = null;
- this._lastUpdateCheck = null;
- this.checkRemoteUpdates = true;
- this.updateAvailable = null;
+ this._tempFile = null;
+ this._unwrap = false;
+ this._updateURL = null;
this._updateVersion = null;
+ this._userExcludes = [];
+ this._userIncludes = [];
+ this._version = null;
+
+ this.checkRemoteUpdates = true;
+ this.needsUninstall = false;
+ this.parseErrors = [];
this.pendingExec = [];
+ this.updateAvailable = null;
if (configNode) this._loadFromConfigNode(configNode);
}
@@ -221,8 +227,10 @@ Script.prototype._loadFromConfigNode = function(node) {
|| !node.hasAttribute("dependhash")
|| !node.hasAttribute("version")
) {
- var parsedScript = GM_util.getService().config.parse(
- this.textContent, GM_util.uriFromUrl(this._downloadURL), !!this);
+ var scope = {};
+ Components.utils.import('resource://greasemonkey/parseScript.js', scope);
+ var parsedScript = scope.parse(
+ this.textContent, GM_util.uriFromUrl(this._downloadURL));
this._modified = this.file.lastModifiedTime;
this._dependhash = GM_util.sha1(parsedScript._rawMeta);
@@ -551,7 +559,9 @@ Script.prototype.checkRemoteVersion = function(req, aCallback) {
if (req.status != 200 && req.status != 0) return aCallback(false);
var source = req.responseText;
- var newScript = GM_util.getService().config.parse(source, this._downloadURL);
+ var scope = {};
+ Components.utils.import('resource://greasemonkey/parseScript.js', scope);
+ var newScript = scope.parse(source, this._downloadURL);
var remoteVersion = newScript.version;
if (!remoteVersion) return aCallback(false);
View
45 content/scripticon.js → modules/scriptIcon.js
@@ -1,3 +1,6 @@
+var EXPORTED_SYMBOLS = ['ScriptIcon'];
+
+Components.utils.import('resource://greasemonkey/scriptResource.js');
Components.utils.import('resource://greasemonkey/util.js');
function ScriptIcon(script) {
@@ -12,26 +15,6 @@ function ScriptIcon(script) {
ScriptIcon.prototype = new ScriptResource();
ScriptIcon.prototype.constructor = ScriptIcon;
-ScriptIcon.prototype.__defineSetter__("metaVal", function(value) {
- // accept data uri schemes for image mime types
- if (/^data:image\//i.test(value)) {
- this._dataURI = value;
- } else if (/^data:/i.test(value)) {
- this.dataUriError = true;
- throw new Error('@icon data: uri must be an image type');
- } else {
- var resUri = GM_util.uriFromUrl(this._script._downloadURL);
- this._downloadURL = GM_util.uriFromUrl(value, resUri).spec;
- }
-});
-
-ScriptIcon.prototype.hasDownloadURL = function() {
- return !!this._downloadURL;
-};
-
-ScriptIcon.prototype.isImage = function(contentType) {
- return /^image\//i.test(contentType);
-};
ScriptIcon.prototype.__defineGetter__("filename",
function ScriptIcon_getFilename() {
@@ -59,3 +42,25 @@ function ScriptIcon_setFileURL(iconURL) {
this._filename = iconURL;
}
});
+
+
+ScriptIcon.prototype.hasDownloadURL = function() {
+ return !!this._downloadURL;
+};
+
+ScriptIcon.prototype.isImage = function(contentType) {
+ return /^image\//i.test(contentType);
+};
+
+ScriptIcon.prototype.setMetaVal = function(value) {
+ // accept data uri schemes for image mime types
+ if (/^data:image\//i.test(value)) {
+ this._dataURI = value;
+ } else if (/^data:/i.test(value)) {
+ this.dataUriError = true;
+ throw new Error('@icon data: uri must be an image type');
+ } else {
+ var resUri = GM_util.uriFromUrl(this._script._downloadURL);
+ this._downloadURL = GM_util.uriFromUrl(value, resUri).spec;
+ }
+};
View
2  content/scriptrequire.js → modules/scriptRequire.js
@@ -1,3 +1,5 @@
+var EXPORTED_SYMBOLS = ['ScriptRequire'];
+
Components.utils.import('resource://greasemonkey/constants.js');
Components.utils.import('resource://greasemonkey/util.js');
View
3  content/scriptresource.js → modules/scriptResource.js
@@ -1,3 +1,6 @@
+var EXPORTED_SYMBOLS = ['ScriptResource'];
+
+Components.utils.import('resource://greasemonkey/scriptRequire.js');
Components.utils.import('resource://greasemonkey/util.js');
function ScriptResource(script) {
View
4 modules/util/showInstallDialog.js
@@ -1,8 +1,8 @@
-Components.utils.import('resource://greasemonkey/util.js');
-
var EXPORTED_SYMBOLS = ['showInstallDialog'];
+Components.utils.import('resource://greasemonkey/util.js');
Components.utils.import('resource://greasemonkey/remoteScript.js');
+
var gWindowWatcher = Components
.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
Please sign in to comment.
Something went wrong with that request. Please try again.