From 4b612baf4f29178214edec130856d83a40bf473d Mon Sep 17 00:00:00 2001 From: Einar Egilsson Date: Sun, 4 Sep 2011 22:53:16 +0200 Subject: Total restructuring of files and deletion of old files --- chrome.manifest | 7 +- chrome/browserOverlay.xul | 30 +++ chrome/code/browserOverlay.xul.js | 109 ----------- chrome/code/component.js | 21 --- chrome/code/editRedirect.xul.js | 103 ---------- chrome/code/proxyserver.js | 58 ------ chrome/code/redirect.js | 213 --------------------- chrome/code/redirector.js | 366 ------------------------------------ chrome/code/redirectorprefs.js | 76 -------- chrome/code/settings.xul.js | 276 --------------------------- chrome/code/unittest/run.html | 99 ---------- chrome/code/unittest/testcases.js | 129 ------------- chrome/code/xpcom.js | 28 --- chrome/css/redirector.css | 40 ++++ chrome/help.html | 182 ++++++++++++++++++ chrome/images/redirector.png | Bin 0 -> 1462 bytes chrome/images/statusactive.png | Bin 0 -> 360 bytes chrome/images/statusinactive.png | Bin 0 -> 396 bytes chrome/js/browserOverlay.js | 95 ++++++++++ chrome/js/component.js | 21 +++ chrome/js/proxyserver.js | 58 ++++++ chrome/js/redirect.js | 213 +++++++++++++++++++++ chrome/js/redirector-ui.js | 207 ++++++++++++++++++++ chrome/js/redirector.js | 366 ++++++++++++++++++++++++++++++++++++ chrome/js/redirectorprefs.js | 76 ++++++++ chrome/js/settings.js | 162 ---------------- chrome/js/xpcom.js | 28 +++ chrome/redirector.html | 71 +++++++ chrome/settings.html | 112 ----------- chrome/ui/browserOverlay.xul | 34 ---- chrome/ui/editRedirect.xul | 58 ------ chrome/ui/help.html | 182 ------------------ chrome/ui/settings.xul | 119 ------------ chrome/ui/skin/movedown.png | Bin 294 -> 0 bytes chrome/ui/skin/movedowndisabled.png | Bin 361 -> 0 bytes chrome/ui/skin/moveup.png | Bin 360 -> 0 bytes chrome/ui/skin/moveupdisabled.png | Bin 282 -> 0 bytes chrome/ui/skin/redirector.css | 15 -- chrome/ui/skin/redirector.png | Bin 1462 -> 0 bytes chrome/ui/skin/statusactive.png | Bin 360 -> 0 bytes chrome/ui/skin/statusinactive.png | Bin 396 -> 0 bytes chrome/unittest/run.html | 99 ++++++++++ chrome/unittest/testcases.js | 129 +++++++++++++ locale/en-US/browserOverlay.dtd | 9 + locale/en-US/browserOverlay.xul.dtd | 9 - 45 files changed, 1626 insertions(+), 2174 deletions(-) create mode 100644 chrome/browserOverlay.xul delete mode 100644 chrome/code/browserOverlay.xul.js delete mode 100644 chrome/code/component.js delete mode 100644 chrome/code/editRedirect.xul.js delete mode 100644 chrome/code/proxyserver.js delete mode 100644 chrome/code/redirect.js delete mode 100644 chrome/code/redirector.js delete mode 100644 chrome/code/redirectorprefs.js delete mode 100644 chrome/code/settings.xul.js delete mode 100644 chrome/code/unittest/run.html delete mode 100644 chrome/code/unittest/testcases.js delete mode 100644 chrome/code/xpcom.js create mode 100644 chrome/css/redirector.css create mode 100644 chrome/help.html create mode 100644 chrome/images/redirector.png create mode 100644 chrome/images/statusactive.png create mode 100644 chrome/images/statusinactive.png create mode 100644 chrome/js/browserOverlay.js create mode 100644 chrome/js/component.js create mode 100644 chrome/js/proxyserver.js create mode 100644 chrome/js/redirect.js create mode 100644 chrome/js/redirector-ui.js create mode 100644 chrome/js/redirector.js create mode 100644 chrome/js/redirectorprefs.js delete mode 100644 chrome/js/settings.js create mode 100644 chrome/js/xpcom.js create mode 100644 chrome/redirector.html delete mode 100644 chrome/settings.html delete mode 100644 chrome/ui/browserOverlay.xul delete mode 100644 chrome/ui/editRedirect.xul delete mode 100644 chrome/ui/help.html delete mode 100644 chrome/ui/settings.xul delete mode 100644 chrome/ui/skin/movedown.png delete mode 100644 chrome/ui/skin/movedowndisabled.png delete mode 100644 chrome/ui/skin/moveup.png delete mode 100644 chrome/ui/skin/moveupdisabled.png delete mode 100644 chrome/ui/skin/redirector.css delete mode 100644 chrome/ui/skin/redirector.png delete mode 100644 chrome/ui/skin/statusactive.png delete mode 100644 chrome/ui/skin/statusinactive.png create mode 100644 chrome/unittest/run.html create mode 100644 chrome/unittest/testcases.js create mode 100644 locale/en-US/browserOverlay.dtd delete mode 100644 locale/en-US/browserOverlay.xul.dtd diff --git a/chrome.manifest b/chrome.manifest index e758bd7..5221095 100644 --- a/chrome.manifest +++ b/chrome.manifest @@ -1,12 +1,9 @@ - content redirector file:chrome/ locale redirector en-US file:locale/en-US/ -skin redirector classic/1.0 file:chrome/ui/skin/ -overlay chrome://browser/content/browser.xul chrome://redirector/content/ui/browserOverlay.xul +overlay chrome://browser/content/browser.xul chrome://redirector/content/browserOverlay.xul -component {b7a7a54f-0581-47ff-b086-d6920cb7a3f7} chrome/code/component.js +component {b7a7a54f-0581-47ff-b086-d6920cb7a3f7} chrome/js/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 category net-channel-event-sinks Redirector @einaregilsson.com/redirector;1 \ No newline at end of file diff --git a/chrome/browserOverlay.xul b/chrome/browserOverlay.xul new file mode 100644 index 0000000..a8dda3c --- /dev/null +++ b/chrome/browserOverlay.xul @@ -0,0 +1,30 @@ + + + + - - -

