diff options
author | Einar Egilsson | 2011-08-26 14:37:27 +0200 |
---|---|---|
committer | Einar Egilsson | 2011-08-26 14:37:27 +0200 |
commit | 3ac1838cba725705a96a9d7b65721b15f4ec67b1 (patch) | |
tree | 6eea260d1e5882ba1031c5bb78bd2b9ac0884fb9 /chrome | |
parent | 5aefd85d7975c8934b32a30a1dec68aa421cdee3 (diff) |
Moved everything to a js module, removed custom xpcom interfaces
Diffstat (limited to 'chrome')
21 files changed, 1820 insertions, 343 deletions
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 Binary files differnew file mode 100644 index 0000000..d32b79c --- /dev/null +++ b/chrome/ui/skin/movedown.png diff --git a/chrome/ui/skin/movedowndisabled.png b/chrome/ui/skin/movedowndisabled.png Binary files differnew file mode 100644 index 0000000..afd8fc6 --- /dev/null +++ b/chrome/ui/skin/movedowndisabled.png diff --git a/chrome/ui/skin/moveup.png b/chrome/ui/skin/moveup.png Binary files differnew file mode 100644 index 0000000..3025378 --- /dev/null +++ b/chrome/ui/skin/moveup.png diff --git a/chrome/ui/skin/moveupdisabled.png b/chrome/ui/skin/moveupdisabled.png Binary files differnew file mode 100644 index 0000000..e526b29 --- /dev/null +++ b/chrome/ui/skin/moveupdisabled.png 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 Binary files differnew file mode 100644 index 0000000..f8de12c --- /dev/null +++ b/chrome/ui/skin/redirector.png diff --git a/chrome/ui/skin/statusactive.png b/chrome/ui/skin/statusactive.png Binary files differnew file mode 100644 index 0000000..06ce766 --- /dev/null +++ b/chrome/ui/skin/statusactive.png diff --git a/chrome/ui/skin/statusinactive.png b/chrome/ui/skin/statusinactive.png Binary files differnew file mode 100644 index 0000000..8b83562 --- /dev/null +++ b/chrome/ui/skin/statusinactive.png |