[Pkg-mozext-commits] [stylish] 01/01: Imported Upstream version 1.4.2

Dmitry Smirnov onlyjob at moszumanska.debian.org
Fri Mar 7 01:02:01 UTC 2014


This is an automated email from the git hooks/post-receive script.

onlyjob pushed a commit to branch upstream
in repository stylish.

commit 69eb2f3 (upstream)
Author: Dmitry Smirnov <onlyjob at member.fsf.org>
Date:   Fri Mar 7 00:55:23 2014

    Imported Upstream version 1.4.2
---
 components/stylishDataSource.js |   6 +-
 components/stylishStartup.js    | 119 +++++++++++++---------
 components/stylishStyle.js      |  56 ++++++++--
 components/stylishStyle.xpt     | Bin 1452 -> 1502 bytes
 content/base-test.js            |  64 ++++++------
 content/common.js               |  28 +++--
 content/edit.js                 | 219 ++++++++++++++++++++++++++++++----------
 content/edit.xul                |   3 +-
 content/install-overlay.js      |  28 +++--
 content/manage.html             |   2 +-
 content/manage.js               |   2 +-
 content/style.xbl               |   2 +-
 idl/stylishStyle.idl            |  13 ++-
 install.rdf                     |   1 +
 locale/zh-CN/common.properties  |   8 +-
 locale/zh-CN/domi.dtd           |   2 +-
 locale/zh-CN/edit.dtd           |   2 +-
 locale/zh-CN/edit.properties    |   8 +-
 locale/zh-CN/extensions.dtd     |  32 +++---
 locale/zh-CN/install.properties |  14 +--
 locale/zh-CN/manage.dtd         |   4 +-
 locale/zh-CN/manage.properties  |  22 ++--
 locale/zh-CN/overlay.dtd        |   2 +-
 locale/zh-CN/overlay.properties |   4 +-
 24 files changed, 430 insertions(+), 211 deletions(-)

