aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEinar Egilsson2011-08-26 14:37:27 +0200
committerEinar Egilsson2011-08-26 14:37:27 +0200
commit3ac1838cba725705a96a9d7b65721b15f4ec67b1 (patch)
tree6eea260d1e5882ba1031c5bb78bd2b9ac0884fb9
parent5aefd85d7975c8934b32a30a1dec68aa421cdee3 (diff)
Moved everything to a js module, removed custom xpcom interfaces
-rw-r--r--build.py2
-rw-r--r--chrome.manifest17
-rw-r--r--chrome/code/browserOverlay.xul.js104
-rw-r--r--chrome/code/component.js21
-rw-r--r--chrome/code/editRedirect.xul.js103
-rw-r--r--chrome/code/redirect.js218
-rw-r--r--chrome/code/redirector.js (renamed from chrome/content/code/redirector.prototype.js)713
-rw-r--r--chrome/code/redirectorprefs.js93
-rw-r--r--chrome/code/settings.xul.js276
-rw-r--r--chrome/code/unittest/run.html99
-rw-r--r--chrome/code/unittest/testcases.js129
-rw-r--r--chrome/ui/browserOverlay.xul34
-rw-r--r--chrome/ui/editRedirect.xul58
-rw-r--r--chrome/ui/help.html182
-rw-r--r--chrome/ui/settings.xul118
-rw-r--r--chrome/ui/skin/movedown.pngbin0 -> 294 bytes
-rw-r--r--chrome/ui/skin/movedowndisabled.pngbin0 -> 361 bytes
-rw-r--r--chrome/ui/skin/moveup.pngbin0 -> 360 bytes
-rw-r--r--chrome/ui/skin/moveupdisabled.pngbin0 -> 282 bytes
-rw-r--r--chrome/ui/skin/redirector.css15
-rw-r--r--chrome/ui/skin/redirector.pngbin0 -> 1462 bytes
-rw-r--r--chrome/ui/skin/statusactive.pngbin0 -> 360 bytes
-rw-r--r--chrome/ui/skin/statusinactive.pngbin0 -> 396 bytes
-rw-r--r--components/interfaces/nsIFile.idl343
-rw-r--r--components/interfaces/nsISimpleEnumerator.idl81
-rw-r--r--components/interfaces/nsISupports.idl77
-rw-r--r--components/interfaces/nsrootidl.idl128
-rw-r--r--components/interfaces/rdIMatchResult.idl13
-rw-r--r--components/interfaces/rdIRedirect.idl29
-rw-r--r--components/interfaces/rdIRedirector.idl23
-rw-r--r--components/interfaces/xpidl.exebin487424 -> 0 bytes
-rw-r--r--install.rdf4
-rw-r--r--locale/en-US/browserOverlay.xul.dtd9
-rw-r--r--locale/en-US/editRedirect.xul.dtd14
-rw-r--r--locale/en-US/redirector.properties33
-rw-r--r--locale/en-US/settings.xul.dtd65
-rw-r--r--locale/zh-CN/browserOverlay.xul.dtd9
-rw-r--r--locale/zh-CN/editRedirect.xul.dtd14
-rw-r--r--locale/zh-CN/redirector.properties33
-rw-r--r--locale/zh-CN/settings.xul.dtd65
40 files changed, 2071 insertions, 1051 deletions
diff --git a/build.py b/build.py
index 886151c..2454945 100644
--- a/build.py
+++ b/build.py
@@ -6,7 +6,7 @@ for f in os.listdir('.'):
os.system(r'xpidl -m typelib -e ..\%s %s' % (f[:-3] + 'xpt',f))
os.chdir(r'..\..')
-xpi = zipfile.ZipFile('redirector-2.5.2.xpi','w')
+xpi = zipfile.ZipFile('redirector-2.6.xpi','w')
for (root, folders, files) in os.walk('.'):
if not '.svn' in root:
for f in files:
diff --git a/chrome.manifest b/chrome.manifest
index bc60f58..3ca72f2 100644
--- a/chrome.manifest
+++ b/chrome.manifest
@@ -1,17 +1,12 @@
-# $Id$
-content redirector file:chrome/content/
-locale redirector en-US file:chrome/locale/en-US/
-locale redirector zh-CN file:chrome/locale/zh-CN/
-skin redirector classic/1.0 file:chrome/skin/
-overlay chrome://browser/content/browser.xul chrome://redirector/content/ui/browserOverlay.xul
+content redirector file:chrome/
+locale redirector en-US file:locale/en-US/
+locale redirector zh-CN file:locale/zh-CN/
+skin redirector classic/1.0 file:chrome/ui/skin/
-#FF4 registration
-interfaces components/rdIMatchResult.xpt
-interfaces components/rdIRedirect.xpt
-interfaces components/rdIRedirector.xpt
+overlay chrome://browser/content/browser.xul chrome://redirector/content/ui/browserOverlay.xul
-component {b7a7a54f-0581-47ff-b086-d6920cb7a3f7} components/redirector.component.js
+component {b7a7a54f-0581-47ff-b086-d6920cb7a3f7} chrome/code/component.js
contract @einaregilsson.com/redirector;1 {b7a7a54f-0581-47ff-b086-d6920cb7a3f7}
category profile-after-change Redirector @einaregilsson.com/redirector;1
category content-policy Redirector @einaregilsson.com/redirector;1
diff --git a/chrome/code/browserOverlay.xul.js b/chrome/code/browserOverlay.xul.js
new file mode 100644
index 0000000..0d2f46e
--- /dev/null
+++ b/chrome/code/browserOverlay.xul.js
@@ -0,0 +1,104 @@
+
+Components.utils.import("chrome://redirector/content/code/redirector.js");
+
+var RedirectorOverlay = {
+
+ strings : null,
+ prefs : null,
+
+ onLoad : function(event) {
+ try {
+
+ // initialization code
+ document.getElementById('contentAreaContextMenu')
+ .addEventListener("popupshowing", function(e) { RedirectorOverlay.showContextMenu(e); }, false);
+
+ this.strings = document.getElementById("redirector-strings");
+ this.prefs = new RedirectorPrefs();
+ this.changedPrefs(this.prefs);
+ this.prefs.addListener(this);
+ } catch(e) {
+ if (this.strings) {
+ alert(this.strings.getString("initError") + "\n\n" + e);
+ } else {
+ alert(e);
+ }
+ }
+ },
+
+ onUnload : function(event) {
+ this.prefs.dispose();
+ Redirector.debug("Finished cleanup");
+ },
+
+ changedPrefs : function(prefs) {
+ var statusImg = document.getElementById('redirector-statusbar-img');
+
+ if (prefs.enabled) {
+ statusImg.src = 'chrome://redirector/skin/statusactive.png'
+ statusImg.setAttribute('tooltiptext', this.strings.getString('enabledTooltip'));
+ } else {
+ statusImg.src = 'chrome://redirector/skin/statusinactive.png'
+ statusImg.setAttribute('tooltiptext', this.strings.getString('disabledTooltip'));
+ }
+
+ document.getElementById('redirector-status').hidden = !prefs.showStatusBarIcon;
+ document.getElementById('redirector-context').hidden = !prefs.showContextMenu;
+ },
+
+ showContextMenu : function(event) {
+ if (gContextMenu.onLink) {
+ document.getElementById("redirector-context").label = this.strings.getString('addLinkUrl');
+ } else {
+ document.getElementById("redirector-context").label = this.strings.getString('addCurrentUrl');
+ }
+ },
+
+ onContextMenuCommand: function(event) {
+ var redirect = new Redirect(window.content.location.href, window.content.location.href);
+ if (gContextMenu.onLink) {
+ redirect.redirectUrl = gContextMenu.link.toString();
+ }
+
+ var args = { saved : false, 'redirect' : redirect };
+ window.openDialog("chrome://redirector/content/ui/editRedirect.xul", "redirect", "chrome,dialog,modal,centerscreen", args);
+ if (args.saved) {
+ Redirector.addRedirect(args.redirect);
+ }
+ },
+
+ onMenuItemCommand: function(event) {
+ this.openSettings();
+ },
+
+ toggleEnabled : function(event) {
+ this.prefs.enabled = !this.prefs.enabled;
+ },
+
+ openSettings : function() {
+ var windowName = "redirectorSettings";
+ var windowsMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
+ var win = windowsMediator.getMostRecentWindow(windowName);
+ if (win) {
+ win.focus();
+ } else {
+ window.openDialog("chrome://redirector/content/ui/settings.xul",
+ windowName,
+ "chrome,dialog,resizable=yes,centerscreen", this);
+ }
+
+ },
+
+ statusBarClick : function(event) {
+ var LEFT = 0, RIGHT = 2;
+
+ if (event.button == LEFT) {
+ RedirectorOverlay.toggleEnabled();
+ } else if (event.button == RIGHT) {
+ this.openSettings();
+ }
+ }
+
+};
+window.addEventListener("load", function(event) { RedirectorOverlay.onLoad(event); }, false);
+window.addEventListener("unload", function(event) { RedirectorOverlay.onUnload(event); }, false);
diff --git a/chrome/code/component.js b/chrome/code/component.js
new file mode 100644
index 0000000..453d134
--- /dev/null
+++ b/chrome/code/component.js
@@ -0,0 +1,21 @@
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Ci = Components.interfaces;
+Cr = Components.results;
+
+Components.utils.import("chrome://redirector/content/code/redirector.js");
+
+function RedirectorComponent() { }
+
+RedirectorComponent.prototype = {
+ classDescription: "My Hello World Javascript XPCOM Component",
+ classID: Components.ID("{b7a7a54f-0581-47ff-b086-d6920cb7a3f7}"),
+ contractID: "@einaregilsson.com/redirector;1",
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIContentPolicy) || iid.equals(Ci.nsIChannelEventSink)) {
+ return Redirector;
+ }
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([RedirectorComponent]);
diff --git a/chrome/code/editRedirect.xul.js b/chrome/code/editRedirect.xul.js
new file mode 100644
index 0000000..0c8f34b
--- /dev/null
+++ b/chrome/code/editRedirect.xul.js
@@ -0,0 +1,103 @@
+//// $Id$
+
+var EditRedirect = {
+ txtExampleUrl : null,
+ txtIncludePattern : null,
+ txtRedirectUrl : null,
+ txtExcludePattern : null,
+ chkUnescapeMatches : null,
+ rdoRegex : null,
+ rdoWildcard : null,
+
+ onLoad : function() {
+ var args = window.arguments[0];
+ var redirect = args.redirect;
+ this.txtExampleUrl = document.getElementById('txtExampleUrl');
+ this.txtIncludePattern = document.getElementById('txtIncludePattern');
+ this.txtRedirectUrl= document.getElementById('txtRedirectUrl');
+ this.txtExcludePattern= document.getElementById('txtExcludePattern');
+ this.chkUnescapeMatches= document.getElementById('chkUnescapeMatches');
+ this.rdoWildcard= document.getElementById('rdoWildcard');
+ this.rdoRegex = document.getElementById('rdoRegex');
+
+ this.txtExampleUrl.value = redirect.exampleUrl;
+ this.txtIncludePattern.value = redirect.includePattern;
+ this.txtExcludePattern.value = redirect.excludePattern;
+ this.txtRedirectUrl.value = redirect.redirectUrl;
+ this.chkUnescapeMatches.setAttribute('checked', redirect.unescapeMatches);
+ this.rdoRegex.setAttribute('selected', redirect.isRegex());
+ this.rdoWildcard.setAttribute('selected', redirect.isWildcard());
+
+ this.txtIncludePattern.focus();
+ this.strings = document.getElementById("redirector-strings");
+ },
+
+ onAccept : function() {
+ var args = window.arguments[0];
+ var msg, title;
+ args.saved = true;
+ this.saveValues(args.redirect);
+
+ var oldDisabled = args.redirect.disabled;
+ args.redirect.disabled = false;
+ if (!/^\s*$/.test(args.redirect.exampleUrl)) {
+ var result = args.redirect.getMatch(args.redirect.exampleUrl);
+ if (!result.isMatch) {
+ title = this.strings.getString('warningExampleUrlDoesntMatchPatternTitle');
+ msg = this.strings.getString('warningExampleUrlDoesntMatchPattern');
+ var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
+ var rv = ps.confirmEx(window, title, msg, ps.STD_YES_NO_BUTTONS, ps.BUTTON_TITLE_YES, ps.BUTTON_TITLE_NO, null, null, {});
+ return rv == 0;
+ } else {
+ var resultUrl = result.redirectTo;
+ if (!resultUrl.match(/https?:/)) {
+ var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
+ var uri = ioService.newURI(args.redirect.exampleUrl, null, null);
+ resultUrl = uri.resolve(resultUrl);
+ }
+
+ var secondResult = args.redirect.getMatch(resultUrl);
+ if (secondResult.isMatch) {
+ title = this.strings.getString('errorExampleUrlMatchesRecursiveTitle');
+ msg = this.strings.getFormattedString('errorExampleUrlMatchesRecursive', [args.redirect.exampleUrl, resultUrl]);
+ this.msgBox(title, msg);
+ return false;
+ }
+ }
+ }
+ return true;
+ },
+
+ msgBox : function(title, text) {
+ Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Components.interfaces.nsIPromptService)
+ .alert(window, title, text);
+ },
+
+ saveValues : function(redirect) {
+ redirect.exampleUrl = this.txtExampleUrl.value;
+ redirect.includePattern = this.txtIncludePattern.value;
+ redirect.excludePattern = this.txtExcludePattern.value;
+ redirect.redirectUrl = this.txtRedirectUrl.value;
+ redirect.patternType = this.rdoRegex.getAttribute('selected') == 'true' ? Redirect.REGEX : Redirect.WILDCARD;
+ var val = this.chkUnescapeMatches.getAttribute('checked');
+ redirect.unescapeMatches = val === 'true' || val === true;
+ //Disabled cannot be set here
+ },
+
+ testPattern : function() {
+ try {
+ var redirect = new Redirect();
+ this.saveValues(redirect);
+ var extName = this.strings.getString('extensionName');
+ var result = redirect.test();
+ if (result.isMatch) {
+ this.msgBox(extName, this.strings.getFormattedString('testPatternSuccess', [redirect.includePattern, redirect.exampleUrl, result.redirectTo]));
+ } else if (result.isExcludeMatch) {
+ this.msgBox(extName, this.strings.getFormattedString('testPatternExclude', [redirect.exampleUrl, redirect.excludePattern]));
+ } else {
+ this.msgBox(extName, this.strings.getFormattedString('testPatternFailure', [redirect.includePattern, redirect.exampleUrl]));
+ }
+ } catch(e) {alert(e);}
+ }
+}; \ No newline at end of file
diff --git a/chrome/code/redirect.js b/chrome/code/redirect.js
new file mode 100644
index 0000000..f9b7f00
--- /dev/null
+++ b/chrome/code/redirect.js
@@ -0,0 +1,218 @@
+
+var EXPORTED_SYMBOLS = ['Redirect'];
+
+
+function Redirect(exampleUrl, includePattern, redirectUrl, patternType, excludePattern, unescapeMatches, disabled) {
+ this._init(exampleUrl, includePattern, redirectUrl, patternType, excludePattern, unescapeMatches, disabled);
+}
+
+//Static
+Redirect.WILDCARD = 'W';
+Redirect.REGEX = 'R';
+
+Redirect.prototype = {
+
+ //attributes
+ exampleUrl : null,
+
+ get includePattern() { return this._includePattern; },
+ set includePattern(value) {
+ this._includePattern = value;
+ this._rxInclude = this._compile(value);
+ },
+
+ get excludePattern() { return this._excludePattern; },
+ set excludePattern(value) {
+ this._excludePattern = value;
+ this._rxExclude = this._compile(value);
+ },
+
+ redirectUrl : null,
+
+ get patternType() { return this._patternType; },
+ set patternType(value) {
+ this._patternType = value;
+ this.compile();
+ },
+
+ unescapeMatches : false,
+
+ disabled : false,
+
+ //Functions
+ clone : function() {
+ return new Redirect(this.exampleUrl, this.includePattern,
+ this.redirectUrl, this.patternType,
+ this.excludePattern, this.unescapeMatches,
+ this.disabled);
+ },
+
+ compile : function() {
+ this._rxInclude = this._compile(this._includePattern);
+ this._rxExclude = this._compile(this._excludePattern);
+ },
+
+ copyValues : function(other) {
+ this.exampleUrl = other.exampleUrl;
+ this.includePattern = other.includePattern;
+ this.excludePattern = other.excludePattern;
+ this.redirectUrl = other.redirectUrl;
+ this.patternType = other.patternType;
+ this.unescapeMatches = other.unescapeMatches;
+ this.disabled = other.disabled;
+ },
+
+ deserialize : function(str) {
+ if (!str || !str.split) {
+ throw Error("Invalid serialized redirect: " + str);
+ }
+ var parts = str.split(',,,');
+ if (parts.length < 5) {
+ throw Error("Invalid serialized redirect, too few fields: " + str);
+ }
+ this._init.apply(this, parts);
+ },
+
+ equals : function(redirect) {
+ return this.exampleUrl == redirect.exampleUrl
+ && this.includePattern == redirect.includePattern
+ && this.excludePattern == redirect.excludePattern
+ && this.redirectUrl == redirect.redirectUrl
+ && this.patternType == redirect.patternType
+ && this.unescapeMatches == redirect.unescapeMatches
+ ;
+ },
+
+ getMatch: function(url) {
+ var result = {
+ isMatch : false,
+ isExcludeMatch : false,
+ isDisabledMatch : false,
+ redirectTo : '',
+ toString : function() { return "{ isMatch : " + this.isMatch +
+ ", isExcludeMatch : " + this.isExcludeMatch +
+ ", isDisabledMatch : " + this.isDisabledMatch +
+ ", redirectTo : \"" + this.redirectTo + "\"" +
+ "}"; }
+ };
+ var redirectTo = null;
+
+ redirectTo = this._includeMatch(url);
+ if (redirectTo !== null) {
+ if (this.disabled) {
+ result.isDisabledMatch = true;
+ } else if (this._excludeMatch(url)) {
+ result.isExcludeMatch = true;
+ } else {
+ result.isMatch = true;
+ result.redirectTo = redirectTo;
+ }
+ }
+ return result;
+ },
+
+ isRegex: function() {
+ return this.patternType == Redirect.REGEX;
+ },
+
+ isWildcard : function() {
+ return this.patternType == Redirect.WILDCARD;
+ },
+
+ serialize : function() {
+ return [ this.exampleUrl
+ , this.includePattern
+ , this.redirectUrl
+ , this.patternType
+ , this.excludePattern
+ , this.unescapeMatches
+ , this.disabled ].join(',,,');
+ },
+
+ test : function() {
+ return this.getMatch(this.exampleUrl);
+ },
+
+
+ //Private functions below
+
+ _includePattern : null,
+ _excludePattern : null,
+ _patternType : null,
+ _rxInclude : null,
+ _rxExclude : null,
+
+ _preparePattern : function(pattern) {
+ if (this.patternType == Redirect.REGEX) {
+ return pattern;
+ } else { //Convert wildcard to regex pattern
+ var converted = '^';
+ for (var i = 0; i < pattern.length; i++) {
+ var ch = pattern.charAt(i);
+ if ('()[]{}?.^$\\+'.indexOf(ch) != -1) {
+ converted += '\\' + ch;
+ } else if (ch == '*') {
+ converted += '(.*?)';
+ } else {
+ converted += ch;
+ }
+ }
+ converted += '$';
+ return converted;
+ }
+ },
+
+ _compile : function(pattern) {
+ if (!pattern) {
+ return null;
+ }
+ return new RegExp(this._preparePattern(pattern),"gi");
+ },
+
+ _init : function(exampleUrl, includePattern, redirectUrl, patternType, excludePattern, unescapeMatches, disabled) {
+ this.exampleUrl = exampleUrl || '';
+ this.includePattern = includePattern || '';
+ this.excludePattern = excludePattern || '';
+ this.redirectUrl = redirectUrl || '';
+ this.patternType = patternType || Redirect.WILDCARD;
+ this.unescapeMatches = (unescapeMatches === 'true' || unescapeMatches === true);
+ this.disabled = (disabled === 'true' || disabled === true);
+ },
+
+ toString : function() {
+ return 'REDIRECT: {'
+ + '\n\tExample url : ' + this.exampleUrl
+ + '\n\tInclude pattern : ' + this.includePattern
+ + '\n\tExclude pattern : ' + this.excludePattern
+ + '\n\tRedirect url : ' + this.redirectUrl
+ + '\n\tPattern type : ' + this.patternType
+ + '\n\tUnescape matches : ' + this.unescapeMatches
+ + '\n\tDisabled : ' + this.disabled
+ + '\n}\n';
+ },
+
+ _includeMatch : function(url) {
+ if (!this._rxInclude) {
+ return null;
+ }
+ var matches = this._rxInclude.exec(url);
+ if (!matches) {
+ return null;
+ }
+ var resultUrl = this.redirectUrl;
+ for (var i = 1; i < matches.length; i++) {
+ resultUrl = resultUrl.replace(new RegExp('\\$' + i, 'gi'), this.unescapeMatches ? unescape(matches[i]) : matches[i]);
+ }
+ this._rxInclude.lastIndex = 0;
+ return resultUrl;
+ },
+
+ _excludeMatch : function(url) {
+ if (!this._rxExclude) {
+ return false;
+ }
+ var shouldExclude = !!this._rxExclude.exec(url);
+ this._rxExclude.lastIndex = 0;
+ return shouldExclude;
+ }
+}; \ No newline at end of file
diff --git a/chrome/content/code/redirector.prototype.js b/chrome/code/redirector.js
index be920d8..8744483 100644
--- a/chrome/content/code/redirector.prototype.js
+++ b/chrome/code/redirector.js
@@ -1,343 +1,370 @@
-//// $Id$
-
-Redirector.prototype = {
-
- //rdIRedirector implementation
- get enabled() {
- return this._prefs && this._prefs.enabled;
- },
-
- set enabled(value) {
- if (this._prefs) {
- this._prefs.enabled = value;
- }
- },
-
- get redirectCount() {
- return this._list.length;
- },
-
- addRedirect : function(redirect) {
- //This runaround is necessary because the redirect
- //that was created somewhere up in the GUI doesn't
- //have the Redirect function in scope.
- var rx = new Redirect();
- rx.copyValues(redirect);
- this._list.push(rx);
- this.save();
- },
-
- debug : function(msg) {
- if (this._prefs.debugEnabled) {
- this._cout.logStringMessage('REDIRECTOR: ' + msg);
- }
- },
-
- deleteRedirectAt : function(index) {
- this._list.splice(index, 1);
- this.save();
- },
-
- exportRedirects : function(file) {
- var fileStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
- const PR_WRONLY = 0x02;
- const PR_CREATE_FILE = 0x08;
- const PR_TRUNCATE = 0x20;
-
- fileStream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644, 0);
- var stream = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
- stream.init(fileStream, "UTF-8", 16384, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
- stream.writeString(this._redirectsAsString('\n'));
- stream.close();
- },
-
- getRedirectAt : function(index) {
- return this._list[index];
- },
-
- //Get the redirect url for the given url. This will not check if we are enabled, and
- //not do any verification on the url, just assume that it is a good string url that is for http/s
- getRedirectUrl : function(url) {
- this.debug("Checking " + url);
-
- for each (var redirect in this._list) {
- var result = redirect.getMatch(url);
- if (result.isExcludeMatch) {
- this.debug(url + ' matched exclude pattern ' + redirect.excludePattern + ' so the redirect ' + redirect.includePattern + ' will not be used');
- } else if (result.isDisabledMatch) {
- this.debug(url + ' matched pattern ' + redirect.includePattern + ' but the redirect is disabled');
- } else if (result.isMatch) {
- redirectUrl = this._makeAbsoluteUrl(url, result.redirectTo);
-
- //check for loops...
- result = redirect.getMatch(redirectUrl);
- if (result.isMatch) {
- var title = this._getString('invalidRedirectTitle');
- var msg = this._getFormattedString('invalidRedirectText', [redirect.includePattern, url, redirectUrl]);
- this.debug(msg);
- redirect.disabled = true;
- this.save();
- this._msgBox(title, msg);
- } else {
- this.debug('Redirecting ' + url + ' to ' + redirectUrl);
- return redirectUrl;
- }
- }
- }
- return null;
- },
-
- importRedirects : function(file) {
- var fileStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
- fileStream.init(file, 0x01, 0444, 0); //TODO: Find the actual constants for these magic numbers
-
- var stream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
- stream.init(fileStream, "UTF-8", 16384, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
- stream = stream.QueryInterface(Ci.nsIUnicharLineInputStream);
-
- var importCount = 0, existsCount = 0;
- var lines = [];
- var line = {value: null};
- stream.readLine(line);
- while (line.value) {
- var redirect = new Redirect();
- redirect.deserialize(line.value.replace('\n', ''));
- if (this._containsRedirect(redirect)) {
- existsCount++;
- } else {
- this._list.push(redirect);
- importCount++;
- }
- stream.readLine(line);
- }
- stream.close();
- this.save();
- return importCount | (existsCount << 16);
- },
-
- reload : function() {
- loader.loadSubScript('chrome://redirector/content/code/redirector.prototype.js');
- loader.loadSubScript('chrome://redirector/content/code/redirect.js');
- var oldEnabled = this.enabled;
- for (var key in Redirector.prototype) {
- if (key != 'redirectCount' && key != 'enabled') {
- this[key] = Redirector.prototype[key];
- }
- }
- this._init();
- this.enabled = oldEnabled;
- },
-
- save : function() {
- this._prefs.redirects = this._redirectsAsString(':::');
- },
-
- switchItems : function(index1, index2) {
- var item = this._list[index1];
- this._list[index1] = this._list[index2];
- this._list[index2] = item;
- this.save();
- },
-
- //End rdIRedirector
-
- // nsIContentPolicy implementation
- shouldLoad: function(contentType, contentLocation, requestOrigin, aContext, mimeTypeGuess, extra) {
- rdump('nsIContentPolicy::ShouldLoad ' + contentLocation.spec);
- try {
- //This is also done in getRedirectUrl, but we want to exit as quickly as possible for performance
- if (!this._prefs.enabled) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-
- if (contentType != Ci.nsIContentPolicy.TYPE_DOCUMENT) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-
- if (contentLocation.scheme != "http" && contentLocation.scheme != "https") {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-
- if (!aContext || !aContext.loadURI) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-
- var redirectUrl = this.getRedirectUrl(contentLocation.spec);
-
- if (!redirectUrl) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-
- aContext.loadURI(redirectUrl, requestOrigin, null);
- return Ci.nsIContentPolicy.REJECT_REQUEST;
- } catch(e) {
- this.debug(e);
- }
-
- },
-
- shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode, mimeType, extra) {
- return Ci.nsIContentPolicy.ACCEPT;
- },
- //end nsIContentPolicy
-
- //nsIChannelEventSink implementation
-
- //For FF4.0. Got this from a thread about adblock plus, https://adblockplus.org/forum/viewtopic.php?t=5895
- asyncOnChannelRedirect: function(oldChannel, newChannel, flags, redirectCallback) {
- this.onChannelRedirect(oldChannel, newChannel, flags);
- redirectCallback.onRedirectVerifyCallback(0);
- },
-
- onChannelRedirect: function(oldChannel, newChannel, flags)
- {
- try {
- let newLocation = newChannel.URI.spec;
- rdump('nsIChannelEventSink::onChannelRedirect ' + newLocation);
-
- if (!(newChannel.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI)) {
- //We only redirect documents...
- return;
- }
-
- if (!this._prefs.enabled) {
- return;
- }
-
- if (!newLocation) {
- return;
- }
- let callbacks = [];
- if (newChannel.notificationCallbacks) {
- callbacks.push(newChannel.notificationCallbacks);
- }
- if (newChannel.loadGroup && newChannel.loadGroup.notificationCallbacks) {
- callbacks.push(newChannel.loadGroup.notificationCallbacks);
- }
- var win;
- var webNav;
- for each (let callback in callbacks)
- {
- try {
- win = callback.getInterface(Ci.nsILoadContext).associatedWindow;
- webNav = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
- break;
- } catch(e) {}
- }
- if (!webNav) {
- return;
- }
- var redirectUrl = this.getRedirectUrl(newLocation);
-
- if (redirectUrl) {
- webNav.loadURI(redirectUrl,null,null,null,null);
- throw Cr.NS_BASE_STREAM_WOULD_BLOCK; //Throw this because the real error we should throw shows up in console...
- }
-
- } catch (e if (e != Cr.NS_BASE_STREAM_WOULD_BLOCK)) {
- // We shouldn't throw exceptions here - this will prevent the redirect.
- dump("Redirector: Unexpected error in onChannelRedirect: " + e + "\n");
- }
- },
- //end nsIChannelEventSink
-
- //Private members and methods
-
- _prefs : null,
- _list : null,
- _strings : null,
- _cout : Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService),
-
- _init : function() {
- if (this._prefs) {
- this._prefs.dispose();
- }
- this._cout.logStringMessage('REDIRECTOR CREATED');
- this._prefs = new RedirectorPrefs();
- //Check if we need to update existing redirects
- var data = this._prefs.redirects;
- var version = this._prefs.version;
- this._loadStrings();
- var currentVersion = '2.5';
- //Here update checks are handled
- if (version == 'undefined') { //Either a fresh install of Redirector, or first time install of v2.0
- if (data) { //There is some data in redirects, we are upgrading from a previous version, need to upgrade data
- var tempList = JSON.parse(data);
- var arr;
- var newArr = []
- for each (arr in tempList) {
- if (arr.length == 5) {
- arr.push(''); //For those that don't have an exclude pattern. Backwards compatibility is a bitch!
- }
- arr.splice(3,1); //Remove the "only if link exists" data
- newArr.push(arr.join(',,,'));
- }
- this._prefs.redirects = newArr.join(':::');
- }
- this._prefs.version = currentVersion;
- } else if (version == '2.0' || version == '2.0.1' || version == '2.0.2') {
- this._prefs.version = currentVersion;
- }
- //Update finished
-
- //Now get from the new format
- data = this._prefs.redirects;
- var arr;
- this._list = [];
- if (data != '') {
- for each (redirectString in data.split(':::')) {
- var redirect = new Redirect();
- redirect.deserialize(redirectString);
- this._list.push(redirect);
- }
- }
- },
-
- _loadStrings : function() {
- var src = 'chrome://redirector/locale/redirector.properties';
- var localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci.nsILocaleService);
- var appLocale = localeService.getApplicationLocale();
- var stringBundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
- this._strings = stringBundleService.createBundle(src, appLocale);
- },
-
- _redirectsAsString : function(seperator) {
- return [r.serialize() for each (r in this._list)].join(seperator);
- },
-
-
- _containsRedirect : function(redirect) {
- for each (var existing in this._list) {
- if (existing.equals(redirect)) {
- return true;
- }
- }
- return false;
- },
-
- _getString : function(name) {
- return this._strings.GetStringFromName(name);
- },
-
- _getFormattedString : function(name, params) {
- return this._strings.formatStringFromName(name, params, params.length);
- },
-
- _msgBox : function(title, text) {
- Cc["@mozilla.org/embedcomp/prompt-service;1"]
- .getService(Ci.nsIPromptService)
- .alert(null, title, text);
- },
-
- _makeAbsoluteUrl : function(currentUrl, relativeUrl) {
-
- if (relativeUrl.match(/https?:/)) {
- return relativeUrl;
- }
-
- var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
- var uri = ioService.newURI(currentUrl, null, null);
-
- return uri.resolve(relativeUrl);
- }
-};
+var EXPORTED_SYMBOLS = ['Redirector', 'rdump'];
+Ci = Components.interfaces;
+Cc = Components.classes;
+Ci = Components.interfaces;
+Cr = Components.results;
+
+Components.utils.import("chrome://redirector/content/code/redirect.js");
+Components.utils.import("chrome://redirector/content/code/redirectorprefs.js");
+
+function rdump(msg) {
+ dump(msg + '\n');
+}
+
+Redirector = {
+
+ get enabled() {
+ return this._prefs && this._prefs.enabled;
+ },
+
+ set enabled(value) {
+ if (this._prefs) {
+ this._prefs.enabled = value;
+ }
+ },
+
+ get redirectCount() {
+ return this._list.length;
+ },
+
+ toString : function() {
+ return "Redirector";
+ },
+
+ addRedirect : function(redirect) {
+ //This runaround is necessary because the redirect
+ //that was created somewhere up in the GUI doesn't
+ //have the Redirect function in scope.
+ var rx = new Redirect();
+ rx.copyValues(redirect);
+ this._list.push(rx);
+ this.save();
+ },
+
+ debug : function(msg) {
+ if (this._prefs.debugEnabled) {
+ this._cout.logStringMessage('REDIRECTOR: ' + msg);
+ }
+ },
+
+ deleteRedirectAt : function(index) {
+ this._list.splice(index, 1);
+ this.save();
+ },
+
+ exportRedirects : function(file) {
+ var fileStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
+ const PR_WRONLY = 0x02;
+ const PR_CREATE_FILE = 0x08;
+ const PR_TRUNCATE = 0x20;
+
+ fileStream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644, 0);
+ var stream = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
+ stream.init(fileStream, "UTF-8", 16384, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
+ stream.writeString(this._redirectsAsString('\n'));
+ stream.close();
+ },
+
+ getRedirectAt : function(index) {
+ return this._list[index];
+ },
+
+ //Get the redirect url for the given url. This will not check if we are enabled, and
+ //not do any verification on the url, just assume that it is a good string url that is for http/s
+ getRedirectUrl : function(url) {
+ this.debug("Checking " + url);
+
+ for each (var redirect in this._list) {
+ var result = redirect.getMatch(url);
+ if (result.isExcludeMatch) {
+ this.debug(url + ' matched exclude pattern ' + redirect.excludePattern + ' so the redirect ' + redirect.includePattern + ' will not be used');
+ } else if (result.isDisabledMatch) {
+ this.debug(url + ' matched pattern ' + redirect.includePattern + ' but the redirect is disabled');
+ } else if (result.isMatch) {
+ redirectUrl = this._makeAbsoluteUrl(url, result.redirectTo);
+
+ //check for loops...
+ result = redirect.getMatch(redirectUrl);
+ if (result.isMatch) {
+ var title = this._getString('invalidRedirectTitle');
+ var msg = this._getFormattedString('invalidRedirectText', [redirect.includePattern, url, redirectUrl]);
+ this.debug(msg);
+ redirect.disabled = true;
+ this.save();
+ this._msgBox(title, msg);
+ } else {
+ this.debug('Redirecting ' + url + ' to ' + redirectUrl);
+ return redirectUrl;
+ }
+ }
+ }
+ return null;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIContentPolicy) || iid.equals(Ci.nsIChannelEventSink)) {
+ return this;
+ }
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+
+ importRedirects : function(file) {
+ var fileStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+ fileStream.init(file, 0x01, 0444, 0); //TODO: Find the actual constants for these magic numbers
+
+ var stream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
+ stream.init(fileStream, "UTF-8", 16384, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
+ stream = stream.QueryInterface(Ci.nsIUnicharLineInputStream);
+
+ var importCount = 0, existsCount = 0;
+ var lines = [];
+ var line = {value: null};
+ stream.readLine(line);
+ while (line.value) {
+ var redirect = new Redirect();
+ redirect.deserialize(line.value.replace('\n', ''));
+ if (this._containsRedirect(redirect)) {
+ existsCount++;
+ } else {
+ this._list.push(redirect);
+ importCount++;
+ }
+ stream.readLine(line);
+ }
+ stream.close();
+ this.save();
+ return importCount | (existsCount << 16);
+ },
+
+ reload : function() {
+ loader.loadSubScript('chrome://redirector/content/code/redirector.prototype.js');
+ loader.loadSubScript('chrome://redirector/content/code/redirect.js');
+ var oldEnabled = this.enabled;
+ for (var key in Redirector.prototype) {
+ if (key != 'redirectCount' && key != 'enabled') {
+ this[key] = Redirector.prototype[key];
+ }
+ }
+ this._init();
+ this.enabled = oldEnabled;
+ },
+
+ save : function() {
+ this._prefs.redirects = this._redirectsAsString(':::');
+ },
+
+ switchItems : function(index1, index2) {
+ var item = this._list[index1];
+ this._list[index1] = this._list[index2];
+ this._list[index2] = item;
+ this.save();
+ },
+
+
+ // nsIContentPolicy implementation
+ shouldLoad: function(contentType, contentLocation, requestOrigin, aContext, mimeTypeGuess, extra) {
+ if (contentLocation.scheme != "http" && contentLocation.scheme != "https") {
+ return Ci.nsIContentPolicy.ACCEPT;
+ } //Immediately, otherwise we will log all sorts of crap
+
+ rdump('nsIContentPolicy::ShouldLoad ' + contentLocation.spec);
+ try {
+ //This is also done in getRedirectUrl, but we want to exit as quickly as possible for performance
+ if (!this._prefs.enabled) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+
+ if (contentType != Ci.nsIContentPolicy.TYPE_DOCUMENT) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+
+ if (contentLocation.scheme != "http" && contentLocation.scheme != "https") {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+
+ if (!aContext || !aContext.loadURI) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+
+ var redirectUrl = this.getRedirectUrl(contentLocation.spec);
+
+ if (!redirectUrl) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+
+ aContext.loadURI(redirectUrl, requestOrigin, null);
+ return Ci.nsIContentPolicy.REJECT_REQUEST;
+ } catch(e) {
+ this.debug(e);
+ }
+
+ },
+
+ shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode, mimeType, extra) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ },
+ //end nsIContentPolicy
+
+ //nsIChannelEventSink implementation
+
+ //For FF4.0. Got this from a thread about adblock plus, https://adblockplus.org/forum/viewtopic.php?t=5895
+ asyncOnChannelRedirect: function(oldChannel, newChannel, flags, redirectCallback) {
+ this.onChannelRedirect(oldChannel, newChannel, flags);
+ redirectCallback.onRedirectVerifyCallback(0);
+ },
+
+ onChannelRedirect: function(oldChannel, newChannel, flags)
+ {
+ try {
+ let newLocation = newChannel.URI.spec;
+ rrdump('nsIChannelEventSink::onChannelRedirect ' + newLocation);
+
+ if (!(newChannel.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI)) {
+ //We only redirect documents...
+ return;
+ }
+
+ if (!this._prefs.enabled) {
+ return;
+ }
+
+ if (!newLocation) {
+ return;
+ }
+ let callbacks = [];
+ if (newChannel.notificationCallbacks) {
+ callbacks.push(newChannel.notificationCallbacks);
+ }
+ if (newChannel.loadGroup && newChannel.loadGroup.notificationCallbacks) {
+ callbacks.push(newChannel.loadGroup.notificationCallbacks);
+ }
+ var win;
+ var webNav;
+ for each (let callback in callbacks)
+ {
+ try {
+ win = callback.getInterface(Ci.nsILoadContext).associatedWindow;
+ webNav = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
+ break;
+ } catch(e) {}
+ }
+ if (!webNav) {
+ return;
+ }
+ var redirectUrl = this.getRedirectUrl(newLocation);
+
+ if (redirectUrl) {
+ webNav.loadURI(redirectUrl,null,null,null,null);
+ throw Cr.NS_BASE_STREAM_WOULD_BLOCK; //Throw this because the real error we should throw shows up in console...
+ }
+
+ } catch (e if (e != Cr.NS_BASE_STREAM_WOULD_BLOCK)) {
+ // We shouldn't throw exceptions here - this will prevent the redirect.
+ rdump("Redirector: Unexpected error in onChannelRedirect: " + e + "\n");
+ }
+ },
+ //end nsIChannelEventSink
+
+ //Private members and methods
+
+ _prefs : null,
+ _list : null,
+ _strings : null,
+ _cout : Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService),
+
+ _init : function() {
+ if (this._prefs) {
+ this._prefs.dispose();
+ }
+ this._cout.logStringMessage('REDIRECTOR CREATED');
+ this._prefs = new RedirectorPrefs();
+ //Check if we need to update existing redirects
+ var data = this._prefs.redirects;
+ var version = this._prefs.version;
+ this._loadStrings();
+ var currentVersion = '2.5';
+ //Here update checks are handled
+ if (version == 'undefined') { //Either a fresh install of Redirector, or first time install of v2.0
+ if (data) { //There is some data in redirects, we are upgrading from a previous version, need to upgrade data
+ var tempList = JSON.parse(data);
+ var arr;
+ var newArr = []
+ for each (arr in tempList) {
+ if (arr.length == 5) {
+ arr.push(''); //For those that don't have an exclude pattern. Backwards compatibility is a bitch!
+ }
+ arr.splice(3,1); //Remove the "only if link exists" data
+ newArr.push(arr.join(',,,'));
+ }
+ this._prefs.redirects = newArr.join(':::');
+ }
+ this._prefs.version = currentVersion;
+ } else if (version == '2.0' || version == '2.0.1' || version == '2.0.2') {
+ this._prefs.version = currentVersion;
+ }
+ //Update finished
+
+ //Now get from the new format
+ data = this._prefs.redirects;
+ var arr;
+ this._list = [];
+ if (data != '') {
+ for each (redirectString in data.split(':::')) {
+ var redirect = new Redirect();
+ redirect.deserialize(redirectString);
+ this._list.push(redirect);
+ }
+ }
+ },
+
+ _loadStrings : function() {
+ var src = 'chrome://redirector/locale/redirector.properties';
+ var localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci.nsILocaleService);
+ var appLocale = localeService.getApplicationLocale();
+ var stringBundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
+ this._strings = stringBundleService.createBundle(src, appLocale);
+ },
+
+ _redirectsAsString : function(seperator) {
+ return [r.serialize() for each (r in this._list)].join(seperator);
+ },
+
+
+ _containsRedirect : function(redirect) {
+ for each (var existing in this._list) {
+ if (existing.equals(redirect)) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ _getString : function(name) {
+ return this._strings.GetStringFromName(name);
+ },
+
+ _getFormattedString : function(name, params) {
+ return this._strings.formatStringFromName(name, params, params.length);
+ },
+
+ _msgBox : function(title, text) {
+ Cc["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Ci.nsIPromptService)
+ .alert(null, title, text);
+ },
+
+ _makeAbsoluteUrl : function(currentUrl, relativeUrl) {
+
+ if (relativeUrl.match(/https?:/)) {
+ return relativeUrl;
+ }
+
+ var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+ var uri = ioService.newURI(currentUrl, null, null);
+
+ return uri.resolve(relativeUrl);
+ }
+};
+
+Redirector._init(); \ No newline at end of file
diff --git a/chrome/code/redirectorprefs.js b/chrome/code/redirectorprefs.js
new file mode 100644
index 0000000..101a3f0
--- /dev/null
+++ b/chrome/code/redirectorprefs.js
@@ -0,0 +1,93 @@
+var EXPORTED_SYMBOLS = ['RedirectorPrefs'];
+
+function RedirectorPrefs() {
+ this.init();
+}
+
+RedirectorPrefs.prototype = {
+
+ //Preferences:
+ _version : null,
+ _enabled : null,
+ _showStatusBarIcon : null,
+ _showContextMenu : null,
+ _debugEnabled : null,
+ _defaultDir : null,
+ _redirects : null,
+
+ _prefBranch : null,
+
+ _listeners : null,
+
+ //Preferences props
+
+ get version() { return this._version; },
+ set version(value) { this._prefBranch.setCharPref('version', value); },
+
+ get enabled() { return this._enabled; },
+ set enabled(value) { this._prefBranch.setBoolPref('enabled', value); },
+
+ get showStatusBarIcon() { return this._showStatusBarIcon; },
+ set showStatusBarIcon(value) { this._prefBranch.setBoolPref('showStatusBarIcon', value); },
+
+ get showContextMenu() { return this._showContextMenu; },
+ set showContextMenu(value) { this._prefBranch.setBoolPref('showContextMenu', value); },
+
+ get debugEnabled() { return this._debugEnabled; },
+ set debugEnabled(value) { this._prefBranch.setBoolPref('debugEnabled', value); },
+
+ get defaultDir() { return this._defaultDir; },
+ set defaultDir(value) { this._prefBranch.setCharPref('defaultDir', value); },
+
+ get redirects() { return this._redirects; },
+ set redirects(value) { this._prefBranch.setCharPref('redirects', value); },
+
+ init : function() {
+ this._prefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("extensions.redirector.");
+ this.reload();
+ this._listeners = [];
+ this.service.addObserver('extensions.redirector', this, false);
+ },
+
+ dispose : function() {
+ this._listeners = null;
+ this.service.removeObserver('extensions.redirector', this);
+ },
+
+ reload : function() {
+ this._version = this._prefBranch.getCharPref('version');
+ this._enabled = this._prefBranch.getBoolPref('enabled');
+ this._showStatusBarIcon = this._prefBranch.getBoolPref('showStatusBarIcon');
+ this._showContextMenu = this._prefBranch.getBoolPref('showContextMenu');
+ this._debugEnabled = this._prefBranch.getBoolPref('debugEnabled');
+ this._defaultDir = this._prefBranch.getCharPref('defaultDir');
+ this._redirects = this._prefBranch.getCharPref('redirects');
+ },
+
+ get service() {
+ return Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranchInternal);
+ },
+
+ observe : function(subject, topic, data) {
+ if (topic != 'nsPref:changed') {
+ return;
+ }
+ this.reload();
+ for each (var listener in this._listeners) {
+ listener && listener.changedPrefs && listener.changedPrefs(this);
+ }
+ },
+
+ addListener : function(listener) {
+ this._listeners.push(listener);
+ },
+
+ removeListener : function(listener) {
+ for (var i = 0; i < this._listeners.length; i++) {
+ if (this._listeners[i] == listener) {
+ this._listeners.splice(i,1);
+ return;
+ }
+ }
+ },
+} \ No newline at end of file
diff --git a/chrome/code/settings.xul.js b/chrome/code/settings.xul.js
new file mode 100644
index 0000000..c7f780a
--- /dev/null
+++ b/chrome/code/settings.xul.js
@@ -0,0 +1,276 @@
+Components.utils.import("chrome://redirector/content/code/redirector.js");
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const nsLocalFile = Components.Constructor("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");
+
+var Settings = {
+
+ lstRedirects: null,
+ btnDelete : null,
+ btnEdit : null,
+ btnUp : null,
+ btnDown : null,
+ btnExport : null,
+ btnImport : null,
+ chkEnableRedirector : null,
+ chkShowStatusBarIcon : null,
+ chkShowContextMenu : null,
+ chkEnableDebugOutput : null,
+ prefs : null,
+
+ onLoad : function() {
+ try {
+ //Get references to controls
+ this.lstRedirects = document.getElementById('lstRedirects');
+ this.btnDelete = document.getElementById('btnDelete');
+ this.btnEdit = document.getElementById('btnEdit');
+ this.btnUp = document.getElementById('btnUp');
+ this.btnDown = document.getElementById('btnDown');
+ this.btnExport = document.getElementById('btnExport');
+ this.btnImport = document.getElementById('btnImport');
+ this.chkEnableRedirector = document.getElementById('chkEnableRedirector');
+ this.chkShowStatusBarIcon = document.getElementById('chkShowStatusBarIcon');
+ this.chkShowContextMenu = document.getElementById('chkShowContextMenu');
+ this.chkEnableDebugOutput = document.getElementById('chkEnableDebugOutput');
+
+ this.prefs = new RedirectorPrefs();
+ //Preferences
+ this.changedPrefs(this.prefs);
+ this.prefs.addListener(this);
+
+ //Redirect list
+ this.lstRedirects.selType = 'single';
+ this.template = document.getElementsByTagName('richlistitem')[0];
+ this.lstRedirects.removeChild(this.template);
+ var list = [];
+ for (var i = 0; i < Redirector.redirectCount; i++) {
+ list.push(Redirector.getRedirectAt(i));
+ }
+ this.addItemsToListBox(list);
+ this.selectionChange();
+
+ this.strings = document.getElementById('redirector-strings');
+ this.strings.getPluralized = function(id, number) {
+ id += number == 1 ? 'Singular' : '';
+ return this.getFormattedString(id, [number]);
+ };
+ } catch(e) {
+ alert(e);
+ }
+ },
+
+ onUnload : function() {
+ this.prefs.dispose();
+ },
+
+ changedPrefs : function(prefs) {
+ this.chkEnableRedirector.setAttribute('checked', prefs.enabled);
+ this.chkShowStatusBarIcon.setAttribute('checked', prefs.showStatusBarIcon);
+ this.chkShowContextMenu.setAttribute('checked', prefs.showContextMenu);
+ this.chkEnableDebugOutput.setAttribute('checked', prefs.debugEnabled);
+ },
+
+ addItemsToListBox : function(items) {
+
+ var item, row, value, newItem;
+
+ for each (item in items) {
+ newItem = this.template.cloneNode(true);
+
+ newItem.getElementsByAttribute('name', 'dscrIncludePattern')[0].setAttribute('value', item.includePattern);
+ newItem.getElementsByAttribute('name', 'dscrExcludePattern')[0].setAttribute('value', item.excludePattern);
+ newItem.getElementsByAttribute('name', 'dscrRedirectTo')[0].setAttribute('value', item.redirectUrl);
+ var checkEnabled = newItem.getElementsByAttribute('name', 'chkEnabled')[0];
+ checkEnabled.setAttribute('checked', !item.disabled);
+ newItem.setAttribute('class', item.disabled ? 'disabledRedirect' : '');
+ newItem.item = item;
+ this.lstRedirects.appendChild(newItem);
+ newItem.setAttribute('selected', false)
+ }
+
+ //Enable, disable functionality
+ this.lstRedirects.addEventListener('click', function(ev) {
+ if (ev.originalTarget && ev.originalTarget.tagName == 'checkbox') {
+ var parent = ev.originalTarget.parentNode;
+ while (!parent.item) {
+ parent = parent.parentNode;
+ }
+ parent.item.disabled = !ev.originalTarget.hasAttribute('checked');
+ parent.setAttribute('class', parent.item.disabled ? 'disabledRedirect' : '');
+ Redirector.save();
+ }
+ },false);
+ },
+
+ moveUp : function(){
+ if (this.lstRedirects.selectedIndex <= 0) {
+ return;
+ }
+ this.switchItems(this.lstRedirects.selectedIndex-1);
+ },
+
+ moveDown : function() {
+ if (this.lstRedirects.selectedIndex == Redirector.redirectCount-1) {
+ return;
+ }
+ this.switchItems(this.lstRedirects.selectedIndex);
+ },
+
+ switchItems : function(firstIndex) {
+ Redirector.switchItems(firstIndex, firstIndex+1);
+ var firstItem = this.lstRedirects.children[firstIndex];
+ var secondItem = this.lstRedirects.children[firstIndex+1];
+ this.lstRedirects.removeChild(secondItem);
+ this.lstRedirects.insertBefore(secondItem, firstItem);
+ this.selectionChange();
+ },
+
+ setListItemValues : function(listItem, item){
+ listItem.getElementsByAttribute('name', 'dscrIncludePattern')[0].setAttribute('value', item.includePattern);
+ listItem.getElementsByAttribute('name', 'dscrExcludePattern')[0].setAttribute('value', item.excludePattern);
+ listItem.getElementsByAttribute('name', 'dscrRedirectTo')[0].setAttribute('value', item.redirectUrl);
+ },
+
+ preferenceChange : function(event) {
+ this.prefs[event.originalTarget.getAttribute('preference')] = event.originalTarget.hasAttribute('checked');
+ },
+
+ addRedirect : function() {
+ var args = { saved : false, redirect : new Redirect() };
+ window.openDialog("chrome://redirector/content/ui/editRedirect.xul", "redirect", "chrome,dialog,modal,centerscreen", args);
+ if (args.saved) {
+ Redirector.addRedirect(args.redirect);
+ //Get it from redirector since it has processed it and it's no longer the same
+ //object as the one we added.
+ this.addItemsToListBox([Redirector.getRedirectAt(Redirector.redirectCount-1)]);
+ this.selectionChange();
+ }
+ },
+
+ editRedirect : function() {
+
+ if (this.lstRedirects.selectedIndex == -1) {
+ return;
+ }
+ //.selectedItem is still there after it has been removed, that's why we have the .selectedIndex check above as well
+ var listItem = this.lstRedirects.selectedItem;
+ if (!listItem) {
+ return;
+ }
+ var redirect = listItem.item;
+ var args = { saved: false, "redirect":redirect.clone()};
+ window.openDialog("chrome://redirector/content/ui/editRedirect.xul", "redirect", "chrome,dialog,modal,centerscreen", args);
+
+ if (args.saved) {
+ redirect.copyValues(args.redirect);
+ this.setListItemValues(listItem, redirect);
+ Redirector.save();
+ this.selectionChange();
+ }
+ },
+
+ deleteRedirect : function() {
+ var index = this.lstRedirects.selectedIndex;
+
+ if (index == -1) {
+ return;
+ }
+
+ var text = this.strings.getString("deleteConfirmationText");
+ var title = this.strings.getString("deleteConfirmationTitle");
+ var reallyDelete = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService).confirm(null, title, text);
+ if (!reallyDelete) {
+ return;
+ }
+
+ try {
+ this.lstRedirects.removeChild(this.lstRedirects.children[index]);
+ Redirector.deleteRedirectAt(index);
+ this.selectionChange();
+ } catch(e) {
+ alert(e);
+ }
+ },
+
+ listKeypress : function(event) {
+ if (event.keyCode == 13) { //Enter button
+ this.editRedirect();
+ } else if (event.keyCode == 46) { //Del button
+ this.deleteRedirect();
+ }
+ },
+
+ selectionChange : function() {
+ if (!this.lstRedirects) {
+ return;
+ }
+ var index = this.lstRedirects.selectedIndex;
+
+ this.btnEdit.disabled = (index == -1);
+ this.btnDelete.disabled = (index == -1);
+ this.btnUp.disabled = (index <= 0);
+ this.btnDown.disabled = (index == -1 || index >= Redirector.redirectCount-1);
+ this.btnExport.disabled = (Redirector.redirectCount== 0);
+ },
+
+ getFile : function(captionKey, mode) {
+ //Mostly borrowed from Adblock Plus
+ var picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ picker.init(window, this.strings.getString(captionKey), mode);
+ picker.defaultExtension = ".rdx";
+ var dir = this.prefs.defaultDir;
+ if (dir) {
+ picker.displayDirectory = new nsLocalFile(dir);
+ }
+ picker.appendFilter(this.strings.getString('redirectorFiles'), '*.rdx');
+
+ if (picker.show() == picker.returnCancel) {
+ return null;
+ }
+ this.prefs.defaultDir = picker.displayDirectory.path;
+ return picker.file;
+ },
+
+ export : function() {
+ var file = this.getFile('exportCaption', Ci.nsIFilePicker.modeSave);
+ if (file) {
+ Redirector.exportRedirects(file);
+ }
+ },
+
+ import : function() {
+ var file = this.getFile('importCaption', Ci.nsIFilePicker.modeOpen);
+ var result;
+ if (!file) {
+ return;
+ }
+ result = Redirector.importRedirects(file);
+ var msg, imported, existed;
+ imported = result & 0xFFFF;
+ existed = result >> 16;
+
+ if (imported > 0) {
+ msg = this.strings.getPluralized('importedMessage', imported);
+ if (existed > 0) {
+ msg += ', ' + this.strings.getPluralized('existedMessage',existed);
+ } else {
+ msg += '.';
+ }
+ } else if (imported == 0 && existed > 0) {
+ msg = this.strings.getPluralized('allExistedMessage', existed);
+ } else { //Both 0
+ msg = this.strings.getString('importedNone');
+ }
+
+ var title = this.strings.getString("importResult");
+ Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService).alert(null, title, msg);
+
+ if (imported > 0) {
+ var newlist = [];
+ for (var i = Redirector.redirectCount-imported; i < Redirector.redirectCount; i++) {
+ newlist.push(Redirector.getRedirectAt(i));
+ }
+ this.addItemsToListBox(newlist);
+ }
+ }
+};
diff --git a/chrome/code/unittest/run.html b/chrome/code/unittest/run.html
new file mode 100644
index 0000000..cc178aa
--- /dev/null
+++ b/chrome/code/unittest/run.html
@@ -0,0 +1,99 @@
+<!-- $Id$ -->
+<html>
+ <head>
+ <title>Redirector Unit Tests</title>
+ <style type="text/css">
+ body { font-family: Verdana, Arial; color:black; background-color:white; font-size:0.8em; width:800px; margin:auto; text-align:center;}
+ a { color:blue; }
+ h1 { text-align:center; margin:10px 0px; }
+ table { margin:10px auto; border:solid 1px black; width:700px; border-collapse:collapse;}
+ td { border:solid 1px black; padding:3px; }
+ td.result { width:20px; height:20px; padding:0;}
+ td.result div { width:70%; height:70%; margin:auto; }
+ button { margin:20px auto; }
+ </style>
+ <script type="text/javascript">
+
+ //Global variables
+ var subscriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
+ var redirector = Components.classes["@einaregilsson.com/redirector;1"].getService(Components.interfaces.rdIRedirector);
+
+ function setupTest(name, testcase) {
+ var table = document.createElement('table');
+ var row = document.createElement('tr');
+ var cell = document.createElement('th');
+ var testdata;
+ cell.setAttribute('colspan', 2);
+ row.appendChild(cell);
+ table.appendChild(row);
+ cell.innerHTML = name;
+ document.getElementById('tests').appendChild(table);
+ for (var i = 0; i < testcase.tests.length; i++) {
+ var testdata = testcase.tests[i];
+ row = document.createElement('tr');
+ cell = document.createElement('td');
+ cell.setAttribute('class', 'result');
+ var dot = document.createElement('div');
+ dot.setAttribute('id', name + '_' + i);
+ cell.appendChild(dot);
+
+ row.appendChild(cell);
+ cell = document.createElement('td');
+ cell.innerHTML = testcase.describe(testdata);
+ row.appendChild(cell);
+ table.appendChild(row);
+ }
+ }
+
+ function setup() {
+ //quick and dirty cleanup
+ document.getElementById('tests').innerHTML = '';
+ subscriptLoader.loadSubScript('chrome://redirector/content/code/redirect.js');
+ subscriptLoader.loadSubScript('chrome://redirector/content/unittest/testcases.js');
+ redirector.reload();
+
+ var sorted = [];
+ for (var name in tests) {
+ sorted.push(name);
+ }
+
+ sorted.sort();
+ for each(var name in sorted) {
+ setupTest(name, tests[name]);
+ }
+ }
+
+ function runTests() {
+ for (var testcaseName in tests) {
+ var testcase = tests[testcaseName];
+ for (var i = 0; i < testcase.tests.length; i++) {
+ try {
+ var dot = document.getElementById(testcaseName + '_' + i);
+ var result = testcase.run(testcase.tests[i]);
+ if (result && result.passed) {
+ dot.style.backgroundColor = '#17f816';
+ } else {
+ dot.style.backgroundColor = '#ff0000';
+ if (result && result.message) {
+ dot.parentNode.nextSibling.innerHTML += '<br/><span style="color:red;">' + result.message + '</span>';
+ }
+ }
+ } catch(e) {
+ dot.style.backgroundColor = '#ff0000';
+ dot.parentNode.nextSibling.innerHTML += '<br/><span style="color:red;">' + e + '</span>';
+ ;
+ }
+ }
+ }
+ }
+
+ </script>
+ </head>
+ <body onload="setup();">
+ <h1>Redirector Unit Tests</h1>
+ <button onclick="runTests();">Run tests</button>
+ <button onclick="setup();">Reload tests</button>
+ <div id="tests">
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/chrome/code/unittest/testcases.js b/chrome/code/unittest/testcases.js
new file mode 100644
index 0000000..0c0c10c
--- /dev/null
+++ b/chrome/code/unittest/testcases.js
@@ -0,0 +1,129 @@
+//// $Id$
+var nsIContentPolicy = Components.interfaces.nsIContentPolicy;
+
+var tests = {
+ "Wildcard matches" : {
+ run : function(data,log) {
+ var pattern = data[0],
+ url = data[1],
+ expected = data[2];
+ var parts = expected.split(',');
+ var redirectUrl = '';
+ if (!(parts.length == 1 && parts[0] == '')) {
+ for (var i in parts) {
+ redirectUrl += '$' + (parseFloat(i)+1) + ',';
+ }
+ redirectUrl = redirectUrl.substr(0, redirectUrl.length-1);
+ }
+ var redirect = new Redirect(null, pattern, redirectUrl, Redirect.WILDCARD);
+ var result = redirect.getMatch(url);
+ return { passed: result.isMatch && (result.redirectTo == expected), message : "Expected '" + expected + "', actual was '" + result.redirectTo + "'"};
+ },
+
+ describe : function(data) { return data[0] + ' == ' + data[1] + ', matches=' + data[2]; },
+ tests : [
+ ['http://foo*', 'http://foobar.is', 'bar.is'],
+ ['http://foo*', 'http://foo', ''],
+ ['*://foo.is', 'http://foo.is', 'http'],
+ ['*http://foo.is', 'http://foo.is', ''],
+ ['http*foo*', 'http://foobar.is', '://,bar.is'],
+ ['http*foo*', 'http://foo', '://,'],
+ ['*://f*.is', 'http://foo.is', 'http,oo'],
+ ['*http://f*.is', 'http://foo.is', ',oo'],
+ ['*foo*', 'http://foo', 'http://,'],
+ ['*foo*', 'foobar.is', ',bar.is'],
+ ['*foo*', 'http://foobar.is', 'http://,bar.is'],
+ ['http://foo.is', 'http://foo.is', ''],
+ ['*', 'http://foo.is', 'http://foo.is'],
+ ['*://*oo*bar*', 'http://foo.is/bar/baz', 'http,f,.is/,/baz'],
+ ['*://**oo*bar*', 'http://foo.is/bar/baz', 'http,,f,.is/,/baz'],
+ ]
+ },
+
+ "Regex matches" : {
+ run : function(data) {
+ var pattern = data[0],
+ url = data[1],
+ expected = data[2];
+ var parts = expected.split(',');
+ var redirectUrl = '';
+ if (!(parts.length == 1 && parts[0] == '')) {
+ for (var i in parts) {
+ redirectUrl += '$' + (parseFloat(i)+1) + ',';
+ }
+ redirectUrl = redirectUrl.substr(0, redirectUrl.length-1);
+ }
+ var redirect = new Redirect(null, pattern, redirectUrl, Redirect.REGEX, null, null);
+ var result = redirect.getMatch(url);
+ return { passed: result.isMatch && result.redirectTo == expected, message : "Expected '" + expected + "', actual was '" + result.redirectTo + "'"};
+ },
+
+ describe : function(data) { return data[0] + ' == ' + data[1] + ', matches=' + data[2]; },
+ tests : [
+ ['http://foo(.*)', 'http://foobar.is', 'bar.is'],
+ ['http://foo(.*)', 'http://foo', ''],
+ ['(.*)://foo.is', 'http://foo.is', 'http'],
+ ['(.*)http://foo\\.is', 'http://foo.is', ''],
+ ['http(.*)foo(.*)', 'http://foobar.is', '://,bar.is'],
+ ['http(.*)foo(.*)', 'http://foo', '://,'],
+ ['(.*)://f(.*)\\.is', 'http://foo.is', 'http,oo'],
+ ['(.*)http://f(.*)\\.is', 'http://foo.is', ',oo'],
+ ['(.*)foo(.*)', 'http://foo', 'http://,'],
+ ['(.*)foo(.*)', 'foobar.is', ',bar.is'],
+ ['(.*)foo(.*)', 'http://foobar.is', 'http://,bar.is'],
+ ['http://foo\.is', 'http://foo.is', ''],
+ ['(.*)', 'http://foo.is', 'http://foo.is'],
+ ['(.*)://(.*)oo(.*)bar(.*)', 'http://foo.is/bar/baz', 'http,f,.is/,/baz'],
+ ['(.*)://(.*?)(.*)oo(.*)bar(.*)', 'http://foo.is/bar/baz', 'http,,f,.is/,/baz'],
+ ]
+ },
+
+ "nsIContentPolicy implementation" : {
+ run : function(data) {
+ var runTest = function() {
+ var args = {
+ contentType : nsIContentPolicy.TYPE_DOCUMENT,
+ contentLocation : "http://foo.is",
+ requestOrigin : null,
+ aContext : { loadURI : function(){}},
+ mimeTypeGuess : null,
+ extra : null
+ };
+ for (var key in data[1]) {
+ args[key] = data[1][key];
+ }
+
+ var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
+ args.contentLocation = ioService.newURI(args.contentLocation, null, null);
+ var contentPolicy = redirector.QueryInterface(nsIContentPolicy);
+ var result = contentPolicy.shouldLoad(args.contentType, args.contentLocation, args.requestOrigin, args.aContext, args.mimeTypeGuess, args.extra);
+ return { passed: result == nsIContentPolicy.ACCEPT, message : "Expected nsIContentPolicy.ACCEPT, actual was " + result };
+ }
+
+ if (typeof data[2] == "function") {
+ return data[2](runTest);
+ } else {
+ return runTest();
+ }
+ },
+
+ describe : function(data) { return data[0]; },
+ tests : [
+ ["Accepts if not TYPE_DOCUMENT", { contentType : nsIContentPolicy.TYPE_STYLESHEET}],
+ ["Accepts if not http or https", { contentLocation : "resource://foo/bar"}],
+ ["Accepts if no aContext", { aContext : null}],
+ ["Accepts if aContext has no loadURI function", { aContext : { foo : function(){}}}],
+ ["Accepts if Redirector is not enabled", {}, function(doFunc) {
+ try {
+ redirector.enabled = false;
+ return doFunc();
+ redirector.enabled = true;
+
+ } catch(e) {
+ redirector.enabled = true;
+ throw e;
+ }
+ }]
+ ]
+ }
+};
diff --git a/chrome/ui/browserOverlay.xul b/chrome/ui/browserOverlay.xul
new file mode 100644
index 0000000..32bf1d5
--- /dev/null
+++ b/chrome/ui/browserOverlay.xul
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- $Id$ -->
+<!DOCTYPE overlay SYSTEM "chrome://redirector/locale/browserOverlay.xul.dtd">
+<overlay id="redirector-overlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="../code/redirect.js"/>
+ <script src="../code/redirectorprefs.js"/>
+ <script src="../code/browserOverlay.xul.js"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="redirector-strings" src="chrome://redirector/locale/redirector.properties"/>
+ </stringbundleset>
+
+ <menupopup id="menu_ToolsPopup">
+ <menuitem id="redirector-menuitem" label="&RedirectorMenuItem.label;"
+ accesskey="&RedirectorMenuItem.accesskey;"
+ oncommand="RedirectorOverlay.onMenuItemCommand(event);"/>
+ </menupopup>
+ <popup id="contentAreaContextMenu">
+ <menuitem id="redirector-context" label="&RedirectorContext.label;"
+ accesskey="&RedirectorContext.accesskey;"
+ insertafter="context-stop"
+ oncommand="RedirectorOverlay.onContextMenuCommand(event)"/>
+ </popup>
+ <statusbar id="status-bar">
+ <statusbarpanel id="redirector-status">
+ <image id="redirector-statusbar-img" src="chrome://redirector/skin/statusactive.png"
+ tooltiptext="Redirector is enabled;"
+ style="width:16px; height:16px;"
+ onclick="RedirectorOverlay.statusBarClick(event);" />
+ </statusbarpanel>
+ </statusbar>
+
+</overlay> \ No newline at end of file
diff --git a/chrome/ui/editRedirect.xul b/chrome/ui/editRedirect.xul
new file mode 100644
index 0000000..6d7b133
--- /dev/null
+++ b/chrome/ui/editRedirect.xul
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- $Id$ -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://redirector/skin/redirector.css" type="text/css"?>
+<!DOCTYPE dialog SYSTEM "chrome://redirector/locale/editRedirect.xul.dtd">
+<dialog title="&redirectWindow.title;"
+ orient="vertical"
+ autostretch="always"
+ onload="EditRedirect.onLoad();"
+ buttons="accept,cancel"
+ ondialogaccept="return EditRedirect.onAccept();"
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/x-javascript" src="../code/redirect.js"/>
+ <script type="application/x-javascript" src="../code/editRedirect.xul.js"/>
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="redirector-strings" src="chrome://redirector/locale/redirector.properties"/>
+ </stringbundleset>
+
+ <grid>
+ <rows class="editRedirects">
+ <row align="center">
+ <text value="&txtExampleUrl.label;" />
+ <textbox id="txtExampleUrl" />
+ </row>
+ <row align="center">
+ <text value="&txtIncludePattern.label;" />
+ <textbox id="txtIncludePattern" taborder="1"/>
+ <button id="btnTestPattern" label="&btnTestPattern.label;" onclick="EditRedirect.testPattern();" taborder="2"/>
+ </row>
+ <row align="center">
+ <text value="&txtExcludePattern.label;" />
+ <textbox id="txtExcludePattern" taborder="3"/>
+ </row>
+ <row align="center">
+ <text value="&txtRedirectUrl.label;" />
+ <textbox id="txtRedirectUrl" taborder="4"/>
+ </row>
+ <row align="center">
+ <text value="&rdoPatternTypes.label;"/>
+ <radiogroup>
+ <hbox>
+ <radio id="rdoWildcard" label="&rdoWildcard.label;" accesskey="&rdoWildcard.accessKey;" selected="true" taborder="5"/>
+ <radio id="rdoRegex" label="&rdoRegex.label;" accesskey="&rdoRegex.accessKey;" taborder="6"/>
+ </hbox>
+ </radiogroup>
+ </row>
+ <row align="center">
+ <text value="&chkUnescapeMatches.label;" />
+ <hbox>
+ <checkbox id="chkUnescapeMatches" label="" taborder="7"/>
+ <spacer flex="1" />
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+</dialog>
diff --git a/chrome/ui/help.html b/chrome/ui/help.html
new file mode 100644
index 0000000..4534d96
--- /dev/null
+++ b/chrome/ui/help.html
@@ -0,0 +1,182 @@
+<!-- $Id$ -->
+<html>
+ <head>
+ <title>Redirector Help</title>
+ <style type="text/css">
+ body { font-family: Verdana, Arial; color:black; background-color:white; font-size:0.9em;}
+ a { color:blue; }
+ </style>
+ </head>
+ <body>
+ <h1>Redirector Help</h1>
+ <h3>Table of contents</h3>
+ <ul>
+ <li><a href="#whatisredirector">What is Redirector?</a></li>
+ <li><a href="#basicusage">Basic usage</a>
+ <ul>
+ <li><a href="#exampleurl">Example url</a></li>
+ <li><a href="#includepattern">Include pattern</a></li>
+ <li><a href="#excludepattern">Exclude pattern</a></li>
+ <li><a href="#redirectto">Redirect to</a></li>
+ <li><a href="#patterntype">Pattern type</a></li>
+ <li><a href="#unescapematches">Unescape matches</a></li>
+ </ul>
+ </li>
+ <li><a href="#wildcards">Wildcards</a></li>
+ <li><a href="#regularexpressions">Regular expressions</a></li>
+ <li><a href="#examples">Examples</a>
+ <ol>
+ <li><a href="#ex1">Static redirect</a></li>
+ <li><a href="#ex2">Redirect using query string parameter and wildcards</a></li>
+ <li><a href="#ex3">Redirect using query string parameter and regular expressions</a></li>
+ <li><a href="#ex4">Redirect to a different folder using wildcards</a></li>
+ <li><a href="#ex5">Redirect http to https using wildcards</a></li>
+ </ol>
+
+ </li>
+ </ul>
+
+
+ <a name="whatisredirector"></a>
+ <h4>What is Redirector?</h4>
+
+ <p>Redirector is an extension for Firefox that allows you to automatically redirect from
+ one webpage to another. For example, every time you visit http://abc.com you will automatically
+ load http://def.com instead. This can be useful for instance to always redirect articles to printer friendly
+ versions, redirect http:// to https:// for sites that support both, bypass advertising pages that appear before
+ being able to view certain pages and more.</p>
+
+ <a name="basicusage"></a>
+ <h4>Basic usage</h4>
+ <p>To add a new redirect you can go to the <em>Tools</em> menuitem and select <em>Redirector</em>. That will
+ open the <em>Redirector settings</em> window which shows all your redirects. The window can also be opened
+ by right clicking on the <strong>R</strong> icon in your statusbar.
+ There you can press the <em>Add...</em> button and then you can enter the details for the new redirect. A redirect
+ consists of a few things:
+ <ul>
+ <li><a name="exampleurl"></a><strong>Example url:</strong> This is an example of an url you want to redirect. It is not really used for anything,
+ it's just there to show what types of urls you're targetting. You can leave this out, but then you can't use the <em>Test pattern</em> button.</li>
+
+ <li><a name="includepattern"></a><strong>Include pattern:</strong> This is pattern for the urls you want to redirect. In the simplest case, where you just want
+ to redirect one specific url to another then this will just be the exact url you want to redirect. For instance, if you just want http://aaa.com to
+ redirect to http://bbb.com then <em>Include pattern</em> will just be http://aaa.com. For more complex patterns that match many
+ urls you can use either <a href="#wildcards">wildcards</a> or <a href="#regularexpressions">regular expressions</a>.</li>
+
+ <li><a name="excludepattern"></a><strong>Exclude pattern:</strong> Urls that match this pattern will never be redirected. This is not necessary to
+ fill out, but can be useful when you want to redirect all urls that contain some text except if they contain some other text.
+ Redirects like that can often be done with a complex regular expression, but using an exclude pattern makes it much simpler. The exclude
+ patterns can use wildcard characters or regular expressions like the include patterns.</li>
+
+ <li><a name="redirectto"></a><strong>Redirect to:</strong> This is the url that you will be redirected to when you open any page where the url matches the
+ include pattern. You can use the special signs $1, $2, $3 etc. in the url, they will be replaced by the results of captures with regular
+ expressions or stars with wildcards. For instance, if you have the include pattern <em>http://google.com/*</em>, redirect to <em>http://froogle.com/$1</em>
+ and you open the page http://google.com/foobar, then you will be redireced to http://froogle.com/foobar, since 'foobar' was what the star replaced. $1 is for the
+ first star in the pattern, $2 for the second and so on. For regular expression $1 is for the first parantheses, $2 for the second etc.</li>
+
+ <li><a name="patterntype"></a><strong>Pattern type:</strong> This specifies how Redirector should interpret the patterns, either as
+ <a href="#wildcards">wildcards</a> or <a href="#regularexpressions#">regular expressions</a>.</li>
+
+ <li><a name="unescapematches"></a><strong>Unescape matches:</strong> A common usage of Redirector is to catch urls like
+ <em>http://foo.com/redirect.php?url=http%3A%2F%2Fbar%2Ecom%2Fpath</em> and try to catch the url parameter and redirect to it. A pattern
+ like <em>http://foo.com/redirect.php?url=*</em> might be used for that purpose. However, if the url parameter is <em>escaped</em> (also known
+ as <em>urlencoded</em>) then that won't work. In the url above we see that it starts with <em>http%3A%2F%2F</em> instead of <em>http://</em>, and Firefox
+ won't accept this as a new url to redirect to. So, in cases like these you can check the <em>Unescape matches</em> option and then all
+ matches will be unescaped (turned from e.g. <em>http%3A%2F%2Fbar%2Ecom</em> to <em>http://bar.com</em>) before being inserted into the target url.
+ </li>
+
+ </ul>
+ </p>
+
+ <a name="wildcards"></a>
+ <h4>Wildcards</h4>
+
+ <p>Wildcards are the simplest way to specify include and exclude patterns. When you create a wildcard pattern there
+ is just one special character, the asterisk *. An asterisk in your pattern will match zero or more characters and you can
+ have more than one star in your pattern. Some examples:
+ <ul>
+ <li><em>http://example.com/*</em> matches http://example.com/, http://example.com/foo, http://example.com/bar and all other urls that start with http://example.com/.</li>
+ <li><em>http://*.example.com</em> matches all subdomains of example.com, like http://www.example.com, http://mail.example.com.</li>
+ <li><em>http*://example.com</em> matches both http://example.com and https://example.com.</li>
+ <li><em>http://example.com/index.asp*</em> matches http://example.com/index.asp, http://example.com/index.asp?a=b&c=d.</li>
+ </ul>
+ $1, $2, $3 in the redirect urls will match the text that the stars matched. Examples:
+ <ul>
+ <li><em>http://example.com/*</em> matches http://example.com/foobar, $1 is foobar.</li>
+ <li><em>http://*.example.com/*</em> matches http://www.example.com/foobar, $1 is www, $2 is foobar.</li>
+ </ul>
+ </p>
+
+ <a name="regularexpressions"></a>
+ <h4>Regular expressions</h4>
+
+ <p>Regular expressions allow for more complicated patterns but they are a lot harder to learn than wildcards. I'm not gonna
+ create a regex tutorial here but normal javascript regex syntax works, look at <a href="http://regular-expressions.info" target="_blank">http://regular-expressions.info</a> for
+ an introduction to regular expressions. $1,$2 etc. can be used in the redirect url and will be replaced with contents of captures in
+ the regular expressions. Captures are specified with parantheses. Example: http://example.com/index.asp\?id=(\d+) will match the url
+ http://example.com/index.asp?id=12345 and $1 will be replaced by 12345. (A common mistake in regex patterns is to forget to escape
+ the ? sign in the querystring of the url. ? is a special character in regular expressions so if you want to match an url with a querystring
+ you should escape it as \?).</p>
+
+ <a name="examples"></a>
+ <h4>Examples</h4>
+
+ <ol>
+ <li>
+ <strong><a name="ex1"></a>Static redirect</strong><br/>
+ Redirects from http://example.com/foo to http://example.com/bar
+ <p>
+ <strong>Include pattern:</strong> http://example.com/foo<br/>
+ <strong>Exclude pattern:</strong><br/>
+ <strong>Redirect to:</strong> http://example.com/bar<br/>
+ <strong>Pattern type:</strong> Wildcard<br />
+ </p>
+
+ </li>
+ <li>
+ <strong><a name="ex2"></a>Redirect using query string parameter and wildcards</strong><br/>
+ Redirects from http://example.com/index.php?id=12345&a=b to http://example.com/printerfriendly.php?id=12345&a=b
+ where 12345 could be any number.
+ <p>
+ <strong>Include pattern:</strong> http://example.com/index.php?id=*&a=b<br/>
+ <strong>Exclude pattern:</strong><br/>
+ <strong>Redirect to:</strong> http://example.com/printerfriendly.com?id=$1&a=b<br/>
+ <strong>Pattern type:</strong> Wildcard<br />
+ </p>
+ </li>
+ <li>
+ <strong><a name="ex3"></a>Redirect using query string parameter and regular expressions</strong><br/>
+ Redirects from http://example.com/index.php?id=12345&a=b to http://example.com/printerfriendly.php?id=12345&a=b
+ where 12345 could be any number.
+ <p>
+ <strong>Include pattern:</strong> http://example.com/index.php\?id=(\d+)&a=b<br/>
+ <strong>Exclude pattern:</strong><br/>
+ <strong>Redirect to:</strong> http://example.com/printerfriendly.com?id=$1&a=b<br/>
+ <strong>Pattern type:</strong> Regular Expression<br />
+ </p>
+ </li>
+ <li>
+ <strong><a name="ex4"></a>Redirect to a different folder using wildcards</strong><br/>
+ Redirects from http://example.com/category/fish/index.php to http://example.com/category/cats/index.php
+ where fish could be any word. The exclude pattern makes sure that there is only one
+ folder there, so for instance http://example.com/category/fish/cat/mouse/index.php would not match.
+ <p>
+ <strong>Include pattern:</strong> http://example.com/category/*/index.php<br/>
+ <strong>Exclude pattern:</strong> http://example.com/category/*/*/index.php<br/>
+ <strong>Redirect to:</strong> http://example.com/category/cats/index.php<br/>
+ <strong>Pattern type:</strong> Wildcard<br />
+ </p>
+ </li>
+ <li>
+ <strong><a name="ex5"></a>Redirect http to https using wildcards</strong><br/>
+ Redirects from http://mail.google.com/randomcharacters to https://mail.google.com/randomcharacters
+ where randomcharacters could be anything.
+ <p>
+ <strong>Include pattern:</strong> http://mail.google.com*<br/>
+ <strong>Exclude pattern:</strong><br/>
+ <strong>Redirect to:</strong> https://mail.google.com$1<br/>
+ <strong>Pattern type:</strong> Wildcard<br />
+ </p>
+ </li>
+ </ol>
+ </body>
+</html> \ No newline at end of file
diff --git a/chrome/ui/settings.xul b/chrome/ui/settings.xul
new file mode 100644
index 0000000..ccb9569
--- /dev/null
+++ b/chrome/ui/settings.xul
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- $Id$ -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://redirector/skin/redirector.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://redirector/locale/settings.xul.dtd">
+<window title="&window.title;"
+ orient="vertical"
+ onload="Settings.onLoad();"
+ onunload="Settings.onUnload();"
+ buttons="accept"
+ width="650px"
+ height="500px"
+ id="redirectorSettings"
+ windowtype="redirectorSettings"
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/x-javascript" src="../code/redirectorprefs.js"/>
+ <script type="application/x-javascript" src="../code/redirect.js"/>
+ <script type="application/x-javascript" src="../code/settings.xul.js"/>
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="redirector-strings" src="chrome://redirector/locale/redirector.properties"/>
+ </stringbundleset>
+ <tabbox flex="1" >
+ <tabs>
+ <tab label="&tabRedirects.label;" accesskey="&tabRedirects.accesskey;" />
+ <tab label="&tabPreferences.label;" accesskey="&tabPreferences.accesskey;" />
+ <tab label="&tabImportExport.label;" accesskey="&tabImportExport.accesskey;" />
+ <tab label="&tabHelp.label;" accesskey="&tabHelp.accesskey;" />
+ </tabs>
+ <tabpanels flex="1">
+ <tabpanel flex="1">
+ <vbox flex="1">
+ <richlistbox seltype="single" id="lstRedirects" flex="1" onkeypress="Settings.listKeypress(event);" ondblclick="Settings.editRedirect();" onselect="Settings.selectionChange();">
+ <richlistitem class="SettingsItem" selected="false">
+ <grid>
+ <cols />
+ <rows class="redirectRows">
+ <row>
+ <label value="&colIncludePattern.label;:" />
+ <description name="dscrIncludePattern" />
+ </row>
+ <row>
+ <label value="&colExcludePattern.label;:" />
+ <description name="dscrExcludePattern" />
+ </row>
+ <row>
+ <label value="&colRedirectTo.label;:" />
+ <description name="dscrRedirectTo" />
+ </row>
+ <row>
+ <label value="&colEnabled.label;:" />
+ <hbox><checkbox checked="false" name="chkEnabled" label="" /> <spacer flex="1"/></hbox>
+ </row>
+ </rows>
+ </grid>
+ </richlistitem>
+ </richlistbox>
+ <hbox>
+ <button id="btnAdd" oncommand="Settings.addRedirect();" accesskey="&btnAdd.accesskey;" label="&btnAdd.label;" tooltiptext="&btnAdd.tooltip;" disabled="false" />
+ <button id="btnEdit" oncommand="Settings.editRedirect();" accesskey="&btnEdit.accesskey;" label="&btnEdit.label;" tooltiptext="&btnEdit.tooltip;" disabled="true" />
+ <button id="btnDelete" oncommand="Settings.deleteRedirect();" accesskey="&btnDelete.accesskey;" label="&btnDelete.label;" tooltiptext="&btnDelete.tooltip;" disabled="true" />
+ <button id="btnUp" oncommand="Settings.moveUp();" tooltiptext="&btnUp.tooltip;" disabled="true" />
+ <button id="btnDown" oncommand="Settings.moveDown();" tooltiptext="&btnDown.tooltip;" disabled="true" />
+ </hbox>
+ </vbox>
+ </tabpanel>
+ <tabpanel>
+ <vbox flex="1">
+ <groupbox>
+ <caption label="&grpGeneralPreferences.label;" />
+ <hbox>
+ <checkbox id="chkEnableRedirector" label="&chkEnableRedirector.label;" oncommand="Settings.preferenceChange(event);" accesskey="&chkEnableRedirector.accesskey;" preference="enabled" />
+ <spacer flex="1" />
+ </hbox>
+ <hbox>
+ <checkbox id="chkShowStatusBarIcon" label="&chkShowStatusBarIcon.label;" oncommand="Settings.preferenceChange(event);" accesskey="&chkShowStatusBarIcon.accesskey;" preference="showStatusBarIcon" />
+ <spacer flex="1" />
+ </hbox>
+ <hbox>
+ <checkbox id="chkShowContextMenu" label="&chkShowContextMenu.label;" oncommand="Settings.preferenceChange(event);" accesskey="&chkShowContextMenu.accesskey;" preference="showContextMenu" />
+ <spacer flex="1" />
+ </hbox>
+ </groupbox>
+ <groupbox>
+ <caption label="&grpDebuggingPreferences.label;" />
+ <hbox>
+ <checkbox id="chkEnableDebugOutput" label="&chkEnableDebugOutput.label;" oncommand="Settings.preferenceChange(event);" accesskey="&chkEnableDebugOutput.accesskey;" preference="debugEnabled" />
+ <spacer flex="1" />
+ </hbox>
+ </groupbox>
+ <spacer flex="1" />
+ </vbox>
+ </tabpanel>
+ <tabpanel>
+ <groupbox flex="1" id="grpImportExport">
+ <vbox>
+ <hbox align="middle">
+ <button id="btnImport" accesskey="&btnImport.accesskey;" oncommand="Settings.import();" label="&btnImport.label;"/>
+ <label id="lblImport" value="&lblImport.label;" />
+ <spacer flex="1" />
+ </hbox>
+ <hbox>
+ <button id="btnExport" accesskey="&btnExport.accesskey;" oncommand="Settings.export();" label="&btnExport.label;"/>
+ <label id="lblExport" value="&lblExport.label;" />
+ <spacer flex="1" />
+ </hbox>
+ <spacer flex="1" />
+ </vbox>
+ </groupbox>
+ </tabpanel>
+ <tabpanel>
+ <browser type="content" src="chrome://redirector/content/ui/help.html" flex="1" />
+ </tabpanel>
+ </tabpanels>
+ </tabbox>
+</window>
diff --git a/chrome/ui/skin/movedown.png b/chrome/ui/skin/movedown.png
new file mode 100644
index 0000000..d32b79c
--- /dev/null
+++ b/chrome/ui/skin/movedown.png
Binary files differ
diff --git a/chrome/ui/skin/movedowndisabled.png b/chrome/ui/skin/movedowndisabled.png
new file mode 100644
index 0000000..afd8fc6
--- /dev/null
+++ b/chrome/ui/skin/movedowndisabled.png
Binary files differ
diff --git a/chrome/ui/skin/moveup.png b/chrome/ui/skin/moveup.png
new file mode 100644
index 0000000..3025378
--- /dev/null
+++ b/chrome/ui/skin/moveup.png
Binary files differ
diff --git a/chrome/ui/skin/moveupdisabled.png b/chrome/ui/skin/moveupdisabled.png
new file mode 100644
index 0000000..e526b29
--- /dev/null
+++ b/chrome/ui/skin/moveupdisabled.png
Binary files differ
diff --git a/chrome/ui/skin/redirector.css b/chrome/ui/skin/redirector.css
new file mode 100644
index 0000000..ff9762e
--- /dev/null
+++ b/chrome/ui/skin/redirector.css
@@ -0,0 +1,15 @@
+/* $Id$ */
+
+.disabledRedirect { color:grey; }
+#lstRedirects richlistitem { border-bottom:dotted 1px grey; padding:3px; }
+.redirectRows > row > label { font-weight:bold;}
+.editRedirects > row > textbox { width: 350px; }
+#redirectorSettings > tabbox { margin:4px; }
+#btnUp { list-style-image: url('chrome://redirector/skin/moveup.png'); }
+#btnDown { list-style-image: url('chrome://redirector/skin/movedown.png'); }
+#btnUp[disabled=true] { list-style-image: url('chrome://redirector/skin/moveupdisabled.png'); }
+#btnDown[disabled=true] { list-style-image: url('chrome://redirector/skin/movedowndisabled.png'); }
+
+#btnUp, #btnDown { width:25px; min-width:25px; }
+#lblExport, #lblImport { padding-top:5px; }
+#grpImportExport { padding-top:10px; padding-left:5px;} \ No newline at end of file
diff --git a/chrome/ui/skin/redirector.png b/chrome/ui/skin/redirector.png
new file mode 100644
index 0000000..f8de12c
--- /dev/null
+++ b/chrome/ui/skin/redirector.png
Binary files differ
diff --git a/chrome/ui/skin/statusactive.png b/chrome/ui/skin/statusactive.png
new file mode 100644
index 0000000..06ce766
--- /dev/null
+++ b/chrome/ui/skin/statusactive.png
Binary files differ
diff --git a/chrome/ui/skin/statusinactive.png b/chrome/ui/skin/statusinactive.png
new file mode 100644
index 0000000..8b83562
--- /dev/null
+++ b/chrome/ui/skin/statusinactive.png
Binary files differ
diff --git a/components/interfaces/nsIFile.idl b/components/interfaces/nsIFile.idl
deleted file mode 100644
index cf97192..0000000
--- a/components/interfaces/nsIFile.idl
+++ /dev/null
@@ -1,343 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-1999
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Doug Turner <dougt@netscape.com>
- * Christopher Blizzard <blizzard@mozilla.org>
- * Darin Fisher <darin@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-interface nsISimpleEnumerator;
-
-/**
- * This is the only correct cross-platform way to specify a file.
- * Strings are not such a way. If you grew up on windows or unix, you
- * may think they are. Welcome to reality.
- *
- * All methods with string parameters have two forms. The preferred
- * form operates on UCS-2 encoded characters strings. An alternate
- * form operates on characters strings encoded in the "native" charset.
- *
- * A string containing characters encoded in the native charset cannot
- * be safely passed to javascript via xpconnect. Therefore, the "native
- * methods" are not scriptable.
- *
- * @status FROZEN
- */
-[scriptable, uuid(c8c0a080-0868-11d3-915f-d9d889d48e3c)]
-interface nsIFile : nsISupports
-{
- /**
- * Create Types
- *
- * NORMAL_FILE_TYPE - A normal file.
- * DIRECTORY_TYPE - A directory/folder.
- */
- const unsigned long NORMAL_FILE_TYPE = 0;
- const unsigned long DIRECTORY_TYPE = 1;
-
- /**
- * append[Native]
- *
- * This function is used for constructing a descendent of the
- * current nsIFile.
- *
- * @param node
- * A string which is intended to be a child node of the nsIFile.
- * For the |appendNative| method, the node must be in the native
- * filesystem charset.
- */
- void append(in AString node);
- [noscript] void appendNative(in ACString node);
-
- /**
- * Normalize the pathName (e.g. removing .. and . components on Unix).
- */
- void normalize();
-
- /**
- * create
- *
- * This function will create a new file or directory in the
- * file system. Any nodes that have not been created or
- * resolved, will be. If the file or directory already
- * exists create() will return NS_ERROR_FILE_ALREADY_EXISTS.
- *
- * @param type
- * This specifies the type of file system object
- * to be made. The only two types at this time
- * are file and directory which are defined above.
- * If the type is unrecongnized, we will return an
- * error (NS_ERROR_FILE_UNKNOWN_TYPE).
- *
- * @param permissions
- * The unix style octal permissions. This may
- * be ignored on systems that do not need to do
- * permissions.
- */
- void create(in unsigned long type, in unsigned long permissions);
-
- /**
- * Accessor to the leaf name of the file itself.
- * For the |nativeLeafName| method, the nativeLeafName must
- * be in the native filesystem charset.
- */
- attribute AString leafName;
- [noscript] attribute ACString nativeLeafName;
-
- /**
- * copyTo[Native]
- *
- * This will copy this file to the specified newParentDir.
- * If a newName is specified, the file will be renamed.
- * If 'this' is not created we will return an error
- * (NS_ERROR_FILE_TARGET_DOES_NOT_EXIST).
- *
- * copyTo may fail if the file already exists in the destination
- * directory.
- *
- * copyTo will NOT resolve aliases/shortcuts during the copy.
- *
- * @param newParentDir
- * This param is the destination directory. If the
- * newParentDir is null, copyTo() will use the parent
- * directory of this file. If the newParentDir is not
- * empty and is not a directory, an error will be
- * returned (NS_ERROR_FILE_DESTINATION_NOT_DIR). For the
- * |CopyToNative| method, the newName must be in the
- * native filesystem charset.
- *
- * @param newName
- * This param allows you to specify a new name for
- * the file to be copied. This param may be empty, in
- * which case the current leaf name will be used.
- */
- void copyTo(in nsIFile newParentDir, in AString newName);
- [noscript] void CopyToNative(in nsIFile newParentDir, in ACString newName);
-
- /**
- * copyToFollowingLinks[Native]
- *
- * This function is identical to copyTo with the exception that,
- * as the name implies, it follows symbolic links. The XP_UNIX
- * implementation always follow symbolic links when copying. For
- * the |CopyToFollowingLinks| method, the newName must be in the
- * native filesystem charset.
- */
- void copyToFollowingLinks(in nsIFile newParentDir, in AString newName);
- [noscript] void copyToFollowingLinksNative(in nsIFile newParentDir, in ACString newName);
-
- /**
- * moveTo[Native]
- *
- * A method to move this file or directory to newParentDir.
- * If a newName is specified, the file or directory will be renamed.
- * If 'this' is not created we will return an error
- * (NS_ERROR_FILE_TARGET_DOES_NOT_EXIST).
- * If 'this' is a file, and the destination file already exists, moveTo
- * will replace the old file.
- *
- * moveTo will NOT resolve aliases/shortcuts during the copy.
- * moveTo will do the right thing and allow copies across volumes.
- * moveTo will return an error (NS_ERROR_FILE_DIR_NOT_EMPTY) if 'this' is
- * a directory and the destination directory is not empty.
- * moveTo will return an error (NS_ERROR_FILE_ACCESS_DENIED) if 'this' is
- * a directory and the destination directory is not writable.
- *
- * @param newParentDir
- * This param is the destination directory. If the
- * newParentDir is empty, moveTo() will rename the file
- * within its current directory. If the newParentDir is
- * not empty and does not name a directory, an error will
- * be returned (NS_ERROR_FILE_DESTINATION_NOT_DIR). For
- * the |moveToNative| method, the newName must be in the
- * native filesystem charset.
- *
- * @param newName
- * This param allows you to specify a new name for
- * the file to be moved. This param may be empty, in
- * which case the current leaf name will be used.
- */
- void moveTo(in nsIFile newParentDir, in AString newName);
- [noscript] void moveToNative(in nsIFile newParentDir, in ACString newName);
-
- /**
- * This will try to delete this file. The 'recursive' flag
- * must be PR_TRUE to delete directories which are not empty.
- *
- * This will not resolve any symlinks.
- */
- void remove(in boolean recursive);
-
- /**
- * Attributes of nsIFile.
- */
-
- attribute unsigned long permissions;
- attribute unsigned long permissionsOfLink;
-
- /**
- * File Times are to be in milliseconds from
- * midnight (00:00:00), January 1, 1970 Greenwich Mean
- * Time (GMT).
- */
- attribute PRInt64 lastModifiedTime;
- attribute PRInt64 lastModifiedTimeOfLink;
-
- /**
- * WARNING! On the Mac, getting/setting the file size with nsIFile
- * only deals with the size of the data fork. If you need to
- * know the size of the combined data and resource forks use the
- * GetFileSizeWithResFork() method defined on nsILocalFileMac.
- */
- attribute PRInt64 fileSize;
- readonly attribute PRInt64 fileSizeOfLink;
-
- /**
- * target & path
- *
- * Accessor to the string path. The native version of these
- * strings are not guaranteed to be a usable path to pass to
- * NSPR or the C stdlib. There are problems that affect
- * platforms on which a path does not fully specify a file
- * because two volumes can have the same name (e.g., mac).
- * This is solved by holding "private", native data in the
- * nsIFile implementation. This native data is lost when
- * you convert to a string.
- *
- * DO NOT PASS TO USE WITH NSPR OR STDLIB!
- *
- * target
- * Find out what the symlink points at. Will give error
- * (NS_ERROR_FILE_INVALID_PATH) if not a symlink.
- *
- * path
- * Find out what the nsIFile points at.
- *
- * Note that the ACString attributes are returned in the
- * native filesystem charset.
- *
- */
- readonly attribute AString target;
- [noscript] readonly attribute ACString nativeTarget;
- readonly attribute AString path;
- [noscript] readonly attribute ACString nativePath;
-
- boolean exists();
- boolean isWritable();
- boolean isReadable();
- boolean isExecutable();
- boolean isHidden();
- boolean isDirectory();
- boolean isFile();
- boolean isSymlink();
- /**
- * Not a regular file, not a directory, not a symlink.
- */
- boolean isSpecial();
-
- /**
- * createUnique
- *
- * This function will create a new file or directory in the
- * file system. Any nodes that have not been created or
- * resolved, will be. If this file already exists, we try
- * variations on the leaf name "suggestedName" until we find
- * one that did not already exist.
- *
- * If the search for nonexistent files takes too long
- * (thousands of the variants already exist), we give up and
- * return NS_ERROR_FILE_TOO_BIG.
- *
- * @param type
- * This specifies the type of file system object
- * to be made. The only two types at this time
- * are file and directory which are defined above.
- * If the type is unrecongnized, we will return an
- * error (NS_ERROR_FILE_UNKNOWN_TYPE).
- *
- * @param permissions
- * The unix style octal permissions. This may
- * be ignored on systems that do not need to do
- * permissions.
- */
- void createUnique(in unsigned long type, in unsigned long permissions);
-
- /**
- * clone()
- *
- * This function will allocate and initialize a nsIFile object to the
- * exact location of the |this| nsIFile.
- *
- * @param file
- * A nsIFile which this object will be initialize
- * with.
- *
- */
- nsIFile clone();
-
- /**
- * Will determine if the inFile equals this.
- */
- boolean equals(in nsIFile inFile);
-
- /**
- * Will determine if inFile is a descendant of this file
- * If |recur| is true, look in subdirectories too
- */
- boolean contains(in nsIFile inFile, in boolean recur);
-
- /**
- * Parent will be null when this is at the top of the volume.
- */
- readonly attribute nsIFile parent;
-
- /**
- * Returns an enumeration of the elements in a directory. Each
- * element in the enumeration is an nsIFile.
- *
- * @return NS_ERROR_FILE_NOT_DIRECTORY if the current nsIFile does
- * not specify a directory.
- */
- readonly attribute nsISimpleEnumerator directoryEntries;
-};
-
-%{C++
-#ifdef MOZILLA_INTERNAL_API
-#include "nsDirectoryServiceUtils.h"
-#endif
-%}
diff --git a/components/interfaces/nsISimpleEnumerator.idl b/components/interfaces/nsISimpleEnumerator.idl
deleted file mode 100644
index 3f0efbf..0000000
--- a/components/interfaces/nsISimpleEnumerator.idl
+++ /dev/null
@@ -1,81 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-/**
- * Used to enumerate over elements defined by its implementor.
- * Although hasMoreElements() can be called independently of getNext(),
- * getNext() must be pre-ceeded by a call to hasMoreElements(). There is
- * no way to "reset" an enumerator, once you obtain one.
- *
- * @status FROZEN
- * @version 1.0
- */
-
-[scriptable, uuid(D1899240-F9D2-11D2-BDD6-000064657374)]
-interface nsISimpleEnumerator : nsISupports {
- /**
- * Called to determine whether or not the enumerator has
- * any elements that can be returned via getNext(). This method
- * is generally used to determine whether or not to initiate or
- * continue iteration over the enumerator, though it can be
- * called without subsequent getNext() calls. Does not affect
- * internal state of enumerator.
- *
- * @see getNext()
- * @return PR_TRUE if there are remaining elements in the enumerator.
- * PR_FALSE if there are no more elements in the enumerator.
- */
- boolean hasMoreElements();
-
- /**
- * Called to retrieve the next element in the enumerator. The "next"
- * element is the first element upon the first call. Must be
- * pre-ceeded by a call to hasMoreElements() which returns PR_TRUE.
- * This method is generally called within a loop to iterate over
- * the elements in the enumerator.
- *
- * @see hasMoreElements()
- * @return NS_OK if the call succeeded in returning a non-null
- * value through the out parameter.
- * NS_ERROR_FAILURE if there are no more elements
- * to enumerate.
- * @return the next element in the enumeration.
- */
- nsISupports getNext();
-};
diff --git a/components/interfaces/nsISupports.idl b/components/interfaces/nsISupports.idl
deleted file mode 100644
index b1127b4..0000000
--- a/components/interfaces/nsISupports.idl
+++ /dev/null
@@ -1,77 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/**
- * The mother of all xpcom interfaces.
- * @status FROZEN
- */
-
-/* In order to get both the right typelib and the right header we force
-* the 'real' output from xpidl to be commented out in the generated header
-* and includes a copy of the original nsISupports.h. This is all just to deal
-* with the Mac specific ": public __comobject" thing.
-*/
-
-#include "nsrootidl.idl"
-
-%{C++
-/*
- * Start commenting out the C++ versions of the below in the output header
- */
-#if 0
-%}
-
-[scriptable, uuid(00000000-0000-0000-c000-000000000046)]
-interface nsISupports {
- void QueryInterface(in nsIIDRef uuid,
- [iid_is(uuid),retval] out nsQIResult result);
- [noscript, notxpcom] nsrefcnt AddRef();
- [noscript, notxpcom] nsrefcnt Release();
-};
-
-%{C++
-/*
- * End commenting out the C++ versions of the above in the output header
- */
-#endif
-%}
-
-
-%{C++
-#include "nsISupportsBase.h"
-#include "nsISupportsUtils.h"
-%}
diff --git a/components/interfaces/nsrootidl.idl b/components/interfaces/nsrootidl.idl
deleted file mode 100644
index 0538e4b..0000000
--- a/components/interfaces/nsrootidl.idl
+++ /dev/null
@@ -1,128 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Dan Rosen <dr@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/**
- * Root idl declarations to be used by all.
- * @status FROZEN
- */
-
-%{C++
-
-#include "nscore.h"
-#include "prtime.h"
-
-/*
- * Forward declarations for new string types
- */
-class nsAString;
-class nsACString;
-
-/*
- * Start commenting out the C++ versions of the below in the output header
- */
-#if 0
-%}
-
-typedef boolean PRBool ;
-typedef octet PRUint8 ;
-typedef unsigned short PRUint16 ;
-typedef unsigned short PRUnichar;
-typedef unsigned long PRUint32 ;
-typedef unsigned long long PRUint64 ;
-typedef unsigned long long PRTime ;
-typedef short PRInt16 ;
-typedef long PRInt32 ;
-typedef long long PRInt64 ;
-
-typedef unsigned long nsrefcnt ;
-typedef unsigned long nsresult ;
-
-// XXX need this built into xpidl compiler so that it's really size_t or PRSize
-// and it's scriptable:
-typedef unsigned long size_t;
-
-[ptr] native voidPtr(void);
-[ptr] native charPtr(char);
-[ptr] native unicharPtr(PRUnichar);
-
-[ref, nsid] native nsIDRef(nsID);
-[ref, nsid] native nsIIDRef(nsIID);
-[ref, nsid] native nsCIDRef(nsCID);
-
-[ptr, nsid] native nsIDPtr(nsID);
-[ptr, nsid] native nsIIDPtr(nsIID);
-[ptr, nsid] native nsCIDPtr(nsCID);
-
-// NOTE: Be careful in using the following 3 types. The *Ref and *Ptr variants
-// are more commonly used (and better supported). Those variants require
-// nsMemory alloc'd copies when used as 'out' params while these types do not.
-// However, currently these types can not be used for 'in' params. And, methods
-// that use them as 'out' params *must* be declared [notxpcom] (with an explicit
-// return type of nsresult). This makes such methods implicitly not scriptable.
-// Use of these types in methods without a [notxpcom] declaration will cause
-// the xpidl compiler to raise an error.
-// See: http://bugzilla.mozilla.org/show_bug.cgi?id=93792
-
-[nsid] native nsIID(nsIID);
-[nsid] native nsID(nsID);
-[nsid] native nsCID(nsCID);
-
-[ptr] native nsQIResult(void);
-
-[ref, domstring] native DOMString(ignored);
-[ref, domstring] native DOMStringRef(ignored);
-[ptr, domstring] native DOMStringPtr(ignored);
-
-[ref, utf8string] native AUTF8String(ignored);
-[ref, utf8string] native AUTF8StringRef(ignored);
-[ptr, utf8string] native AUTF8StringPtr(ignored);
-
-[ref, cstring] native ACString(ignored);
-[ref, cstring] native ACStringRef(ignored);
-[ptr, cstring] native ACStringPtr(ignored);
-
-[ref, astring] native AString(ignored);
-[ref, astring] native AStringRef(ignored);
-[ptr, astring] native AStringPtr(ignored);
-
-%{C++
-/*
- * End commenting out the C++ versions of the above in the output header
- */
-#endif
-%}
diff --git a/components/interfaces/rdIMatchResult.idl b/components/interfaces/rdIMatchResult.idl
deleted file mode 100644
index 371a7d7..0000000
--- a/components/interfaces/rdIMatchResult.idl
+++ /dev/null
@@ -1,13 +0,0 @@
-/* $Id$ */
-#include "nsISupports.idl"
-
-[scriptable, uuid(cf89b480-bce3-11de-a0dd-028037ec0200)]
-interface rdIMatchResult : nsISupports {
-
- /* Attributes */
- readonly attribute wstring redirectTo;
- readonly attribute boolean isMatch;
- readonly attribute boolean isExcludeMatch;
- readonly attribute boolean isDisabledMatch;
-};
-
diff --git a/components/interfaces/rdIRedirect.idl b/components/interfaces/rdIRedirect.idl
deleted file mode 100644
index 0421d8e..0000000
--- a/components/interfaces/rdIRedirect.idl
+++ /dev/null
@@ -1,29 +0,0 @@
-/* $Id$ */
-#include "nsISupports.idl"
-#include "rdIMatchResult.idl"
-
-[scriptable, uuid(cb69ddf0-bce1-11de-8251-028037ec0200)]
-interface rdIRedirect : nsISupports {
-
- /* Attributes */
- attribute wstring exampleUrl;
- attribute wstring includePattern;
- attribute wstring excludePattern;
- attribute wstring redirectUrl;
- attribute wchar patternType;
- attribute boolean unescapeMatches;
- attribute boolean disabled;
-
- /* Methods */
- rdIRedirect clone();
- void compile();
- void copyValues(in rdIRedirect other);
- void deserialize(in wstring data);
- boolean equals(in rdIRedirect other);
- rdIMatchResult getMatch(in wstring url);
- boolean isRegex();
- boolean isWildcard();
- wstring serialize();
- rdIMatchResult test(in wstring url);
-};
-
diff --git a/components/interfaces/rdIRedirector.idl b/components/interfaces/rdIRedirector.idl
deleted file mode 100644
index 2bd2eff..0000000
--- a/components/interfaces/rdIRedirector.idl
+++ /dev/null
@@ -1,23 +0,0 @@
-/* $Id$ */
-#include "nsISupports.idl"
-#include "nsIFile.idl"
-#include "rdIRedirect.idl"
-
-[scriptable, uuid(cdf25d91-bce1-11de-aee1-028037ec0200)]
-interface rdIRedirector : nsISupports {
-
- attribute boolean enabled;
- readonly attribute short redirectCount;
-
- void addRedirect(in rdIRedirect redirect);
- void debug(in wstring msg);
- void deleteRedirectAt(in short index);
- void exportRedirects(in nsIFile file);
- rdIRedirect getRedirectAt(in short index);
- wstring getRedirectUrl(in wstring url);
- long importRedirects(in nsIFile file);
- void reload();
- void save();
- void switchItems(in short index1, in short index2);
-};
-
diff --git a/components/interfaces/xpidl.exe b/components/interfaces/xpidl.exe
deleted file mode 100644
index cb39712..0000000
--- a/components/interfaces/xpidl.exe
+++ /dev/null
Binary files differ
diff --git a/install.rdf b/install.rdf
index 5ce1025..6e3a212 100644
--- a/install.rdf
+++ b/install.rdf
@@ -4,7 +4,7 @@
<Description about="urn:mozilla:install-manifest">
<em:id>redirector@einaregilsson.com</em:id>
<em:name>Redirector</em:name>
- <em:version>2.5.2</em:version>
+ <em:version>2.6</em:version>
<em:creator>Einar Egilsson</em:creator>
<em:contributor>quaful Zhang (Simplified Chinese locale)</em:contributor>
<em:description>Automatically redirects to user-defined urls on certain pages</em:description>
@@ -15,7 +15,7 @@
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- firefox -->
- <em:minVersion>3.5</em:minVersion>
+ <em:minVersion>5.0</em:minVersion>
<em:maxVersion>6.*</em:maxVersion>
</Description>
</em:targetApplication>
diff --git a/locale/en-US/browserOverlay.xul.dtd b/locale/en-US/browserOverlay.xul.dtd
new file mode 100644
index 0000000..3aaa0fc
--- /dev/null
+++ b/locale/en-US/browserOverlay.xul.dtd
@@ -0,0 +1,9 @@
+<!-- $Id$ -->
+<!ENTITY RedirectorMenuItem.label "Redirector">
+<!ENTITY RedirectorMenuItem.accesskey "R">
+<!ENTITY RedirectorContext.label "Add url to Redirector...">
+<!ENTITY RedirectorContext.accesskey "A">
+<!ENTITY RedirectorManageRedirects.label "Manage redirects">
+<!ENTITY RedirectorManageRedirects.accesskey "M">
+<!ENTITY RedirectorHelp.label "Help">
+<!ENTITY RedirectorHelp.accesskey "H">
diff --git a/locale/en-US/editRedirect.xul.dtd b/locale/en-US/editRedirect.xul.dtd
new file mode 100644
index 0000000..4b48d9c
--- /dev/null
+++ b/locale/en-US/editRedirect.xul.dtd
@@ -0,0 +1,14 @@
+<!-- $Id$ -->
+<!ENTITY redirectWindow.title "Redirect">
+<!ENTITY txtExampleUrl.label "Example url">
+<!ENTITY txtIncludePattern.label "Include Pattern">
+<!ENTITY txtExcludePattern.label "Exclude Pattern">
+<!ENTITY txtRedirectUrl.label "Redirect to">
+<!ENTITY btnTestPattern.label "Test pattern">
+<!ENTITY rdoWildcard.label "Wildcard">
+<!ENTITY rdoWildcard.accessKey "W">
+<!ENTITY rdoRegex.label "Regular Expression">
+<!ENTITY rdoPatternTypes.label "Pattern Type">
+<!ENTITY rdoRegex.accessKey "R">
+<!ENTITY chkUnescapeMatches.label "Unescape matches">
+
diff --git a/locale/en-US/redirector.properties b/locale/en-US/redirector.properties
new file mode 100644
index 0000000..128d08e
--- /dev/null
+++ b/locale/en-US/redirector.properties
@@ -0,0 +1,33 @@
+# $Id$
+initError=Failed to initialize Redirector.
+extensions.redirector@einaregilsson.com.description=Automatically redirects to user-defined urls on certain pages
+extensionName=Redirector
+addCurrentUrl=Add current url to Redirector
+addLinkUrl=Add link url to Redirector
+recursiveError=A redirect with the pattern %S matches %S and is trying to redirect to it again. You should change this rule so it won't work recursively.
+enabledTooltip=Redirector is enabled
+disabledTooltip=Redirector is disabled
+testPatternSuccess=The pattern %S matches example URL %S, and would redirect you to url: %S
+testPatternFailure=The pattern %S does not match example URL %S
+testPatternExclude=Example URL %S matches the exclude pattern %S and so would not be redirected
+regexPatternError=The pattern '%S' is not a legal regular expression pattern. Details: %S
+xpathDeprecated=XPath patterns are no longer supported as of version 1.5.1, please remove those redirects.
+redirectorFiles=Redirector files (*.rdx)
+exportCaption=Export redirects...
+importCaption=Import redirects...
+deleteConfirmationText=Are you sure you want to permanently delete this redirect?
+deleteConfirmationTitle=Delete redirect?
+importedMessage=%S redirects were imported
+importedMessageSingular=%S redirect was imported
+existedMessage=%S redirects were identical to existing redirects and were therefore not imported.
+existedMessageSingular=%S redirect was identical to an existing redirect and was therefore not imported.
+allExistedMessage=All %S redirects in the file were identical to existing redirects, no redirects were imported.
+allExistedMessageSingular=The single redirect in the file was identical to an existing redirect and was therefore not imported.
+importedNone=There were no usable redirects in the given file, no redirects were imported.
+importResult=Import results
+invalidRedirectTitle=Redirector Add-on: Invalid Redirect detected
+invalidRedirectText=The pattern "%S" redirected the url %S to %S which also matches the pattern. This will cause an endless loop and so the redirect has been disabled to prevent this from happening. You should edit this redirect to fix it.
+warningExampleUrlDoesntMatchPatternTitle=Warning: Example url does not match redirect
+warningExampleUrlDoesntMatchPattern=The example url does not match the redirect specified. Are you sure you want to save this redirect?
+errorExampleUrlMatchesRecursiveTitle=Error: Recursive match detected
+errorExampleUrlMatchesRecursive=The example url %S matches the redirect and would redirect you to %S, which also matches the redirect. This is not allowed as it can cause an endless loop of requests. \ No newline at end of file
diff --git a/locale/en-US/settings.xul.dtd b/locale/en-US/settings.xul.dtd
new file mode 100644
index 0000000..ff55b68
--- /dev/null
+++ b/locale/en-US/settings.xul.dtd
@@ -0,0 +1,65 @@
+<!-- $Id$ -->
+<!ENTITY window.title "Redirector">
+
+<!-- ListItems with redirects -->
+<!ENTITY colIncludePattern.label "Include pattern">
+<!ENTITY colExcludePattern.label "Exclude pattern">
+<!ENTITY colRedirectTo.label "Redirect to">
+<!ENTITY colEnabled.label "Enabled">
+
+<!-- Buttons -->
+<!ENTITY btnAdd.label "Add...">
+<!ENTITY btnAdd.accesskey "A">
+<!ENTITY btnAdd.tooltip "Create a new redirect">
+
+<!ENTITY btnEdit.label "Edit...">
+<!ENTITY btnEdit.accesskey "E">
+<!ENTITY btnEdit.tooltip "Edit the selected redirect">
+
+<!ENTITY btnDelete.label "Delete">
+<!ENTITY btnDelete.accesskey "D">
+<!ENTITY btnDelete.tooltip "Delete the selected redirect">
+
+<!ENTITY btnUp.tooltip "Move the selected redirect up">
+
+<!ENTITY btnDown.tooltip "Move the selected redirect down">
+
+<!ENTITY btnImport.label "Import...">
+<!ENTITY btnImport.accesskey "I">
+<!ENTITY btnImport.tooltip "Import redirects from a file">
+
+<!ENTITY btnExport.label "Export...">
+<!ENTITY btnExport.accesskey "E">
+<!ENTITY btnExport.tooltip "Export redirects to a file">
+
+
+<!-- Tab headers -->
+<!ENTITY tabRedirects.label "Redirects">
+<!ENTITY tabRedirects.accesskey "R">
+<!ENTITY tabPreferences.label "Preferences">
+<!ENTITY tabPreferences.accesskey "P">
+<!ENTITY tabImportExport.label "Import/Export">
+<!ENTITY tabImportExport.accesskey "m">
+<!ENTITY tabHelp.label "Help">
+<!ENTITY tabHelp.accesskey "H">
+
+<!-- Preferences -->
+<!ENTITY grpGeneralPreferences.label "General">
+<!ENTITY grpDebuggingPreferences.label "Debugging">
+
+<!ENTITY chkEnableRedirector.label "Enable Redirector">
+<!ENTITY chkEnableRedirector.accesskey "E">
+
+<!ENTITY chkShowStatusBarIcon.label "Show status bar icon">
+<!ENTITY chkShowStatusBarIcon.accesskey "s">
+
+<!ENTITY chkShowContextMenu.label "Show context menu">
+<!ENTITY chkShowContextMenu.accesskey "c">
+
+<!ENTITY chkEnableDebugOutput.label "Enable debug output">
+<!ENTITY chkEnableDebugOutput.accesskey "d">
+
+
+<!-- Import / Export -->
+<!ENTITY lblExport.label "Export your redirects to a file">
+<!ENTITY lblImport.label "Import new redirects from a file">
diff --git a/locale/zh-CN/browserOverlay.xul.dtd b/locale/zh-CN/browserOverlay.xul.dtd
new file mode 100644
index 0000000..b0567f8
--- /dev/null
+++ b/locale/zh-CN/browserOverlay.xul.dtd
@@ -0,0 +1,9 @@
+<!-- $Id: browserOverlay.xul.dtd 249 2009-09-15 21:41:06Z einar@einaregilsson.com $ -->
+<!ENTITY RedirectorMenuItem.label "管理 URL 重定向">
+<!ENTITY RedirectorMenuItem.accesskey "R">
+<!ENTITY RedirectorContext.label "把 URL 添加到 Redirector 规则...">
+<!ENTITY RedirectorContext.accesskey "A">
+<!ENTITY RedirectorManageRedirects.label "管理规则">
+<!ENTITY RedirectorManageRedirects.accesskey "M">
+<!ENTITY RedirectorHelp.label "帮助">
+<!ENTITY RedirectorHelp.accesskey "H">
diff --git a/locale/zh-CN/editRedirect.xul.dtd b/locale/zh-CN/editRedirect.xul.dtd
new file mode 100644
index 0000000..f0ab422
--- /dev/null
+++ b/locale/zh-CN/editRedirect.xul.dtd
@@ -0,0 +1,14 @@
+<!-- $Id: editRedirect.xul.dtd 253 2009-10-07 11:24:44Z einar@einaregilsson.com $ -->
+<!ENTITY redirectWindow.title "Redirector">
+<!ENTITY txtExampleUrl.label "用于测试的 URL">
+<!ENTITY txtIncludePattern.label "使用规则">
+<!ENTITY txtExcludePattern.label "例外规则">
+<!ENTITY txtRedirectUrl.label "重定向到">
+<!ENTITY btnTestPattern.label "测试">
+<!ENTITY rdoWildcard.label "通配符(*)">
+<!ENTITY rdoWildcard.accessKey "W">
+<!ENTITY rdoRegex.label "正则表达式">
+<!ENTITY rdoPatternTypes.label "规则类型">
+<!ENTITY rdoRegex.accessKey "R">
+<!ENTITY chkUnescapeMatches.label "Unescape 匹配部分">
+
diff --git a/locale/zh-CN/redirector.properties b/locale/zh-CN/redirector.properties
new file mode 100644
index 0000000..866ec1a
--- /dev/null
+++ b/locale/zh-CN/redirector.properties
@@ -0,0 +1,33 @@
+# $Id: redirector.properties 288 2009-10-26 08:13:15Z einar@einaregilsson.com $
+initError=Failed to initialize Redirector.
+extensions.redirector@einaregilsson.com.description=根据您设置的规则自动跳转网页。
+extensionName=Redirector
+addCurrentUrl=Add current url to Redirector
+addLinkUrl=为链接创建 Redirector 规则
+recursiveError=A redirect with the pattern %S matches %S and is trying to redirect to it again. You should change this rule so it won't work recursively.
+enabledTooltip=Redirector is enabled
+disabledTooltip=Redirector is disabled
+testPatternSuccess=The pattern %S matches example URL %S, and would redirect you to url: %S
+testPatternFailure=The pattern %S does not match example URL %S
+testPatternExclude=Example URL %S matches the exclude pattern %S and so would not be redirected
+regexPatternError=The pattern '%S' is not a legal regular expression pattern. Details: %S
+xpathDeprecated=XPath patterns are no longer supported as of version 1.5.1, please remove those redirects.
+redirectorFiles=Redirector files (*.rdx)
+exportCaption=Export redirects...
+importCaption=Import redirects...
+deleteConfirmationText=Are you sure you want to permanently delete this redirect?
+deleteConfirmationTitle=Delete redirect?
+importedMessage=%S redirects were imported
+importedMessageSingular=%S redirect was imported
+existedMessage=%S redirects were identical to existing redirects and were therefore not imported.
+existedMessageSingular=%S redirect was identical to an existing redirect and was therefore not imported.
+allExistedMessage=All %S redirects in the file were identical to existing redirects, no redirects were imported.
+allExistedMessageSingular=The single redirect in the file was identical to an existing redirect and was therefore not imported.
+importedNone=There were no usable redirects in the given file, no redirects were imported.
+importResult=Import results
+invalidRedirectTitle=Redirector Add-on: Invalid Redirect detected
+invalidRedirectText=The pattern "%S" redirected the url %S to %S which also matches the pattern. This will cause an endless loop and so the redirect has been disabled to prevent this from happening. You should edit this redirect to fix it.
+warningExampleUrlDoesntMatchPatternTitle=Warning: Example url does not match redirect
+warningExampleUrlDoesntMatchPattern=The example url does not match the redirect specified. Are you sure you want to save this redirect?
+errorExampleUrlMatchesRecursiveTitle=Error: Recursive match detected
+errorExampleUrlMatchesRecursive=The example url %S matches the redirect and would redirect you to %S, which also matches the redirect. This is not allowed as it can cause an endless loop of requests. \ No newline at end of file
diff --git a/locale/zh-CN/settings.xul.dtd b/locale/zh-CN/settings.xul.dtd
new file mode 100644
index 0000000..0facb3a
--- /dev/null
+++ b/locale/zh-CN/settings.xul.dtd
@@ -0,0 +1,65 @@
+<!-- $Id: settings.xul.dtd 266 2009-10-14 11:13:03Z einar@einaregilsson.com $ -->
+<!ENTITY window.title "Redirector">
+
+<!-- ListItems with redirects -->
+<!ENTITY colIncludePattern.label "使用规则">
+<!ENTITY colExcludePattern.label "例外规则">
+<!ENTITY colRedirectTo.label "重定向到">
+<!ENTITY colEnabled.label "启用">
+
+<!-- Buttons -->
+<!ENTITY btnAdd.label "增加规则...">
+<!ENTITY btnAdd.accesskey "A">
+<!ENTITY btnAdd.tooltip "新建一条规则">
+
+<!ENTITY btnEdit.label "编辑规则...">
+<!ENTITY btnEdit.accesskey "E">
+<!ENTITY btnEdit.tooltip "编辑选定的规则">
+
+<!ENTITY btnDelete.label "删除规则">
+<!ENTITY btnDelete.accesskey "D">
+<!ENTITY btnDelete.tooltip "删除选定的规则">
+
+<!ENTITY btnUp.tooltip "把规则向上移动一条位置">
+
+<!ENTITY btnDown.tooltip "把规则向下移动一条位置">
+
+<!ENTITY btnImport.label "导入...">
+<!ENTITY btnImport.accesskey "I">
+<!ENTITY btnImport.tooltip "从文件导入规则">
+
+<!ENTITY btnExport.label "导出...">
+<!ENTITY btnExport.accesskey "E">
+<!ENTITY btnExport.tooltip "导出规则到文件">
+
+
+<!-- Tab headers -->
+<!ENTITY tabRedirects.label "重定向规则">
+<!ENTITY tabRedirects.accesskey "R">
+<!ENTITY tabPreferences.label "选项">
+<!ENTITY tabPreferences.accesskey "P">
+<!ENTITY tabImportExport.label "导入/导出">
+<!ENTITY tabImportExport.accesskey "m">
+<!ENTITY tabHelp.label "帮助">
+<!ENTITY tabHelp.accesskey "H">
+
+<!-- Preferences -->
+<!ENTITY grpGeneralPreferences.label "一般">
+<!ENTITY grpDebuggingPreferences.label "调试">
+
+<!ENTITY chkEnableRedirector.label "启用 Redirector">
+<!ENTITY chkEnableRedirector.accesskey "E">
+
+<!ENTITY chkShowStatusBarIcon.label "在状态栏显示图标">
+<!ENTITY chkShowStatusBarIcon.accesskey "s">
+
+<!ENTITY chkShowContextMenu.label "在右键菜单中显示">
+<!ENTITY chkShowContextMenu.accesskey "c">
+
+<!ENTITY chkEnableDebugOutput.label "启用调试输出">
+<!ENTITY chkEnableDebugOutput.accesskey "d">
+
+
+<!-- Import / Export -->
+<!ENTITY lblExport.label "将规则导出到文件">
+<!ENTITY lblImport.label "从文件导入规则">