Redirector Unit Tests

- - -
-
- - \ No newline at end of file diff --git a/chrome/code/unittest/testcases.js b/chrome/code/unittest/testcases.js deleted file mode 100644 index 0c0c10c..0000000 --- a/chrome/code/unittest/testcases.js +++ /dev/null @@ -1,129 +0,0 @@ -//// $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/code/xpcom.js b/chrome/code/xpcom.js deleted file mode 100644 index e81f58e..0000000 --- a/chrome/code/xpcom.js +++ /dev/null @@ -1,28 +0,0 @@ - -Ci = Components.interfaces; -Cc = Components.classes; -Cr = Components.results; - -const ServerSocket = Components.Constructor("@mozilla.org/network/server-socket;1", "nsIServerSocket", "init"); -const ScriptableInputStream = Components.Constructor("@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", "init"); -const FileInputStream = Components.Constructor("@mozilla.org/network/file-input-stream;1", "nsIFileInputStream", "init"); -const ConverterInputStream = Components.Constructor("@mozilla.org/intl/converter-input-stream;1", "nsIConverterInputStream", "init"); -const LocalFile = Components.Constructor("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath"); -const FilePicker = Components.Constructor("@mozilla.org/filepicker;1", "nsIFilePicker", "init"); -const FilePickerMode = { save : Ci.nsIFilePicker.modeSave, open : Ci.nsIFilePicker.modeOpen }; - -function Service(className, interfaceName) { - return Cc[className].getService(Ci[interfaceName]); -} - -const PromptService = Service("@mozilla.org/embedcomp/prompt-service;1","nsIPromptService"); -const IOService = Service("@mozilla.org/network/io-service;1","nsIIOService"); -const LocaleService = Service("@mozilla.org/intl/nslocaleservice;1", "nsILocaleService"); -const StringBundleService = Service("@mozilla.org/intl/stringbundle;1", "nsIStringBundleService"); - -var EXPORTED_SYMBOLS = []; -for (var name in this) { - if (name != 'Service' && name != 'QueryInterface' && name != 'name' && name != 'EXPORTED_SYMBOLS') { - EXPORTED_SYMBOLS.push(name); - } -} diff --git a/chrome/css/redirector.css b/chrome/css/redirector.css new file mode 100644 index 0000000..a9880d3 --- /dev/null +++ b/chrome/css/redirector.css @@ -0,0 +1,40 @@ +body { + font-family: Tahoma, Arial, sans-serif; + font-size:10pt; + min-height:100%; + background:-moz-linear-gradient(top, #bcd, #fff); + margin:0; padding:0; +} +html { + height:100%; + margin:0; + padding:0; +} +h1 { font-family: ; text-align:center; color:#555; text-shadow:0px 0px 15px white; font-size:34pt; padding:0px; margin:20px 0px 0px 0px; letter-spacing:2px; } +label { display:table-cell; } +input { display:table-cell; } +li { margin:0; padding:5px; border-top: dotted 1px grey;} +li:first-child { border-top:none;} +#actions { width:600px; margin:10px auto; text-align:center; } +#redirect-list { + -moz-user-select: -moz-none; + + border:solid 1px grey; + margin:auto; + padding:0px; + width: 700px; + list-style-type:none; + list-style-position:outside; + border-radius:3px; + box-shadow: 0px 0px 2px grey; + text-shadow: 1px 1px 1px white; +} +#config { display:none; position:fixed; margin:auto;} +button { border:solid 1px grey; border-radius:4px; background:-moz-linear-gradient(top, #ccc, #fff); cursor:pointer;} +h5 { text-align:center; margin:0px; padding-left:130px; } +.pattern, .redirectTo { color:blue; font-style:italic;} +.exampleUrl, .redirectResult { color:blue; } +#redirect-list li:nth-child(odd) { background:-moz-linear-gradient(top, #ddf, #cce); } +#redirect-list li:nth-child(even) { background-color:#fff; } +#redirect-form div { display:table-row; } +#redirect-form { display:none; position:fixed; margin:0 auto; border:solid 1px black; } diff --git a/chrome/help.html b/chrome/help.html new file mode 100644 index 0000000..4534d96 --- /dev/null +++ b/chrome/help.html @@ -0,0 +1,182 @@ + + + + Redirector Help + + + +