diff --git a/components/stylishDataSource.js b/components/stylishDataSource.js
index b3fb929..283976b 100644
--- a/components/stylishDataSource.js
+++ b/components/stylishDataSource.js
@@ -68,7 +68,7 @@ StylishDataSource.prototype = {
 	_file: null,
 
 	migrate: function(connection) {
-		var expectedDataVersion = 5;
+		var expectedDataVersion = 6;
 		var currentDataVersion = connection.schemaVersion;
 		if (currentDataVersion >= expectedDataVersion)
 			return;
@@ -94,6 +94,10 @@ StylishDataSource.prototype = {
 				try {
 					connection.executeSimpleSQL("ALTER TABLE styles ADD COLUMN applyBackgroundUpdates INTEGER NOT NULL DEFAULT 1;"); // 1 = AddonManager.AUTOUPDATE_DEFAULT
 				} catch (ex) {}
+			case 5:
+				try {
+					connection.executeSimpleSQL("ALTER TABLE styles ADD COLUMN originalMd5 TEXT NULL;");
+				} catch (ex) {}
 		}
 		connection.schemaVersion = expectedDataVersion;
 		connection.commitTransaction();
diff --git a/components/stylishStartup.js b/components/stylishStartup.js
index ff54ed8..398871d 100644
--- a/components/stylishStartup.js
+++ b/components/stylishStartup.js
@@ -146,7 +146,9 @@ function getUserStyleWrapper(style) {
 		},
 
 		get permissions() {
-			return AddonManager.PERM_CAN_UNINSTALL | (style.enabled ? AddonManager.PERM_CAN_DISABLE : AddonManager.PERM_CAN_ENABLE) | (style.updateUrl != null && style.updateUrl != "" && Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch).getBoolPref("extensions.stylish.updatesEnabled") ? AddonManager.PERM_CAN_UPGRADE : 0);
+			return AddonManager.PERM_CAN_UNINSTALL | 
+				(style.enabled ? AddonManager.PERM_CAN_DISABLE : AddonManager.PERM_CAN_ENABLE) | 
+				(style.updateUrl != null && style.updateUrl != "" && style.updateUrl.length <= 2000 && Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch).getBoolPref("extensions.stylish.updatesEnabled") ? AddonManager.PERM_CAN_UPGRADE : 0); // if the url length is too long, a GET won't work, and it's probably going to be too much server-side to handle
 		},
 
 		get isActive() {
@@ -210,7 +212,7 @@ function getUserStyleWrapper(style) {
 		},
 
 		findUpdates: function(listener, flags) {
-			style.checkForUpdates(getUserStyleObserver(this, listener));
+			style.checkForUpdates(getUserStyleUpdateCheckObserver(this, listener));
 		},
 
 		isCompatibleWith: function(appVersion, platformVersion) {
@@ -228,65 +230,31 @@ function getUserStyleWrapper(style) {
 	};
 }
 
-function getUserStyleObserver(addonItem, listener) {
+// An observer for style update checks.
+function getUserStyleUpdateCheckObserver(addonItem, listener) {
 	return {
 		addonItem: addonItem,
 		listener: listener,
 		observe: function(subject, topic, data) {
+			var mainUpdateObject = this;
 			if (subject.id == this.addonItem.id) {
+				// Results of "check for updates"
 				switch (topic) {
 					case "stylish-style-update-check-done":
 						if (data == "update-available" && "onUpdateAvailable" in this.listener) {
-							var installItem = {
-								name: addonItem.name,
-								type: "userstyle",
-								state: AddonManager.STATE_AVAILABLE,
-								addon: addonItem,
-								existingAddon: addonItem,
-								listeners: [],
-								install: function() {
-									this.listeners.forEach(function(l) {
-										if ("onInstallStarted" in l) {
-											l.onInstallStarted(this, this.addon);
-										}
-									}, this);
-									var service = Components.classes["@userstyles.org/style;1"].getService(Components.interfaces.stylishStyle);
-									service.find(this.existingAddon.id, service.CALCULATE_META | service.REGISTER_STYLE_ON_CHANGE).applyUpdate();
-									pendingUpdates = pendingUpdates.filter(function(item) {
-										return item.addon.id != this.addon.id;
-									}, this);
-									this.listeners.forEach(function(l) {
-										if ("onInstallEnded" in l) {
-											l.onInstallEnded(this, this.addon);
-										}
-									}, this);
-								},
-								cancel: function() {
-									throw "Cancelling updates not implemented.";
-								},
-								addListener: function(listener) {
-									if (this.listeners.indexOf(listener) == -1) {
-										this.listeners.push(listener);
-									}
-								},
-								removeListener: function(listener) {
-									this.listeners = this.listeners.filter(function(l) {
-										return l != listener;
-									});
-								}
-							}
+							var installItem = getUserStyleUpdateInstallItem(this.addonItem);
 							if (!pendingUpdates.some(function(item) {
 								return item.addon.id == installItem.addon.id;
 							})) {
 								pendingUpdates.push(installItem);
 							}
-							this.listener.onUpdateAvailable(this.addonItem, installItem);
+							mainUpdateObject.listener.onUpdateAvailable(mainUpdateObject.addonItem, installItem);
 							AddonManagerPrivate.callInstallListeners("onNewInstall", [], installItem);
 						} else if ((data == "no-update-available" || data == "update-check-error") && "onNoUpdateAvailable" in this.listener) {
-							this.listener.onNoUpdateAvailable(this.addonItem);
+							mainUpdateObject.listener.onNoUpdateAvailable(mainUpdateObject.addonItem);
 						}
-						if ("onUpdateFinished" in this.listener) {
-							this.listener.onUpdateFinished(this.addonItem, (data == "update-available" || data == "no-update-available") ? AddonManager.UPDATE_STATUS_NO_ERROR : AddonManager.UPDATE_STATUS_DOWNLOAD_ERROR);
+						if ("onUpdateFinished" in mainUpdateObject.listener) {
+							mainUpdateObject.listener.onUpdateFinished(mainUpdateObject.addonItem, (data == "update-available" || data == "no-update-available") ? AddonManager.UPDATE_STATUS_NO_ERROR : AddonManager.UPDATE_STATUS_DOWNLOAD_ERROR);
 						}
 				}
 			}
@@ -294,6 +262,67 @@ function getUserStyleObserver(addonItem, listener) {
 	}
 }
 
+// Returns an InstallItem representing an update to the user style
+function getUserStyleUpdateInstallItem(addonItem) {
+	return {
+		name: addonItem.name,
+		type: "userstyle",
+		state: AddonManager.STATE_AVAILABLE,
+		addon: addonItem,
+		existingAddon: addonItem,
+		listeners: [],
+		install: function() {
+			this.listeners.forEach(function(l) {
+				if ("onInstallStarted" in l) {
+					l.onInstallStarted(this, this.addon);
+				}
+			}, this);
+			var service = Components.classes["@userstyles.org/style;1"].getService(Components.interfaces.stylishStyle);
+			var that = this;
+			
+			// Results for "apply updates"
+			var updateAttemptObserver = {
+				observe: function(subject, topic, data) {
+					if (topic != "stylish-style-update-done") {
+						return;
+					}
+					switch (data) {
+						case "update-failure":
+						case "no-update-possible":
+							// This is what XPIProvider.jsm does, but for some reason this isn't giving us the right message in the addons manager on an individual check.
+							that.state = AddonManager.STATE_DOWNLOAD_FAILED;
+							that.error = AddonManager.ERROR_FILE_ACCESS;
+							AddonManagerPrivate.callInstallListeners("onDownloadFailed", that.listeners, that);
+							break;
+						case "update-success":
+							AddonManagerPrivate.callInstallListeners("onInstallEnded", that.listeners, that);
+							break;
+					}
+
+					pendingUpdates = pendingUpdates.filter(function(item) {
+						return item.addon.id != this.addon.id;
+					}, that);
+				}
+			}
+			service.find(this.existingAddon.id, service.CALCULATE_META | service.REGISTER_STYLE_ON_CHANGE).applyUpdate(updateAttemptObserver);
+		},
+		cancel: function() {
+			throw "Cancelling updates not implemented.";
+		},
+		addListener: function(listener) {
+			if (this.listeners.indexOf(listener) == -1) {
+				this.listeners.push(listener);
+			}
+		},
+		removeListener: function(listener) {
+			this.listeners = this.listeners.filter(function(l) {
+				return l != listener;
+			});
+		}
+	}
+
+}
+
 var pendingUpdates = [];
 
 
diff --git a/components/stylishStyle.js b/components/stylishStyle.js
index f442935..58c4070 100644
--- a/components/stylishStyle.js
+++ b/components/stylishStyle.js
@@ -6,6 +6,8 @@ function Style() {
 	this.idUrl = null;
 	this.updateUrl = null;
 	this.md5Url = null;
+	this.originalCode = null;
+	this.originalMd5 = null;
 	this.appliedInfo = null;
 	this.lastSavedCode = null;
 	this.applyBackgroundUpdates = null;
@@ -140,7 +142,7 @@ Style.prototype = {
 	/*
 		stylishStyle instance methods
 	*/
-	init: function(url, idUrl, updateUrl, md5Url, name, code, enabled, originalCode, applyBackgroundUpdates) {
+	init: function(url, idUrl, updateUrl, md5Url, name, code, enabled, originalCode, originalMd5, applyBackgroundUpdates) {
 		//the mode may contain a flag that indicates that this is a load rather than a new style
 		var shouldRegister;
 		if (this.mode & this.INTERNAL_LOAD_EVENT) {
@@ -149,7 +151,7 @@ Style.prototype = {
 		} else {
 			shouldRegister = this.shouldRegisterOnChange()
 		}
-		this.initInternal(url, idUrl, updateUrl, md5Url, name, code, enabled, originalCode, shouldRegister, applyBackgroundUpdates);
+		this.initInternal(url, idUrl, updateUrl, md5Url, name, code, enabled, originalCode, originalMd5, shouldRegister, applyBackgroundUpdates);
 	},
 
 	get name() {
@@ -229,9 +231,9 @@ Style.prototype = {
 			that.bind(statement, name, value);
 		}
 		if (this.id == 0) {
-			statement = connection.createStatement("INSERT INTO styles (`url`, `idUrl`, `updateUrl`, `md5Url`, `name`, `code`, `enabled`, `originalCode`, `applyBackgroundUpdates`) VALUES (:url, :idUrl, :updateUrl, :md5Url, :name, :code, :enabled, :originalCode, :applyBackgroundUpdates);");
+			statement = connection.createStatement("INSERT INTO styles (`url`, `idUrl`, `updateUrl`, `md5Url`, `name`, `code`, `enabled`, `originalCode`, `applyBackgroundUpdates`, `originalMd5`) VALUES (:url, :idUrl, :updateUrl, :md5Url, :name, :code, :enabled, :originalCode, :applyBackgroundUpdates, :originalMd5);");
 		} else {
-			statement = connection.createStatement("UPDATE styles SET `url` = :url, `idUrl` = :idUrl, `updateUrl` = :updateUrl, `md5Url` = :md5Url, `name` = :name, `code` = :code, `enabled` = :enabled, `originalCode` = :originalCode, `applyBackgroundUpdates` = :applyBackgroundUpdates WHERE `id` = :id;");
+			statement = connection.createStatement("UPDATE styles SET `url` = :url, `idUrl` = :idUrl, `updateUrl` = :updateUrl, `md5Url` = :md5Url, `name` = :name, `code` = :code, `enabled` = :enabled, `originalCode` = :originalCode, `applyBackgroundUpdates` = :applyBackgroundUpdates, `originalMd5` = :originalMd5 WHERE `id` = :id;");
 			b("id", this.id);
 		}
 
@@ -262,6 +264,7 @@ Style.prototype = {
 		b("code", this.code);
 		b("enabled", this.enabled);
 		b("applyBackgroundUpdates", this.applyBackgroundUpdates);
+		b("originalMd5", this.originalMd5);
 
 		try {
 			statement.execute();
@@ -409,6 +412,9 @@ Style.prototype = {
 	},
 
 	get md5() {
+		if (this.originalMd5 != null) {
+			return this.originalMd5;
+		}
 		//https://developer.mozilla.org/en/nsICryptoHash#Computing_the_Hash_of_a_String
 		var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
 		converter.charset = "UTF-8";
@@ -475,7 +481,7 @@ Style.prototype = {
 		}
 	},
 
-	applyUpdate: function() {
+	applyUpdate: function(observer) {
 		var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
 		observerService.notifyObservers(this, "stylish-style-update-start", null);
 
@@ -483,6 +489,9 @@ Style.prototype = {
 
 		function notifyDone(result) {
 			observerService.notifyObservers(that, "stylish-style-update-done", result);
+			if (observer) {
+				observer.observe(that, "stylish-style-update-done", result);
+			}
 		}
 
 		function handleFailure() {
@@ -494,11 +503,16 @@ Style.prototype = {
 				notifyDone("update-failure");
 				return;
 			}
-			that.code = code;
 			//we're back to being in sync
 			that.originalCode = code;
-			that.save("update");
-			notifyDone("update-success");
+			that.code = code;
+			that.downloadMd5(that.md5Url, function(md5Sum) {
+				if (md5Sum != null) {
+					that.originalMd5 = md5Sum;
+				}
+				that.save("update");
+				notifyDone("update-success");
+			});
 		}
 		if (this.updateUrl) {
 			this.download(this.updateUrl, handleSuccess, handleFailure);
@@ -793,7 +807,7 @@ Style.prototype = {
 					style.mode = mode;
 				// since we can't call initInternal because we're not "inside" the new style, we'll pass a secret flag in the mode
 				style.mode += this.INTERNAL_LOAD_EVENT
-				style.init(e("url"), e("idUrl"), e("updateUrl"), e("md5Url"), e("name"), e("code"), e("enabled"), e("originalCode"), e("applyBackgroundUpdates"));
+				style.init(e("url"), e("idUrl"), e("updateUrl"), e("md5Url"), e("name"), e("code"), e("enabled"), e("originalCode"), e("originalMd5"), e("applyBackgroundUpdates"));
 				style.id = e("id");
 				styles.push(style);
 				styleMap[style.id] = style;
@@ -861,7 +875,7 @@ Style.prototype = {
 		this.calculateInternalMeta();
 	},
 
-	initInternal: function(url, idUrl, updateUrl, md5Url, name, code, enabled, originalCode, shouldRegister, applyBackgroundUpdates) {
+	initInternal: function(url, idUrl, updateUrl, md5Url, name, code, enabled, originalCode, originalMd5, shouldRegister, applyBackgroundUpdates) {
 		this.url = url;
 		this.idUrl = idUrl;
 		this.updateUrl = updateUrl;
@@ -869,6 +883,7 @@ Style.prototype = {
 		this.name = name;
 		this._enabled = enabled;
 		this.originalCode = originalCode;
+		this.originalMd5 = originalMd5;
 		this.setCode(code, shouldRegister);
 		if (!shouldRegister && this.enabled) {
 			this.appliedInfoToBeCalculated = true;
@@ -928,7 +943,9 @@ Style.prototype = {
 					successCallback(request.responseText, contentType);
 				} else {
 					Components.utils.reportError("Download of '" + url + "' resulted in status " + request.status);
-					failureCallback();
+					if (failureCallback) {
+						failureCallback();
+					}
 				}
 			}
 		}, false);
@@ -974,6 +991,23 @@ Style.prototype = {
 			return this.sss.AGENT_SHEET;
 		}
 		return this.sss.AUTHOR_SHEET;
+	},
+
+	downloadMd5: function(md5Url, callback) {
+		if (!md5Url) {
+			callback(null);
+			return;
+		}
+		this.download(md5Url,
+			function(text, contentType) {
+				if (text.length == 32) {
+					callback(text);
+				} else {
+					Components.utils.reportError("Invalid md5 at URL '" + md5Url + "'.");
+					callback(null)
+				}
+			}, null
+		);
 	}
 
 };
diff --git a/components/stylishStyle.xpt b/components/stylishStyle.xpt
index e0693b8..3544f61 100644
Binary files a/components/stylishStyle.xpt and b/components/stylishStyle.xpt differ
diff --git a/content/base-test.js b/content/base-test.js
index f465c88..2be76b4 100644
--- a/content/base-test.js
+++ b/content/base-test.js
@@ -11,7 +11,7 @@ function testStyleSave() {
 	const md5Url = "http://example.com/md5";
 	const name = "Example style";
 	const code = "#example { color: red;}";
-	style.init(url, url, updateUrl, md5Url, name, code, false, null, null);
+	style.init(url, url, updateUrl, md5Url, name, code, false, null, null, null, null);
 	checkValues(style, url, updateUrl, md5Url, name, code);
 	style.save();
 	assert("Style didn't get an ID", style.id != null && style.id != 0);
@@ -32,7 +32,7 @@ function testStyleSave() {
 //Test styles getting applied
 function testStyleApplied() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test", "button { text-decoration: underline !important;}", true, null, null);
+	style.init(null, null, null, null, "Unit test", "button { text-decoration: underline !important;}", true, null, null, null);
 	assert("Style was not enabled", style.enabled);
 	assert("Style was not applied", getButtonStyle().textDecoration == "underline");
 	style.enabled = false;
@@ -53,7 +53,7 @@ function testStyleApplied() {
 //Tests that deleted styles get unapplied
 function testDeleteAndUnapply() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test", "button { text-decoration: underline !important;}", true, null, null);
+	style.init(null, null, null, null, "Unit test", "button { text-decoration: underline !important;}", true, null, null, null);
 	style.save();
 	style = Style.find(style.id, Style.REGISTER_STYLE_ON_CHANGE);
 	style.delete();
@@ -63,7 +63,7 @@ function testDeleteAndUnapply() {
 //Test appliesToUrl on url rules
 function testUrlMatch() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test", "@-moz-document url('http://google.com') { * {color: blue}}", false, null, null);
+	style.init(null, null, null, null, "Unit test", "@-moz-document url('http://google.com') { * {color: blue}}", false, null, null, null);
 	function v() {
 		assert("Style not marked as applied.", style.appliesToUrl("http://google.com"));
 		assert("Style incorrectly marked as applied.", !style.appliesToUrl("http://yahoo.com"));
@@ -82,7 +82,7 @@ function testUrlMatch() {
 //Test appliesToUrl on url prefix rules
 function testUrlPrefixMatch() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test", "@-moz-document url-prefix('http://google.com') { * {color: blue}}", false, null, null);
+	style.init(null, null, null, null, "Unit test", "@-moz-document url-prefix('http://google.com') { * {color: blue}}", false, null, null, null);
 	function v() {
 		assert("Style not marked as applied.", style.appliesToUrl("http://google.com"));
 		assert("Style not marked as applied.", style.appliesToUrl("http://google.com/foo"));
@@ -101,7 +101,7 @@ function testUrlPrefixMatch() {
 //Test appliesToUrl on domain rules
 function testDomainMatch() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test", "@-moz-document domain('google.com') { * {color: blue}}", false, null, null);
+	style.init(null, null, null, null, "Unit test", "@-moz-document domain('google.com') { * {color: blue}}", false, null, null, null);
 	function v() {
 		assert("Style not marked as applied 1.", style.appliesToUrl("http://google.com"));
 		assert("Style not marked as applied 2.", style.appliesToUrl("http://google.com/foo"));
@@ -127,7 +127,7 @@ function testLoadAndUpdateCode() {
 		//first make the style
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		var code = "* {font-style: italic;}";
-		style.init(null, null, null, null, "Unit test - load and update code", code, true, null, null);
+		style.init(null, null, null, null, "Unit test - load and update code", code, true, null, null, null);
 		style.save();
 		//now load it
 		style = Style.find(style.id, Style.REGISTER_STYLE_ON_CHANGE);
@@ -147,7 +147,7 @@ function testLoadAndUpdateName() {
 		//first make the style
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		var code = "* {font-style: italic;}";
-		style.init(null, null, null, null, "Unit test - load and update name", code, true, null, null);
+		style.init(null, null, null, null, "Unit test - load and update name", code, true, null, null, null);
 		style.save();
 		//now load it
 		style = Style.find(style.id, Style.REGISTER_STYLE_ON_CHANGE);
@@ -167,7 +167,7 @@ function testPreviewEnabled() {
 	try {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		var code = "* {font-style: italic;}";
-		style.init(null, null, null, null, "Unit test - preview", code, true, null, null);
+		style.init(null, null, null, null, "Unit test - preview", code, true, null, null, null);
 		assert("Style not initially applied", getButtonStyle().fontStyle == "italic");
 		style.setPreview(true);
 		assert("Style no longer applied after preview turned on", getButtonStyle().fontStyle == "italic");
@@ -187,7 +187,7 @@ function testPreviewDisabled() {
 	try {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		var code = "* {font-style: italic;}";
-		style.init(null, null, null, null, "Unit test - preview", code, false, null, null);
+		style.init(null, null, null, null, "Unit test - preview", code, false, null, null, null);
 		assert("Style initially applied", getButtonStyle().fontStyle != "italic");
 		style.setPreview(true);
 		assert("Style not applied after preview turned on", getButtonStyle().fontStyle == "italic");
@@ -207,7 +207,7 @@ function testPreviewDisabledSaved() {
 	try {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		var code = "* {font-style: italic;}";
-		style.init(null, null, null, null, "Unit test - preview", code, false, null, null);
+		style.init(null, null, null, null, "Unit test - preview", code, false, null, null, null);
 		assert("Style applied on init", getButtonStyle().fontStyle != "italic");
 		style.save();
 		assert("Style applied on init", getButtonStyle().fontStyle != "italic");
@@ -254,7 +254,7 @@ function testBlankApply() {
 //Test arbritrary meta values
 function testMeta() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "foo", "* {}", true, null, null);
+	style.init(null, null, null, null, "foo", "* {}", true, null, null, null);
 	style.addMeta("foo", "bar");
 	style.save();
 	try {
@@ -291,7 +291,7 @@ function testAppliesSearch() {
 	var styles = Style.findForUrl("http://thisisnotarealdomain.com", false, 0, {});
 	assert("Style pre-existing", styles.length == 0);
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "foo", "@-moz-document domain('thisisnotarealdomain.com') {}", true, null, null);
+	style.init(null, null, null, null, "foo", "@-moz-document domain('thisisnotarealdomain.com') {}", true, null, null, null);
 	style.save();
 	var id = style.id;
 	try {
@@ -327,7 +327,7 @@ function testType() {
 		assert(message + " - expected '" + type + "' got '" + currentType +"'", arraysEqual(currentType, type));
 	}
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test", "* {color: blue}", false, null, null);
+	style.init(null, null, null, null, "Unit test", "* {color: blue}", false, null, null, null);
 	ensureType("No namespace no moz-doc", "global");
 	style.code = "@namespace url('http://www.w3.org/1999/xhtml');* {color: blue}";
 	ensureType("HTML namespace no moz-doc", "global");
@@ -357,7 +357,7 @@ function testFindByUrl() {
 	try {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		const url = "http://example.com/foo/bar";
-		style.init(url, url, null, null, "Unit test", "/**/", false, null, null);
+		style.init(url, url, null, null, "Unit test", "/**/", false, null, null, null);
 		style.save();
 		style = Style.findByUrl(url, 0);
 		assert("Style not found", style);
@@ -370,7 +370,7 @@ function testSaveOriginalCodeNoUpdate() {
 	try {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		const url = "http://example.com/foo/bar";
-		style.init(url, url, null, null, "Unit test - save original code no update", "/* original code */", false, null, null);
+		style.init(url, url, null, null, "Unit test - save original code no update", "/* original code */", false, null, null, null);
 		style.save();
 		assert("Style got original code for no reason", style.originalCode == null);
 		style.code = "/* new code */";
@@ -386,7 +386,7 @@ function testSaveOriginalCodeWithUpdate() {
 	try {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		const url = "http://example.com/foo/bar";
-		style.init(url, url, "http://example.com/update", "http://example.com/update", "Unit test - save original code with update", "/* original code */", false, null, null);
+		style.init(url, url, "http://example.com/update", "http://example.com/update", "Unit test - save original code with update", "/* original code */", false, null, null, null);
 		style.save();
 		assert("Style got original code for no reason", style.originalCode == null);
 		style.code = "/* new code */";
@@ -405,7 +405,7 @@ function testSaveOriginalCodeInitial() {
 	try {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		const url = "http://example.com/foo/bar";
-		style.init(url, url, "http://example.com/update", "http://example.com/update", "Unit test - save original code initial", "/* original code */", false, "/* original code */", null);
+		style.init(url, url, "http://example.com/update", "http://example.com/update", "Unit test - save original code initial", "/* original code */", false, "/* original code */", null, null);
 		style.code = "/* new code */";
 		style.save();
 		assert("Style didn't get new code", style.code == "/* new code */");
@@ -418,7 +418,7 @@ function testSaveOriginalCodeInitial() {
 function testLineBreak() {
 	try {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-		style.init(null, null, null, null, "Unit test - whitespace", "#test {\nbackground-image: url('data:image/png;base64,\niVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9kHGBYWMPyCHp4AAAGVSURBVDjL\ntZM/S5tBHMc/jzkxSynRteASOtSOlg4ZBIlB6htw7dI4dHGza1+CFMzTsRAVFHRSsOQtJLhkESqF\nLl2a8Dw+9+R57rk7h2gS8+TRIvqb7o7vffn+uYMnGOd2Uau5Ngu0sVF17iMRo5tq9VMK4LrfH1Ry\nhySU8lF2xKgVz/cmgsattlpNXNd1UkoqlRWkDFMElcpK6qzVak62c3b2M1Pu [...]
+		style.init(null, null, null, null, "Unit test - whitespace", "#test {\nbackground-image: url('data:image/png;base64,\niVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9kHGBYWMPyCHp4AAAGVSURBVDjL\ntZM/S5tBHMc/jzkxSynRteASOtSOlg4ZBIlB6htw7dI4dHGza1+CFMzTsRAVFHRSsOQtJLhkESqF\nLl2a8Dw+9+R57rk7h2gS8+TRIvqb7o7vffn+uYMnGOd2Uau5Ngu0sVF17iMRo5tq9VMK4LrfH1Ry\nhySU8lF2xKgVz/cmgsattlpNXNd1UkoqlRWkDFMElcpK6qzVak62c3b2M1Pu [...]
 		style.enabled = true;
 		delay(100);
 		assert("Style with line breaks worked", getButtonStyle().backgroundImage == "none");
@@ -439,7 +439,7 @@ var updateMd5NoUpdateObserver = {
 }
 function testUpdateMd5NoUpdate() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, "data:text/plain,2642306a8b25001880ccb55e68456165", "testUpdateMd5NoUpdate", "* {color: blue}", false, null, null);
+	style.init(null, null, null, "data:text/plain,2642306a8b25001880ccb55e68456165", "testUpdateMd5NoUpdate", "* {color: blue}", false, null, null, null);
 	observerService.addObserver(updateMd5NoUpdateObserver, "stylish-style-update-check-done", false);
 	style.checkForUpdates(null);
 }
@@ -459,7 +459,7 @@ var updateMd5WithUpdateObserver = {
 }
 function testUpdateMd5WithUpdate() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, "data:text/plain,12345678901234567890123456789012", "testUpdateMd5WithUpdate", "* {color: blue}", false, null, null);
+	style.init(null, null, null, "data:text/plain,12345678901234567890123456789012", "testUpdateMd5WithUpdate", "* {color: blue}", false, null, null, null);
 	observerService.addObserver(updateMd5WithUpdateObserver, "stylish-style-update-check-done", false);
 	style.checkForUpdates(null);
 }
@@ -479,7 +479,7 @@ var updateUrlNoUpdateObserver = {
 }
 function testUpdateUrlNoUpdate() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, "data:text/css,* {color: blue}", null, "testUpdateUrlNoUpdate", "* {color: blue}", false, null, null);
+	style.init(null, null, "data:text/css,* {color: blue}", null, "testUpdateUrlNoUpdate", "* {color: blue}", false, null, null, null);
 	observerService.addObserver(updateUrlNoUpdateObserver, "stylish-style-update-check-done", false);
 	style.checkForUpdates(null);
 }
@@ -499,7 +499,7 @@ var updateUrlWithUpdateObserver = {
 }
 function testUpdateUrlWithUpdate() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, "data:text/css,* { color: red}", null, "testUpdateUrlWithUpdate", "* {color: blue}", false, null, null);
+	style.init(null, null, "data:text/css,* { color: red}", null, "testUpdateUrlWithUpdate", "* {color: blue}", false, null, null, null);
 	observerService.addObserver(updateUrlWithUpdateObserver, "stylish-style-update-check-done", false);
 	style.checkForUpdates(null);
 }
@@ -519,7 +519,7 @@ var updateNotAvailableObserver = {
 }
 function testUpdateNotAvailable() {
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "testUpdateNotAvailable", "* {color: blue}", false, null, null);
+	style.init(null, null, null, null, "testUpdateNotAvailable", "* {color: blue}", false, null, null, null);
 	observerService.addObserver(updateNotAvailableObserver, "stylish-style-update-check-done", false);
 	style.checkForUpdates(null);
 }
@@ -541,9 +541,9 @@ var runUpdateAvailableObserver = {
 }
 function testRunUpdateAvailable() {
 	runUpdateAvailableStyle = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	runUpdateAvailableStyle.init(null, null, "data:text/css,* {color: red}", null, "Unit test testRunUpdateAvailable", "* {color: blue}", false, null, null);
+	runUpdateAvailableStyle.init(null, null, "data:text/css,* {color: red}", null, "Unit test testRunUpdateAvailable", "* {color: blue}", false, null, null, null);
 	observerService.addObserver(runUpdateAvailableObserver, "stylish-style-update-done", false);
-	runUpdateAvailableStyle.applyUpdate();
+	runUpdateAvailableStyle.applyUpdate(null);
 }
 function asyncRunUpdateAvailable() {
 	observerService.removeObserver(runUpdateAvailableObserver, "stylish-style-update-done");
@@ -565,9 +565,9 @@ var runUpdateNotAvailableObserver = {
 };
 function testRunUpdateNotAvailable() {
 	runUpdateNotAvailableStyle = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	runUpdateNotAvailableStyle.init(null, null, null, null, "Unit test testRunUpdateNotAvailable", "* {color: blue}", false, null, null);
+	runUpdateNotAvailableStyle.init(null, null, null, null, "Unit test testRunUpdateNotAvailable", "* {color: blue}", false, null, null, null);
 	observerService.addObserver(runUpdateNotAvailableObserver, "stylish-style-update-done", false);
-	runUpdateNotAvailableStyle.applyUpdate();
+	runUpdateNotAvailableStyle.applyUpdate(null);
 }
 function asyncRunUpdateNotAvailable() {
 	observerService.removeObserver(runUpdateNotAvailableObserver, "stylish-style-update-done");
@@ -583,7 +583,7 @@ function getPrettyAppliesToItemsFromCode(code) {
 	const updateUrl = "http://example.com/update";
 	const md5Url = "http://example.com/md5";
 	const name = "Example style";
-	style.init(url, url, updateUrl, md5Url, name, code, false, null, null);
+	style.init(url, url, updateUrl, md5Url, name, code, false, null, null, null);
 	return style.getPrettyAppliesTo({});
 }
 
@@ -653,13 +653,13 @@ function testRegexpIsNotGlobal() {
 		assert(message + " - expected '" + type + "' got '" + currentType +"'", arraysEqual(currentType, type));
 	}
 	var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test - testRegexpIsNotGlobal", '@-moz-document regexp("^https?://((www|gist|help|status).)?github.*") { * { color: blue} }', false, null, null);
+	style.init(null, null, null, null, "Unit test - testRegexpIsNotGlobal", '@-moz-document regexp("^https?://((www|gist|help|status).)?github.*") { * { color: blue} }', false, null, null, null);
 	ensureType('@-moz-document regexp("^https?://((www|gist|help|status).)?github.*") { * { color: blue} }', "site");
 	style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test - testRegexpIsNotGlobal", '@-moz-document regexp("^http(s)?://((www|gist|help|status).)?github.*") { * { color: blue} }', false, null, null);
+	style.init(null, null, null, null, "Unit test - testRegexpIsNotGlobal", '@-moz-document regexp("^http(s)?://((www|gist|help|status).)?github.*") { * { color: blue} }', false, null, null, null);
 	ensureType('@-moz-document regexp("^http(s)?://((www|gist|help|status).)?github.*") { * { color: blue} }', "site");
 	style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
-	style.init(null, null, null, null, "Unit test - testRegexpIsNotGlobal", '@-moz-document regexp("^http(s)?://.*") { * { color: blue} }', false, null, null);
+	style.init(null, null, null, null, "Unit test - testRegexpIsNotGlobal", '@-moz-document regexp("^http(s)?://.*") { * { color: blue} }', false, null, null, null);
 	ensureType('@-moz-document regexp("^http(s)?://.*") { * { color: blue} }', "global");
 }
 
diff --git a/content/common.js b/content/common.js
index d9aba6d..0d3155a 100644
--- a/content/common.js
+++ b/content/common.js
@@ -18,12 +18,22 @@ var stylishCommon = {
 			element.setAttribute(i, json[i]);
 	},
 
-	dispatchEvent: function(doc, type) {
+	// CustomEvent is available in Firefox 11. Before that, the "data" parameter does nothing. If "data"
+	// is a custom object, __exposedProps__ must be set.
+	dispatchEvent: function(doc, type, data) {
 		if (!doc) {
 			return;
 		}
-		var stylishEvent = doc.createEvent("Events");
-		stylishEvent.initEvent(type, false, false, doc.defaultView, null);
+		if (typeof data == "undefined") {
+			data = null;
+		}
+		var stylishEvent = null;
+		if (typeof doc.defaultView.CustomEvent != "undefined") {
+			stylishEvent = new doc.defaultView.CustomEvent(type, {detail: data});
+		} else {
+			stylishEvent = doc.createEvent("Events");
+			stylishEvent.initEvent(type, false, false, doc.defaultView, null);
+		}
 		doc.dispatchEvent(stylishEvent);
 	},
 
@@ -213,8 +223,10 @@ var stylishCommon = {
 	
 	// Callback passes a string parameter - installed, failure, cancelled, existing
 	installFromSite: function(doc, callback) {
-		var resourcesNeeded = [{name: "stylish-code", download: true}, {name: "stylish-description", download: true}, {name: "stylish-install-ping-url"}, {name: "stylish-update-url"}, {name: "stylish-md5-url"}, {name: "stylish-id-url"}];
-		
+		// we want both the url and the content of the md5
+		var md5Url = stylishCommon.getMeta(doc, "stylish-md5-url");
+		var resourcesNeeded = [{name: "stylish-code", download: true}, {name: "stylish-description", download: true}, {name: "stylish-install-ping-url"}, {name: "stylish-update-url"}, {name: "stylish-md5-url", download: true}, {name: "stylish-id-url"}];
+
 		stylishCommon.getResourcesFromMetas(doc, resourcesNeeded, function(results) {
 			// This is the only required property
 			if (results["stylish-code"] == null || results["stylish-code"].length == 0) {
@@ -228,7 +240,7 @@ var stylishCommon = {
 
 			var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 			style.mode = style.CALCULATE_META | style.REGISTER_STYLE_ON_CHANGE;
-			style.init(uri, results["stylish-id-url"], results["stylish-update-url"], results["stylish-md5-url"], results["stylish-description"], results["stylish-code"], false, results["stylish-code"], null);
+			style.init(uri, results["stylish-id-url"], results["stylish-update-url"], md5Url, results["stylish-description"], results["stylish-code"], false, results["stylish-code"], results["stylish-md5-url"], null);
 
 			stylishCommon.openInstall({style: style, installPingURL: results["stylish-install-ping-url"], installCallback: callback});
 
@@ -324,7 +336,7 @@ var stylishCommon = {
 		uri = stylishCommon.cleanURI(uri);
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		style.mode = style.CALCULATE_META | style.REGISTER_STYLE_ON_CHANGE;
-		style.init(uri, uri, uri, null, null, css, false, css, null);
+		style.init(uri, uri, uri, null, null, css, false, css, null, null);
 		stylishCommon.openInstall({style: style, installCallback: callback});
 	},
 
@@ -417,7 +429,7 @@ var stylishCommon = {
 	addCode: function(code) {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		style.mode = style.CALCULATE_META | style.REGISTER_STYLE_ON_CHANGE;
-		style.init(null, null, null, null, null, code, false, null, null);
+		style.init(null, null, null, null, null, code, false, null, null, null);
 		stylishCommon.openEdit(stylishCommon.getWindowName("stylishEdit"), {style: style});
 	},
 
diff --git a/content/edit.js b/content/edit.js
index 89aba79..a570d42 100644
--- a/content/edit.js
+++ b/content/edit.js
@@ -1,6 +1,13 @@
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
-var require = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+var require = null;
+var autocompleter = null;
+try {
+	require = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+	autocompleter = require("devtools/sourceeditor/autocomplete");
+} catch (ex) {
+	// file not available...
+}
 
 var saved = false;
 var style = null;
@@ -27,10 +34,12 @@ function init() {
 	if (prefs.getIntPref("editor") == 0) {
 		// sourceeditor, firefox 27+
 		let Editor = null;
-		try {
-			Editor = require("devtools/sourceeditor/editor");
-		} catch (ex) {
-			//unavailable
+		if (require) {
+			try {
+				Editor = require("devtools/sourceeditor/editor");
+			} catch (ex) {
+				//unavailable
+			}
 		}
 		if (Editor && ("modes" in Editor)) {
 			document.getElementById("itsalltext").style.visibility = "hidden";
@@ -155,6 +164,14 @@ function init2() {
 		wrapLinesE.checked = wrapLines;
 		wrapLinesE.style.display = "";
 	}
+	if (sourceEditorType == "sourceeditor") {
+		// Up to Firefox 28, sometimes "require" will return an object when something is not available instead of throwing.
+		// Rather than trying to detect if autocompleter is available, let's just try to use it.
+		try {
+			sourceEditor.extend(autocompleter);
+			sourceEditor.setupAutoCompletion(null);
+		} catch (ex) { }
+	}
 
 	initStyle();
 
@@ -424,52 +441,135 @@ function insertDataURI() {
 	insertCodeAtCaret("data:" + contentType + ";base64," + encoded);
 }
 
-var finder = {
-	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsITypeAheadFind, Components.interfaces.nsISupports]),
-	nsITAF: Components.interfaces.nsITypeAheadFind,
-
-	init: function(docshell) {},
-
-	find: function(s, linksOnly) {
-		this.searchString = s;
-		return this.findFromIndex(0, false);
-	},
-
-	findAgain: function(backwards, linksOnly) {
-		return this.findFromIndex(codeElementWrapper.selectionStart + (backwards ? 0 : 1), backwards);
-	},
-
-	findFromIndex: function(index, backwards) {
-		var start = backwards ? codeElementWrapper.value.substring(0, index).lastIndexOf(this.searchString) : codeElementWrapper.value.indexOf(this.searchString, index);
-		var result;
-		if (start >= 0) {
-			result = this.nsITAF.FIND_FOUND;
-		} else if (index == 0) {
-			result = this.nsITAF.FIND_NOTFOUND;
-		} else {
-			// try again, start from the start
-			start = backwards ? codeElementWrapper.value.lastIndexOf(this.searchString) : codeElementWrapper.value.indexOf(this.searchString);
-			result = start == -1 ? this.nsITAF.FIND_NOTFOUND : this.nsITAF.FIND_WRAPPED;
-		}
-		codeE.editor.selection.removeAllRanges();
-		if (start >= 0) {
-			codeElementWrapper.setSelectionRange(start, start + this.searchString.length);
-			codeE.editor.selectionController.setDisplaySelection(2);
-			codeE.editor.selectionController.scrollSelectionIntoView(1, 0, false);
-		} else
-			codeElementWrapper.setSelectionRange(0, 0);
-		return result;
-	},
-
-	setDocShell: function(docshell) {},
-	setSelectionModeAndRepaint: function(toggle) {},
-	collapseSelection: function(toggle) {},
+// Firefox 27 changed this interface
+var finderJsmStyle = false;
+try {
+	Components.utils.import("resource://gre/modules/Finder.jsm", {});
+	finderJsmStyle = true;
+} catch (ex) {
+	// file not available...
+}
 
-	searchString: null,
-	caseSensitive: false,
-	foundLink: null,
-	foundEditable: null,
-	currentWindow: null
+var finder = null;
+if (finderJsmStyle) {
+	finder = {
+		_listeners: [],
+		searchString: null,
+		caseSensitive: false,
+
+		addResultListener: function (aListener) {
+			if (this._listeners.indexOf(aListener) === -1)
+				this._listeners.push(aListener);
+		},
+
+		removeResultListener: function (aListener) {
+			this._listeners = this._listeners.filter(function(l) {return l != aListener;});
+		},
+
+		_notify: function (aSearchString, aResult, aFindBackwards, aDrawOutline) {
+			this.searchString = aSearchString;
+
+			let data = {
+				result: aResult,
+				findBackwards: aFindBackwards,
+				linkURL: null,
+				rect: {top: 0, right: 0, bottom: 0, left: 0},
+				searchString: this._searchString,
+			};
+
+			this._listeners.forEach(function(l) {
+				l.onFindResult(data);
+			});
+		},
+
+		fastFind: function(aSearchString, aLinksOnly, aDrawOutline) {
+			this.searchString = aSearchString;
+			let result = this._findFromIndex(0, false);
+			this._notify(aSearchString, result, false, aDrawOutline);
+		},
+
+		findAgain: function(aFindBackwards, aLinksOnly, aDrawOutline) {
+			let result = this._findFromIndex(codeElementWrapper.selectionStart + (aFindBackwards ? 0 : 1), aFindBackwards);
+			this._notify(this.searchString, result, aFindBackwards, aDrawOutline);
+		},
+
+		_findFromIndex: function(index, backwards) {
+			var start = backwards ? codeElementWrapper.value.substring(0, index).lastIndexOf(this.searchString) : codeElementWrapper.value.indexOf(this.searchString, index);
+			var result;
+			var iface = Components.interfaces.nsITypeAheadFind;
+			if (start >= 0) {
+				result = iface.FIND_FOUND;
+			} else if (index == 0) {
+				result = iface.FIND_NOTFOUND;
+			} else {
+				// try again, start from the start
+				start = backwards ? codeElementWrapper.value.lastIndexOf(this.searchString) : codeElementWrapper.value.indexOf(this.searchString);
+				result = start == -1 ? iface.FIND_NOTFOUND : iface.FIND_WRAPPED;
+			}
+			codeE.editor.selection.removeAllRanges();
+			if (start >= 0) {
+				codeElementWrapper.setSelectionRange(start, start + this.searchString.length);
+				codeE.editor.selectionController.setDisplaySelection(2);
+				codeE.editor.selectionController.scrollSelectionIntoView(1, 0, false);
+			} else
+				codeElementWrapper.setSelectionRange(0, 0);
+			return result;
+		},
+
+		highlight: function(aHighlight, aWord) {},
+		enableSelection: function() {},
+		removeSelection: function() {},
+		focusContent: function() {},
+		keyPress: function (aEvent) {}
+	};
+} else {
+	finder = {
+		QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsITypeAheadFind, Components.interfaces.nsISupports]),
+		nsITAF: Components.interfaces.nsITypeAheadFind,
+
+		init: function(docshell) {},
+
+		find: function(s, linksOnly) {
+			this.searchString = s;
+			return this.findFromIndex(0, false);
+		},
+
+		findAgain: function(backwards, linksOnly) {
+			return this.findFromIndex(codeElementWrapper.selectionStart + (backwards ? 0 : 1), backwards);
+		},
+
+		findFromIndex: function(index, backwards) {
+			var start = backwards ? codeElementWrapper.value.substring(0, index).lastIndexOf(this.searchString) : codeElementWrapper.value.indexOf(this.searchString, index);
+			var result;
+			if (start >= 0) {
+				result = this.nsITAF.FIND_FOUND;
+			} else if (index == 0) {
+				result = this.nsITAF.FIND_NOTFOUND;
+			} else {
+				// try again, start from the start
+				start = backwards ? codeElementWrapper.value.lastIndexOf(this.searchString) : codeElementWrapper.value.indexOf(this.searchString);
+				result = start == -1 ? this.nsITAF.FIND_NOTFOUND : this.nsITAF.FIND_WRAPPED;
+			}
+			codeE.editor.selection.removeAllRanges();
+			if (start >= 0) {
+				codeElementWrapper.setSelectionRange(start, start + this.searchString.length);
+				codeE.editor.selectionController.setDisplaySelection(2);
+				codeE.editor.selectionController.scrollSelectionIntoView(1, 0, false);
+			} else
+				codeElementWrapper.setSelectionRange(0, 0);
+			return result;
+		},
+
+		setDocShell: function(docshell) {},
+		setSelectionModeAndRepaint: function(toggle) {},
+		collapseSelection: function(toggle) {},
+
+		searchString: null,
+		caseSensitive: false,
+		foundLink: null,
+		foundEditable: null,
+		currentWindow: null
+	}
 }
 
 var codeElementWrapper = {
@@ -543,9 +643,26 @@ window.addEventListener("load", function() {
 	// sourceeditor has its own way of doing this
 	if (sourceEditorType != "sourceeditor") {
 		var findBar = document.getElementById("findbar");
-		document.getElementById("internal-code").fastFind = finder;
+		if (finderJsmStyle) {
+			var editor = document.getElementById("internal-code");
+			editor.finder = finder;
+			findBar.browser = editor;
+		} else {
+			document.getElementById("internal-code").fastFind = finder;
+		}
+		findBar._findField.value = "";
 		findBar.open();
 	}
+	// On the find bar, swallow any enter keypresses that would close the dialog
+	document.getElementById("findbar").addEventListener("keypress", function(event) {
+		if (event.keyCode == 13) {
+			// why this is different, i don't know
+			if (!finderJsmStyle) {
+				event.target._findAgain();
+			}
+			event.preventDefault();
+		}
+	}, false);
 }, false);
 
 // if the style we're editing has been deleted, turn off preview and close the window
diff --git a/content/edit.xul b/content/edit.xul
index 3a91de7..261b416 100644
--- a/content/edit.xul
+++ b/content/edit.xul
@@ -1,6 +1,7 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://stylish/skin/edit.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 
 <!DOCTYPE dialog [
@@ -40,7 +41,7 @@
 
 	<keyset>
 		<key id="save-key" modifiers="control" key="S" oncommand="save()"/>
-		<key id="find-key" modifiers="control" key="F" oncommand="document.getElementById('findbar')._findField.focus()"/>
+		<key id="find-key" modifiers="control" key="F" oncommand="document.getElementById('findbar').open();document.getElementById('findbar')._findField.focus()"/>
 	</keyset>
 
 	<commandset>
diff --git a/content/install-overlay.js b/content/install-overlay.js
index 82f077f..783d2a1 100644
--- a/content/install-overlay.js
+++ b/content/install-overlay.js
@@ -58,12 +58,15 @@ var stylishInstallOverlay = {
 
 	checkUpdateEvent: function(doc, style) {
 		var code = stylishInstallOverlay.getCodeFromPage(doc);
+		// Give the page the updateUrl so it can initialize settings. exposedProps lets us get around security restrictions.
+		var data = {updateUrl: style.updateUrl, __exposedProps__ : { updateUrl : "r"}};
 		if (!stylishCommon.cssAreEqual((style.originalCode || style.code), code)) {
-			stylishCommon.dispatchEvent(doc, "styleCanBeUpdated");
-			doc.addEventListener("stylishUpdate", stylishInstallOverlay.updateFromSite, false);
+			stylishCommon.dispatchEvent(doc, "styleCanBeUpdated", data);
 		} else {
-			stylishCommon.dispatchEvent(doc, "styleAlreadyInstalled");
+			stylishCommon.dispatchEvent(doc, "styleAlreadyInstalled", data);
 		}
+		// listen to this regardless, the page may decide to allow updates anyway (e.g. for styles with settings)
+		doc.addEventListener("stylishUpdate", stylishInstallOverlay.updateFromSite, false);
 	},
 
 	getIdUrl: function(doc) {
@@ -78,12 +81,13 @@ var stylishInstallOverlay = {
 			//style installed status
 			var style = stylishInstallOverlay.service.findByUrl(stylishInstallOverlay.getIdUrl(doc), 0);
 			if (style) {
-				//if the code isn't available, ask for it and wait
+				// If the code isn't available, ask for it and wait
 				var code = stylishInstallOverlay.getCodeFromPage(doc);
 				if (code) {
 					stylishInstallOverlay.checkUpdateEvent(doc, style);
 				} else {
 					doc.addEventListener("stylishCodeLoaded", function(){stylishInstallOverlay.checkUpdateEvent(doc, style)}, false);
+					doc.addEventListener("stylishCodeCantBeLoaded", function(){stylishInstallOverlay.checkUpdateEvent(doc, style)}, false);
 					stylishCommon.dispatchEvent(doc, "styleLoadCode");
 				}
 			} else {
@@ -127,10 +131,20 @@ var stylishInstallOverlay = {
 		var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
 		if (prompts.confirmEx(window, stylishInstallOverlay.STRINGS.formatStringFromName("updatestyletitle", [], 0), prompt, prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_IS_STRING + prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_CANCEL, stylishInstallOverlay.STRINGS.formatStringFromName("updatestyleok", [], 0), null, null, null, {}) == 0) {
 			style.code = code;
-			//we're now in sync with the remote style
+
+			//we're now in sync with the remote style, so let's set things appropriately
 			style.originalCode = code;
-			style.save();
-			stylishCommon.dispatchEvent(doc, "styleInstalled");
+			// we want both the url and the content of the md5
+			var md5Url = stylishCommon.getMeta(doc, "stylish-md5-url");
+			style.md5Url = md5Url;
+			var originalMd5 = null;
+			var resourcesNeeded = [{name: "stylish-update-url"}, {name: "stylish-md5-url", download: true}];
+			stylishCommon.getResourcesFromMetas(doc, resourcesNeeded, function(results) {
+				style.updateUrl = results["stylish-update-url"];
+				style.originalMd5 = results["stylish-md5-url"];
+				style.save();
+				stylishCommon.dispatchEvent(doc, "styleInstalled");
+			});
 		}
 	},
 
diff --git a/content/manage.html b/content/manage.html
index 61c3f39..05f616d 100644
--- a/content/manage.html
+++ b/content/manage.html
@@ -232,7 +232,7 @@
 		var ui = getUI(id);
 		var button = ui.querySelector('.update-style');
 		updateButtonStatus(button, true);
-		get(id).applyUpdate();
+		get(id).applyUpdate(null);
 		// that should fire stylish-style-change when done, which we're listening to above, and refresh it
 	}
 	
diff --git a/content/manage.js b/content/manage.js
index eccba99..c1d5c37 100644
--- a/content/manage.js
+++ b/content/manage.js
@@ -257,7 +257,7 @@ var stylishManage = {
 	newStyle: function() {
 		var style = Components.classes["@userstyles.org/style;1"].createInstance(Components.interfaces.stylishStyle);
 		style.mode = style.CALCULATE_META | style.REGISTER_STYLE_ON_CHANGE;
-		style.init(null, null, null, null, null, "", false, null, null);
+		style.init(null, null, null, null, null, "", false, null, null, null);
 		stylishCommon.openEdit(stylishCommon.getWindowName("stylishEdit"), {style: style});
 	},
 
diff --git a/content/style.xbl b/content/style.xbl
index a5cd472..1a6f418 100644
--- a/content/style.xbl
+++ b/content/style.xbl
@@ -193,7 +193,7 @@
 			</method>
 			<method name="update">
 				<body>
-					this.styleObject.applyUpdate();
+					this.styleObject.applyUpdate(null);
 				</body>
 			</method>
 			<method name="delete">
diff --git a/idl/stylishStyle.idl b/idl/stylishStyle.idl
index 6e4e3e8..96a99b2 100644
--- a/idl/stylishStyle.idl
+++ b/idl/stylishStyle.idl
@@ -3,7 +3,7 @@
 #include "nsIConsoleListener.idl"
 #include "nsIObserver.idl"
 
-[scriptable, uuid(ea17a766-cdd4-444b-8d8d-b5bb935a2a22)]
+[scriptable, uuid(6cd15928-080e-4c0f-b3af-90922cf4f9e8)]
 interface stylishStyle : nsIClassInfo
 {
 	attribute unsigned long id;
@@ -17,6 +17,7 @@ interface stylishStyle : nsIClassInfo
 	attribute boolean enabled;
 	// the original code installed. can be null if it's the same as "code" or this wasn't an install
 	attribute AString originalCode;
+	attribute AString originalMd5;
 	attribute short applyBackgroundUpdates;
 
 	//see the flags below. you can set this before initing the style
@@ -38,7 +39,7 @@ interface stylishStyle : nsIClassInfo
 	AString regexToSample(in AString regex);
 
 	// applyBackgroundUpdates is really a short, but set as a string here so that we can pass null for default
-	void init(in AString url, in AString idUrl, in AString updateUrl, in AString md5Url, in AString name, in AString code, in boolean enabled, in AString originalCode, in AString applyBackgroundUpdates);
+	void init(in AString url, in AString idUrl, in AString updateUrl, in AString md5Url, in AString name, in AString code, in boolean enabled, in AString originalCode, in AString originalMd5, in AString applyBackgroundUpdates);
 	void save();
 	void delete();
 	boolean appliesToUrl(in AString url);
@@ -50,7 +51,13 @@ interface stylishStyle : nsIClassInfo
 	//     - update-available
 	//     - no-update-possible
 	void checkForUpdates(in nsIObserver observer);
-	void applyUpdate();
+	// observer's topics can be:
+	//   - stylish-style-update-start
+	//   - stylish-style-update-done with data:
+	//     - update-failure
+	//     - update-success
+	//     - no-update-possible
+	void applyUpdate(in nsIObserver observer);
 	void setPreview(in boolean on);
 	void revert();
 	void addMeta(in AString name, in AString value);
diff --git a/install.rdf b/install.rdf
index c75f420..938b629 100644
--- a/install.rdf
+++ b/install.rdf
@@ -50,6 +50,7 @@
 		<em:translator>banthaz - sv-SE</em:translator>
 		<em:translator>kenmooda - fi</em:translator>
 		<em:translator>mdr.ksk - et-EE</em:translator>
+		<em:translator>yfdyh000 - zh-CN</em:translator>
 		<em:contributor>t1470258 - icons</em:contributor>
 		<em:contributor>LouCypher - icons</em:contributor>
 		<em:contributor>s4nji - icons</em:contributor>
diff --git a/locale/zh-CN/common.properties b/locale/zh-CN/common.properties
index 99740df..bf91f97 100644
--- a/locale/zh-CN/common.properties
+++ b/locale/zh-CN/common.properties
@@ -1,10 +1,10 @@
-changeTags=移除\'%S\'标签并添加以下标签:
-changeTagsNoCurrent=添加以下标签:
+changeTags=移除“%S”标签并添加以下标签:
+changeTagsNoCurrent=添加以下标签:
 changeTagsTitle=更改标签
-deleteStyle=你确定要卸载 \'%S\'吗?
+deleteStyle=你确定要卸载“%S”吗?
 deleteStyleTitle=卸载样式?
 deleteStyleOK=卸载
-deleteStyles=你确定要卸载这%S个样式吗?
+deleteStyles=你确定要卸载这 %S 个样式吗?
 deleteStylesTitle=卸载样式?
 deleteStylesOK=卸载
 extensions.{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}.description=让网页和用户界面有个新形象
diff --git a/locale/zh-CN/domi.dtd b/locale/zh-CN/domi.dtd
index ca2529e..4353ca7 100644
--- a/locale/zh-CN/domi.dtd
+++ b/locale/zh-CN/domi.dtd
@@ -1 +1 @@
-<!ENTITY copyselector "复制所选">
+<!ENTITY copyselector "复制选择器">
diff --git a/locale/zh-CN/edit.dtd b/locale/zh-CN/edit.dtd
index 99c7b64..0dc7ffc 100644
--- a/locale/zh-CN/edit.dtd
+++ b/locale/zh-CN/edit.dtd
@@ -14,7 +14,7 @@
 <!ENTITY preview.ak "P">
 <!ENTITY save "保存">
 <!ENTITY save.ak "S">
-<!ENTITY switchtoinstall "切换到安装">
+<!ENTITY switchtoinstall "切换到安装模式">
 <!ENTITY switchtoinstall.ak "I">
 <!ENTITY tags "标签">
 <!ENTITY tags.ak "T">
diff --git a/locale/zh-CN/edit.properties b/locale/zh-CN/edit.properties
index 87a2d54..98604d2 100644
--- a/locale/zh-CN/edit.properties
+++ b/locale/zh-CN/edit.properties
@@ -1,7 +1,7 @@
 dataURIDialogTitle=选择要插入的文件
-editstyletitle=正在编辑 \'%S\'
+editstyletitle=正在编辑“%S”
 newstyletitle=新样式
-missingcode=输入该样式的代码。
-missingname=命名该样式。
-unsavedchanges=是否保存对此样式的更改?
+missingcode=输入此样式的代码。
+missingname=给此样式起个名字。
+unsavedchanges=是否要保存对此样式的更改?
 unsavedchangestitle=保存更改?
diff --git a/locale/zh-CN/extensions.dtd b/locale/zh-CN/extensions.dtd
index 9298414..6277b88 100644
--- a/locale/zh-CN/extensions.dtd
+++ b/locale/zh-CN/extensions.dtd
@@ -18,7 +18,7 @@
 <!ENTITY cmd.checkUpdatesAll.accesskey "F">
 <!ENTITY cmd.checkUpdatesAllAddon.tooltip "查找附加组件的更新">
 <!ENTITY cmd.checkUpdatesAllTheme.tooltip "查找主题的更新">
-<!ENTITY cmd.checkUpdatesAllPlugin.tooltip "发现您插件的更新">
+<!ENTITY cmd.checkUpdatesAllPlugin.tooltip "查找您的插件的更新">
 <!ENTITY cmd.installLocalFile.label "安装…">
 <!ENTITY cmd.installLocalFile.accesskey "n">
 <!ENTITY cmd.installFileAddon.tooltip "安装附加组件">
@@ -37,10 +37,10 @@
 <!ENTITY cmd.continue.tooltip "继续载入 &brandShortName;">
 <!ENTITY cmd.enableAll.label "全部启用">
 <!ENTITY cmd.enableAll.accesskey "a">
-<!ENTITY cmd.enableAll.tooltip "启用全部显示的附加组件">
+<!ENTITY cmd.enableAll.tooltip "启用显示的所有附加组件">
 <!ENTITY cmd.disableAll.label "全部禁用">
 <!ENTITY cmd.disableAll.accesskey "s">
-<!ENTITY cmd.disableAll.tooltip "禁用全部显示的附加组件">
+<!ENTITY cmd.disableAll.tooltip "禁用所有显示的附加组件">
 <!-- Displayed in the selected Add-on\'s richlistitem and context menu -->
 <!ENTITY cmd.useTheme.label "使用主题">
 <!ENTITY cmd.useTheme.accesskey "T">
@@ -53,19 +53,19 @@
 <!ENTITY cmd.optionsUnix.tooltip "编辑所选扩展的首选项">
 <!ENTITY cmd.enable.label "启用">
 <!ENTITY cmd.enable.accesskey "E">
-<!ENTITY cmd.enable.tooltip "&brandShortName; 重启后启用此附加组件">
+<!ENTITY cmd.enable.tooltip "&brandShortName; 重启后将启用此附加组件">
 <!ENTITY cmd.disable.label "禁用">
 <!ENTITY cmd.disable.accesskey "D">
-<!ENTITY cmd.disable.tooltip "&brandShortName; 重启后禁用此附加组件">
+<!ENTITY cmd.disable.tooltip "&brandShortName; 重启后将禁用此附加组件">
 <!ENTITY cmd.uninstall.label "卸载">
 <!ENTITY cmd.uninstall2.accesskey "U">
-<!ENTITY cmd.uninstall2.tooltip "在 &brandShortName; 重启后卸载此附加组件">
+<!ENTITY cmd.uninstall2.tooltip "&brandShortName; 重启后将卸载此附加组件">
 <!ENTITY cmd.cancelUninstall.label "取消卸载">
 <!ENTITY cmd.cancelUninstall.accesskey "C">
-<!ENTITY cmd.cancelUninstall.tooltip "取消安装附加组件">
+<!ENTITY cmd.cancelUninstall.tooltip "取消卸载此附加组件">
 <!ENTITY cmd.cancelInstall.label "取消安装">
 <!ENTITY cmd.cancelInstall.accesskey "C">
-<!ENTITY cmd.cancelInstall.tooltip "取消安装附加组件">
+<!ENTITY cmd.cancelInstall.tooltip "取消安装此附加组件">
 <!ENTITY cmd.cancelUpgrade.label "取消更新">
 <!ENTITY cmd.cancelUpgrade.accesskey "C">
 <!ENTITY cmd.cancelUpgrade.tooltip "取消更新此附加组件">
@@ -101,15 +101,15 @@
 <!ENTITY includeUpdate.accesskey "n">
 <!ENTITY includeUpdate.tooltip "安装更新时包括此附加组件">
 <!-- Status Messsages -->
-<!ENTITY insecureUpdate.label "不提供安全更新。">
+<!ENTITY insecureUpdate.label "没有提供安全地更新。">
 <!ENTITY needsDependencies.label "需要其他项。">
-<!ENTITY blocklisted.label "出于保护目的禁用">
+<!ENTITY blocklisted.label "出于保护目的禁用。">
 <!ENTITY softBlocklisted.label "已知会导致安全或稳定性的问题。">
-<!ENTITY outdated.label "新的更安全的版本已可用。">
+<!ENTITY outdated.label "更新更安全的版本现已可用。">
 <!ENTITY toBeDisabled.label "此附加组件将在 &brandShortName; 重启后禁用。">
 <!ENTITY toBeEnabled.label "此附加组件将在 &brandShortName; 重启后启用。">
 <!ENTITY toBeInstalled.label "此附加组件将在 &brandShortName; 重启后安装。">
-<!ENTITY toBeUninstalled.label "此附加组件将在 &brandShortName; 重启后卸载。 ">
+<!ENTITY toBeUninstalled.label "此附加组件将在 &brandShortName; 重启后卸载。">
 <!ENTITY toBeUpdated.label "此附加组件将在重启 &brandShortName; 时更新。">
 <!ENTITY getExtensions.label "获取扩展">
 <!ENTITY getThemes.label "获取主题">
@@ -143,14 +143,14 @@
 <!ENTITY infoNoAddonSelected.label "未选中更新">
 <!ENTITY infoNoUpdateInfo.label "此更新不包含任何附加信息">
 <!ENTITY infoUpdateInfoError.label "载入此更新的信息时出错">
-<!ENTITY updateSuccess.label "成功完成更新。">
-<!ENTITY installSuccess.label "安装成功完成">
+<!ENTITY updateSuccess.label "更新已成功完成。">
+<!ENTITY installSuccess.label "安装已成功完成。">
 <!ENTITY installSuccessRestart.label "重启以完成安装。">
 <!ENTITY updateSuccessRestart.label "重启以完成更新。">
 <!ENTITY installWaiting.label "等待…">
-<!ENTITY installIncompatibleUpdate.label "检查兼容性…">
+<!ENTITY installIncompatibleUpdate.label "正在检查兼容性…">
 <!ENTITY installFinishing.label "正在安装…">
-<!ENTITY installFailure.label "安装失败">
+<!ENTITY installFailure.label "安装失败。">
 <!ENTITY progressStatus.label "检查更新">
 <!ENTITY eula.title "用户协议">
 <!ENTITY eula.width "560px">
diff --git a/locale/zh-CN/install.properties b/locale/zh-CN/install.properties
index 463789e..ebee9bd 100644
--- a/locale/zh-CN/install.properties
+++ b/locale/zh-CN/install.properties
@@ -1,8 +1,8 @@
-installintro=你正准备安装 “%S” 到 Stylish。
+installintro=你正准备安装“%S”到 Stylish。
 installintrononame=你正准备安装一个新样式到 Stylish。
-installapp=该样式会对 %S 的用户界面生效,你需要重新启动 %S 让该样式生效。
-installglobal=该样式会对所有页面生效。
-installsite=该样式会对以下页面生效:
-installnotype=该样式会对页面或者 %S 的用户界面生效。
-missingname=命名该样式
-preview.tooltip=暂时应用该样式以便预览效果
+installapp=该样式可以对 %S 的用户界面产生影响。你可能需要重新启动 %S 以使该样式生效。
+installglobal=该样式可以对所有网站页面生效。
+installsite=该样式可以对以下网站生效:
+installnotype=该样式可以对网站或者 %S 的用户界面生效。
+missingname=给该样式起个名字。
+preview.tooltip=临时应用该样式以便预览其效果。
diff --git a/locale/zh-CN/manage.dtd b/locale/zh-CN/manage.dtd
index 657ee32..6a080f5 100644
--- a/locale/zh-CN/manage.dtd
+++ b/locale/zh-CN/manage.dtd
@@ -3,7 +3,7 @@
 <!ENTITY done "完成">
 <!ENTITY done.ak "D">
 <!ENTITY filter "搜索">
-<!ENTITY installfromurls "Install from URLs...">
+<!ENTITY installfromurls "从 URL 安装...">
 <!ENTITY manageaddonstitle "用户样式">
 <!ENTITY managetitle "Stylish">
 <!ENTITY nostylesstart "浏览">
@@ -12,7 +12,7 @@
 <!ENTITY sortname "名称">
 <!ENTITY sorttag "标签">
 <!ENTITY sorttype "类型">
-<!ENTITY sortgroup "分类:">
+<!ENTITY sortgroup "排序按:">
 <!ENTITY update "更新">
 <!ENTITY update.ak "U">
 <!ENTITY writenew "编写新样式">
diff --git a/locale/zh-CN/manage.properties b/locale/zh-CN/manage.properties
index aef9a66..391841d 100644
--- a/locale/zh-CN/manage.properties
+++ b/locale/zh-CN/manage.properties
@@ -4,21 +4,21 @@ groupTagNone=无标签
 groupTypeApp=程序
 groupTypeGlobal=全局
 groupTypeNone=无分类
-groupTypeSite=页面
-styleRegistrationOff=所有样式已停用
+groupTypeSite=网站
+styleRegistrationOff=所有样式已停用。
 styleRegistrationTurnOn=启用样式
 styleRegistrationTurnOn.ak=T
-updateAvailable=有可用更新。
-updateCheckError=查找更新时遇到错误
+updateAvailable=有可用的更新。
+updateCheckError=查找更新时遇到错误。
 updateCompleted=更新完成。
 updateFailed=更新失败。
 updateNotFound=没有找到更新。
 updateNotPossible=不能更新。
-appstyledescription=更改界面。
-globalstyledescription=不能更改。
-sitestyledescription=生效 %S。
-tagstyledescription=标签: %S。
+appstyledescription=影响用户界面。
+globalstyledescription=影响所有地方。
+sitestyledescription=生效于 %S。
+tagstyledescription=标签:%S。
 manageaddonstitle=用户样式
-installfromurlsprompttitle=Install from URLs
-installfromurlsprompt=Enter URLs of user styles to install. These can be pages on userstyles.org or CSS files. Separate multiple URLs by spaces.
-installfromurlserror=Could not install from the following URLs: %s.
+installfromurlsprompttitle=从 URL 安装
+installfromurlsprompt=请输入要安装的用户样式的 URL。可以是 userstyles.org 上的页面,或是 CSS 文件。可用空格分隔多个 URL。
+installfromurlserror=无法从下列 URL 安装:%s。
diff --git a/locale/zh-CN/overlay.dtd b/locale/zh-CN/overlay.dtd
index af553ca..e084743 100644
--- a/locale/zh-CN/overlay.dtd
+++ b/locale/zh-CN/overlay.dtd
@@ -1,6 +1,6 @@
 <!ENTITY addfile "安装文件...">
 <!ENTITY addfile.ak "I">
-<!ENTITY findstylebrowser "查找适合该页面的样式...">
+<!ENTITY findstylebrowser "查找适合此网站的样式...">
 <!ENTITY findstylebrowser.ak "F">
 <!ENTITY managestyles "管理样式...">
 <!ENTITY managestyles.ak "M">
diff --git a/locale/zh-CN/overlay.properties b/locale/zh-CN/overlay.properties
index 4acf88f..8fa2cc3 100644
--- a/locale/zh-CN/overlay.properties
+++ b/locale/zh-CN/overlay.properties
@@ -1,6 +1,6 @@
-tooltip=Stylish - %S 页面样式, %S 全局样式
+tooltip=Stylish - %S 个页面样式,%S 个全局样式
 tooltipStylesOff=Stylish - 已停用样式
-updatestyle=你确定要更新 \'%S\' 吗?
+updatestyle=你确定要更新“%S”吗?
 updatestyleok=更新
 updatestyletitle=更新样式
 writeblank=空白样式...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/stylish.git



More information about the Pkg-mozext-commits mailing list