Redirector Help

+

Table of contents

+ + + + +

What is Redirector?

+ +

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.

+ + +

Basic usage

+

To add a new redirect you can go to the Tools menuitem and select Redirector. That will + open the Redirector settings window which shows all your redirects. The window can also be opened + by right clicking on the R icon in your statusbar. + There you can press the Add... button and then you can enter the details for the new redirect. A redirect + consists of a few things: +

+

+ + +

Wildcards

+ +

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: +

+ $1, $2, $3 in the redirect urls will match the text that the stars matched. Examples: + +

+ + +

Regular expressions

+ +

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 http://regular-expressions.info 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 \?).

+ + +

Examples

+ +
    +
  1. + Static redirect
    + Redirects from http://example.com/foo to http://example.com/bar +

    + Include pattern: http://example.com/foo
    + Exclude pattern:
    + Redirect to: http://example.com/bar
    + Pattern type: Wildcard
    +

    + +
  2. +
  3. + Redirect using query string parameter and wildcards
    + 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. +

    + Include pattern: http://example.com/index.php?id=*&a=b
    + Exclude pattern:
    + Redirect to: http://example.com/printerfriendly.com?id=$1&a=b
    + Pattern type: Wildcard
    +

    +
  4. +
  5. + Redirect using query string parameter and regular expressions
    + 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. +

    + Include pattern: http://example.com/index.php\?id=(\d+)&a=b
    + Exclude pattern:
    + Redirect to: http://example.com/printerfriendly.com?id=$1&a=b
    + Pattern type: Regular Expression
    +

    +
  6. +
  7. + Redirect to a different folder using wildcards
    + 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. +

    + Include pattern: http://example.com/category/*/index.php
    + Exclude pattern: http://example.com/category/*/*/index.php
    + Redirect to: http://example.com/category/cats/index.php
    + Pattern type: Wildcard
    +

    +
  8. +
  9. + Redirect http to https using wildcards
    + Redirects from http://mail.google.com/randomcharacters to https://mail.google.com/randomcharacters + where randomcharacters could be anything. +

    + Include pattern: http://mail.google.com*
    + Exclude pattern:
    + Redirect to: https://mail.google.com$1
    + Pattern type: Wildcard
    +

    +
  10. +
+ + \ No newline at end of file diff --git a/chrome/images/redirector.png b/chrome/images/redirector.png new file mode 100644 index 0000000..f8de12c Binary files /dev/null and b/chrome/images/redirector.png differ diff --git a/chrome/images/statusactive.png b/chrome/images/statusactive.png new file mode 100644 index 0000000..06ce766 Binary files /dev/null and b/chrome/images/statusactive.png differ diff --git a/chrome/images/statusinactive.png b/chrome/images/statusinactive.png new file mode 100644 index 0000000..8b83562 Binary files /dev/null and b/chrome/images/statusinactive.png differ diff --git a/chrome/js/browserOverlay.js b/chrome/js/browserOverlay.js new file mode 100644 index 0000000..ed1ab73 --- /dev/null +++ b/chrome/js/browserOverlay.js @@ -0,0 +1,95 @@ +Components.utils.import("chrome://redirector/content/js/redirectorprefs.js"); +Components.utils.import("chrome://redirector/content/js/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); + document.addEventListener('keypress', function(event) { + if ((event.charCode == 114) && event.altKey) { //alt+r + RedirectorOverlay.toggleEnabled(); + } + }, true); + } 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/content/images/statusactive.png' + statusImg.setAttribute('tooltiptext', this.strings.getString('enabledTooltip')); + } else { + statusImg.src = 'chrome://redirector/content/images/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(); + } + + gBrowser.selectedTab = gBrowser.addTab("chrome://redirector/content/redirector.html"); + }, + + onMenuItemCommand: function(event) { + this.openSettings(); + }, + + toggleEnabled : function(event) { + this.prefs.enabled = !this.prefs.enabled; + }, + + openSettings : function() { + gBrowser.selectedTab = gBrowser.addTab("chrome://redirector/content/redirector.html"); + }, + + 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/js/component.js b/chrome/js/component.js new file mode 100644 index 0000000..7b19109 --- /dev/null +++ b/chrome/js/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/js/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/js/proxyserver.js b/chrome/js/proxyserver.js new file mode 100644 index 0000000..4881f59 --- /dev/null +++ b/chrome/js/proxyserver.js @@ -0,0 +1,58 @@ +Components.utils.import("chrome://redirector/content/js/xpcom.js"); + +var EXPORTED_SYMBOLS = ['RedirectorProxy']; + +var RedirectorProxy = { + + start : function(port, getUrl) { + dump('Opening Proxy Server Socket on port ' + port); + this.getUrl = getUrl; + this.serverSocket = new ServerSocket(port, true, -1); + this.serverSocket.asyncListen(this); + }, + + onSocketAccepted: function(serverSocket, clientSocket) { + dump("Accepted connection on "+clientSocket.host+":"+clientSocket.port); + var requestStream = clientSocket.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream); + var responseStream = clientSocket.openOutputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0); + var tm = Cc["@mozilla.org/thread-manager;1"].getService(); + requestStream.asyncWait({ + onInputStreamReady : function(inputStream) { + RedirectorProxy.processRequest(clientSocket, inputStream, responseStream); + } + },0,0,tm.mainThread); + }, + + processRequest : function(clientSocket, inputStream, responseStream) { + var requestStream = new ScriptableInputStream(inputStream); + requestStream.available(); + var request = ''; + while (requestStream.available()) { + request = request + requestStream.read(2048); + } + var parts = request.split(' '); + dump('\n\n\n' + request + '\n\n\n'); + dump("\n" + parts[0] + " request for " + parts[1]); + var redirectUrl = 'http://einaregilsson.com';//Redirector.getRedirectUrl(parts[1]); + var outp = 'HTTP/1.1 302 Moved Temporarily'; + outp += '\r\nContent-Length: '; + outp += '\r\nLocation: ' + redirectUrl; + outp += '\r\nX-Redirected-By: Redirector Firefox Extension' + outp += '\r\n\r\n'; + var cl = outp.length -4; + if (cl < 100) { + cl+=2; + } else if (cl < 1000) { + cl += 3; + } else if (cl < 10000) { + cl += 4; + } else if (cl < 100000) { + cl += 5; + } + outp = outp.replace('', cl); + dump(outp); + responseStream.write(outp, outp.length); + responseStream.close(); + inputStream.close(); + } +} \ No newline at end of file diff --git a/chrome/js/redirect.js b/chrome/js/redirect.js new file mode 100644 index 0000000..8b0b1d2 --- /dev/null +++ b/chrome/js/redirect.js @@ -0,0 +1,213 @@ + +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().fromObject(this.toObject()); + }, + + compile : function() { + this._rxInclude = this._compile(this._includePattern); + this._rxExclude = this._compile(this._excludePattern); + }, + + toObject : function() { + return { + exampleUrl : this.exampleUrl, + includePattern : this.includePattern, + excludePattern : this.excludePattern, + redirectUrl : this.redirectUrl, + patternType : this.patternType, + unescapeMatches : this.unescapeMatches, + disabled : !!this.disabled + }; + }, + + fromObject : function(o) { + for (var prop in o) { + this[prop] = o[prop]; + } + return this; + }, + + 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; + }, + + 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; + }, + + 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/js/redirector-ui.js b/chrome/js/redirector-ui.js new file mode 100644 index 0000000..e0a5d07 --- /dev/null +++ b/chrome/js/redirector-ui.js @@ -0,0 +1,207 @@ +Components.utils.import("chrome://redirector/content/js/redirector.js"); +Components.utils.import("chrome://redirector/content/js/redirectorprefs.js"); +Components.utils.import("chrome://redirector/content/js/xpcom.js"); + +$(document).ready(function() { + var prefs = new RedirectorPrefs(); + var strings = StringBundleService.createBundle('chrome://redirector/locale/redirector.properties', LocaleService.getApplicationLocale()); + function tr(name) { + return strings.GetStringFromName(name); + } + + function trPlural(name, count) { + name += count == 1 ? 'Singular' : ''; + return strings.formatStringFromName(name, [count],1); + } + + function getFile(captionKey, mode) { + var picker = new FilePicker(window, tr(captionKey), mode); + picker.defaultExtension = ".rjson"; + var dir = prefs.defaultDir; + if (dir) { + picker.displayDirectory = new LocalFile(dir); + } + picker.appendFilter(tr('redirectorFiles'), '*.rjson'); + + if (picker.show() == picker.returnCancel) { + return null; + } + prefs.defaultDir = picker.displayDirectory.path; + return picker.file; + } + + function exportRedirects() { + var file = getFile('exportCaption', FilePickerMode.save); + if (file) { + Redirector.exportRedirects(file); + } + } + + function importRedirects() { + var file = getFile('importCaption', FilePickerMode.open); + var result; + if (!file) { + return; + } + result = Redirector.importRedirects(file); + var msg, imported, existed; + imported = result & 0xFFFF; + existed = result >> 16; + + if (imported > 0) { + msg = trPlural('importedMessage', imported); + if (existed > 0) { + msg += ', ' + trPlural('existedMessage',existed); + } else { + msg += '.'; + } + } else if (imported == 0 && existed > 0) { + msg = trPlural('allExistedMessage', existed); + } else { //Both 0 + msg = tr('importedNone'); + } + + var title = tr('importResult'); + PromptService.alert(null, title, msg); + + if (imported > 0) { + var newlist = []; + for (var i = Redirector.redirectCount-imported; i < Redirector.redirectCount; i++) { + newlist.push(Redirector.getRedirectAt(i)); + } + } + } + + var template = $('#redirect-list').html().replace(/^\s*|\s$/g, ''); + function databind() { + $('#redirect-list').empty(); + for (var i = 0; i < Redirector.redirectCount; i++) { + var redirect = Redirector.getRedirectAt(i); + var node = $(template); + node.find('.pattern').html(redirect.includePattern); + node.find('.redirectTo').html(redirect.redirectUrl); + node.find('.exampleUrl').html(redirect.exampleUrl); + node.find('.redirectResult').html(redirect.getMatch(redirect.exampleUrl).redirectTo); + node.appendTo('#redirect-list'); + node.data('redirect', redirect); + } + + } + + $('#redirect-list li div a.delete').live('click', function(ev) { + var redirect = $(this.parentNode.parentNode).data('redirect'); + if (PromptService.confirm(null, tr("deleteConfirmationTitle"), tr("deleteConfirmationText"))) { + Redirector.deleteRedirect(redirect); + $(this.parentNode.parentNode).fadeOut(function() { $(this).remove(); }); + } + ev.preventDefault(); + }); + + $('#redirect-list li div a.edit').live('click', function(ev) { + var redirect = $(this.parentNode.parentNode).data('redirect'); + $('#redirect-form').show(); + ev.preventDefault(); + }); + + databind(); + $('#import').click(importRedirects); + $('#export').click(exportRedirects); + + function bindConfig() { + $('#config input[type="checkbox"]').each(function() { + var pref = $(this).attr('data-pref'); + $(this).attr('checked', prefs[pref]); + }); + } + + bindConfig(); + prefs.addListener({ changedPrefs:bindConfig}); + + var movingElement = null; + $('li').mousedown(function(ev) { + if (ev.target && ev.target.tagName == 'A') { + return; + } + $(this).css('background', '-moz-linear-gradient(top, #aac, #99b)'); + $('#redirect-list').css('cursor', 'move'); + movingElement = this; + }); + + + $('li').mouseover(function() { + if (movingElement && this !== movingElement) { + if ($(movingElement).offset().top > $(this).offset().top) { + $(movingElement).detach().insertBefore(this); + } else { + $(movingElement).detach().insertAfter(this); + } + } + }); + + $(document).mouseup(function() { + if (movingElement) { + $(movingElement).css('background', ''); + movingElement = null; + $('#redirect-list').css('cursor', ''); + var newOrder = {}; + $('#redirect-list li').each(function(i) { + newOrder[$(this).data('redirect')] = i; + }); + + Redirector.sortRedirects(function(a,b) { + return newOrder[a] - newOrder[b]; + }); + } + }); + + $('#config input[type="checkbox"]').bind('CheckboxStateChange', function() { + var pref = $(this).attr('data-pref'); + prefs[pref] = !!$(this).attr('checked'); + }); +}); + +/* + 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);} + } + + 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; + } + } + } + + +*/ \ No newline at end of file diff --git a/chrome/js/redirector.js b/chrome/js/redirector.js new file mode 100644 index 0000000..21268e6 --- /dev/null +++ b/chrome/js/redirector.js @@ -0,0 +1,366 @@ +Components.utils.import("chrome://redirector/content/js/xpcom.js"); +Components.utils.import("chrome://redirector/content/js/redirect.js"); +Components.utils.import("chrome://redirector/content/js/redirectorprefs.js"); +Components.utils.import("chrome://redirector/content/js/proxyserver.js"); + +var EXPORTED_SYMBOLS = ['Redirector', 'rdump']; + +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._list.push(redirect); + this.save(); + }, + + debug : function(msg) { + if (this._prefs.debugEnabled) { + this._cout.logStringMessage('REDIRECTOR: ' + msg); + } + }, + + deleteRedirect : function(redirect) { + this._list.splice(this._list.indexOf(redirect), 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); + var rjson = { createdBy : 'Redirector v' + this._prefs.version, createdAt : new Date(), redirects :[]}; + for each (var re in this._list) { + rjson.redirects.push(re.toObject()); + } + stream.writeString(JSON.stringify(rjson, null, 4)); + 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; + }, + + _getRedirectsFile : function() { + var file = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile); + file.append('redirector.rjson'); + return file; + }, + + handleUpgrades : function(){ + var currentVersion = '2.6'; + this._list = []; + + if (this._prefs.version == currentVersion) { + return; + } + //Here update checks are handled + + try { + var branch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.redirector."); + var data = branch.getCharPref("redirects"); + } catch(e) { + this._prefs.version = currentVersion; + return; + } + var arr; + this._list = []; + if (data != '') { + for each (redirectString in data.split(':::')) { + if (!redirectString || !redirectString.split) { + continue; + rdump('Invalid old redirect: ' + redirectString); + } + var parts = redirectString.split(',,,'); + if (parts.length < 5) { + throw Error("Invalid serialized redirect, too few fields: " + redirectString); + } + var redirect = new Redirect(); + redirect._init.apply(redirect, parts); + this._list.push(redirect); + } + this.save(); + this._list = []; //Let the real loading start this properly + } + branch.deleteBranch('redirects'); + this._prefs.version = currentVersion; + }, + + importRedirects : function(file) { + var fileStream = new FileInputStream(file, 0x01, 0444, 0); + var stream = new ConverterInputStream(fileStream, "UTF-8", 16384, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + var str = {}; + var rjson = ''; + while (stream.readString(4096, str) != 0) { + rjson += str.value; + } + stream.close(); + var importCount = 0, existsCount = 0; + rjson = JSON.parse(rjson); + for each (var rd in rjson.redirects) { + var redirect = new Redirect(); + redirect.fromObject(rd); + if (this._containsRedirect(redirect)) { + existsCount++; + } else { + this._list.push(redirect); + importCount++; + } + } + this.save(); + return importCount | (existsCount << 16); + }, + + save : function() { + var file = this._getRedirectsFile(); + this.exportRedirects(file); + }, + + sortRedirects : function(sortFunc) { + this._list.sort(sortFunc); + 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; + 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. + 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(); + this._list = []; + this.handleUpgrades(); + var redirectsFile = this._getRedirectsFile(); + if (redirectsFile.exists()) { + this.importRedirects(redirectsFile); + } + + RedirectorProxy.start(this._prefs.proxyServerPort); + rdump('Registering as Proxy Filter'); + //var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(Ci.nsIProtocolProxyService); + //pps.registerFilter(this, 0); + }, + + _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); + }, + + _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/js/redirectorprefs.js b/chrome/js/redirectorprefs.js new file mode 100644 index 0000000..75ef936 --- /dev/null +++ b/chrome/js/redirectorprefs.js @@ -0,0 +1,76 @@ +var EXPORTED_SYMBOLS = ['RedirectorPrefs']; + +function RedirectorPrefs() { + this.init(); +} + +RedirectorPrefs.prototype = { + + _listeners : null, + 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(addWatch) { + var b = this._prefBranch; + for each (var name in b.getChildList('')){ + this.unwatch(name); + var type = b.getPrefType(name); + if (type == b.PREF_STRING) { + this[name] = b.getCharPref(name); + } else if (type == b.PREF_INT) { + this[name] = b.getIntPref(name); + } else if (type == b.PREF_BOOL) { + this[name] = b.getBoolPref(name); + } + + this.watch(name, function(id,oldval,newval) { + var type = b.getPrefType(id); + if (type == b.PREF_STRING) { + b.setCharPref(id,newval); + } else if (type == b.PREF_INT) { + b.setIntPref(id, newval); + } else if (type == b.PREF_BOOL) { + dump(id+ ' ' + newval) + b.setBoolPref(id, newval); + } + return newval; + }); + } + }, + + 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(false); + 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++) { + this._listeners.splice(i,1); + if (this._listeners[i] == listener) { + return; + } + } + }, +} \ No newline at end of file diff --git a/chrome/js/settings.js b/chrome/js/settings.js deleted file mode 100644 index c12562b..0000000 --- a/chrome/js/settings.js +++ /dev/null @@ -1,162 +0,0 @@ -Components.utils.import("chrome://redirector/content/code/redirector.js"); -Components.utils.import("chrome://redirector/content/code/redirectorprefs.js"); -Components.utils.import("chrome://redirector/content/code/xpcom.js"); - -$(document).ready(function() { - var prefs = new RedirectorPrefs(); - var strings = StringBundleService.createBundle('chrome://redirector/locale/redirector.properties', LocaleService.getApplicationLocale()); - function tr(name) { - return strings.GetStringFromName(name); - } - - function trPlural(name, count) { - name += count == 1 ? 'Singular' : ''; - return strings.formatStringFromName(name, [count],1); - } - - function getFile(captionKey, mode) { - var picker = new FilePicker(window, tr(captionKey), mode); - picker.defaultExtension = ".rjson"; - var dir = prefs.defaultDir; - if (dir) { - picker.displayDirectory = new LocalFile(dir); - } - picker.appendFilter(tr('redirectorFiles'), '*.rjson'); - - if (picker.show() == picker.returnCancel) { - return null; - } - prefs.defaultDir = picker.displayDirectory.path; - return picker.file; - } - - function exportRedirects() { - var file = getFile('exportCaption', FilePickerMode.save); - if (file) { - Redirector.exportRedirects(file); - } - } - - function importRedirects() { - var file = getFile('importCaption', FilePickerMode.open); - var result; - if (!file) { - return; - } - result = Redirector.importRedirects(file); - var msg, imported, existed; - imported = result & 0xFFFF; - existed = result >> 16; - - if (imported > 0) { - msg = trPlural('importedMessage', imported); - if (existed > 0) { - msg += ', ' + trPlural('existedMessage',existed); - } else { - msg += '.'; - } - } else if (imported == 0 && existed > 0) { - msg = trPlural('allExistedMessage', existed); - } else { //Both 0 - msg = tr('importedNone'); - } - - var title = tr('importResult'); - PromptService.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); - } - } - - var template = $('#redirect-list').html().replace(/^\s*|\s$/g, ''); - function databind() { - $('#redirect-list').empty(); - for (var i = 0; i < Redirector.redirectCount; i++) { - var redirect = Redirector.getRedirectAt(i); - var node = $(template); - node.find('.pattern').html(redirect.includePattern); - node.find('.redirectTo').html(redirect.redirectUrl); - node.find('.exampleUrl').html(redirect.exampleUrl); - node.find('.redirectResult').html(redirect.getMatch(redirect.exampleUrl).redirectTo); - node.appendTo('#redirect-list'); - node.data('redirect', redirect); - } - - } - - $('#redirect-list li div a.delete').live('click', function(ev) { - var redirect = $(this.parentNode.parentNode).data('redirect'); - if (PromptService.confirm(null, tr("deleteConfirmationTitle"), tr("deleteConfirmationText"))) { - Redirector.deleteRedirect(redirect); - $(this.parentNode.parentNode).fadeOut(function() { $(this).remove(); }); - } - ev.preventDefault(); - }); - - $('#redirect-list li div a.edit').live('click', function(ev) { - var redirect = $(this.parentNode.parentNode).data('redirect'); - $('#redirect-form').show(); - ev.preventDefault(); - }); - - databind(); - $('#import').click(importRedirects); - $('#export').click(exportRedirects); - - function bindConfig() { - $('#config input[type="checkbox"]').each(function() { - var pref = $(this).attr('data-pref'); - $(this).attr('checked', prefs[pref]); - }); - } - - bindConfig(); - prefs.addListener({ changedPrefs:bindConfig}); - - var movingElement = null; - $('li').mousedown(function(ev) { - if (ev.target && ev.target.tagName == 'A') { - return; - } - $(this).css('background', '-moz-linear-gradient(top, #aac, #99b)'); - $('#redirect-list').css('cursor', 'move'); - movingElement = this; - }); - - - $('li').mouseover(function() { - if (movingElement && this !== movingElement) { - if ($(movingElement).offset().top > $(this).offset().top) { - $(movingElement).detach().insertBefore(this); - } else { - $(movingElement).detach().insertAfter(this); - } - } - }); - - $(document).mouseup(function() { - if (movingElement) { - $(movingElement).css('background', ''); - movingElement = null; - $('#redirect-list').css('cursor', ''); - var newOrder = {}; - $('#redirect-list li').each(function(i) { - newOrder[$(this).data('redirect')] = i; - }); - - Redirector.sortRedirects(function(a,b) { - return newOrder[a] - newOrder[b]; - }); - } - }); - - $('#config input[type="checkbox"]').bind('CheckboxStateChange', function() { - var pref = $(this).attr('data-pref'); - prefs[pref] = !!$(this).attr('checked'); - }); -}); \ No newline at end of file diff --git a/chrome/js/xpcom.js b/chrome/js/xpcom.js new file mode 100644 index 0000000..e81f58e --- /dev/null +++ b/chrome/js/xpcom.js @@ -0,0 +1,28 @@ + +Ci = Components.interfaces; +Cc = Components.classes; +Cr = Components.results; + +const ServerSocket = Components.Constructor("@mozilla.org/network/server-socket;1", "nsIServerSocket", "init"); +const ScriptableInputStream = Components.Constructor("@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", "init"); +const FileInputStream = Components.Constructor("@mozilla.org/network/file-input-stream;1", "nsIFileInputStream", "init"); +const ConverterInputStream = Components.Constructor("@mozilla.org/intl/converter-input-stream;1", "nsIConverterInputStream", "init"); +const LocalFile = Components.Constructor("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath"); +const FilePicker = Components.Constructor("@mozilla.org/filepicker;1", "nsIFilePicker", "init"); +const FilePickerMode = { save : Ci.nsIFilePicker.modeSave, open : Ci.nsIFilePicker.modeOpen }; + +function Service(className, interfaceName) { + return Cc[className].getService(Ci[interfaceName]); +} + +const PromptService = Service("@mozilla.org/embedcomp/prompt-service;1","nsIPromptService"); +const IOService = Service("@mozilla.org/network/io-service;1","nsIIOService"); +const LocaleService = Service("@mozilla.org/intl/nslocaleservice;1", "nsILocaleService"); +const StringBundleService = Service("@mozilla.org/intl/stringbundle;1", "nsIStringBundleService"); + +var EXPORTED_SYMBOLS = []; +for (var name in this) { + if (name != 'Service' && name != 'QueryInterface' && name != 'name' && name != 'EXPORTED_SYMBOLS') { + EXPORTED_SYMBOLS.push(name); + } +} diff --git a/chrome/redirector.html b/chrome/redirector.html new file mode 100644 index 0000000..9bb6cc7 --- /dev/null +++ b/chrome/redirector.html @@ -0,0 +1,71 @@ + + + + Redirector Extension Settings + + + + + +

Redirector

+
Go where YOU want!
+
+ + + + +
+
    +
  • +
    Redirect: pattern to
    +
    Example: becomes
    +
    + Edit + Delete +
    +
  • +
+
+

Configuration

+ Enabled + Statusbar icon + Context menu + Debug output +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + Regular Expression + Wildcard +
+
+ + +
+
+ + +
+
+ + \ No newline at end of file diff --git a/chrome/settings.html b/chrome/settings.html deleted file mode 100644 index 43318bf..0000000 --- a/chrome/settings.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - Redirector Extension Settings - - - - - -

Redirector

-
Go where YOU want!
-
- - - - -
-
    -
  • -
    Redirect: pattern to
    -
    Example: becomes
    -
    - Edit - Delete -
    -
  • -
-
-

Configuration

- Enabled - Statusbar icon - Context menu - Debug output -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - Regular Expression - Wildcard -
-
- - -
-
- - -
-
- - \ No newline at end of file diff --git a/chrome/ui/browserOverlay.xul b/chrome/ui/browserOverlay.xul deleted file mode 100644 index 32bf1d5..0000000 --- a/chrome/ui/browserOverlay.xul +++ /dev/null @@ -1,34 +0,0 @@ - - - - - + + +

Redirector Unit Tests

+ + +
+
+ + \ No newline at end of file diff --git a/chrome/unittest/testcases.js b/chrome/unittest/testcases.js new file mode 100644 index 0000000..0c0c10c --- /dev/null +++ b/chrome/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/locale/en-US/browserOverlay.dtd b/locale/en-US/browserOverlay.dtd new file mode 100644 index 0000000..3aaa0fc --- /dev/null +++ b/locale/en-US/browserOverlay.dtd @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/locale/en-US/browserOverlay.xul.dtd b/locale/en-US/browserOverlay.xul.dtd deleted file mode 100644 index 3aaa0fc..0000000 --- a/locale/en-US/browserOverlay.xul.dtd +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - -- cgit v1.2.3-70-g09d2