[Pkg-mozext-commits] [requestpolicy] 42/280: current dev state for e10s

David Prévot taffit at moszumanska.debian.org
Sat May 2 20:29:57 UTC 2015


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

taffit pushed a commit to branch master
in repository requestpolicy.

commit 8c27a0ecfd1b4a64ed7f213a4cd38c8594f4a332
Author: Martin Kimmerle <dev at 256k.de>
Date:   Tue Dec 2 23:33:34 2014 +0100

    current dev state for e10s
---
 src/bootstrap.js                                   |   4 +-
 src/content/lib/about-uri.jsm                      | 106 +++
 src/content/lib/constants.jsm                      |   2 +
 src/content/lib/content-policy.jsm                 |   4 +-
 src/content/lib/domain-util.jsm                    |   8 +-
 src/content/lib/logger.jsm                         | 126 +++-
 src/content/lib/policy-manager.jsm                 |   6 +-
 src/content/lib/pref-manager.js                    | 184 +++++
 src/content/lib/prefs.jsm                          | 242 ++-----
 src/content/lib/request-processor.jsm              | 154 +++--
 src/content/lib/request.jsm                        |   6 +-
 src/content/lib/requestpolicy-service.jsm          |  31 +-
 src/content/lib/script-loader.jsm                  |  44 +-
 src/content/lib/string-utils.jsm                   |  65 ++
 src/content/lib/utils.jsm                          |  65 +-
 src/content/lib/utils/process-info.jsm             |  36 +
 src/content/lib/window-manager.jsm                 |  16 +-
 src/content/lib/xul-utils.jsm                      |   4 +-
 src/content/settings/advancedprefs.html            |  17 +-
 src/content/settings/advancedprefs.js              |  32 +-
 src/content/settings/basicprefs.html               |  17 +-
 src/content/settings/basicprefs.js                 |  21 +-
 src/content/settings/common.js                     |  13 +-
 src/content/settings/defaultpolicy.html            |  23 +-
 src/content/settings/defaultpolicy.js              |  10 +-
 src/content/settings/oldrules.html                 |  13 +-
 src/content/settings/setup.html                    |  11 +-
 src/content/settings/setup.js                      |  35 +-
 src/content/settings/subscriptions.html            |  25 +-
 src/content/settings/subscriptions.js              |   4 +-
 src/content/settings/yourpolicy.html               |  19 +-
 src/content/ui/classicmenu.js                      |  19 +-
 src/content/ui/frame.blocked-content.js            | 101 +++
 src/content/ui/frame.doc-manager.js                |  53 ++
 src/content/ui/frame.dom-content-loaded.js         | 261 +++++++
 src/content/ui/frame.js                            |  96 +++
 src/content/ui/menu.js                             |  31 +-
 src/content/ui/overlay.js                          | 759 ++++++---------------
 src/content/ui/request-log-tree-view.js            |   8 +-
 src/content/ui/request-log.js                      |   9 +-
 src/install.rdf                                    |   2 +-
 tests/content/iframe_3.html                        |   6 +-
 tests/mozmill/lib/domain-util.js                   |  18 +
 tests/mozmill/tests/testPolicy/lib/iframe.js       |  26 +-
 .../tests/testPolicy/lib/num-request-counter.js    | 117 ++++
 .../testPolicy/lib/temporary-rule-iterator.js      |  31 +
 tests/mozmill/tests/testPolicy/manifest.ini        |   3 +-
 .../tests/testPolicy/testIframeTree/manifest.ini   |   1 +
 .../testIframeTree/testDefaultPolicies.js          |   3 +-
 ...estDefaultPolicies.js => testTemporaryRules.js} |  44 +-
 .../mozmill/tests/testRedirect/testAutoRedirect.js |   6 +-
 .../tests/testRedirect/testLinkClickRedirect.js    |   4 +-
 52 files changed, 1902 insertions(+), 1039 deletions(-)

diff --git a/src/bootstrap.js b/src/bootstrap.js
index 70d9296..f6f7dbe 100644
--- a/src/bootstrap.js
+++ b/src/bootstrap.js
@@ -21,7 +21,8 @@ let bootstrapper = (function() {
   let managers = {
     // id     :   object name of the manager
     'requestpolicy-service': 'rpService',
-    'window-manager': 'rpWindowManager'
+    'window-manager': 'rpWindowManager',
+    'about-uri': 'AboutRequestPolicy'
   };
 
   /**
@@ -70,6 +71,7 @@ let bootstrapper = (function() {
       ScriptLoader.importModule("logger", globalScope);
       ScriptLoader.importModule("requestpolicy-service", globalScope);
       ScriptLoader.importModule("window-manager", globalScope);
+      ScriptLoader.importModule("about-uri", globalScope);
     },
 
     finish: function() {
diff --git a/src/content/lib/about-uri.jsm b/src/content/lib/about-uri.jsm
new file mode 100644
index 0000000..14480fa
--- /dev/null
+++ b/src/content/lib/about-uri.jsm
@@ -0,0 +1,106 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+
+let EXPORTED_SYMBOLS = ["AboutRequestPolicy"];
+
+var filenames = {
+  "basicprefs": "basicprefs.html",
+  "advancedprefs": "advancedprefs.html",
+  "yourpolicy": "yourpolicy.html",
+  "defaultpolicy": "defaultpolicy.html",
+  "subscriptions": "subscriptions.html",
+  "setup": "setup.html"
+};
+
+function getURI(aURI) {
+  let id;
+  let index = aURI.path.indexOf("?");
+  dump("path: "+aURI.path+"\n");
+  if (index >= 0 && aURI.path.length > index) {
+    id = aURI.path.substr(index+1);
+    dump("id: "+id+"\n");
+  }
+  if (!id || !(id in filenames)) {
+    id = "basicprefs";
+  }
+  return "chrome://requestpolicy/content/settings/" + filenames[id];
+}
+
+
+
+let AboutRequestPolicy = (function() {
+  let self = {
+    classDescription: "about:requestpolicy",
+    contractID: "@mozilla.org/network/protocol/about;1?what=requestpolicy",
+    classID: Components.ID("{ad30f46c-42a6-45cd-ad0b-08b37f87435a}"),
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
+
+    getURIFlags: function(aURI) {
+      return Ci.nsIAboutModule.ALLOW_SCRIPT;
+    },
+
+    newChannel: function(aURI) {
+      let uri = getURI(aURI)
+      let channel = Services.io.newChannel(uri, null, null);
+      channel.originalURI = aURI;
+      return channel;
+    },
+
+
+
+    startup: function() {
+      Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+          .registerFactory(self.classID, self.classDescription, self.contractID,
+              self);
+    },
+
+    shutdown: function() {
+      let registrar = Components.manager
+          .QueryInterface(Ci.nsIComponentRegistrar);
+      // This needs to run asynchronously, see bug 753687
+      Utils.runAsync(function() {
+        registrar.unregisterFactory(self.classID, self);
+      });
+    },
+
+    //
+    // nsIFactory interface implementation
+    //
+
+    createInstance: function(outer, iid) {
+      if (outer) {
+        throw Cr.NS_ERROR_NO_AGGREGATION;
+      }
+      return self.QueryInterface(iid);
+    }
+  }
+  return self;
+}());
diff --git a/src/content/lib/constants.jsm b/src/content/lib/constants.jsm
index 4d2ac13..9f450ed 100644
--- a/src/content/lib/constants.jsm
+++ b/src/content/lib/constants.jsm
@@ -28,6 +28,7 @@ const Cu = Components.utils;
 let EXPORTED_SYMBOLS = [
   "EXTENSION_ID",
   "FIREFOX_ID",
+  "MMID",
 
   "APP_STARTUP",
   "APP_SHUTDOWN",
@@ -47,6 +48,7 @@ let EXPORTED_SYMBOLS = [
 
 const EXTENSION_ID = "requestpolicy at requestpolicy.com";
 const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+const MMID = EXTENSION_ID; // message manager ID
 
 // reason constants for startup(), shutdown(), install() and uninstall()
 // see https://developer.mozilla.org/en-US/Add-ons/Bootstrapped_extensions#Reason_constants
diff --git a/src/content/lib/content-policy.jsm b/src/content/lib/content-policy.jsm
index a9bb54d..0dcf7d5 100644
--- a/src/content/lib/content-policy.jsm
+++ b/src/content/lib/content-policy.jsm
@@ -101,7 +101,7 @@ let PolicyImplementation = (function() {
 
     // https://developer.mozilla.org/en/nsIContentPolicy
 
-    shouldLoad : function(aContentType, aContentLocation, aRequestOrigin,
+    shouldLoad: function(aContentType, aContentLocation, aRequestOrigin,
         aContext, aMimeTypeGuess, aExtra, aRequestPrincipal) {
       var request = new NormalRequest(
           aContentType, aContentLocation, aRequestOrigin, aContext,
@@ -112,7 +112,7 @@ let PolicyImplementation = (function() {
       //     aContext, aMimeTypeGuess, aExtra, aRequestPrincipal);
     },
 
-    shouldProcess : (() => CP_OK),
+    shouldProcess: (() => CP_OK),
 
     //
     // nsIFactory interface implementation
diff --git a/src/content/lib/domain-util.jsm b/src/content/lib/domain-util.jsm
index 5aab65d..6860898 100644
--- a/src/content/lib/domain-util.jsm
+++ b/src/content/lib/domain-util.jsm
@@ -293,9 +293,9 @@ DomainUtil.destinationIsSubdomainOfOrigin = function(destinationHost,
  *
  * @param {String}
  *          refreshString The original content of a refresh header or meta tag.
- * @return {Array} First element is the delay in seconds, second element is the
- *         url to refresh to. The url may be an empty string if the current url
- *         should be refreshed.
+ * @return {Object} The delay in seconds and the url to refresh to.
+ *                  The url may be an empty string if the current url should be
+ *                  refreshed.
  * @throws Generic
  *           exception if the refreshString has an invalid format, including if
  *           the seconds can't be parsed as a float.
@@ -318,7 +318,7 @@ DomainUtil.parseRefresh = function(refreshString) {
       url = url.substring(1, url.length - 1);
     }
   }
-  return [delay, url];
+  return {delay: delay, destURI: url};
 }
 
 /**
diff --git a/src/content/lib/logger.jsm b/src/content/lib/logger.jsm
index 79a48b3..34d528f 100644
--- a/src/content/lib/logger.jsm
+++ b/src/content/lib/logger.jsm
@@ -29,37 +29,18 @@ let EXPORTED_SYMBOLS = ["Logger"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 
+
+// We can't import the ScriptLoader when the Logger is loaded, because the
+// Logger gets imported by the ScriptLoader itself on startup.
+let ScriptLoader = null;
+
+
 /**
  * Provides logging methods
  */
 let Logger = (function() {
-  // private variables and functions
-
-  function doLog(level, type, message, e) {
-    if (self.enabled && level >= self.level && self.types & type) {
-      let levelName = self._LEVEL_NAMES[level.toString()];
-      let typeName = self._TYPE_NAMES[type.toString()];
-
-      let stack = (e && e.stack) ? ", stack was:\n" + e.stack : "";
-      self.printFunc("[RequestPolicy] [" + levelName + "] [" + typeName + "] "
-          + message + stack + "\n");
-
-      if (level == self.LEVEL_SEVERE && type == self.TYPE_ERROR) {
-        let windowtype = 'navigator:browser';
-        let mostRecentWindow  = Services.wm.getMostRecentWindow(windowtype);
-
-        if (mostRecentWindow) {
-          mostRecentWindow.alert("Sorry, RequestPolicy crashed! " + message);
-        }
-      }
-    }
-  }
-
-
 
   let self = {
-    // public attributes and methods
-
     TYPE_CONTENT: 1, // content whose origin isn't known more specifically
     TYPE_META_REFRESH: 2, // info related to meta refresh
     TYPE_HEADER_REDIRECT: 4, // info related to header redirects
@@ -90,15 +71,98 @@ let Logger = (function() {
   self._LEVEL_NAMES[self.LEVEL_INFO.toString()] = "INFO";
   self._LEVEL_NAMES[self.LEVEL_DEBUG.toString()] = "DEBUG";
 
+  // function to use to print out the log
+  self.printFunc = dump;
+
+
+
+
+  let initialized = false;
+  let rpPrefBranch = null;
+
+  // initially, enable logging. later the logging preferences of the user will
+  // will be loaded.
+  let enabled = true;
   // These can be set to change logging level, what types of messages are
   // logged, and to enable/disable logging.
-  self.level = self.LEVEL_INFO;
-  self.types = self.TYPE_ALL;
-  // enable logging before RP finished initializing
-  self.enabled = true;
+  let level = self.LEVEL_INFO;
+  let types = self.TYPE_ALL;
+
+  function updateLoggingSettings(rp) {
+    enabled = rpPrefBranch.getBoolPref("log");
+    level = rpPrefBranch.getIntPref("log.level");
+    types = rpPrefBranch.getIntPref("log.types");
+  }
+
+  let prefObserver = {
+    observe: function(subject, topic, data) {
+      if (topic == "nsPref:changed") {
+        updateLoggingSettings();
+      }
+    }
+  };
+
+  function displayMessageNotInitiallized() {
+    dump("[RequestPolicy] [INFO] [INTERNAL] preferences are not available " +
+         "yet, so logging is still enabled.\n");
+  }
+
+  function init() {
+    if (!ScriptLoader) {
+      // try to import the ScriptLoader
+      try {
+        // We can't import the ScriptLoader when the Logger is loaded, because the
+        // Logger gets imported by the ScriptLoader itself on startup.
+        let mod = {};
+        Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+        ScriptLoader = mod.ScriptLoader;
+      } catch (e) {
+        ScriptLoader = null;
+        displayMessageNotInitiallized();
+        return;
+      }
+    }
+    let prefScope = ScriptLoader.importModule("prefs");
+    if (!("rpPrefBranch" in prefScope)) {
+      displayMessageNotInitiallized();
+      return;
+    }
+    rpPrefBranch = prefScope.rpPrefBranch;
+    initialized = true;
+    rpPrefBranch.addObserver("log", prefObserver, false);
+    updateLoggingSettings();
+  }
+
+
+
+
+  function doLog(aLevel, aType, aMessage, aError) {
+    if (!initialized) {
+      init();
+    }
+
+    if (enabled && aLevel >= level && types & aType) {
+      let levelName = self._LEVEL_NAMES[aLevel.toString()];
+      let typeName = self._TYPE_NAMES[aType.toString()];
+
+      let stack = (aError && aError.stack) ?
+                  ", stack was:\n" + aError.stack : "";
+      self.printFunc("[RequestPolicy] [" + levelName + "] [" + typeName + "] "
+          + aMessage + stack + "\n");
+
+      // TODO: remove the following after finishing e10s
+      if (aLevel == self.LEVEL_SEVERE && aType == self.TYPE_ERROR) {
+        let windowtype = 'navigator:browser';
+        let mostRecentWindow  = Services.wm.getMostRecentWindow(windowtype);
+
+        if (mostRecentWindow) {
+          mostRecentWindow.alert("Sorry, RequestPolicy crashed! " + message);
+        }
+      }
+    }
+  }
+
 
-  // function to use to print out the log
-  self.printFunc = dump;
 
   self.severe = doLog.bind(self, self.LEVEL_SEVERE);
   self.severeError = doLog.bind(self, self.LEVEL_SEVERE, self.TYPE_ERROR);
diff --git a/src/content/lib/policy-manager.jsm b/src/content/lib/policy-manager.jsm
index a6b6daf..62d44a2 100644
--- a/src/content/lib/policy-manager.jsm
+++ b/src/content/lib/policy-manager.jsm
@@ -249,7 +249,7 @@ let PolicyManager = (function(self) {
     assertRuleAction(ruleAction);
     // TODO: check rule format validity
     userRulesets["temp"].rawRuleset.addRule(ruleAction, ruleData,
-        userRulesets["temp"].ruleset);
+          userRulesets["temp"].ruleset);
 
     //userRulesets["temp"].ruleset.print();
 
@@ -266,9 +266,9 @@ let PolicyManager = (function(self) {
     // TODO: check rule format validity
     // TODO: use noStore
     userRulesets["user"].rawRuleset.removeRule(ruleAction, ruleData,
-        userRulesets["user"].ruleset);
+          userRulesets["user"].ruleset);
     userRulesets["temp"].rawRuleset.removeRule(ruleAction, ruleData,
-        userRulesets["temp"].ruleset);
+          userRulesets["temp"].ruleset);
 
     // TODO: only save if we actually removed a rule. This will require
     // modifying |RawRuleset.removeRule()| to indicate whether a rule
diff --git a/src/content/lib/pref-manager.js b/src/content/lib/pref-manager.js
new file mode 100644
index 0000000..994d479
--- /dev/null
+++ b/src/content/lib/pref-manager.js
@@ -0,0 +1,184 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2011 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+const MMID = "requestpolicy at requestpolicy.com";
+
+let EXPORTED_SYMBOLS = [];
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ScriptLoader.importModules(["logger"], this);
+
+
+
+let prefInitFunctions = {
+  getGenericPref: function(branch, prefName) {
+    switch (branch.getPrefType(prefName)) {
+      case 32:
+        // PREF_STRING
+        return this.getUCharPref(prefName, branch);
+
+      case 64:
+        // PREF_INT
+        return branch.getIntPref(prefName);
+
+      case 128:
+        // PREF_BOOL
+        return branch.getBoolPref(prefName);
+
+      case 0:
+      default:
+        // PREF_INVALID
+        return undefined;
+    }
+  },
+
+  setGenericPref: function(branch, prefName, prefValue) {
+    switch (typeof prefValue) {
+      case "string":
+        this.setUCharPref(prefName, prefValue, branch);
+        return;
+      case "number":
+        branch.setIntPref(prefName, prefValue);
+        return;
+      case "boolean":
+        branch.setBoolPref(prefName, prefValue);
+        return;
+    }
+  },
+
+  setDefaultPref: function(prefName, prefValue) {
+    var defaultBranch = Services.prefs.getDefaultBranch(null);
+    this.setGenericPref(defaultBranch, prefName, prefValue);
+  },
+
+  getUCharPref: function(prefName, branch) {  // Unicode getCharPref
+    branch = branch || Services.prefs;
+    return branch.getComplexValue(prefName, Ci.nsISupportsString).data;
+  },
+
+  setUCharPref: function(prefName, text, branch) { // Unicode setCharPref
+    var string = Cc["@mozilla.org/supports-string;1"]
+        .createInstance(Ci.nsISupportsString);
+    string.data = text;
+    branch = branch || Services.prefs;
+    branch.setComplexValue(prefName, Ci.nsISupportsString, string);
+  }
+};
+
+let defaultPrefScriptScope = {
+  pref: prefInitFunctions.setDefaultPref,
+  setGenericPref: prefInitFunctions.setGenericPref,
+  setUCharPref: prefInitFunctions.setUCharPref
+};
+
+
+//
+// Load default preferences (if necessary)
+//
+
+try {
+  // this is necessary for restartless extensions:
+  // ( See https://developer.mozilla.org/en-US/Add-ons/
+  //   How_to_convert_an_overlay_extension_to_restartless
+  //   #Step_4.3A_Manually_handle_default_preferences )
+  Services.scriptloader.loadSubScript(
+      "chrome://requestpolicy/content/lib/default-preferences.js",
+      defaultPrefScriptScope);
+} catch (e) {}
+
+
+//
+// Do what else to do on startup.
+//
+
+var rpPrefBranch = Services.prefs.getBranch("extensions.requestpolicy.")
+    .QueryInterface(Ci.nsIPrefBranch2);
+var rootPrefBranch = Services.prefs.getBranch("")
+    .QueryInterface(Ci.nsIPrefBranch2);
+
+// Disable link prefetch.
+if (rpPrefBranch.getBoolPref("prefetch.link.disableOnStartup")) {
+  if (rootPrefBranch.getBoolPref("network.prefetch-next")) {
+    rootPrefBranch.setBoolPref("network.prefetch-next", false);
+    Logger.info(Logger.TYPE_INTERNAL, "Disabled link prefetch.");
+  }
+}
+// Disable DNS prefetch.
+if (rpPrefBranch.getBoolPref("prefetch.dns.disableOnStartup")) {
+  // network.dns.disablePrefetch only exists starting in Firefox 3.1 (and it
+  // doesn't have a default value, at least in 3.1b2, but if and when it
+  // does have a default it will be false).
+  if (!rootPrefBranch.prefHasUserValue("network.dns.disablePrefetch") ||
+      !rootPrefBranch.getBoolPref("network.dns.disablePrefetch")) {
+    rootPrefBranch.setBoolPref("network.dns.disablePrefetch", true);
+    Logger.info(Logger.TYPE_INTERNAL, "Disabled DNS prefetch.");
+  }
+}
+
+// Clean up old, unused prefs (removed in 0.2.0).
+let deletePrefs = [
+  "temporarilyAllowedOrigins",
+  "temporarilyAllowedDestinations",
+  "temporarilyAllowedOriginsToDestinations"
+];
+for (var i = 0; i < deletePrefs.length; i++) {
+  if (rpPrefBranch.prefHasUserValue(deletePrefs[i])) {
+    rpPrefBranch.clearUserPref(deletePrefs[i]);
+  }
+}
+Services.prefs.savePrefFile(null);
+
+
+function setPref(aPrefBranch, aPrefName, aDataType, aValue) {
+  let functionName;
+  switch (aDataType) {
+    case Services.prefs.PREF_BOOL:
+      functionName = "setBoolPref";
+      break;
+    case Services.prefs.PREF_INT:
+      functionName = "setIntPref";
+      break;
+    case Services.prefs.PREF_STRING:
+    default:
+      functionName = "setCharPref";
+      break;
+  }
+  aPrefBranch[functionName](aPrefName, aValue);
+}
+
+let globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
+    .getService(Ci.nsIMessageListenerManager);
+
+globalMM.addMessageListener(MMID + ":setPref", function(message) {
+    let {name, dataType, value} = message.data;
+    setPref(rpPrefBranch, name, dataType, value);
+  });
+globalMM.addMessageListener(MMID + ":setRootPref", function(message) {
+    let {name, dataType, value} = message.data;
+    setPref(rootPrefBranch, name, dataType, value);
+  });
diff --git a/src/content/lib/prefs.jsm b/src/content/lib/prefs.jsm
index 8b62bfd..8e828b0 100644
--- a/src/content/lib/prefs.jsm
+++ b/src/content/lib/prefs.jsm
@@ -21,211 +21,46 @@
  * ***** END LICENSE BLOCK *****
  */
 
+debugger;
+
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
-let EXPORTED_SYMBOLS = ['prefs', 'prefsRoot', 'Prefs'];
+let EXPORTED_SYMBOLS = ['rpPrefBranch', 'rootPrefBranch', 'Prefs'];
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
-ScriptLoader.importModules(["logger"], this);
-
-
-let DefaultPrefInit = (function() {
-  function getGenericPref(branch, prefName) {
-    switch (branch.getPrefType(prefName)) {
-      case 32:
-        // PREF_STRING
-        return getUCharPref(prefName, branch);
+let {isMainProcess} = ScriptLoader.importModule("utils/process-info");
 
-      case 64:
-        // PREF_INT
-        return branch.getIntPref(prefName);
-
-      case 128:
-        // PREF_BOOL
-        return branch.getBoolPref(prefName);
-
-      case 0:
-      default:
-        // PREF_INVALID
-        return undefined;
-    }
-  }
-
-  function setGenericPref(branch, prefName, prefValue) {
-    switch (typeof prefValue) {
-      case "string":
-        setUCharPref(prefName, prefValue, branch);
-        return;
-      case "number":
-        branch.setIntPref(prefName, prefValue);
-        return;
-      case "boolean":
-        branch.setBoolPref(prefName, prefValue);
-        return;
-    }
-  }
+let prefManagerScope = {};
 
-  function setDefaultPref(prefName, prefValue) {
-    var defaultBranch = Services.prefs.getDefaultBranch(null);
-    setGenericPref(defaultBranch, prefName, prefValue);
-  }
 
-  function getUCharPref(prefName, branch) {  // Unicode getCharPref
-    branch = branch || Services.prefs;
-    return branch.getComplexValue(prefName, Ci.nsISupportsString).data;
-  }
+if (isMainProcess) {
+  // if it's the main process (the chrome process). We will get here on startup.
+  // initialize preferences:
+  let uri = "chrome://requestpolicy/content/lib/pref-manager.js";
+  Services.scriptloader.loadSubScript(uri, prefManagerScope);
+}
 
-  function setUCharPref(prefName, text, branch) { // Unicode setCharPref
-    var string = Cc["@mozilla.org/supports-string;1"]
-        .createInstance(Ci.nsISupportsString);
-    string.data = text;
-    branch = branch || Services.prefs;
-    branch.setComplexValue(prefName, Ci.nsISupportsString, string);
-  }
+let rpPrefBranch = Services.prefs.getBranch("extensions.requestpolicy.")
+    .QueryInterface(Ci.nsIPrefBranch2);
+let rootPrefBranch = Services.prefs.getBranch("")
+    .QueryInterface(Ci.nsIPrefBranch2);
 
-  let initialized = false;
 
-  let self = {
-    init: function() {
-      if (!initialized) {
-        initialized = true;
-        try {
-          // this is necessary for restartless extensions:
-          // ( See https://developer.mozilla.org/en-US/Add-ons/
-          //   How_to_convert_an_overlay_extension_to_restartless
-          //   #Step_4.3A_Manually_handle_default_preferences )
-          Services.scriptloader.loadSubScript(
-              "chrome://requestpolicy/content/lib/default-preferences.js",
-              {pref: setDefaultPref, setGenericPref: setGenericPref,
-                  setUCharPref: setUCharPref});
-        } catch (e) {
-        }
-      }
-    }
-  };
-  return self;
-}());
 
 
 let Prefs = (function() {
   let self = {};
 
-
   let defaultAllow = true;
   let defaultAllowSameDomain = true;
   let blockingDisabled = false;
 
 
-  function updateLoggingSettings() {
-    Logger.enabled = self.prefs.getBoolPref("log");
-    Logger.level = self.prefs.getIntPref("log.level");
-    Logger.types = self.prefs.getIntPref("log.types");
-  }
-
-  /**
-   * Take necessary actions when preferences are updated.
-   *
-   * @paramString{} prefName NAme of the preference that was updated.
-   */
-  function prefChanged(prefName) {
-    switch (prefName) {
-      case "log" :
-      case "log.level" :
-      case "log.types" :
-        updateLoggingSettings();
-        break;
-      case "defaultPolicy.allow" :
-        defaultAllow = self.prefs.getBoolPref("defaultPolicy.allow");
-        break;
-      case "defaultPolicy.allowSameDomain" :
-        defaultAllowSameDomain = self.prefs.getBoolPref(
-            "defaultPolicy.allowSameDomain");
-        break;
-      default :
-        break;
-    }
-    Services.obs.notifyObservers(null, "requestpolicy-prefs-changed", null);
-  }
-
-  function syncFromPrefs() {
-    // Load the logging preferences before the others.
-    updateLoggingSettings();
-
-    defaultAllow = self.prefs.getBoolPref("defaultPolicy.allow");
-    defaultAllowSameDomain = self.prefs.getBoolPref("defaultPolicy.allowSameDomain");
-    blockingDisabled = self.prefs.getBoolPref("startWithAllowAllEnabled");
-
-    // Disable link prefetch.
-    if (self.prefs.getBoolPref("prefetch.link.disableOnStartup")) {
-      if (self.prefsRoot.getBoolPref("network.prefetch-next")) {
-        self.prefsRoot.setBoolPref("network.prefetch-next", false);
-        Logger.info(Logger.TYPE_INTERNAL, "Disabled link prefetch.");
-      }
-    }
-    // Disable DNS prefetch.
-    if (self.prefs.getBoolPref("prefetch.dns.disableOnStartup")) {
-      // network.dns.disablePrefetch only exists starting in Firefox 3.1 (and it
-      // doesn't have a default value, at least in 3.1b2, but if and when it
-      // does have a default it will be false).
-      if (!self.prefsRoot.prefHasUserValue("network.dns.disablePrefetch") ||
-          !self.prefsRoot.getBoolPref("network.dns.disablePrefetch")) {
-        self.prefsRoot.setBoolPref("network.dns.disablePrefetch", true);
-        Logger.info(Logger.TYPE_INTERNAL, "Disabled DNS prefetch.");
-      }
-    }
-
-    // Clean up old, unused prefs (removed in 0.2.0).
-    let deletePrefs = [
-      "temporarilyAllowedOrigins",
-      "temporarilyAllowedDestinations",
-      "temporarilyAllowedOriginsToDestinations"
-    ];
-    for (var i = 0; i < deletePrefs.length; i++) {
-      if (self.prefs.prefHasUserValue(deletePrefs[i])) {
-        self.prefs.clearUserPref(deletePrefs[i]);
-      }
-    }
-    Services.prefs.savePrefFile(null);
-  }
-
-  let prefObserver = {
-    observe: function(subject, topic, data) {
-      switch (topic) {
-        case "nsPref:changed":
-          prefChanged(data);
-          break;
-        default:
-          break;
-      }
-    }
-  };
-
-  function init() {
-    // the DefaultPrefInit is needed in restartless addons. see:
-    // https://developer.mozilla.org/en-US/Add-ons/
-    // How_to_convert_an_overlay_extension_to_restartless
-    // #Step_4.3A_Manually_handle_default_preferences
-    DefaultPrefInit.init();
-
-    self.prefs = Services.prefs.getBranch("extensions.requestpolicy.")
-        .QueryInterface(Ci.nsIPrefBranch2);
-    self.prefs.addObserver("", prefObserver, false);
-
-    self.prefsRoot = Services.prefs.getBranch("")
-        .QueryInterface(Ci.nsIPrefBranch2);
-    self.prefsRoot.addObserver("network.prefetch-next", prefObserver, false);
-    self.prefsRoot.addObserver("network.dns.disablePrefetch", prefObserver,
-        false);
 
-    syncFromPrefs();
-  }
-
-  self.prefs = null;
-  self.prefsRoot = null;
   self.save = function() {
     Services.prefs.savePrefFile(null);
   };
@@ -241,23 +76,43 @@ let Prefs = (function() {
   };
   self.setBlockingDisabled = function(disabled) {
     blockingDisabled = disabled;
-    self.prefs.setBoolPref('startWithAllowAllEnabled', disabled);
+    rootPrefBranch.setBoolPref('startWithAllowAllEnabled', disabled);
     self.save();
   };
   self.isPrefetchEnabled = function() {
     // network.dns.disablePrefetch only exists starting in Firefox 3.1
     try {
-      return self.prefsRoot.getBoolPref("network.prefetch-next")
-          || !self.prefsRoot.getBoolPref("network.dns.disablePrefetch");
+      return rootPrefBranch.getBoolPref("network.prefetch-next")
+          || !rootPrefBranch.getBoolPref("network.dns.disablePrefetch");
     } catch (e) {
-      return self.prefsRoot.getBoolPref("network.prefetch-next");
+      return rootPrefBranch.getBoolPref("network.prefetch-next");
     }
   };
 
 
+  /**
+   * Take necessary actions when preferences are updated.
+   *
+   * @paramString{} prefName name of the preference that was updated.
+   */
+  function prefChanged(prefName) {
+    switch (prefName) {
+      case "defaultPolicy.allow" :
+        defaultAllow = rpPrefBranch.getBoolPref("defaultPolicy.allow");
+        break;
+      case "defaultPolicy.allowSameDomain" :
+        defaultAllowSameDomain = rpPrefBranch.getBoolPref(
+            "defaultPolicy.allowSameDomain");
+        break;
+      default:
+        break;
+    }
+    Services.obs.notifyObservers(null, "requestpolicy-prefs-changed", null);
+  }
+
   function isPrefEmpty(pref) {
     try {
-      let value = self.prefs.getComplexValue(pref, Ci.nsISupportsString).data;
+      let value = rpPrefBranch.getComplexValue(pref, Ci.nsISupportsString).data;
       return value == '';
     } catch (e) {
       return true;
@@ -270,6 +125,23 @@ let Prefs = (function() {
              isPrefEmpty('allowedOriginsToDestinations'));
   };
 
+  function init() {
+    defaultAllow = rpPrefBranch.getBoolPref("defaultPolicy.allow");
+    defaultAllowSameDomain = rpPrefBranch.getBoolPref("defaultPolicy.allowSameDomain");
+    blockingDisabled = rpPrefBranch.getBoolPref("startWithAllowAllEnabled");
+  }
+
+  let prefObserver = {
+    observe: function(subject, topic, data) {
+      if (topic == "nsPref:changed") {
+        prefChanged(data);
+      }
+    }
+  };
+
+  rpPrefBranch.addObserver("", prefObserver, false);
+  rootPrefBranch.addObserver("network.prefetch-next", prefObserver, false);
+  rootPrefBranch.addObserver("network.dns.disablePrefetch", prefObserver, false);
 
   init();
 
diff --git a/src/content/lib/request-processor.jsm b/src/content/lib/request-processor.jsm
index 77d0cde..a1b8266 100644
--- a/src/content/lib/request-processor.jsm
+++ b/src/content/lib/request-processor.jsm
@@ -23,6 +23,7 @@
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
+const Cr = Components.results;
 const Cu = Components.utils;
 
 let EXPORTED_SYMBOLS = ["RequestProcessor"];
@@ -44,6 +45,7 @@ ScriptLoader.importModules([
   "prefs",
   "policy-manager",
   "domain-util",
+  "utils",
   "request",
   "request-result",
   "request-set"
@@ -204,7 +206,42 @@ let RequestProcessor = (function() {
     try {
       if (!Prefs.isBlockingDisabled()) {
         httpChannel.setResponseHeader(headerType, "", false);
-        self._blockedRedirects[originURI] = destURI;
+
+        /* start - do not edit here */
+        let interfaceRequestor = httpChannel.notificationCallbacks
+            .QueryInterface(Ci.nsIInterfaceRequestor);
+        let loadContext = null;
+        try {
+          loadContext = interfaceRequestor.getInterface(Ci.nsILoadContext);
+        } catch (ex) {
+          try {
+            loadContext = aSubject.loadGroup.notificationCallbacks
+                .getInterface(Ci.nsILoadContext);
+          } catch (ex2) {}
+        }
+        /*end do not edit here*/
+
+
+        let browser;
+        try {
+          if (loadContext.topFrameElement) {
+            // the top frame element should be already the browser element
+            browser = loadContext.topFrameElement;
+          } else {
+            // we hope the associated window is available. in multiprocessor
+            // firefox it's not available.
+            browser = Utils.getBrowserForWindow(loadContext.topWindow);
+          }
+          // save all blocked redirects directly in the browser element. the
+          // blocked elements will be checked later when the DOM content
+          // finished loading.
+          browser.requestpolicy = browser.requestpolicy || {blockedRedirects: {}};
+          browser.requestpolicy.blockedRedirects[originURI] = destURI;
+        } catch (e) {
+          Logger.warning(Logger.TYPE_HEADER_REDIRECT, "The redirection's " +
+                         "Load Context couldn't be found! " + e);
+        }
+
 
         try {
           contentDisp = httpChannel.getResponseHeader("Content-Disposition");
@@ -235,15 +272,13 @@ let RequestProcessor = (function() {
         // that get set in the redirection process, to stop the redirection.
         var iterations = 0;
         const ASSUME_REDIRECT_LOOP = 100; // Chosen arbitrarily.
-        while (allowedRedirectsReverse[initialOrigin]) {
-          if (iterations++ >= ASSUME_REDIRECT_LOOP) {
-            break;
-          }
+        while (initialOrigin in allowedRedirectsReverse &&
+               iterations++ < ASSUME_REDIRECT_LOOP) {
           initialDest = initialOrigin;
           initialOrigin = allowedRedirectsReverse[initialOrigin];
         }
 
-        if (clickedLinksReverse[initialOrigin]) {
+        if (initialOrigin in clickedLinksReverse) {
           for (var i in clickedLinksReverse[initialOrigin]) {
             // We hope there's only one possibility of a source page (that is,
             // ideally there will be one iteration of this loop).
@@ -557,7 +592,7 @@ let RequestProcessor = (function() {
             var dest = requests[originUri][destBase][destIdent][destUri];
             for (var i in dest) {
               // TODO: This variable could have been created easily already in
-              //       getAllRequestsOnDocument(). ==> rewrite RequestSet to
+              //       getAllRequestsInBrowser(). ==> rewrite RequestSet to
               //       contain a blocked list, an allowed list (and maybe a list
               //       of all requests).
               if (isAllowed === dest[i].isAllowed) {
@@ -684,8 +719,6 @@ let RequestProcessor = (function() {
     _rejectedRequests: new RequestSet(),
     _allowedRequests: new RequestSet(),
 
-    // TODO: make it private
-    _blockedRedirects: {},
 
 
     /**
@@ -705,35 +738,49 @@ let RequestProcessor = (function() {
         var originURI = request.originURI;
         var destURI = request.destURI;
 
-        // Fx 16 changed the following: 1) we should be able to count on the
-        // referrer (aRequestOrigin) being set to something besides
-        // moz-nullprincipal when there is a referrer, and 2) the new argument
-        // aRequestPrincipal is provided. This means our hackery to set the
-        // referrer based on aContext when aRequestOrigin is moz-nullprincipal
-        // is now causing requests that don't have a referrer (namely, URLs
-        // entered in the address bar) to be blocked and trigger a top-level
-        // document redirect notification.
-        if (request.aRequestOrigin.scheme == "moz-nullprincipal" &&
-            request.aRequestPrincipal) {
-          Logger.warning(
-              Logger.TYPE_CONTENT,
-              "Allowing request that appears to be a URL entered in the "
-                  + "location bar or some other good explanation: " + destURI);
-          return CP_OK;
-        }
+        if (request.aRequestOrigin.scheme == "moz-nullprincipal") {
+          let browser = null;
+
+          // Fx 16 changed the following: 1) we should be able to count on the
+          // referrer (aRequestOrigin) being set to something besides
+          // moz-nullprincipal when there is a referrer, and 2) the new argument
+          // aRequestPrincipal is provided. This means our hackery to set the
+          // referrer based on aContext when aRequestOrigin is moz-nullprincipal
+          // is now causing requests that don't have a referrer (namely, URLs
+          // entered in the address bar) to be blocked and trigger a top-level
+          // document redirect notification.
+          if (request.aRequestPrincipal) {
+            Logger.warning(
+                Logger.TYPE_CONTENT,
+                "Allowing request that appears to be a URL entered in the " +
+                "location bar or some other good explanation: " + destURI);
+            return CP_OK;
+          }
 
-        // Note: Assuming the Fx 16 moz-nullprincipal+aRequestPrincipal check
-        // above is correct, this should be able to be removed when Fx < 16 is
-        // no longer supported.
-        if (request.aRequestOrigin.scheme == "moz-nullprincipal" &&
-            request.aContext) {
-          var newOriginURI = DomainUtil
-                .stripFragment(request.aContext.contentDocument.documentURI);
-          Logger.info(Logger.TYPE_CONTENT,
-              "Considering moz-nullprincipal origin <"
-                  + originURI + "> to be origin <" + newOriginURI + ">");
-          originURI = newOriginURI;
-          request.setOriginURI(originURI);
+          // Note: Assuming the Fx 16 moz-nullprincipal+aRequestPrincipal check
+          // above is correct, this should be able to be removed when Fx < 16 is
+          // no longer supported.
+          if (request.aContext) {
+            try {
+              let domElement = request.aContext.QueryInterface(Ci.nsIDOMElement);
+              if (domElement && domElement.tagName == "") {
+                var newOriginURI = DomainUtil
+                      .stripFragment(request.aContext.contentDocument.documentURI);
+                Logger.info(Logger.TYPE_CONTENT,
+                    "Considering moz-nullprincipal origin <"
+                        + originURI + "> to be origin <" + newOriginURI + ">");
+                originURI = newOriginURI;
+                request.setOriginURI(originURI);
+              }
+            } catch (e if e.result == Cr.NS_ERROR_NO_INTERFACE) {
+              try {
+                let domWin = request.aContext.QueryInterface(Ci.nsIDOMWindow);
+            return CP_OK;
+                if (domWin && domWin.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE) {
+                }
+              } catch (e if e.result == Cr.NS_ERROR_NO_INTERFACE) {}
+            }
+          }
         }
 
         if (request.aRequestOrigin.scheme == "view-source") {
@@ -766,7 +813,7 @@ let RequestProcessor = (function() {
           let domNode;
           try {
             domNode = request.aContext.QueryInterface(Ci.nsIDOMNode);
-          } catch (e if e.result == Components.results.NS_ERROR_NO_INTERFACE) {}
+          } catch (e if e.result == Cr.NS_ERROR_NO_INTERFACE) {}
           if (domNode && domNode.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE) {
             var newOriginURI;
             if (request.aContext.documentURI &&
@@ -812,7 +859,7 @@ let RequestProcessor = (function() {
           let domNode;
           try {
             domNode = request.aContext.QueryInterface(Ci.nsIDOMNode);
-          } catch (e if e.result == Components.results.NS_ERROR_NO_INTERFACE) {}
+          } catch (e if e.result == Cr.NS_ERROR_NO_INTERFACE) {}
 
           if (domNode && domNode.nodeName == "LINK" &&
               (domNode.rel == "icon" || domNode.rel == "shortcut icon")) {
@@ -923,7 +970,7 @@ let RequestProcessor = (function() {
           let domNode;
           try {
             domNode = request.aContext.QueryInterface(Ci.nsIDOMNode);
-          } catch (e if e.result == Components.results.NS_ERROR_NO_INTERFACE) {}
+          } catch (e if e.result == Cr.NS_ERROR_NO_INTERFACE) {}
 
           if (domNode && domNode.nodeName == "xul:browser" &&
               domNode.currentURI && domNode.currentURI.spec == "about:blank") {
@@ -1105,7 +1152,7 @@ let RequestProcessor = (function() {
      * available on the channel. The response can be accessed and modified via
      * nsITraceableChannel.
      */
-    _examineHttpResponse: function(observedSubject) {
+    _examineHttpResponse: function(aSubject) {
       // Currently, if a user clicks a link to download a file and that link
       // redirects and is subsequently blocked, the user will see the blocked
       // destination in the menu. However, after they have allowed it from
@@ -1117,7 +1164,7 @@ let RequestProcessor = (function() {
       // TODO: Make user aware of blocked headers so they can allow them if
       // desired.
 
-      var httpChannel = observedSubject.QueryInterface(Ci.nsIHttpChannel);
+      var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
 
       var headerType;
       var dest;
@@ -1138,7 +1185,11 @@ let RequestProcessor = (function() {
           return;
         }
         try {
-          var parts = DomainUtil.parseRefresh(refreshString);
+          // We can ignore the delay because we aren't manually doing
+          // the refreshes. Allowed refreshes we still leave to the browser.
+          // The dest may be empty if the origin is what should be refreshed.
+          // This will be handled by DomainUtil.determineRedirectUri().
+          var dest = DomainUtil.parseRefresh(refreshString).destURI;
         } catch (e) {
           Logger.warning(Logger.TYPE_HEADER_REDIRECT,
               "Invalid refresh header: <" + refreshString + ">");
@@ -1147,11 +1198,6 @@ let RequestProcessor = (function() {
           }
           return;
         }
-        // We can ignore the delay (parts[0]) because we aren't manually doing
-        // the refreshes. Allowed refreshes we still leave to the browser.
-        // The dest may be empty if the origin is what should be refreshed. This
-        // will be handled by DomainUtil.determineRedirectUri().
-        dest = parts[1];
       }
 
       // For origins that are IDNs, this will always be in ACE format. We want
@@ -1209,8 +1255,8 @@ let RequestProcessor = (function() {
      * Currently this just looks for prefetch requests that are getting through
      * which we currently can't stop.
      */
-    _examineHttpRequest: function(observedSubject) {
-      var httpChannel = observedSubject.QueryInterface(Ci.nsIHttpChannel);
+    _examineHttpRequest: function(aSubject) {
+      var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
       try {
         // Determine if prefetch requests are slipping through.
         if (httpChannel.getRequestHeader("X-moz") == "prefetch") {
@@ -1398,11 +1444,11 @@ let RequestProcessor = (function() {
      * cases where the user has multiple tabs/windows open to the same page.
      *
      * @param {}
-     *          document
+     *          browser
      * @return {}
      *          RequestSet
      */
-    getAllRequestsOnDocument: function(document) {
+    getAllRequestsInBrowser: function(browser) {
       //var origins = {};
       var reqSet = new RequestSet();
 
@@ -1412,8 +1458,8 @@ let RequestProcessor = (function() {
       // main allowed/denied request sets before adding them.
       //_getOtherOriginsHelperFromDOM(document, reqSet);
 
-      var documentURI = DomainUtil.stripFragment(document.documentURI);
-      _addRecursivelyAllRequestsFromURI(documentURI, reqSet, {});
+      var uri = DomainUtil.stripFragment(browser.currentURI.spec);
+      _addRecursivelyAllRequestsFromURI(uri, reqSet, {});
       return reqSet;
     }
   };
diff --git a/src/content/lib/request.jsm b/src/content/lib/request.jsm
index 838babb..704dcaa 100644
--- a/src/content/lib/request.jsm
+++ b/src/content/lib/request.jsm
@@ -303,8 +303,10 @@ NormalRequest.prototype.checkURISchemes = function() {
       if (!win) {
         throw "The window could not be extracted from aContext.";
       }
-      Utils.getChromeWindow(win).requestpolicy.overlay
-           .showSchemeNotification(win, scheme);
+      let overlay = Utils.getChromeWindow(win).requestpolicy.overlay;
+      Utils.runAsync(function() {
+        overlay.showSchemeNotification(win, scheme);
+      });
     } catch (e) {
       Logger.warning(Logger.TYPE_ERROR,
                      "The user could not be informed about the " +
diff --git a/src/content/lib/requestpolicy-service.jsm b/src/content/lib/requestpolicy-service.jsm
index 619eb99..496e2be 100644
--- a/src/content/lib/requestpolicy-service.jsm
+++ b/src/content/lib/requestpolicy-service.jsm
@@ -59,7 +59,7 @@ let rpService = (function() {
 
   let conflictingExtensions = [];
   let compatibilityRules = [];
-  let topLevelDocTranslationRules = [];
+  let topLevelDocTranslationRules = {};
 
   let subscriptions = null;
 
@@ -76,19 +76,19 @@ let rpService = (function() {
 
   function handleUninstallOrDisable() {
     Logger.debug(Logger.TYPE_INTERNAL, "Performing 'disable' operations.");
-    var resetLinkPrefetch = Prefs.prefs.getBoolPref(
+    var resetLinkPrefetch = rpPrefBranch.getBoolPref(
         "prefetch.link.restoreDefaultOnUninstall");
-    var resetDNSPrefetch = Prefs.prefs.getBoolPref(
+    var resetDNSPrefetch = rpPrefBranch.getBoolPref(
         "prefetch.dns.restoreDefaultOnUninstall");
 
     if (resetLinkPrefetch) {
-      if (Prefs.prefsRoot.prefHasUserValue("network.prefetch-next")) {
-        Prefs.prefsRoot.clearUserPref("network.prefetch-next");
+      if (rootPrefBranch.prefHasUserValue("network.prefetch-next")) {
+        rootPrefBranch.clearUserPref("network.prefetch-next");
       }
     }
     if (resetDNSPrefetch) {
-      if (Prefs.prefsRoot.prefHasUserValue("network.dns.disablePrefetch")) {
-        Prefs.prefsRoot.clearUserPref("network.dns.disablePrefetch");
+      if (rootPrefBranch.prefHasUserValue("network.dns.disablePrefetch")) {
+        rootPrefBranch.clearUserPref("network.dns.disablePrefetch");
       }
     }
     Services.prefs.savePrefFile(null);
@@ -209,7 +209,7 @@ let rpService = (function() {
             "Using extension compatibility rules for: " + ext.name);
         var orig = "chrome://updatescan/content/diffPage.xul";
         var translated = "data:text/html";
-        topLevelDocTranslationRules.push([orig, translated]);
+        topLevelDocTranslationRules[orig] = translated;
         break;
       case "FirefoxAddon at similarWeb.com" : // SimilarWeb
         Logger.info(Logger.TYPE_INTERNAL,
@@ -349,7 +349,8 @@ let rpService = (function() {
     subscriptions = new UserSubscriptions();
     PolicyManager.loadUserRules();
 
-    var defaultPolicy = Prefs.prefs.defaultAllow ? 'allow' : 'deny';
+    Prefs.isDefaultAllow;
+    var defaultPolicy = rpPrefBranch.defaultAllow ? 'allow' : 'deny';
 
     var failures = PolicyManager.loadSubscriptionRules(
           subscriptions.getSubscriptionInfo(defaultPolicy));
@@ -419,8 +420,8 @@ let rpService = (function() {
   }
 
   function showWelcomeWindow() {
-    if (!Prefs.prefs.getBoolPref("welcomeWindowShown")) {
-      var url = "chrome://requestpolicy/content/settings/setup.html";
+    if (!rpPrefBranch.getBoolPref("welcomeWindowShown")) {
+      var url = "about:requestpolicy?setup";
 
       var wm = Cc['@mozilla.org/appshell/window-mediator;1'].
           getService(Ci.nsIWindowMediator);
@@ -434,7 +435,7 @@ let rpService = (function() {
 
       _gBrowser.selectedTab = _gBrowser.addTab(url);
 
-      Prefs.prefs.setBoolPref("welcomeWindowShown", true);
+      rpPrefBranch.setBoolPref("welcomeWindowShown", true);
       Services.prefs.savePrefFile(null);
     }
   }
@@ -500,8 +501,10 @@ let rpService = (function() {
       return conflictingExtensions;
     },
 
-    getTopLevelDocTranslations : function() {
-      return topLevelDocTranslationRules;
+    getTopLevelDocTranslation : function(uri) {
+      // We're not sure if the array will be fully populated during init. This
+      // is especially a concern given the async addon manager API in Firefox 4.
+      return topLevelDocTranslationRules[uri] || null;
     },
 
     /**
diff --git a/src/content/lib/script-loader.jsm b/src/content/lib/script-loader.jsm
index 2c1fd28..c60e938 100644
--- a/src/content/lib/script-loader.jsm
+++ b/src/content/lib/script-loader.jsm
@@ -39,7 +39,25 @@ function getModuleURI(id) {
 let loggerURI = getModuleURI("logger");
 Cu.import(loggerURI);
 
+/*
+// remove
+
+might be helpful. This will import an arbitrary module into a
+singleton object, which it returns. If the argument is not an
+absolute path, the module is imported relative to the caller's
+filename.
+
+function module(uri) {
+  if (!/^[a-z-]+:/.exec(uri)) {
+    uri = /([^ ]+\/)[^\/]+$/.exec(Components.stack.caller.filename)[1] + uri + ".jsm";
+  }
+
+  let obj = {};
+  Components.utils.import(uri, obj);
+  return obj;
+}
 
+*/
 
 let ScriptLoader = (function() {
   /*let modules = [
@@ -69,6 +87,10 @@ let ScriptLoader = (function() {
 
   let scopes = {};
 
+  // contains the module IDs that are currently being imported initially and
+  // have not finished importing yet.
+  let modulesCurrentlyBeingImported = {};
+
 
   let self = {
     // public attributes and methods
@@ -99,10 +121,28 @@ let ScriptLoader = (function() {
     importModule: function(moduleID, scope) {
       scope = scope || {};
 
+      // avoid import loops.
+      if (moduleID in modulesCurrentlyBeingImported) {
+        return scope;
+      }
+
       let uri = getModuleURI(moduleID);
-      Cu.import(uri, scope);
-      importedModuleURIs[uri] = true;
+      try {
+        if (!(uri in importedModuleURIs)) {
+          // the module hasn't been imported yet
+          modulesCurrentlyBeingImported[moduleID] = true;
+        }
+
+        Cu.import(uri, scope);
+        importedModuleURIs[uri] = true;
 
+        if (moduleID in modulesCurrentlyBeingImported) {
+          delete modulesCurrentlyBeingImported[moduleID];
+        }
+      } catch (e) {
+        Logger.severeError("Failed to import module \"" + moduleID + "\": " + e,
+                           e);
+      }
       return scope;
     },
 
diff --git a/src/content/lib/string-utils.jsm b/src/content/lib/string-utils.jsm
new file mode 100644
index 0000000..505643c
--- /dev/null
+++ b/src/content/lib/string-utils.jsm
@@ -0,0 +1,65 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2009 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+/**
+ * Note: The string utils are used also in the content process (see
+ * e10s/multiprocessor firefox), so this file shouldn't contain code which is
+ * limited to the chrome process.
+ */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+let EXPORTED_SYMBOLS = ["StringUtils"];
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+
+let StringUtils = (function() {
+  let self = {};
+
+  XPCOMUtils.defineLazyGetter(self, "strbundle", function() {
+    return loadPropertiesFile(
+        "chrome://requestpolicy/locale/requestpolicy.properties");
+  });
+  // from https://developer.mozilla.org/en-US/Add-ons/
+  // How_to_convert_an_overlay_extension_to_restartless
+  // #Step_10.3A_Bypass_cache_when_loading_properties_files
+  function loadPropertiesFile(path)
+  {
+    /* HACK: The string bundle cache is cleared on addon shutdown, however it
+     * doesn't appear to do so reliably. Errors can erratically happen on next
+     * load of the same file in certain instances. (at minimum, when strings are
+     * added/removed) The apparently accepted solution to reliably load new
+     * versions is to always create bundles with a unique URL so as to bypass the
+     * cache. This is accomplished by passing a random number in a parameter after
+     * a '?'. (this random ID is otherwise ignored) The loaded string bundle is
+     * still cached on startup and should still be cleared out of the cache on
+     * addon shutdown. This just bypasses the built-in cache for repeated loads of
+     * the same path so that a newly installed update loads cleanly. */
+    return Services.strings.createBundle(path + "?" + Math.random());
+  }
+
+  return self;
+}());
diff --git a/src/content/lib/utils.jsm b/src/content/lib/utils.jsm
index 0a9585e..15f42b7 100644
--- a/src/content/lib/utils.jsm
+++ b/src/content/lib/utils.jsm
@@ -39,41 +39,17 @@ ScriptLoader.importModules(["prefs", "constants"], this);
 
 
 let Utils = (function() {
-  // private variables and functions
-
   let self = {};
 
-  XPCOMUtils.defineLazyGetter(self, "strbundle", function() {
-    return loadPropertiesFile(
-        "chrome://requestpolicy/locale/requestpolicy.properties");
-  });
-  // from https://developer.mozilla.org/en-US/Add-ons/
-  // How_to_convert_an_overlay_extension_to_restartless
-  // #Step_10.3A_Bypass_cache_when_loading_properties_files
-  function loadPropertiesFile(path)
-  {
-    /* HACK: The string bundle cache is cleared on addon shutdown, however it
-     * doesn't appear to do so reliably. Errors can erratically happen on next
-     * load of the same file in certain instances. (at minimum, when strings are
-     * added/removed) The apparently accepted solution to reliably load new
-     * versions is to always create bundles with a unique URL so as to bypass the
-     * cache. This is accomplished by passing a random number in a parameter after
-     * a '?'. (this random ID is otherwise ignored) The loaded string bundle is
-     * still cached on startup and should still be cleared out of the cache on
-     * addon shutdown. This just bypasses the built-in cache for repeated loads of
-     * the same path so that a newly installed update loads cleanly. */
-    return Services.strings.createBundle(path + "?" + Math.random());
-  }
-
-
   /**
-   * (taken from Adblock Plus, see
-   *  https://hg.adblockplus.org/adblockplus/file/0d76ad7eb80b/lib/utils.js)
    * Posts an action to the event queue of the current thread to run it
    * asynchronously. Any additional parameters to this function are passed
    * as parameters to the callback.
+   *
+   * @param {Function} callback
+   * @param {Object} thisPtr
    */
-  self.runAsync = function(/**Function*/ callback, /**Object*/ thisPtr) {
+  self.runAsync = function(callback, thisPtr) {
     let params = Array.prototype.slice.call(arguments, 2);
     let runnable = {
       run: function() {
@@ -95,12 +71,12 @@ let Utils = (function() {
 
   // get/set last/current RP version
   {
-    self.info.lastRPVersion = Prefs.prefs.getCharPref("lastVersion");
+    self.info.lastRPVersion = rpPrefBranch.getCharPref("lastVersion");
 
     self.info.curRPVersion = "0.0";
     // curRPVersion needs to be set asynchronously
     AddonManager.getAddonByID(EXTENSION_ID, function(addon) {
-      Prefs.prefs.setCharPref("lastVersion", addon.version);
+      rpPrefBranch.setCharPref("lastVersion", addon.version);
       self.info.curRPVersion = addon.version;
       if (self.info.lastRPVersion != self.info.curRPVersion) {
         Services.prefs.savePrefFile(null);
@@ -110,11 +86,11 @@ let Utils = (function() {
 
   // get/set last/current app (e.g. firefox) version
   {
-    self.info.lastAppVersion = Prefs.prefs.getCharPref("lastAppVersion");
+    self.info.lastAppVersion = rpPrefBranch.getCharPref("lastAppVersion");
 
     let curAppVersion = Services.appinfo.version;
     self.info.curAppVersion = curAppVersion;
-    Prefs.prefs.setCharPref("lastAppVersion", curAppVersion);
+    rpPrefBranch.setCharPref("lastAppVersion", curAppVersion);
 
     if (self.info.lastAppVersion != self.info.curAppVersion) {
       Services.prefs.savePrefFile(null);
@@ -126,12 +102,25 @@ let Utils = (function() {
       Services.vc.compare(Services.appinfo.platformVersion, '29') >= 0;
 
   self.getChromeWindow = function(aContentWindow) {
-    return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShellTreeItem)
-                         .rootTreeItem
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindow);
+    return aContentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIWebNavigation)
+                             .QueryInterface(Ci.nsIDocShellTreeItem)
+                             .rootTreeItem
+                             .QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindow);
+  };
+
+  self.getBrowserForWindow = function(aContentWindow) {
+    let win = self.getChromeWindow(aContentWindow);
+    let tab = win.gBrowser._getTabForContentWindow(aContentWindow.top);
+    return tab.linkedBrowser;
+  }
+
+  self.getChromeWindowForDocShell = function(aDocShell) {
+    return aDocShell.QueryInterface(Ci.nsIDocShellTreeItem)
+                    .rootTreeItem
+                    .QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIDOMWindow);
   };
 
   return self;
diff --git a/src/content/lib/utils/process-info.jsm b/src/content/lib/utils/process-info.jsm
new file mode 100644
index 0000000..ca4bcd6
--- /dev/null
+++ b/src/content/lib/utils/process-info.jsm
@@ -0,0 +1,36 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+let EXPORTED_SYMBOLS = ["xulRuntime", "processType", "isMainProcess"];
+
+let xulRuntime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
+
+let processType = xulRuntime.processType;
+
+// The "default" type means that we're on the main process, the chrome process.
+// This is relevant for multiprocessor firefox aka Electrolysis aka e10s.
+let isMainProcess = processType === xulRuntime.PROCESS_TYPE_DEFAULT;
diff --git a/src/content/lib/window-manager.jsm b/src/content/lib/window-manager.jsm
index a6eb464..0771845 100644
--- a/src/content/lib/window-manager.jsm
+++ b/src/content/lib/window-manager.jsm
@@ -23,21 +23,23 @@
 
 let EXPORTED_SYMBOLS = ["rpWindowManager"];
 
+let globalScope = this;
+
 let rpWindowManager = (function(self) {
 
   const Ci = Components.interfaces;
   const Cc = Components.classes;
   const Cu = Components.utils;
 
-  Cu.import("resource://gre/modules/Services.jsm", this);
-  Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+  Cu.import("resource://gre/modules/Services.jsm", globalScope);
+  Cu.import("resource://gre/modules/XPCOMUtils.jsm", globalScope);
 
-  Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", this);
+  Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", globalScope);
   ScriptLoader.importModules([
     "utils",
     "xul-utils",
     "constants"
-  ], this);
+  ], globalScope);
 
   let styleSheets = [
     "chrome://requestpolicy/skin/requestpolicy.css",
@@ -89,7 +91,6 @@ let rpWindowManager = (function(self) {
 
     // create a scope variable for RP for this window
     window.requestpolicy = {};
-
     Services.scriptloader.loadSubScript(
         "chrome://requestpolicy/content/ui/overlay.js", window);
     Services.scriptloader.loadSubScript(
@@ -103,6 +104,8 @@ let rpWindowManager = (function(self) {
     // everything else is ready
     window.requestpolicy.overlay.init();
     window.requestpolicy.overlay.onWindowLoad();
+    window.messageManager.loadFrameScript(
+        "chrome://requestpolicy/content/ui/frame.js", true);
   }
 
   function unloadFromWindow(window) {
@@ -180,4 +183,5 @@ let rpWindowManager = (function(self) {
 
 // extend rpWindowManager
 Services.scriptloader.loadSubScript(
-    "chrome://requestpolicy/content/lib/window-manager-toolbarbutton.js", this);
+    "chrome://requestpolicy/content/lib/window-manager-toolbarbutton.js",
+    globalScope);
diff --git a/src/content/lib/xul-utils.jsm b/src/content/lib/xul-utils.jsm
index ed998d0..25a20f7 100644
--- a/src/content/lib/xul-utils.jsm
+++ b/src/content/lib/xul-utils.jsm
@@ -28,7 +28,7 @@ const Cu = Components.utils;
 Cu.import("resource://gre/modules/Services.jsm");
 
 Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
-ScriptLoader.importModules(["logger", "utils"], this);
+ScriptLoader.importModules(["logger", "string-utils"], this);
 
 let EXPORTED_SYMBOLS = ["XULUtils"];
 
@@ -79,7 +79,7 @@ function getAttrValue(element, attr) {
   let value = element[attr];
   if (value.charAt(0) == "&" && value.charAt(value.length-1) == ";") {
     try {
-      value = Utils.strbundle
+      value = StringUtils.strbundle
           .GetStringFromName(value.substr(1, value.length-2));
     } catch (e) {
       Logger.severe(Logger.TYPE_ERROR, e, e);
diff --git a/src/content/settings/advancedprefs.html b/src/content/settings/advancedprefs.html
index b64d75a..c7b2a55 100644
--- a/src/content/settings/advancedprefs.html
+++ b/src/content/settings/advancedprefs.html
@@ -2,10 +2,11 @@
 <html>
 <head>
   <title>RequestPolicy - Advanced Preferences</title>
-  <script src="jquery.min.js"></script>
-  <script src="common.js"></script>
-  <script src="advancedprefs.js"></script>
-  <link href="settings.css" rel="stylesheet" type="text/css"/>
+  <script src="chrome://requestpolicy/content/settings/jquery.min.js"></script>
+  <script src="chrome://requestpolicy/content/settings/common.js"></script>
+  <script src="chrome://requestpolicy/content/settings/advancedprefs.js"></script>
+  <link href="chrome://requestpolicy/content/settings/settings.css"
+        rel="stylesheet" type="text/css"/>
 </head>
 <body onload="onload()">
 <table>
@@ -16,16 +17,16 @@
         <div id="requestpolicy">RequestPolicy</div>
         <div id="mainnav" class="nav">
           <ul>
-            <li><a href="basicprefs.html" selected="true" data-string="preferences"></a></li>
-            <li><a href="yourpolicy.html" data-string="managePolicies"></a></li>
+            <li><a href="about:requestpolicy?basicprefs" selected="true" data-string="preferences"></a></li>
+            <li><a href="about:requestpolicy?yourpolicy" data-string="managePolicies"></a></li>
             <li><a href="https://github.com/RequestPolicyContinued/requestpolicy/wiki#help-and-support-for-users-and-developers" data-string="help" target="_blank"></a></li>
             <li><a href="https://requestpolicycontinued.github.io/#about" data-string="about" target="_blank"></a></li>
           </ul>
         </div>
         <div id="subnav1" class="nav subnav">
           <ul>
-            <li><a href="basicprefs.html" data-string="basic"></a></li>
-            <li><a href="advancedprefs.html" selected="true" data-string="advanced"></a></li>
+            <li><a href="about:requestpolicy?basicprefs" data-string="basic"></a></li>
+            <li><a href="about:requestpolicy?advancedprefs" selected="true" data-string="advanced"></a></li>
           </ul>
         </div>
       </div>
diff --git a/src/content/settings/advancedprefs.js b/src/content/settings/advancedprefs.js
index d3a4ba4..4879d96 100644
--- a/src/content/settings/advancedprefs.js
+++ b/src/content/settings/advancedprefs.js
@@ -30,27 +30,27 @@ var prefsChangedObserver = null;
 function updateDisplay() {
   // Link prefetch.
   document.getElementById('pref-linkPrefetch').checked =
-      Prefs.prefsRoot.getBoolPref('network.prefetch-next');
+      rootPrefBranch.getBoolPref('network.prefetch-next');
 
   document.getElementById('pref-prefetch.link.disableOnStartup').checked =
-      Prefs.prefs.getBoolPref('prefetch.link.disableOnStartup');
+      rpPrefBranch.getBoolPref('prefetch.link.disableOnStartup');
 
   document.getElementById('pref-prefetch.link.restoreDefaultOnUninstall').checked =
-      Prefs.prefs.getBoolPref('prefetch.link.restoreDefaultOnUninstall');
+      rpPrefBranch.getBoolPref('prefetch.link.restoreDefaultOnUninstall');
 
   // DNS prefetch.
   document.getElementById('pref-dnsPrefetch').checked =
-      !Prefs.prefsRoot.getBoolPref('network.dns.disablePrefetch');
+      !rootPrefBranch.getBoolPref('network.dns.disablePrefetch');
 
   document.getElementById('pref-prefetch.dns.disableOnStartup').checked =
-      Prefs.prefs.getBoolPref('prefetch.dns.disableOnStartup');
+      rpPrefBranch.getBoolPref('prefetch.dns.disableOnStartup');
 
   document.getElementById('pref-prefetch.dns.restoreDefaultOnUninstall').checked =
-      Prefs.prefs.getBoolPref('prefetch.dns.restoreDefaultOnUninstall');
+      rpPrefBranch.getBoolPref('prefetch.dns.restoreDefaultOnUninstall');
 
   // TODO: Create a class which acts as an API for preferences and which ensures
   // that the returned value is always a valid value for "string" preferences.
-  var sorting = Prefs.prefs.getCharPref('menu.sorting');
+  var sorting = rpPrefBranch.getCharPref('menu.sorting');
 
   if (sorting == document.getElementById('sortByNumRequests').value) {
     document.getElementById('sortByNumRequests').checked = true;
@@ -67,7 +67,7 @@ function updateDisplay() {
   }
 
   document.getElementById('menu.info.showNumRequests').checked =
-      Prefs.prefs.getBoolPref('menu.info.showNumRequests');
+      rpPrefBranch.getBoolPref('menu.info.showNumRequests');
 }
 
 
@@ -77,14 +77,14 @@ function onload() {
   // Link prefetch.
   document.getElementById('pref-linkPrefetch').addEventListener('change',
       function (event) {
-        Prefs.prefsRoot.setBoolPref('network.prefetch-next', event.target.checked);
+        rootPrefBranch.setBoolPref('network.prefetch-next', event.target.checked);
         Services.prefs.savePrefFile(null);
       }
   );
 
   document.getElementById('pref-prefetch.link.disableOnStartup').addEventListener('change',
       function (event) {
-        Prefs.prefs.setBoolPref('prefetch.link.disableOnStartup',
+        rpPrefBranch.setBoolPref('prefetch.link.disableOnStartup',
             event.target.checked);
         Services.prefs.savePrefFile(null);
       }
@@ -92,7 +92,7 @@ function onload() {
 
   document.getElementById('pref-prefetch.link.restoreDefaultOnUninstall').addEventListener('change',
       function (event) {
-        Prefs.prefs.setBoolPref('prefetch.link.restoreDefaultOnUninstall', event.target.checked);
+        rpPrefBranch.setBoolPref('prefetch.link.restoreDefaultOnUninstall', event.target.checked);
         Services.prefs.savePrefFile(null);
       }
   );
@@ -100,27 +100,27 @@ function onload() {
   // DNS prefetch.
   document.getElementById('pref-dnsPrefetch').addEventListener('change',
       function (event) {
-        Prefs.prefsRoot.setBoolPref('network.dns.disablePrefetch', !event.target.checked);
+        rootPrefBranch.setBoolPref('network.dns.disablePrefetch', !event.target.checked);
         Services.prefs.savePrefFile(null);
       }
   );
 
   document.getElementById('pref-prefetch.dns.disableOnStartup').addEventListener('change',
       function (event) {
-        Prefs.prefs.setBoolPref('prefetch.dns.disableOnStartup', event.target.checked);
+        rpPrefBranch.setBoolPref('prefetch.dns.disableOnStartup', event.target.checked);
         Services.prefs.savePrefFile(null);
       }
   );
 
   document.getElementById('pref-prefetch.dns.restoreDefaultOnUninstall').addEventListener('change',
       function (event) {
-        Prefs.prefs.setBoolPref('prefetch.dns.restoreDefaultOnUninstall', event.target.checked);
+        rpPrefBranch.setBoolPref('prefetch.dns.restoreDefaultOnUninstall', event.target.checked);
         Services.prefs.savePrefFile(null);
       }
   );
 
   var sortingListener = function (event) {
-    Prefs.prefs.setCharPref('menu.sorting', event.target.value);
+    rpPrefBranch.setCharPref('menu.sorting', event.target.value);
     Services.prefs.savePrefFile(null);
   };
   document.getElementById('sortByNumRequests').addEventListener('change', sortingListener);
@@ -129,7 +129,7 @@ function onload() {
 
   document.getElementById('menu.info.showNumRequests').addEventListener('change',
       function (event) {
-        Prefs.prefs.setBoolPref('menu.info.showNumRequests', event.target.checked);
+        rpPrefBranch.setBoolPref('menu.info.showNumRequests', event.target.checked);
         Services.prefs.savePrefFile(null);
       }
   );
diff --git a/src/content/settings/basicprefs.html b/src/content/settings/basicprefs.html
index 908e6f1..3e68eeb 100644
--- a/src/content/settings/basicprefs.html
+++ b/src/content/settings/basicprefs.html
@@ -2,10 +2,11 @@
 <html>
 <head>
   <title>RequestPolicy - Preferences</title>
-  <script src="jquery.min.js"></script>
-  <script src="common.js"></script>
-  <script src="basicprefs.js"></script>
-  <link href="settings.css" rel="stylesheet" type="text/css"/>
+  <script src="chrome://requestpolicy/content/settings/jquery.min.js"></script>
+  <script src="chrome://requestpolicy/content/settings/common.js"></script>
+  <script src="chrome://requestpolicy/content/settings/basicprefs.js"></script>
+  <link href="chrome://requestpolicy/content/settings/settings.css"
+        rel="stylesheet" type="text/css"/>
 </head>
 <body onload="onload()">
 <table>
@@ -16,16 +17,16 @@
         <div id="requestpolicy">RequestPolicy</div>
         <div id="mainnav" class="nav">
           <ul>
-            <li><a href="basicprefs.html" selected="true" data-string="preferences"></a></li>
-            <li><a href="yourpolicy.html" data-string="managePolicies"></a></li>
+            <li><a href="about:requestpolicy?basicprefs" selected="true" data-string="preferences"></a></li>
+            <li><a href="about:requestpolicy?yourpolicy" data-string="managePolicies"></a></li>
             <li><a href="https://github.com/RequestPolicyContinued/requestpolicy/wiki#help-and-support-for-users-and-developers" data-string="help" target="_blank"></a></li>
             <li><a href="https://requestpolicycontinued.github.io/#about" data-string="about" target="_blank"></a></li>
           </ul>
         </div>
         <div id="subnav1" class="nav subnav">
           <ul>
-            <li><a href="basicprefs.html" selected="true" data-string="basic"></a></li>
-            <li><a href="advancedprefs.html" data-string="advanced"></a></li>
+            <li><a href="about:requestpolicy?basicprefs" selected="true" data-string="basic"></a></li>
+            <li><a href="about:requestpolicy?advancedprefs" data-string="advanced"></a></li>
           </ul>
         </div>
       </div>
diff --git a/src/content/settings/basicprefs.js b/src/content/settings/basicprefs.js
index 43dc258..6bdb8ac 100644
--- a/src/content/settings/basicprefs.js
+++ b/src/content/settings/basicprefs.js
@@ -19,20 +19,20 @@ var prefsChangedObserver = null;
 
 
 function updateDisplay() {
-  var indicate = rpService.prefs.getBoolPref('indicateBlockedObjects');
+  var indicate = rpPrefBranch.getBoolPref('indicateBlockedObjects');
   document.getElementById('pref-indicateBlockedObjects').checked = indicate;
   document.getElementById('indicateBlockedImages-details').hidden = !indicate;
 
   document.getElementById('pref-dontIndicateBlacklistedObjects').checked =
-      !rpService.prefs.getBoolPref('indicateBlacklistedObjects');
+      !rpPrefBranch.getBoolPref('indicateBlacklistedObjects');
 
   document.getElementById('pref-autoReload').checked =
-      Prefs.prefs.getBoolPref('autoReload');
+      rpPrefBranch.getBoolPref('autoReload');
 
   document.getElementById('pref-privateBrowsingPermanentWhitelisting').checked =
-      Prefs.prefs.getBoolPref('privateBrowsingPermanentWhitelisting');
+      rpPrefBranch.getBoolPref('privateBrowsingPermanentWhitelisting');
 
-//  if (Prefs.prefs.getBoolPref('defaultPolicy.allow')) {
+//  if (rpPrefBranch.getBoolPref('defaultPolicy.allow')) {
 //    var word = 'allow';
 //  } else {
 //    var word = 'block';
@@ -46,7 +46,7 @@ function onload() {
 
   document.getElementById('pref-indicateBlockedObjects').addEventListener('change',
       function (event) {
-        Prefs.prefs.setBoolPref('indicateBlockedObjects', event.target.checked);
+        rpPrefBranch.setBoolPref('indicateBlockedObjects', event.target.checked);
         Services.prefs.savePrefFile(null);
         updateDisplay();
       }
@@ -62,15 +62,16 @@ function onload() {
 
   document.getElementById('pref-autoReload').addEventListener('change',
     function(event) {
-      Prefs.prefs.setBoolPref('autoReload', event.target.checked);
+      rpPrefBranch.setBoolPref('autoReload', event.target.checked);
       Services.prefs.savePrefFile(null);
       updateDisplay();
     }
   );
 
-  document.getElementById('pref-privateBrowsingPermanentWhitelisting').addEventListener('change',
-      function (event) {
-        Prefs.prefs.setBoolPref('privateBrowsingPermanentWhitelisting', event.target.checked);
+  document.getElementById('pref-privateBrowsingPermanentWhitelisting')
+      .addEventListener('change', function (event) {
+        rpPrefBranch.setBoolPref('privateBrowsingPermanentWhitelisting',
+                                 event.target.checked);
         Services.prefs.savePrefFile(null);
         updateDisplay();
       }
diff --git a/src/content/settings/common.js b/src/content/settings/common.js
index 5627b82..4885be8 100644
--- a/src/content/settings/common.js
+++ b/src/content/settings/common.js
@@ -7,7 +7,8 @@ Cu.import("resource://gre/modules/Services.jsm");
 
 Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
 ScriptLoader.importModules([
-  "utils",
+  "constants",
+  "string-utils",
   "prefs",
   "domain-util",
   "logger",
@@ -16,6 +17,8 @@ ScriptLoader.importModules([
   "requestpolicy-service"
 ], this);
 
+Logger.dump("  p!! ");
+
 
 const COMMON_STRINGS = [
   'preferences',
@@ -30,9 +33,9 @@ const COMMON_STRINGS = [
 function _(msg, args) {
   if (args) {
     args = Array.prototype.slice.call(arguments, 1);
-    return Utils.strbundle.formatStringFromName(msg, args, args.length);
+    return StringUtils.strbundle.formatStringFromName(msg, args, args.length);
   } else {
-    return Utils.strbundle.GetStringFromName(msg);
+    return StringUtils.strbundle.GetStringFromName(msg);
   }
 }
 
@@ -192,8 +195,8 @@ common.prefStringToObj = function (prefString) {
 
 common.clearPref = function (pref) {
   try {
-    if (Prefs.prefs.prefHasUserValue(pref)) {
-      Prefs.prefs.clearUserPref(pref);
+    if (rpPrefBranch.prefHasUserValue(pref)) {
+      rpPrefBranch.clearUserPref(pref);
     }
   } catch (e) {
     Logger.dump('Clearing pref failed: ' + e.toString());
diff --git a/src/content/settings/defaultpolicy.html b/src/content/settings/defaultpolicy.html
index ed856a7..ac3da13 100644
--- a/src/content/settings/defaultpolicy.html
+++ b/src/content/settings/defaultpolicy.html
@@ -2,10 +2,11 @@
 <html>
 <head>
   <title>RequestPolicy - Default Policy</title>
-  <script src="jquery.min.js"></script>
-  <script src="common.js"></script>
-  <script src="defaultpolicy.js"></script>
-  <link href="settings.css" rel="stylesheet" type="text/css"/>
+  <script src="chrome://requestpolicy/content/settings/jquery.min.js"></script>
+  <script src="chrome://requestpolicy/content/settings/common.js"></script>
+  <script src="chrome://requestpolicy/content/settings/defaultpolicy.js"></script>
+  <link href="chrome://requestpolicy/content/settings/settings.css"
+        rel="stylesheet" type="text/css"/>
 </head>
 <body onload="onload()">
 <table>
@@ -16,17 +17,17 @@
         <div id="requestpolicy">RequestPolicy</div>
         <div id="mainnav" class="nav">
           <ul>
-            <li><a href="basicprefs.html" data-string="preferences"></a></li>
-            <li><a href="yourpolicy.html" selected="true" data-string="managePolicies"></a></li>
+            <li><a href="about:requestpolicy?basicprefs" data-string="preferences"></a></li>
+            <li><a href="about:requestpolicy?yourpolicy" selected="true" data-string="managePolicies"></a></li>
             <li><a href="https://github.com/RequestPolicyContinued/requestpolicy/wiki#help-and-support-for-users-and-developers" data-string="help" target="_blank"></a></li>
             <li><a href="https://requestpolicycontinued.github.io/#about" data-string="about" target="_blank"></a></li>
           </ul>
         </div>
         <div id="subnav1" class="nav subnav">
           <ul>
-            <li><a href="yourpolicy.html" data-string="yourPolicy"></a></li>
-            <li><a href="defaultpolicy.html" selected="true" data-string="defaultPolicy"></a></li>
-            <li><a href="subscriptions.html" data-string="subscriptions"></a></li>
+            <li><a href="about:requestpolicy?yourpolicy" data-string="yourPolicy"></a></li>
+            <li><a href="about:requestpolicy?defaultpolicy" selected="true" data-string="defaultPolicy"></a></li>
+            <li><a href="about:requestpolicy?subscriptions" data-string="subscriptions"></a></li>
           </ul>
         </div>
       </div>
@@ -38,7 +39,7 @@
 
         <p class="learnmore">
           <span data-string="defaultPolicyDefinition"></span>
-          <a href="https://www.requestpolicy.com/help-default-policy.html"
+          <a href="https://www.requestpolicy.com/help-defaultpolicy.html"
              target="_blank" data-string="learnMore"></a></p>
 
           <label><input type="radio" name="defaultrule" id="defaultallow"
@@ -72,7 +73,7 @@
 
         <div id="subscriptionschanged" style="display: none">
           <span data-string="differentSubscriptionsAreAvailable"></span>
-          <a href="subscriptions.html" data-string="manageSubscriptions"></a>
+          <a href="about:requestpolicy?subscriptions" data-string="manageSubscriptions"></a>
         </div>
       </div>
     </td>
diff --git a/src/content/settings/defaultpolicy.js b/src/content/settings/defaultpolicy.js
index 35e417e..b3aed6c 100644
--- a/src/content/settings/defaultpolicy.js
+++ b/src/content/settings/defaultpolicy.js
@@ -21,7 +21,7 @@ var prefsChangedObserver = null;
 
 
 function updateDisplay() {
-  var defaultallow = Prefs.prefs.getBoolPref('defaultPolicy.allow');
+  var defaultallow = rpPrefBranch.getBoolPref('defaultPolicy.allow');
   if (defaultallow) {
     document.getElementById('defaultallow').checked = true;
     document.getElementById('defaultdenysetting').hidden = true;
@@ -30,7 +30,7 @@ function updateDisplay() {
     document.getElementById('defaultdenysetting').hidden = false;
   }
 
-  var allowsamedomain = Prefs.prefs.getBoolPref('defaultPolicy.allowSameDomain');
+  var allowsamedomain = rpPrefBranch.getBoolPref('defaultPolicy.allowSameDomain');
   document.getElementById('allowsamedomain').checked = allowsamedomain;
 }
 
@@ -44,7 +44,7 @@ function onload() {
   document.getElementById('defaultallow').addEventListener('change',
       function (event) {
         var allow = event.target.checked;
-        Prefs.prefs.setBoolPref('defaultPolicy.allow', allow);
+        rpPrefBranch.setBoolPref('defaultPolicy.allow', allow);
         Services.prefs.savePrefFile(null);
         // Reload all subscriptions because it's likely that different
         // subscriptions will now be active.
@@ -56,7 +56,7 @@ function onload() {
   document.getElementById('defaultdeny').addEventListener('change',
       function (event) {
         var deny = event.target.checked;
-        Prefs.prefs.setBoolPref('defaultPolicy.allow', !deny);
+        rpPrefBranch.setBoolPref('defaultPolicy.allow', !deny);
         Services.prefs.savePrefFile(null);
         // Reload all subscriptions because it's likely that different
         // subscriptions will now be active.
@@ -68,7 +68,7 @@ function onload() {
   document.getElementById('allowsamedomain').addEventListener('change',
       function (event) {
         var allowSameDomain = event.target.checked;
-        Prefs.prefs.setBoolPref('defaultPolicy.allowSameDomain',
+        rpPrefBranch.setBoolPref('defaultPolicy.allowSameDomain',
             allowSameDomain);
         Services.prefs.savePrefFile(null);
       }
diff --git a/src/content/settings/oldrules.html b/src/content/settings/oldrules.html
index 0ca6940..57a4298 100644
--- a/src/content/settings/oldrules.html
+++ b/src/content/settings/oldrules.html
@@ -2,10 +2,11 @@
 <html>
 <head>
   <title>RequestPolicy - Old Rules</title>
-  <script src="jquery.min.js"></script>
-  <script src="common.js"></script>
-  <script src="oldrules.js"></script>
-  <link href="settings.css" rel="stylesheet" type="text/css"/>
+  <script src="chrome://requestpolicy/content/settings/jquery.min.js"></script>
+  <script src="chrome://requestpolicy/content/settings/common.js"></script>
+  <script src="chrome://requestpolicy/content/settings/oldrules.js"></script>
+  <link href="chrome://requestpolicy/content/settings/settings.css"
+        rel="stylesheet" type="text/css"/>
 </head>
 <body onload="onload()">
 <table>
@@ -112,7 +113,7 @@
             </div>
             <div id="importdone">
               Done! Your old rules have been imported into
-              <a href="yourpolicy.html">your policy</a>.
+              <a href="about:requestpolicy?yourpolicy">your policy</a>.
             </div>
 
           </div>
@@ -129,4 +130,4 @@
 </table>
 
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/src/content/settings/setup.html b/src/content/settings/setup.html
index 39caeab..2a7cabf 100644
--- a/src/content/settings/setup.html
+++ b/src/content/settings/setup.html
@@ -2,10 +2,11 @@
 <html>
 <head>
   <title>RequestPolicy - Setup</title>
-  <script src="jquery.min.js"></script>
-  <script src="common.js"></script>
-  <script src="setup.js"></script>
-  <!--<link href="setup.css" rel="stylesheet" type="text/css"/>-->
+  <script src="chrome://requestpolicy/content/settings/jquery.min.js"></script>
+  <script src="chrome://requestpolicy/content/settings/common.js"></script>
+  <script src="chrome://requestpolicy/content/settings/setup.js"></script>
+  <!--<link href="chrome://requestpolicy/content/settings/setup.css"
+      rel="stylesheet" type="text/css"/>-->
   <style>
     html {
       height: 100%;
@@ -218,4 +219,4 @@
 </div>
 
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/src/content/settings/setup.js b/src/content/settings/setup.js
index a68c5db..385b7bd 100644
--- a/src/content/settings/setup.js
+++ b/src/content/settings/setup.js
@@ -28,7 +28,7 @@ function showConfigure() {
 }
 
 function handleDefaultPolicyChange() {
-  Prefs.prefs.setBoolPref('defaultPolicy.allow',
+  rpPrefBranch.setBoolPref('defaultPolicy.allow',
       $('#defaultallow').prop('checked'));
   Services.prefs.savePrefFile(null);
   setAllowSameDomainBlockDisplay();
@@ -36,7 +36,7 @@ function handleDefaultPolicyChange() {
 }
 
 function handleAllowSameDomainChange() {
-  Prefs.prefs.setBoolPref('defaultPolicy.allowSameDomain',
+  rpPrefBranch.setBoolPref('defaultPolicy.allowSameDomain',
       $('#allowsamedomain').prop('checked'));
   Services.prefs.savePrefFile(null);
 }
@@ -79,16 +79,35 @@ function handleSubscriptionsChange() {
   }
 }
 
+/*
+function getArguments(args) {
+  let urlQuery = document.location.search || "";
+  if (urlQuery.length > 1) {
+    urlQuery = decodeURIComponent(urlQuery.substr(1));
+  }
+  let queryArgs = split("&");
+  for (let i in queryArgs) {
+    let tmp = queryArgs.split("=");
+    if (args.hasOwnProperty(tmp)) {
+      args[tmp[0]] = tmp[1];
+    }
+  }
+  return args;
+}*/
+
 function onload() {
+  var lastRPVersion = rpPrefBranch.getCharPref("lastVersion");
+
   // Populate the form values based on the user's current settings.
   // If the use has just upgrade from an 0.x version, populate based on the old
   // preferences and also do a rule import based on the old strictness settings.
   // Note: using version 1.0.0a8 instead of 1.0 as that was the last version
   // before this setup window was added.
-  if (Services.vc.compare(Utils.info.lastRPVersion, '0.0') > 0 &&
-      Services.vc.compare(Utils.info.lastRPVersion, '1.0.0a8') <= 0) {
-    if (Prefs.prefs.prefHasUserValue('uriIdentificationLevel')) {
-      var identLevel = Prefs.prefs.getIntPref('uriIdentificationLevel');
+  if (lastRPVersion &&
+      Services.vc.compare(lastRPVersion, '0.0') > 0 &&
+      Services.vc.compare(lastRPVersion, '1.0.0a8') <= 0) {
+    if (rpPrefBranch.prefHasUserValue('uriIdentificationLevel')) {
+      var identLevel = rpPrefBranch.getIntPref('uriIdentificationLevel');
     } else {
       var identLevel = 1;
     }
@@ -116,14 +135,14 @@ function onload() {
     // Skip the welcome screen.
     showConfigure();
   } else {
-    var defaultAllow = Prefs.prefs.getBoolPref('defaultPolicy.allow');
+    var defaultAllow = rpPrefBranch.getBoolPref('defaultPolicy.allow');
     $('#defaultallow').prop('checked', defaultAllow);
     $('#defaultdeny').prop('checked', !defaultAllow);
     if (!defaultAllow) {
       $('#allowsamedomainblock').css('display', 'block');
     }
     $('#allowsamedomain').prop('checked',
-        Prefs.prefs.getBoolPref('defaultPolicy.allowSameDomain'));
+        rpPrefBranch.getBoolPref('defaultPolicy.allowSameDomain'));
     // Subscriptions are only simple here if we assume the user won't open the
     // setup window again after changing their individual subscriptions through
     // the preferences. So, let's assume that as the worst case is that the setup
diff --git a/src/content/settings/subscriptions.html b/src/content/settings/subscriptions.html
index 8f6c0bf..3226713 100644
--- a/src/content/settings/subscriptions.html
+++ b/src/content/settings/subscriptions.html
@@ -2,10 +2,11 @@
 <html>
 <head>
   <title>RequestPolicy - Subscriptions</title>
-  <script src="jquery.min.js"></script>
-  <script src="common.js"></script>
-  <script src="subscriptions.js"></script>
-  <link href="settings.css" rel="stylesheet" type="text/css"/>
+  <script src="chrome://requestpolicy/content/settings/jquery.min.js"></script>
+  <script src="chrome://requestpolicy/content/settings/common.js"></script>
+  <script src="chrome://requestpolicy/content/settings/subscriptions.js"></script>
+  <link href="chrome://requestpolicy/content/settings/settings.css"
+        rel="stylesheet" type="text/css"/>
   <style>
   </style>
 </head>
@@ -18,17 +19,17 @@
         <div id="requestpolicy">RequestPolicy</div>
         <div id="mainnav" class="nav">
           <ul>
-            <li><a href="basicprefs.html" data-string="preferences"></a></li>
-            <li><a href="yourpolicy.html" selected="true" data-string="managePolicies"></a></li>
+            <li><a href="about:requestpolicy?basicprefs" data-string="preferences"></a></li>
+            <li><a href="about:requestpolicy?yourpolicy" selected="true" data-string="managePolicies"></a></li>
             <li><a href="https://github.com/RequestPolicyContinued/requestpolicy/wiki#help-and-support-for-users-and-developers" data-string="help" target="_blank"></a></li>
             <li><a href="https://requestpolicycontinued.github.io/#about" data-string="about" target="_blank"></a></li>
           </ul>
         </div>
         <div id="subnav1" class="nav subnav">
           <ul>
-            <li><a href="yourpolicy.html" data-string="yourPolicy"></a></li>
-            <li><a href="defaultpolicy.html" data-string="defaultPolicy"></a></li>
-            <li><a href="subscriptions.html" selected="true" data-string="subscriptions"></a></li>
+            <li><a href="about:requestpolicy?yourpolicy" data-string="yourPolicy"></a></li>
+            <li><a href="about:requestpolicy?defaultpolicy" data-string="defaultPolicy"></a></li>
+            <li><a href="about:requestpolicy?subscriptions" selected="true" data-string="subscriptions"></a></li>
           </ul>
         </div>
       </div>
@@ -44,7 +45,7 @@
              target="_blank" data-string="learnMore"></a></p>
 
         <table class="settings">
-          <tr data-default-policy="allow">
+          <tr data-defaultpolicy="allow">
             <td data-string="privacy"></td>
             <td>
               <div id="deny_trackers" class="subscription">
@@ -55,7 +56,7 @@
               </div>
             </td>
           </tr>
-          <tr data-default-policy="deny">
+          <tr data-defaultpolicy="deny">
             <td data-string="usability"></td>
             <td>
               <div id="allow_sameorg" class="subscription">
@@ -78,7 +79,7 @@
               </div>
             </td>
           </tr>
-          <tr data-default-policy="deny">
+          <tr data-defaultpolicy="deny">
             <td data-string="browser"></td>
             <td>
               <div id="allow_mozilla" class="subscription">
diff --git a/src/content/settings/subscriptions.js b/src/content/settings/subscriptions.js
index f44e4ac..cb08a8a 100644
--- a/src/content/settings/subscriptions.js
+++ b/src/content/settings/subscriptions.js
@@ -27,7 +27,7 @@ var prefsChangedObserver = null;
  *                 "allow" or "deny"
  */
 function getDefaultPolicyElements(policy) {
-  var selector = '[data-default-policy=' + policy + ']';
+  var selector = '[data-defaultpolicy=' + policy + ']';
   var matches = document.body.querySelectorAll(selector);
   var elements = Array.prototype.slice.call(matches);
   return elements;
@@ -126,7 +126,7 @@ function onload() {
     el.addEventListener('change', handleSubscriptionCheckboxChange);
   }
 
-  var selector = '[data-default-policy=' +
+  var selector = '[data-defaultpolicy=' +
     (Prefs.isDefaultAllow() ? 'deny' : 'allow') + ']';
   var matches = document.body.querySelectorAll(selector);
   var hideElements = Array.prototype.slice.call(matches);
diff --git a/src/content/settings/yourpolicy.html b/src/content/settings/yourpolicy.html
index 751de20..af135cf 100644
--- a/src/content/settings/yourpolicy.html
+++ b/src/content/settings/yourpolicy.html
@@ -2,10 +2,11 @@
 <html>
 <head>
   <title>RequestPolicy - Your Policy</title>
-  <script src="jquery.min.js"></script>
-  <script src="common.js"></script>
-  <script src="yourpolicy.js"></script>
-  <link href="settings.css" rel="stylesheet" type="text/css"/>
+  <script src="chrome://requestpolicy/content/settings/jquery.min.js"></script>
+  <script src="chrome://requestpolicy/content/settings/common.js"></script>
+  <script src="chrome://requestpolicy/content/settings/yourpolicy.js"></script>
+  <link href="chrome://requestpolicy/content/settings/settings.css"
+        rel="stylesheet" type="text/css"/>
 </head>
 <body onload="onload()">
 <table>
@@ -16,17 +17,17 @@
         <div id="requestpolicy">RequestPolicy</div>
         <div id="mainnav" class="nav">
           <ul>
-            <li><a href="basicprefs.html" data-string="preferences"></a></li>
-            <li><a href="yourpolicy.html" selected="true" data-string="managePolicies"></a></li>
+            <li><a href="about:requestpolicy?basicprefs" data-string="preferences"></a></li>
+            <li><a href="about:requestpolicy?yourpolicy" selected="true" data-string="managePolicies"></a></li>
             <li><a href="https://github.com/RequestPolicyContinued/requestpolicy/wiki#help-and-support-for-users-and-developers" data-string="help" target="_blank"></a></li>
             <li><a href="https://requestpolicycontinued.github.io/#about" data-string="about" target="_blank"></a></li>
           </ul>
         </div>
         <div id="subnav1" class="nav subnav">
           <ul>
-            <li><a href="yourpolicy.html" selected="true" data-string="yourPolicy"></a></li>
-            <li><a href="defaultpolicy.html" data-string="defaultPolicy"></a></li>
-            <li><a href="subscriptions.html" data-string="subscriptions"></a></li>
+            <li><a href="about:requestpolicy?yourpolicy" selected="true" data-string="yourPolicy"></a></li>
+            <li><a href="about:requestpolicy?defaultpolicy" data-string="defaultPolicy"></a></li>
+            <li><a href="about:requestpolicy?subscriptions" data-string="subscriptions"></a></li>
           </ul>
         </div>
       </div>
diff --git a/src/content/ui/classicmenu.js b/src/content/ui/classicmenu.js
index af90152..b5ec1b4 100644
--- a/src/content/ui/classicmenu.js
+++ b/src/content/ui/classicmenu.js
@@ -30,10 +30,9 @@ requestpolicy.classicmenu = (function() {
   Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
   ScriptLoader.importModules([
     "prefs",
-    "utils",
-    "requestpolicy-service"
+    "string-utils"
   ], mod);
-  let Prefs = mod.Prefs, Utils = mod.Utils, rpService = mod.rpService;
+  let rpPrefBranch = mod.rpPrefBranch, StringUtils = mod.StringUtils;
 
 
   let self = {
@@ -42,7 +41,7 @@ requestpolicy.classicmenu = (function() {
     * be reloaded.
     */
     _conditionallyReloadDocument : function() {
-      if (rpService.Prefs.getBoolPref("autoReload")) {
+      if (rpPrefBranch.getBoolPref("autoReload")) {
         content.document.location.reload(false);
       }
     },
@@ -67,7 +66,7 @@ requestpolicy.classicmenu = (function() {
 
 
     addMenuItemTemporarilyAllowOrigin : function(menu, originHost) {
-      var label = Utils.strbundle.formatStringFromName("allowOriginTemporarily",
+      var label = StringUtils.strbundle.formatStringFromName("allowOriginTemporarily",
           [originHost], 1);
       var command = "requestpolicy.overlay.temporarilyAllowOrigin('"
           + requestpolicy.menu._sanitizeJsFunctionArg(originHost) + "');";
@@ -77,7 +76,7 @@ requestpolicy.classicmenu = (function() {
     },
 
     addMenuItemAllowOrigin : function(menu, originHost) {
-      var label = Utils.strbundle.formatStringFromName("allowOrigin",
+      var label = StringUtils.strbundle.formatStringFromName("allowOrigin",
           [originHost], 1);
       var command = "requestpolicy.overlay.allowOrigin('"
           + requestpolicy.menu._sanitizeJsFunctionArg(originHost) + "');";
@@ -87,7 +86,7 @@ requestpolicy.classicmenu = (function() {
 
     addMenuItemTemporarilyAllowOriginToDest : function(menu, originHost,
                                                        destHost) {
-      var label = Utils.strbundle.formatStringFromName(
+      var label = StringUtils.strbundle.formatStringFromName(
           "allowOriginToDestinationTemporarily", [originHost, destHost], 2);
       var command = "requestpolicy.overlay.temporarilyAllowOriginToDestination('"
           + requestpolicy.menu._sanitizeJsFunctionArg(originHost) + "', '"
@@ -98,7 +97,7 @@ requestpolicy.classicmenu = (function() {
     },
 
     addMenuItemAllowOriginToDest : function(menu, originHost, destHost) {
-      var label = Utils.strbundle.formatStringFromName(
+      var label = StringUtils.strbundle.formatStringFromName(
           "allowOriginToDestination", [originHost, destHost], 2);
       var command = "requestpolicy.overlay.allowOriginToDestination('"
           + requestpolicy.menu._sanitizeJsFunctionArg(originHost) + "', '"
@@ -110,7 +109,7 @@ requestpolicy.classicmenu = (function() {
 
 
     addMenuItemTemporarilyAllowDest : function(menu, destHost) {
-      var label = Utils.strbundle.formatStringFromName(
+      var label = StringUtils.strbundle.formatStringFromName(
           "allowDestinationTemporarily", [destHost], 1);
       var command = "requestpolicy.overlay.temporarilyAllowDestination('"
           + requestpolicy.menu._sanitizeJsFunctionArg(destHost) + "');";
@@ -120,7 +119,7 @@ requestpolicy.classicmenu = (function() {
     },
 
     addMenuItemAllowDest : function(menu, destHost) {
-      var label = Utils.strbundle.formatStringFromName("allowDestination",
+      var label = StringUtils.strbundle.formatStringFromName("allowDestination",
           [destHost], 1);
       var command = "requestpolicy.overlay.allowDestination('"
           + requestpolicy.menu._sanitizeJsFunctionArg(destHost) + "');";
diff --git a/src/content/ui/frame.blocked-content.js b/src/content/ui/frame.blocked-content.js
new file mode 100644
index 0000000..47d2aae
--- /dev/null
+++ b/src/content/ui/frame.blocked-content.js
@@ -0,0 +1,101 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2011 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+var MMID = "requestpolicy at requestpolicy.com";
+
+let ManagerForBlockedContent = (function() {
+  let self = {};
+
+
+  let missingImageDataUri = "data:image/png;base64,"
+      + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c"
+      + "6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0"
+      + "SU1FB9gMFRANL5LXnioAAAJWSURBVDjLnZI/ixtXFMV/972ZNzPSrmTtalex"
+      + "lsWBGMfEYOzaVciXyKdIkW/hFKnS22WafIDUxk0g2AQSgm0csIPWK42ktaSR"
+      + "NPP+pRBK5SLOqS7cew7ccw4xxrPJ+8XdHx4+7AE8e3Cj++zLm71fvrqT8x+Q"
+      + "AK35dJr2n/x89urTa+eDm/cS+eI2y3eT+Lx/bt8u1vNqfDH++teXdk/6ThAf"
+      + "UUBIgL9ku75z/8WL7LOlhXIGJ0Pyw75wMcnGv//xSQ2DH4ddu9k01dXWsWzc"
+      + "ofhYaiiViLjiWi9UWQa1gzcjWF7hgfzzW5ydnXB62JLjg0PTLfJertNepnQS"
+      + "IA+gE4Cs03UuNYYQYP4e5jPogmSG9vA6rrjC+0AxN2i5Qk0DpXVJhCQB0EVR"
+      + "rzqdFgB1DZfvCDHixiV2NqO6LHHKIKnQMoaWbFBgIrQVgIXaDc+JCHgP5QRZ"
+      + "r4jzGWFbo6yncRYviiiQKUhBRch3Lyix4bgPWsAkcDkmZAV2OiE0DaI1WoES"
+      + "hRKF3sWnmt01pFBnJydEpZDEwHSGt47lYsls43AIXjTWV9R1Qx0DGahqLyAh"
+      + "bqrj0/ib0nRzXNoyCo0Kkor2llV0eKOwdUMg4pSQA7JPQXvnJv1B+GlwOvrG"
+      + "laXB6fV2lb5t6qOtike56DSJgYDGBQcOAsQAfueBMeHR48fhadb1j/58HWAR"
+      + "dt6yBv7+/vpBe2o5OogxlcaKdt5aKCNsk309W0WxKQjmQ33/9mJVAdWHdmo/"
+      + "tNvtRZIkfCz+ZQwGg6rT6Zj/LTAajTbD4bD5WIF/AAseEisPFO8uAAAAAElF"
+      + "TkSuQmCC";
+
+  let transparentImageDataUri = "data:image/gif;base64,R0lGODlhAQABAIAAA"
+      + "AAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
+
+  function indicateBlockedVisibleObjects(message) {
+    let {blockedURIs, docID} = message.data;
+    let document = DocManager.getDocument(docID);
+    if (!document) {
+      return;
+    }
+    let images = document.getElementsByTagName("img");
+
+    // Ideally, want the image to be a broken image so that the alt text
+    // shows. By default, the blocked image will just not show up at all.
+    // Setting img.src to a broken resource:// causes "save page as" to fail
+    // for some earlier Fx 3.x versions. Also, using a broken resource://
+    // causes our width setting to be ignored, as does using null for img.src.
+    // With Firefox 4, setting img.src to null doesn't work reliably for
+    // having the rest of the styles (e.g. background and border) applied.
+    // So, for now we're punting on trying to display alt text. We'll just use
+    // a transparent image as the replacement image.
+    // Note that with our changes to the image here, "save page as" works but
+    // different data is saved depending on what type of "save page as" the
+    // user performs. With "save all files", the saved source includes the
+    // original, blocked image src. With "web page, complete" the saved source
+    // has changes we make here to show the blocked request indicator.
+
+    for (var i = 0; i < images.length; i++) {
+      var img = images[i];
+      // Note: we're no longer checking img.requestpolicyBlocked here.
+      if (!img.requestpolicyIdentified && img.src in blockedURIs) {
+        img.requestpolicyIdentified = true;
+        img.style.border = "solid 1px #fcc";
+        img.style.backgroundRepeat = "no-repeat";
+        img.style.backgroundPosition = "center center";
+        img.style.backgroundImage = "url('" + missingImageDataUri + "')";
+        if (!img.width) {
+          img.width = 50;
+        }
+        if (!img.height) {
+          img.height = 50;
+        }
+        img.title = "[" + blockedURIs[img.src].identifier + "]"
+            + (img.title ? " " + img.title : "")
+            + (img.alt ? " " + img.alt : "");
+        img.src = transparentImageDataUri;
+      }
+    }
+  }
+
+  addMessageListener(MMID + ":indicateBlockedVisibleObjects",
+                     indicateBlockedVisibleObjects);
+
+  return self;
+}());
diff --git a/src/content/ui/frame.doc-manager.js b/src/content/ui/frame.doc-manager.js
new file mode 100644
index 0000000..8d77325
--- /dev/null
+++ b/src/content/ui/frame.doc-manager.js
@@ -0,0 +1,53 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+/**
+ * This singleton module can be used for having a reference to documents
+ * (whether top level or frame documents). This is necessary when chrome code
+ * needs to call functions on specific documents.
+ */
+let DocManager = (function() {
+  let self = {};
+
+  let nextDocID = 0;
+  let documents = [];
+
+  self.generateDocID = function(doc) {
+    let docID = nextDocID++;
+    documents[docID] = doc;
+
+    // Destructor function:
+    // As soon as the document is unloaded, delete the reference.
+    // The unload event is called when the document's location changes.
+    content.addEventListener("unload", function() {
+      delete documents[docID];
+    });
+
+    return docID;
+  };
+
+  self.getDocument = function(docID) {
+    return documents[docID] || null;
+  };
+
+  return self;
+}());
diff --git a/src/content/ui/frame.dom-content-loaded.js b/src/content/ui/frame.dom-content-loaded.js
new file mode 100644
index 0000000..9326949
--- /dev/null
+++ b/src/content/ui/frame.dom-content-loaded.js
@@ -0,0 +1,261 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2011 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+var MMID = "requestpolicy at requestpolicy.com";
+
+
+let ManagerForDOMContentLoaded = (function() {
+  let self = {};
+
+  let mod = {};
+  const Ci = Components.interfaces;
+  const Cc = Components.classes;
+  const Cu = Components.utils;
+  Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+  mod.ScriptLoader.importModules(["domain-util", "logger"], mod);
+
+
+  function htmlAnchorTagClicked(event) {
+    // Note: need to use currentTarget so that it is the <a> element. See:
+    // https://developer.mozilla.org/en-US/docs/Web/API/Event.currentTarget
+    sendSyncMessage(MMID + ":notifyLinkClicked",
+                    {origin: event.currentTarget.ownerDocument.URL,
+                     dest: event.currentTarget.href});
+    dump("<a> clicked\n");
+  }
+
+
+  /**
+   * Determines if documentToCheck is the main document loaded in the currently
+   * active tab.
+   *
+   * @param {document}
+   *          documentToCheck
+   * @return {Boolean}
+   */
+  function isActiveTopLevelDocument(documentToCheck) {
+    return documentToCheck === content.document;
+  }
+
+  /**
+   * Things to do when a page has loaded (after images, etc., have been loaded).
+   *
+   * @param {Event}
+   *          event
+   */
+  function onDOMContentLoaded(event) {
+    // TODO: This is getting called multiple times for a page, should only be
+    // called once.
+    let doc = event.originalTarget;
+    if (doc.nodeName != "#document") {
+      // only documents
+      return;
+    }
+/*
+    if (!doc) {
+      // onDOMContentLoaded getting called more often than it should? document
+      // isn't set on new tab open when this is called.
+      return;
+    }*/
+    dump("onDOMContentLoaded called for " + doc.documentURI + "\n");
+
+    onDocumentLoaded(doc);
+    let docID = DocManager.generateDocID(doc);
+    sendAsyncMessage(MMID + ":notifyDocumentLoaded",
+                     {docID: docID, documentURI: doc.documentURI});
+
+
+    if (isActiveTopLevelDocument(doc)) {
+      sendAsyncMessage(MMID + ":notifyTopLevelDocumentLoaded");
+    }
+  }
+
+  /**
+   * Things to do when a page or a frame within the page has loaded.
+   *
+   * @param {Event} event
+   */
+  function onDOMFrameContentLoaded(event) {
+    // TODO: This only works for (i)frames that are direct children of the main
+    // document, not (i)frames within those (i)frames.
+    var iframe = event.target;
+    // Flock's special home page is about:myworld. It has (i)frames in it
+    // that have no contentDocument. It's probably related to the fact that
+    // that is an xul page.
+    if (iframe.contentDocument === undefined) {
+      return;
+    }
+    //dump("onDOMFrameContentLoaded called for <" +
+    //    iframe.contentDocument.documentURI + "> in <" +
+    //    iframe.ownerDocument.documentURI + ">");
+
+    // TODO: maybe this can check if the iframe's documentURI is in the other
+    // origins of the current document, and that way not just be limited to
+    // direct children of the main document. That would require building the
+    // other origins every time an iframe is loaded. Maybe, then, this should
+    // use a timeout like observerBlockedRequests does.
+    if (isActiveTopLevelDocument(iframe.ownerDocument)) {
+      sendAsyncMessage(MMID + ":notifyDOMFrameContentLoaded");
+      /*
+      // This has an advantage over just relying on the
+      // observeBlockedRequest() call in that this will clear a blocked
+      // content notification if there no longer blocked content. Another way
+      // to solve this would be to observe allowed requests as well as blocked
+      // requests.
+      self._updateBlockedContentState(iframe.ownerDocument);
+      */
+    }
+  }
+
+
+  /**
+   * Perform the actions required once the DOM is loaded. This may be being
+   * called for more than just the page content DOM. It seems to work for now.
+   *
+   * @param {Event} event
+   */
+  function onDocumentLoaded(document) {
+    let documentURI = document.documentURI;
+
+    let metaRefreshes = [];
+
+    // Find all meta redirects.
+    var metaTags = document.getElementsByTagName("meta");
+    for (var i = 0; i < metaTags.length; i++) {
+      let metaTag = metaTags[i];
+      if (!metaTag.httpEquiv || metaTag.httpEquiv.toLowerCase() != "refresh") {
+        continue;
+      }
+
+      let originalDestURI = null;
+
+      // TODO: Register meta redirects so we can tell which blocked requests
+      // were meta redirects in the statusbar menu.
+      // TODO: move this logic to the requestpolicy service.
+
+      // The dest may be empty if the origin is what should be refreshed. This
+      // will be handled by DomainUtil.determineRedirectUri().
+      let {delay, destURI} = mod.DomainUtil.parseRefresh(metaTag.content);
+
+      // If destURI isn't a valid uri, assume it's a relative uri.
+      if (!mod.DomainUtil.isValidUri(destURI)) {
+        originalDestURI = destURI;
+        destURI = document.documentURIObject.resolve(destURI);
+      }
+
+      metaRefreshes.push({delay: delay, destURI: destURI,
+                          originalDestURI: originalDestURI});
+    }
+
+    if (metaRefreshes.length > 0) {
+      dump("meta refreshes found.\n");
+
+      var docShell = document.defaultView
+                             .QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIWebNavigation)
+                             .QueryInterface(Ci.nsIDocShell);
+      if (!docShell.allowMetaRedirects) {
+        Logger.warning(Logger.TYPE_META_REFRESH,
+            "Another extension disabled docShell.allowMetaRedirects.");
+      }
+
+      sendAsyncMessage(MMID + ":handleMetaRefreshes",
+          {documentURI: documentURI, metaRefreshes: metaRefreshes});
+    }
+
+    // Find all anchor tags and add click events (which also fire when enter
+    // is pressed while the element has focus).
+    // This seems to be a safe approach in that the MDC states that javascript
+    // can't be used to initiate a click event on a link:
+    // http://developer.mozilla.org/en/DOM/element.click
+    // We keep this even though we have the document looking for clicks because
+    // for certain links the target will not be the link (and we can't use the
+    // currentTarget in the other case it seems, as we can here). There probably
+    // is some solution when handling the click events at the document level,
+    // but I just don't know what it is. For now, there remains the risk of
+    // dynamically added links whose target of the click event isn't the anchor
+    // tag.
+    var anchorTags = document.getElementsByTagName("a");
+    for (var i = 0; i < anchorTags.length; i++) {
+      anchorTags[i].addEventListener("click", htmlAnchorTagClicked, false);
+    }
+
+    wrapWindowFunctions(document.defaultView);
+  }
+
+  /**
+   * This function wraps an existing method of a window object.
+   * If that method is being called after being wrapped, first the custom
+   * function will be called and then the original function.
+   *
+   * @param {Window} aWindow
+   * @param {String} aFunctionName The name of the window's method.
+   * @param {Function} aNewFunction
+   */
+  function wrapWindowFunction(aWindow, aFunctionName, aNewFunction) {
+    let originals = aWindow.rpOriginalFunctions
+        = aWindow.rpOriginalFunctions || {};
+    if (!(aFunctionName in originals)) {
+      originals[aFunctionName] = aWindow[aFunctionName];
+      aWindow[aFunctionName] = function() {
+        aNewFunction.apply(aWindow, arguments);
+        return originals[aFunctionName].apply(aWindow, arguments);
+      }
+    }
+  }
+
+  /**
+   * Wraps the window's open() and openDialog() methods so that RequestPolicy
+   * can know the origin and destination URLs of the window being opened. Assume
+   * that if window.open() calls have made it this far, it's a window the user
+   * wanted open (e.g. they have allowed the popup). Unfortunately, this method
+   * (or our timing of doing self) doesn't seem to work for popups that are
+   * allowed popups (the user has allowed popups from the domain). So, the
+   * workaround was to also add the 'if(aContext.nodeName == "xul:browser" &&
+   * aContext.currentURI && aContext.currentURI.spec == "about:blank")' to
+   * shouldLoad().
+   *
+   * @param {Window} aWindow
+   */
+  function wrapWindowFunctions(aWindow) {
+    wrapWindowFunction(aWindow, "open",
+        function(url, windowName, windowFeatures) {
+          rpService.registerLinkClicked(aWindow.document.documentURI, url);
+        });
+
+    wrapWindowFunction(aWindow, "openDialog",
+        function() {
+          // openDialog(url, name, features, arg1, arg2, ...)
+          rpService.registerLinkClicked(aWindow.document.documentURI,
+              arguments[0]);
+        });
+  }
+
+
+  addEventListener("DOMContentLoaded", onDOMContentLoaded, true);
+
+  // DOMFrameContentLoaded is same DOMContentLoaded but also fires for
+  // enclosed frames.
+  addEventListener("DOMFrameContentLoaded", onDOMFrameContentLoaded, true);
+
+  return self;
+}());
diff --git a/src/content/ui/frame.js b/src/content/ui/frame.js
new file mode 100644
index 0000000..661850c
--- /dev/null
+++ b/src/content/ui/frame.js
@@ -0,0 +1,96 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2011 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+var MMID = "requestpolicy at requestpolicy.com";
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+/*
+let scope = (function() {
+  let self = {};
+
+
+
+
+  // Listen for click events so that we can allow requests that result from
+  // user-initiated link clicks and form submissions.
+  addEventListener("click", function(event) {
+    // If mozInputSource is undefined or zero, then this was a javascript-generated event.
+    // If there is a way to forge mozInputSource from javascript, then that could be used
+    // to bypass RequestPolicy.
+    if (!event.mozInputSource) {
+      return;
+    }
+    // The following show up as button value 0 for links and form input submit buttons:
+    // * left-clicks
+    // * enter key while focused
+    // * space bar while focused (no event sent for links in this case)
+    if (event.button != 0) {
+      return;
+    }
+    // Link clicked.
+    // I believe an empty href always gets filled in with the current URL so
+    // it will never actually be empty. However, I don't know this for certain.
+    if (event.target.nodeName.toLowerCase() == "a" && event.target.href) {
+      sendSyncMessage(MMID + ":notifyLinkClicked",
+                      {origin: event.target.ownerDocument.URL,
+                       dest: event.target.href});
+      return;
+    }
+    // Form submit button clicked. This can either be directly (e.g. mouseclick,
+    // enter/space while the the submit button has focus) or indirectly (e.g.
+    // pressing enter when a text input has focus).
+    if (event.target.nodeName.toLowerCase() == "input" &&
+        event.target.type.toLowerCase() == "submit" &&
+        event.target.form && event.target.form.action) {
+      sendSyncMessage(MMID + ":registerFormSubmitted",
+                      {origin: event.target.ownerDocument.URL,
+                       dest: event.target.form.action});
+      return;
+    }
+  }, true);*/
+
+  addMessageListener(MMID + ":reload", function() {
+    content.document.location.reload(false);
+  });
+
+  addMessageListener(MMID + ":setLocation", function(message) {
+    let location = content.document.location;
+    // When refreshing a page that wants to redirect, sometimes the
+    // targetDocument.location is null. If that's the case, just use
+    // do the redirection in the current content pane.
+    if (location == null) {
+      //dump("in setLocation: content.document.location == null, " +
+      //    "using content.location instead");
+      location = content.location;
+    }
+    location.href = message.data.uri;
+  });
+/*
+  return self;
+}());*/
+
+Services.scriptloader.loadSubScript(
+    'chrome://requestpolicy/content/ui/frame.blocked-content.js');
+Services.scriptloader.loadSubScript(
+    'chrome://requestpolicy/content/ui/frame.dom-content-loaded.js');
+Services.scriptloader.loadSubScript(
+    'chrome://requestpolicy/content/ui/frame.doc-manager.js');
diff --git a/src/content/ui/menu.js b/src/content/ui/menu.js
index dcffa3a..ab5baa6 100644
--- a/src/content/ui/menu.js
+++ b/src/content/ui/menu.js
@@ -39,18 +39,18 @@ requestpolicy.menu = (function() {
     "domain-util",
     "ruleset",
     "gui-location",
-    "utils",
+    "string-utils",
 
     "requestpolicy-service",
     "constants"
   ], mod);
-  let Logger = mod.Logger, Prefs = mod.Prefs,
+  let Logger = mod.Logger, rpPrefBranch = mod.rpPrefBranch, Prefs = mod.Prefs,
       RequestProcessor = mod.RequestProcessor,
       PolicyManager = mod.PolicyManager, DomainUtil = mod.DomainUtil,
       Ruleset = mod.Ruleset, GUILocation = mod.GUILocation,
       GUIOrigin = mod.GUIOrigin, GUIDestination = mod.GUIDestination,
       GUILocationProperties = mod.GUILocationProperties,
-      Utils = mod.Utils, rpService = mod.rpService;
+      StringUtils = mod.StringUtils, rpService = mod.rpService;
 
 
   Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
@@ -146,18 +146,18 @@ requestpolicy.menu = (function() {
           return;
         }
 
-        // The fact that getAllRequestsOnDocument uses documentURI directly from
-        // content.document is important because getTopLevelDocumentUri will
-        // not return the real documentURI if there is an applicable
+        // The fact that getAllRequestsInBrowser uses currentURI.spec directly
+        // from the browser is important because getTopLevelDocumentUri will
+        // not return the real URI if there is an applicable
         // top-level document translation rule (these are used sometimes
         // for extension compatibility). For example, this is essential to the
         // menu showing relevant info when using the Update Scanner extension.
         self._allRequestsOnDocument = RequestProcessor
-              .getAllRequestsOnDocument(content.document);
+              .getAllRequestsInBrowser(gBrowser.selectedBrowser);
         self._allRequestsOnDocument.print("_allRequestsOnDocument");
 
         self._privateBrowsingEnabled = rpService.isPrivateBrowsingEnabled()
-            && !Prefs.prefs.getBoolPref("privateBrowsingPermanentWhitelisting");
+            && !rpPrefBranch.getBoolPref("privateBrowsingPermanentWhitelisting");
 
         self._setPrivateBrowsingStyles();
 
@@ -166,8 +166,9 @@ requestpolicy.menu = (function() {
     //      self._itemPrefetchWarningSeparator.hidden = hidePrefetchInfo;
     //
     //      if (isChromeUri) {
-    //        self._itemUnrestrictedOrigin.setAttribute("label", Utils.strbundle
-    //          .formatStringFromName("unrestrictedOrigin", ["chrome://"]), 1);
+    //        self._itemUnrestrictedOrigin.setAttribute("label",
+    //            StringUtils.strbundle.formatStringFromName(
+    //                "unrestrictedOrigin", ["chrome://"]), 1);
     //        self._itemUnrestrictedOrigin.hidden = false;
     //        return;
     //      }
@@ -186,7 +187,7 @@ requestpolicy.menu = (function() {
 
     _populateMenuForUncontrollableOrigin: function() {
       self._originDomainnameItem.setAttribute('value',
-          Utils.strbundle.GetStringFromName('noOrigin'));
+          StringUtils.strbundle.GetStringFromName('noOrigin'));
       self._originNumRequestsItem.setAttribute('value', '');
       self._originItem.removeAttribute("default-policy");
       self._originItem.removeAttribute("requests-blocked");
@@ -212,8 +213,8 @@ requestpolicy.menu = (function() {
 
       if (true === guiLocations) {
         // get prefs
-        var sorting = Prefs.prefs.getCharPref('menu.sorting');
-        var showNumRequests = Prefs.prefs.getBoolPref('menu.info.showNumRequests');
+        var sorting = rpPrefBranch.getCharPref('menu.sorting');
+        var showNumRequests = rpPrefBranch.getBoolPref('menu.info.showNumRequests');
 
         if (sorting == "numRequests") {
           values.sort(GUILocation.sortByNumRequestsCompareFunction);
@@ -251,7 +252,7 @@ requestpolicy.menu = (function() {
     _populateOrigin: function() {
       self._originDomainnameItem.setAttribute("value", self._currentBaseDomain);
 
-      var showNumRequests = Prefs.prefs
+      var showNumRequests = rpPrefBranch
           .getBoolPref('menu.info.showNumRequests');
 
       var props = self._getOriginGUILocationProperties();
@@ -928,7 +929,7 @@ requestpolicy.menu = (function() {
 
     _addMenuItemHelper: function(list, ruleData, fmtStrName, fmtStrArgs,
         ruleAction, cssClass) {
-      var label = Utils.strbundle.formatStringFromName(fmtStrName, fmtStrArgs,
+      var label = StringUtils.strbundle.formatStringFromName(fmtStrName, fmtStrArgs,
           fmtStrArgs.length);
       var item = self._addListItem(list, 'rp-od-item', label);
       item.requestpolicyRuleData = ruleData;
diff --git a/src/content/ui/overlay.js b/src/content/ui/overlay.js
index c05dd95..529448d 100644
--- a/src/content/ui/overlay.js
+++ b/src/content/ui/overlay.js
@@ -35,20 +35,21 @@ requestpolicy.overlay = (function() {
   Cu.import("resource://gre/modules/Services.jsm");
 
   let mod = {};
-  Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
-  ScriptLoader.importModules([
+  Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+  mod.ScriptLoader.importModules([
+    "constants",
     "logger",
     "prefs",
     "request-processor",
     "domain-util",
-    "utils",
+    "string-utils",
     "requestpolicy-service",
     "policy-manager"
   ], mod);
-  let Logger = mod.Logger, Prefs = mod.Prefs,
-      RequestProcessor = mod.RequestProcessor, DomainUtil = mod.DomainUtil,
-      Utils = mod.Utils, rpService = mod.rpService,
-      PolicyManager = mod.PolicyManager;
+  let MMID = mod.MMID, Logger = mod.Logger, rpPrefBranch = mod.rpPrefBranch,
+      Prefs = mod.Prefs, RequestProcessor = mod.RequestProcessor,
+      DomainUtil = mod.DomainUtil, StringUtils = mod.StringUtils,
+      rpService = mod.rpService, PolicyManager = mod.PolicyManager;
 
   //let _extensionConflictInfoUri = "http://www.requestpolicy.com/conflict?ext=";
 
@@ -62,7 +63,7 @@ requestpolicy.overlay = (function() {
 
   let overlayId = 0;
 
-  let blockedContentCheckTimeoutDelay = 250; // milliseconds
+  let blockedContentStateUpdateDelay = 250; // milliseconds
   let blockedContentCheckTimeoutId = null;
   let blockedContentCheckMinWaitOnObservedBlockedRequest = 500;
   let blockedContentCheckLastTime = 0;
@@ -79,28 +80,6 @@ requestpolicy.overlay = (function() {
 
   let isFennec = false;
 
-  let missingImageDataUri = "data:image/png;base64,"
-      + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c"
-      + "6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0"
-      + "SU1FB9gMFRANL5LXnioAAAJWSURBVDjLnZI/ixtXFMV/972ZNzPSrmTtalex"
-      + "lsWBGMfEYOzaVciXyKdIkW/hFKnS22WafIDUxk0g2AQSgm0csIPWK42ktaSR"
-      + "NPP+pRBK5SLOqS7cew7ccw4xxrPJ+8XdHx4+7AE8e3Cj++zLm71fvrqT8x+Q"
-      + "AK35dJr2n/x89urTa+eDm/cS+eI2y3eT+Lx/bt8u1vNqfDH++teXdk/6ThAf"
-      + "UUBIgL9ku75z/8WL7LOlhXIGJ0Pyw75wMcnGv//xSQ2DH4ddu9k01dXWsWzc"
-      + "ofhYaiiViLjiWi9UWQa1gzcjWF7hgfzzW5ydnXB62JLjg0PTLfJertNepnQS"
-      + "IA+gE4Cs03UuNYYQYP4e5jPogmSG9vA6rrjC+0AxN2i5Qk0DpXVJhCQB0EVR"
-      + "rzqdFgB1DZfvCDHixiV2NqO6LHHKIKnQMoaWbFBgIrQVgIXaDc+JCHgP5QRZ"
-      + "r4jzGWFbo6yncRYviiiQKUhBRch3Lyix4bgPWsAkcDkmZAV2OiE0DaI1WoES"
-      + "hRKF3sWnmt01pFBnJydEpZDEwHSGt47lYsls43AIXjTWV9R1Qx0DGahqLyAh"
-      + "bqrj0/ib0nRzXNoyCo0Kkor2llV0eKOwdUMg4pSQA7JPQXvnJv1B+GlwOvrG"
-      + "laXB6fV2lb5t6qOtike56DSJgYDGBQcOAsQAfueBMeHR48fhadb1j/58HWAR"
-      + "dt6yBv7+/vpBe2o5OogxlcaKdt5aKCNsk309W0WxKQjmQ33/9mJVAdWHdmo/"
-      + "tNvtRZIkfCz+ZQwGg6rT6Zj/LTAajTbD4bD5WIF/AAseEisPFO8uAAAAAElF"
-      + "TkSuQmCC";
-
-  let transparentImageDataUri = "data:image/gif;base64,R0lGODlhAQABAIAAA"
-      + "AAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
-
 
 
   let self = {
@@ -147,7 +126,7 @@ requestpolicy.overlay = (function() {
           // object's observerBlockedRequests() method will be called.
           RequestProcessor.addRequestObserver(self);
 
-          //self.setContextMenuEnabled(Prefs.prefs.getBoolPref("contextMenu"));
+          //self.setContextMenuEnabled(rpPrefBranch.getBoolPref("contextMenu"));
           self._setPermissiveNotification(Prefs.isBlockingDisabled());
         }
       } catch (e) {
@@ -177,58 +156,12 @@ requestpolicy.overlay = (function() {
      *          event
      */
     onWindowLoad: function() {
-      try {
+      //try {
         // Info on detecting page load at:
         // http://developer.mozilla.org/En/Code_snippets/On_page_load
         var appcontent = document.getElementById("appcontent"); // browser
         const requestpolicyOverlay = this;
         if (appcontent) {
-          appcontent.addEventListener("DOMContentLoaded", function(event) {
-            requestpolicyOverlay.onAppContentLoaded(event);
-          }, true);
-
-          // DOMFrameContentLoaded is same DOMContentLoaded but also fires for
-          // enclosed frames.
-          appcontent.addEventListener("DOMFrameContentLoaded", function(event) {
-            requestpolicyOverlay.onAppFrameContentLoaded(event);
-          }, true);
-
-          // Listen for click events so that we can allow requests that result from
-          // user-initiated link clicks and form submissions.
-          appcontent.addEventListener("click", function(event) {
-            // If mozInputSource is undefined or zero, then this was a javascript-generated event.
-            // If there is a way to forge mozInputSource from javascript, then that could be used
-            // to bypass RequestPolicy.
-            if (!event.mozInputSource) {
-              return;
-            }
-            // The following show up as button value 0 for links and form input submit buttons:
-            // * left-clicks
-            // * enter key while focused
-            // * space bar while focused (no event sent for links in this case)
-            if (event.button != 0) {
-              return;
-            }
-            // Link clicked.
-            // I believe an empty href always gets filled in with the current URL so
-            // it will never actually be empty. However, I don't know this for certain.
-            if (event.target.nodeName.toLowerCase() == "a" && event.target.href) {
-              RequestProcessor.registerLinkClicked(
-                  event.target.ownerDocument.URL, event.target.href);
-              return;
-            }
-            // Form submit button clicked. This can either be directly (e.g. mouseclick,
-            // enter/space while the the submit button has focus) or indirectly (e.g.
-            // pressing enter when a text input has focus).
-            if (event.target.nodeName.toLowerCase() == "input" &&
-                event.target.type.toLowerCase() == "submit" &&
-                event.target.form && event.target.form.action) {
-              RequestProcessor.registerFormSubmitted(
-                event.target.ownerDocument.URL, event.target.form.action);
-              return;
-            }
-          }, true);
-
           if (isFennec) {
             appcontent.addEventListener("TabSelect", function(event) {
               requestpolicyOverlay.tabChanged();
@@ -236,6 +169,114 @@ requestpolicy.overlay = (function() {
           }
         }
 
+
+
+        messageManager.addMessageListener(
+            MMID + ":notifyDocumentLoaded",
+            function(message) {
+              dump("notifyDocumentLoaded\n\n");
+              let {docID, documentURI} = message.data;
+
+              // the <browser> element of the corresponding tab.
+              let browser = message.target;
+
+              if (rpPrefBranch.getBoolPref("indicateBlockedObjects")) {
+                var indicateBlacklisted = rpPrefBranch
+                    .getBoolPref("indicateBlacklistedObjects");
+
+                var rejectedRequests = RequestProcessor._rejectedRequests
+                    .getOriginUri(documentURI);
+                let blockedURIs = {};
+                for (var destBase in rejectedRequests) {
+                  for (var destIdent in rejectedRequests[destBase]) {
+                    for (var destUri in rejectedRequests[destBase][destIdent]) {
+                      // case 1: indicateBlacklisted == true
+                      //         ==> indicate the object has been blocked
+                      //
+                      // case 2: indicateBlacklisted == false
+                      // case 2a: all requests have been blocked because of a blacklist
+                      //          ==> do *not* indicate
+                      //
+                      // case 2b: at least one of the blocked (identical) requests has been
+                      //          blocked by a rule *other than* the blacklist
+                      //          ==> *do* indicate
+                      let requests = rejectedRequests[destBase][destIdent][destUri];
+                      if (indicateBlacklisted ||
+                          requestpolicyOverlay._containsNonBlacklistedRequests(
+                              requests)) {
+                        blockedURIs[destUri] = blockedURIs[destUri] ||
+                            {identifier: DomainUtil.getIdentifier(destUri)};
+                      }
+                    }
+                  }
+                }
+                message.target.messageManager.sendAsyncMessage(
+                    MMID + ":indicateBlockedVisibleObjects",
+                    {blockedURIs: blockedURIs, docID: docID});
+              }
+
+              if ("requestpolicy" in browser &&
+                  documentURI in browser.requestpolicy.blockedRedirects) {
+                var dest = browser.requestpolicy.blockedRedirects[documentURI];
+                Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+                    "Showing notification for blocked redirect. To <" + dest +
+                    "> " + "from <" + documentURI + ">");
+                self._showRedirectNotification(browser, dest);
+
+                delete browser.requestpolicy.blockedRedirects[documentURI];
+              }
+            });
+
+        messageManager.addMessageListener(
+            MMID + ":notifyTopLevelDocumentLoaded",
+            function (message) {
+              // Clear any notifications that may have been present.
+              self._setContentBlockedState(false);
+              // We don't do this immediately anymore because slow systems might have
+              // this slow down the loading of the page, which is noticable
+              // especially with CSS loading delays (it's not unlikely that slow
+              // webservers have a hand in this, too).
+              // Note that the change to _updateBlockedContentStateAfterTimeout seems to have
+              // added a bug where opening a blank tab and then quickly switching back
+              // to the original tab can cause the original tab's blocked content
+              // notification to be cleared. A simple compensation was to decrease
+              // the timeout from 1000ms to 250ms, making it much less likely the tab
+              // switch can be done in time for a blank opened tab. This isn't a real
+              // solution, though.
+              self._updateBlockedContentStateAfterTimeout();
+            });
+
+        messageManager.addMessageListener(
+            MMID + ":notifyDOMFrameContentLoaded",
+            function (message) {
+              // This has an advantage over just relying on the
+              // observeBlockedRequest() call in that this will clear a blocked
+              // content notification if there no longer blocked content. Another way
+              // to solve this would be to observe allowed requests as well as blocked
+              // requests.
+              blockedContentCheckLastTime = (new Date()).getTime();
+              self._stopBlockedContentCheckTimeout();
+              self._updateBlockedContentState(message.target);
+            });
+
+        messageManager.addMessageListener(MMID + ":handleMetaRefreshes",
+                                          self.handleMetaRefreshes);
+
+        messageManager.addMessageListener(
+            MMID + ":notifyLinkClicked", function (message) {
+                RequestProcessor.registerLinkClicked(message.data.origin,
+                                                     message.data.dest);
+            });
+
+        messageManager.addMessageListener(
+            MMID + ":notifyFormSubmitted", function (message) {
+                RequestProcessor.registerFormSubmitted(message.data.origin,
+                                                       message.data.dest);
+            });
+
+
+
+
         // Add an event listener for when the contentAreaContextMenu (generally
         // the right-click menu within the document) is shown.
         var contextMenu = document.getElementById("contentAreaContextMenu");
@@ -260,10 +301,45 @@ requestpolicy.overlay = (function() {
           self._addHistoryObserver();
         }
 
-      } catch (e) {
-        Logger.severeError("Fatal Error, " + e, e);
-        Logger.severeError(
-            "Unable to complete requestpolicy.overlay.onWindowLoad actions.");
+      //} catch (e) {
+      //  Logger.severeError("Fatal Error, " + e, e);
+      //  Logger.severeError(
+      //      "Unable to complete requestpolicy.overlay.onWindowLoad actions.");
+      //}
+    },
+
+    handleMetaRefreshes: function(message) {
+      let {documentURI, metaRefreshes} = message.data;
+      let browser = message.target;
+
+      for (let i = 0, len = metaRefreshes.length; i < len; ++i) {
+        let {delay, destURI, originalDestURI} = metaRefreshes[i];
+
+        Logger.info(Logger.TYPE_META_REFRESH, "meta refresh to <" +
+            destURI + "> (" + delay + " second delay) found in document at <" +
+            documentURI + ">");
+
+        if (originalDestURI) {
+          Logger.info(Logger.TYPE_META_REFRESH,
+              "meta refresh destination <" + originalDestURI + "> " +
+              "appeared to be relative to <" + documentURI + ">, so " +
+              "it has been resolved to <" + destURI + ">");
+        }
+
+        // We don't automatically perform any allowed redirects. Instead, we
+        // just detect when they will be blocked and show a notification. If
+        // the docShell has allowMetaRedirects disabled, it will be respected.
+        if (!Prefs.isBlockingDisabled()
+            && !RequestProcessor.isAllowedRedirect(documentURI, destURI)) {
+          // Ignore redirects to javascript. The browser will ignore them, as well.
+          if (DomainUtil.getUriObject(destURI).schemeIs("javascript")) {
+            Logger.warning(Logger.TYPE_META_REFRESH,
+                "Ignoring redirect to javascript URI <" + destURI + ">");
+            continue;
+          }
+          // The request will be blocked by shouldLoad.
+          self._showRedirectNotification(browser, destURI, delay);
+        }
       }
     },
 
@@ -321,14 +397,13 @@ requestpolicy.overlay = (function() {
      * Shows a notification that a redirect was requested by a page (meta refresh
      * or with headers).
      *
-     * @param {document}
-     *          targetDocument
+     * @param {<browser> element} browser
      * @param {String}
      *          redirectTargetUri
      * @param {int}
      *          delay
      */
-    _showRedirectNotification: function(targetDocument, redirectTargetUri, delay) {
+    _showRedirectNotification: function(browser, redirectTargetUri, delay) {
       // TODO: Do something with the delay. Not sure what the best thing to do is
       // without complicating the UI.
 
@@ -350,13 +425,7 @@ requestpolicy.overlay = (function() {
         return;
       }
 
-      if (!self._isTopLevelDocument(targetDocument)) {
-        // Don't show notification if this isn't the main document of a tab;
-        return;
-      }
-
-      var targetBrowser = gBrowser.getBrowserForDocument(targetDocument);
-      var notificationBox = gBrowser.getNotificationBox(targetBrowser)
+      var notificationBox = gBrowser.getNotificationBox(browser)
       var notificationValue = "request-policy-meta-redirect";
 
       // There doesn't seem to be a way to use the xul crop attribute with the
@@ -370,17 +439,17 @@ requestpolicy.overlay = (function() {
         shortUri = redirectTargetUri
             .substring(0, Math.max(prePathLength, maxLength)) + "...";
       }
-      var notificationLabel = Utils.strbundle.formatStringFromName(
+      var notificationLabel = StringUtils.strbundle.formatStringFromName(
           "redirectNotification", [shortUri], 1);
 
-      var notificationButtonOptions = Utils.strbundle.GetStringFromName("more");
-      var notificationButtonOptionsKey = Utils.strbundle
+      var notificationButtonOptions = StringUtils.strbundle.GetStringFromName("more");
+      var notificationButtonOptionsKey = StringUtils.strbundle
           .GetStringFromName("more.accesskey");
-      var notificationButtonAllow = Utils.strbundle.GetStringFromName("allow");
-      var notificationButtonAllowKey = Utils.strbundle
+      var notificationButtonAllow = StringUtils.strbundle.GetStringFromName("allow");
+      var notificationButtonAllowKey = StringUtils.strbundle
           .GetStringFromName("allow.accesskey");
-      var notificationButtonDeny = Utils.strbundle.GetStringFromName("deny");
-      var notificationButtonDenyKey = Utils.strbundle
+      var notificationButtonDeny = StringUtils.strbundle.GetStringFromName("deny");
+      var notificationButtonDenyKey = StringUtils.strbundle
           .GetStringFromName("deny.accesskey");
 
       var optionsPopupName = "requestpolicyRedirectNotificationOptions";
@@ -423,19 +492,12 @@ requestpolicy.overlay = (function() {
             accessKey : notificationButtonAllowKey,
             popup : null,
             callback : function() {
-              var location = targetDocument.location;
-              // When refreshing a page that wants to redirect, sometimes the
-              // targetDocument.location is null. If that's the case, just use
-              // do the redirection in the current content pane.
-              if (targetDocument.location == null) {
-                Logger.dump("in callback: targetDocument.location == null, " +
-                    "using content.location instead");
-                location = content.location;
-              }
               // Fx 3.7a5+ calls shouldLoad for location.href changes.
-              RequestProcessor.registerAllowedRedirect(location.href,
-                                                       redirectTargetUri);
-              location.href = redirectTargetUri;
+              RequestProcessor.registerAllowedRedirect(
+                  browser.documentURI.specIgnoringRef, redirectTargetUri);
+
+              browser.messageManager.sendAsyncMessage(MMID + ":setLocation",
+                  {uri: redirectTargetUri});
             }
           },
           {
@@ -459,34 +521,6 @@ requestpolicy.overlay = (function() {
       }
     },
 
-    /**
-     * Determines if documentToCheck is the main document loaded in any tab.
-     *
-     * @param {document}
-     *          documentToCheck
-     * @return {Boolean}
-     */
-    _isTopLevelDocument: function(documentToCheck) {
-      var num = gBrowser.browsers.length;
-      for (var i = 0; i < num; i++) {
-        if (gBrowser.getBrowserAtIndex(i).contentDocument == documentToCheck) {
-          return true;
-        }
-      }
-      return false;
-    },
-
-    /**
-     * Determines if documentToCheck is the main document loaded in the currently
-     * active tab.
-     *
-     * @param {document}
-     *          documentToCheck
-     * @return {Boolean}
-     */
-    _isActiveTopLevelDocument: function(documentToCheck) {
-      return documentToCheck == content.document;
-    },
 
     /**
      * Performs actions required to be performed after a tab change.
@@ -495,228 +529,50 @@ requestpolicy.overlay = (function() {
       // TODO: verify the Fennec and all supported browser versions update the
       // status bar properly with only the ProgressListener. Once verified,
       // remove calls to tabChanged();
-      // self._checkForBlockedContent(content.document);
+      // self._updateBlockedContentState(content.document);
     },
 
-    /**
-     * Things to do when a page has loaded (after images, etc., have been loaded).
-     *
-     * @param {Event}
-     *          event
-     */
-    onAppContentLoaded: function(event) {
-      // TODO: This is getting called multiple times for a page, should only be
-      // called once.
-      try {
-        if (event.originalTarget.nodeName != "#document") {
-          // It's a favicon. See the note at
-          // http://developer.mozilla.org/En/Code_snippets/On_page_load
-          return;
-        }
-
-        var document = event.target;
-        if (!document) {
-          // onAppContentLoaded getting called more often than it should? document
-          // isn't set on new tab open when this is called.
-          return;
-        }
-        Logger.warning(Logger.TYPE_INTERNAL,
-            "onAppContentLoaded called for " + document.documentURI);
-
-        self._onDOMContentLoaded(document);
-
-        if (self._isActiveTopLevelDocument(document)) {
-          // Clear any notifications that may have been present.
-          self._setBlockedContentNotification(false);
-          // We don't do this immediately anymore because slow systems might have
-          // this slow down the loading of the page, which is noticable
-          // especially with CSS loading delays (it's not unlikely that slow
-          // webservers have a hand in this, too).
-          // Note that the change to _setBlockedContentCheckTimeout seems to have
-          // added a bug where opening a blank tab and then quickly switching back
-          // to the original tab can cause the original tab's blocked content
-          // notification to be cleared. A simple compensation was to decrease
-          // the timeout from 1000ms to 250ms, making it much less likely the tab
-          // switch can be done in time for a blank opened tab. This isn't a real
-          // solution, though.
-          // self._checkForBlockedContent(document);
-          self._setBlockedContentCheckTimeout();
+    _containsNonBlacklistedRequests: function(requests) {
+      for (let i = 0, len = requests.length; i < len; i++) {
+        if (!requests[i].isOnBlacklist()) {
+          // This request has not been blocked by the blacklist
+          return true;
         }
-      } catch (e) {
-        Logger.severe(Logger.TYPE_ERROR,
-            "Fatal Error, " + e + ", stack was: " + e.stack);
-        Logger.severe(Logger.TYPE_ERROR,
-            "Unable to complete requestpolicy.overlay.onAppContentLoaded actions.");
-        throw e;
-      }
-    },
-
-    /**
-     * Things to do when a page or a frame within the page has loaded.
-     *
-     * @param {Event}
-     *          event
-     */
-    onAppFrameContentLoaded: function(event) {
-      // TODO: This only works for (i)frames that are direct children of the main
-      // document, not (i)frames within those (i)frames.
-      try {
-        var iframe = event.target;
-        // Flock's special home page is about:myworld. It has (i)frames in it
-        // that have no contentDocument. It's probably related to the fact that
-        // that is an xul page.
-        if (iframe.contentDocument === undefined) {
-          return;
-        }
-        Logger.debug(Logger.TYPE_INTERNAL,
-            "onAppFrameContentLoaded called for <" +
-            iframe.contentDocument.documentURI + "> in <" +
-            iframe.ownerDocument.documentURI + ">");
-        // TODO: maybe this can check if the iframe's documentURI is in the other
-        // origins of the current document, and that way not just be limited to
-        // direct children of the main document. That would require building the
-        // other origins every time an iframe is loaded. Maybe, then, this should
-        // use a timeout like observerBlockedRequests does.
-        if (self._isActiveTopLevelDocument(iframe.ownerDocument)) {
-          // This has an advantage over just relying on the
-          // observeBlockedRequest() call in that this will clear a blocked
-          // content notification if there no longer blocked content. Another way
-          // to solve this would be to observe allowed requests as well as blocked
-          // requests.
-          self._checkForBlockedContent(iframe.ownerDocument);
-        }
-      } catch (e) {
-        Logger.severe(Logger.TYPE_ERROR,
-            "Fatal Error, " + e + ", stack was: " + e.stack);
-        Logger.severe(Logger.TYPE_ERROR, "Unable to complete " +
-            "requestpolicy.overlay.onAppFrameContentLoaded actions.");
-        throw e;
       }
+      return false;
     },
 
     /**
      * Checks if the document has blocked content and shows appropriate
      * notifications.
      */
-    _checkForBlockedContent: function(document) {
-      // TODO: this probably needs to be rewritten or at least thought through
-      // again in light of it being years later and much of RP changing. It's
-      // likely that there's wasted work happening during time-critical page
-      // loading going on in here.
+    _updateBlockedContentState: function() {
       try {
-        var documentUri = DomainUtil
-            .stripFragment(document.documentURI);
+        let browser = gBrowser.selectedBrowser;
+        let uri = DomainUtil.stripFragment(browser.currentURI.spec);
         Logger.debug(Logger.TYPE_INTERNAL,
-            "Checking for blocked requests from page <" + documentUri + ">");
-        blockedContentCheckLastTime = (new Date()).getTime();
-        self._stopBlockedContentCheckTimeout();
-
-        var allRequestsOnDocument = RequestProcessor
-            .getAllRequestsOnDocument(document);
-
-        if (true === allRequestsOnDocument.containsBlockedRequests()) {
-          Logger.debug(Logger.TYPE_INTERNAL, "Requests have been blocked.");
-          self._setBlockedContentNotification(true);
-          self._indicateBlockedVisibleObjects(document);
-          return;
-        } else {
-          Logger.debug(Logger.TYPE_INTERNAL, "No requests have been blocked.");
-          self._setBlockedContentNotification(false);
-        }
+            "Checking for blocked requests from page <" + uri + ">");
+
+        // TODO: this needs to be rewritten. checking if there is blocked
+        // content could be done much more efficiently.
+        let documentContainsBlockedContent = RequestProcessor
+            .getAllRequestsInBrowser(browser).containsBlockedRequests();
+        self._setContentBlockedState(documentContainsBlockedContent);
+
+        let logText = documentContainsBlockedContent ?
+                      "Requests have been blocked." :
+                      "No requests have been blocked.";
+        Logger.debug(Logger.TYPE_INTERNAL, logText);
       } catch (e) {
-        Logger.severe(Logger.TYPE_ERROR,
-            "Fatal Error, " + e + ", stack was: " + e.stack);
-        Logger.severe(Logger.TYPE_ERROR, "Unable to complete " +
-            "requestpolicy.overlay._checkForBlockedContent actions.");
-        throw e;
-      }
-    },
-
-    _containsNonBlacklistedRequests: function(requests) {
-      for (let i = 0, len = requests.length; i < len; i++) {
-        if (!requests[i].isOnBlacklist()) {
-          // This request has not been blocked by the blacklist
-          return true;
-        }
-      }
-      return false;
-    },
-
-    _indicateBlockedVisibleObjects: function(document) {
-      if (!Prefs.prefs.getBoolPref("indicateBlockedObjects")) {
-        return;
-      }
-      var indicateBlacklisted = Prefs.prefs
-          .getBoolPref("indicateBlacklistedObjects");
-
-      var images = document.getElementsByTagName("img");
-      var rejectedRequests = RequestProcessor._rejectedRequests
-          .getOriginUri(document.location);
-      var blockedUrisToIndicate = {};
-      for (var destBase in rejectedRequests) {
-        for (var destIdent in rejectedRequests[destBase]) {
-          for (var destUri in rejectedRequests[destBase][destIdent]) {
-            // case 1: indicateBlacklisted == true
-            //         ==> indicate the object has been blocked
-            //
-            // case 2: indicateBlacklisted == false
-            // case 2a: all requests have been blocked because of a blacklist
-            //          ==> do *not* indicate
-            //
-            // case 2b: at least one of the blocked (identical) requests has been
-            //          blocked by a rule *other than* the blacklist
-            //          ==> *do* indicate
-            let requests = rejectedRequests[destBase][destIdent][destUri];
-            if (indicateBlacklisted ||
-                self._containsNonBlacklistedRequests(requests)) {
-              blockedUrisToIndicate[destUri] = true;
-            }
-          }
-        }
-      }
-
-      // Ideally, want the image to be a broken image so that the alt text
-      // shows. By default, the blocked image will just not show up at all.
-      // Setting img.src to a broken resource:// causes "save page as" to fail
-      // for some earlier Fx 3.x versions. Also, using a broken resource://
-      // causes our width setting to be ignored, as does using null for img.src.
-      // With Firefox 4, setting img.src to null doesn't work reliably for
-      // having the rest of the styles (e.g. background and border) applied.
-      // So, for now we're punting on trying to display alt text. We'll just use
-      // a transparent image as the replacement image.
-      // Note that with our changes to the image here, "save page as" works but
-      // different data is saved depending on what type of "save page as" the
-      // user performs. With "save all files", the saved source includes the
-      // original, blocked image src. With "web page, complete" the saved source
-      // has changes we make here to show the blocked request indicator.
-
-      for (var i = 0; i < images.length; i++) {
-        var img = images[i];
-        // Note: we're no longer checking img.requestpolicyBlocked here.
-        if (!img.requestpolicyIdentified && img.src in blockedUrisToIndicate) {
-          img.requestpolicyIdentified = true;
-          img.style.border = "solid 1px #fcc";
-          img.style.backgroundRepeat = "no-repeat";
-          img.style.backgroundPosition = "center center";
-          img.style.backgroundImage = "url('" + missingImageDataUri + "')";
-          if (!img.width) {
-            img.width = 50;
-          }
-          if (!img.height) {
-            img.height = 50;
-          }
-          img.title = "[" + DomainUtil.getIdentifier(img.src) + "]"
-              + (img.title ? " " + img.title : "")
-              + (img.alt ? " " + img.alt : "");
-          img.src = transparentImageDataUri;
-        }
+        Logger.severeError(
+            "Unable to complete _updateBlockedContentState actions: " + e, e);
       }
     },
 
     /**
      * Sets the blocked content notifications visible to the user.
      */
-    _setBlockedContentNotification: function(isContentBlocked) {
+    _setContentBlockedState: function(isContentBlocked) {
       var button = document.getElementById(toolbarButtonId);
       if (button) {
         button.setAttribute("requestpolicyBlocked", isContentBlocked);
@@ -808,21 +664,20 @@ requestpolicy.overlay = (function() {
      * window separately to look for a document to show a notification in.
      */
     observeBlockedTopLevelDocRequest: function (originUri, destUri) {
-      const document = self._getDocumentAtUri(originUri);
-      if (!document) {
+      const browser = self._getBrowserAtUri(originUri);
+      if (!browser) {
         return;
       }
       // We're called indirectly from shouldLoad so we can't block.
       window.setTimeout(function() {
-        requestpolicy.overlay._showRedirectNotification(document, destUri, 0);
+        requestpolicy.overlay._showRedirectNotification(browser, destUri, 0);
       }, 0);
     },
 
-    _getDocumentAtUri: function(uri) {
-      var num = gBrowser.browsers.length;
-      for (var i = 0; i < num; i++) {
+    _getBrowserAtUri: function(uri) {
+      for (let i = 0, len = gBrowser.browsers.length; i < len; i++) {
         if (gBrowser.getBrowserAtIndex(i).currentURI.spec == uri) {
-          return gBrowser.getBrowserAtIndex(i).contentDocument;
+          return gBrowser.getBrowserAtIndex(i);
         }
       }
       return null;
@@ -832,15 +687,15 @@ requestpolicy.overlay = (function() {
 
     _updateNotificationDueToBlockedContent: function() {
       if (!blockedContentCheckTimeoutId) {
-        self._setBlockedContentCheckTimeout();
+        self._updateBlockedContentStateAfterTimeout();
       }
     },
 
-    _setBlockedContentCheckTimeout: function() {
-      const document = content.document;
+    _updateBlockedContentStateAfterTimeout: function() {
+      const browser = gBrowser.selectedBrowser;
       blockedContentCheckTimeoutId = window.setTimeout(function() {
-        requestpolicy.overlay._checkForBlockedContent(document);
-      }, blockedContentCheckTimeoutDelay);
+        requestpolicy.overlay._updateBlockedContentState(browser);
+      }, blockedContentStateUpdateDelay);
     },
 
     _stopBlockedContentCheckTimeout: function() {
@@ -850,110 +705,6 @@ requestpolicy.overlay = (function() {
       }
     },
 
-    _getDocShellAllowMetaRedirects: function(document) {
-      var docShell = document.defaultView
-          .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-          .getInterface(Components.interfaces.nsIWebNavigation)
-          .QueryInterface(Components.interfaces.nsIDocShell);
-      return docShell.allowMetaRedirects;
-    },
-
-    _htmlAnchorTagClicked: function(event) {
-      // Note: need to use currentTarget so that it is the link, not
-      // something else within the link that got clicked, it seems.
-      RequestProcessor.registerLinkClicked(event.currentTarget.ownerDocument.URL,
-          event.currentTarget.href);
-    },
-
-    /**
-     * Perform the actions required once the DOM is loaded. This may be being
-     * called for more than just the page content DOM. It seems to work for now.
-     *
-     * @param {Event}
-     *          event
-     */
-    _onDOMContentLoaded: function(document) {
-      Logger.warning(Logger.TYPE_INTERNAL, "_onDOMContentLoaded called.");
-
-      // Find all meta redirects.
-      var metaTags = document.getElementsByTagName("meta");
-      for (var i = 0; i < metaTags.length; i++) {
-        if (metaTags[i].httpEquiv
-            && metaTags[i].httpEquiv.toLowerCase() == "refresh") {
-          // TODO: Register meta redirects so we can tell which blocked requests
-          // were meta redirects in the statusbar menu.
-          // TODO: move this logic to the requestpolicy service.
-          var parts = DomainUtil.parseRefresh(metaTags[i].content);
-          var delay = parts[0];
-          // The dest may be empty if the origin is what should be refreshed. This
-          // will be handled by DomainUtil.determineRedirectUri().
-          var dest = parts[1];
-          Logger.info(Logger.TYPE_META_REFRESH, "meta refresh to <" +
-              dest + "> (" + delay + " second delay) found in document at <" +
-              document.location + ">");
-          // If dest isn't a valid uri, assume it's a relative uri.
-          if (!DomainUtil.isValidUri(dest)) {
-            var origDest = dest;
-            dest = document.documentURIObject.resolve(dest);
-            Logger.info(Logger.TYPE_META_REFRESH,
-                "meta refresh destination <" + origDest
-                    + "> appeared to be relative to <" + document.documentURI
-                    + ">, so it has been resolved to <" + dest + ">");
-          }
-
-          if (!self._getDocShellAllowMetaRedirects(document)) {
-            Logger.warning(Logger.TYPE_META_REFRESH,
-                "Another extension disabled docShell.allowMetaRedirects.");
-          }
-
-          // We don't automatically perform any allowed redirects. Instead, we
-          // just detect when they will be blocked and show a notification. If
-          // the docShell has allowMetaRedirects disabled, it will be respected.
-          if (!Prefs.isBlockingDisabled()
-              && !RequestProcessor.isAllowedRedirect(document.location.href, dest)) {
-            // Ignore redirects to javascript. The browser will ignore them, as well.
-            if (DomainUtil.getUriObject(dest).schemeIs("javascript")) {
-              Logger.warning(Logger.TYPE_META_REFRESH,
-                  "Ignoring redirect to javascript URI <" + dest + ">");
-              continue;
-            }
-            // The request will be blocked by shouldLoad.
-            self._showRedirectNotification(document, dest, delay);
-          }
-        }
-      }
-
-      // Find all anchor tags and add click events (which also fire when enter
-      // is pressed while the element has focus).
-      // This seems to be a safe approach in that the MDC states that javascript
-      // can't be used to initiate a click event on a link:
-      // http://developer.mozilla.org/en/DOM/element.click
-      // We keep this even though we have the document looking for clicks because
-      // for certain links the target will not be the link (and we can't use the
-      // currentTarget in the other case it seems, as we can here). There probably
-      // is some solution when handling the click events at the document level,
-      // but I just don't know what it is. For now, there remains the risk of
-      // dynamically added links whose target of the click event isn't the anchor
-      // tag.
-      var anchorTags = document.getElementsByTagName("a");
-      for (var i = 0; i < anchorTags.length; i++) {
-        anchorTags[i].addEventListener("click", self._htmlAnchorTagClicked,
-            false);
-      }
-
-      // TODO: implement a function in RequestProcessor for this
-      if (RequestProcessor._blockedRedirects[document.location]) {
-        var dest = RequestProcessor._blockedRedirects[document.location];
-        Logger.warning(Logger.TYPE_HEADER_REDIRECT,
-            "Showing notification for blocked redirect. To <" + dest + "> " +
-            "from <" + document.location + ">");
-        self._showRedirectNotification(document, dest);
-        delete RequestProcessor._blockedRedirects[document.location];
-      }
-
-      self._wrapWindowOpen(document.defaultView);
-    },
-
     /**
      * Called as an event listener when popupshowing fires on the
      * contentAreaContextMenu.
@@ -1086,40 +837,6 @@ requestpolicy.overlay = (function() {
       }
     },
 
-    /**
-     * Wraps the window's open() method so that RequestPolicy can know the origin
-     * and destination URLs of the window being opened. Assume that if
-     * window.open() calls have made it this far, it's a window the user wanted
-     * open (e.g. they have allowed the popup). Unfortunately, this method (or our
-     * timing of doing self) doesn't seem to work for popups that are allowed
-     * popups (the user has allowed popups from the domain). So, the workaround
-     * was to also add the 'if(aContext.nodeName == "xul:browser" &&
-     * aContext.currentURI && aContext.currentURI.spec == "about:blank")' to
-     * shouldLoad().
-     *
-     * @param {Window}
-     *          window
-     */
-    _wrapWindowOpen: function(window) {
-      if (!window.requestpolicyOrigOpen) {
-        window.requestpolicyOrigOpen = window.open;
-        window.open = function(url, windowName, windowFeatures) {
-          RequestProcessor.registerLinkClicked(window.document.documentURI, url);
-          return window.requestpolicyOrigOpen(url, windowName, windowFeatures);
-        };
-      }
-
-      if (!window.requestpolicyOrigOpenDialog) {
-        window.requestpolicyOrigOpenDialog = window.openDialog;
-        window.openDialog = function() {
-          // openDialog(url, name, features, arg1, arg2, ...)
-          RequestProcessor.registerLinkClicked(window.document.documentURI,
-              arguments[0]);
-          return window.requestpolicyOrigOpenDialog.apply(window, arguments);
-        };
-      }
-    },
-
     _addLocationObserver: function() {
       self.locationListener = {
         onLocationChange : function(aProgress, aRequest, aURI) {
@@ -1127,7 +844,8 @@ requestpolicy.overlay = (function() {
           // The timer is running on the main window, not the document's window,
           // so we want to stop the timer when the tab is changed.
           requestpolicy.overlay._stopBlockedContentCheckTimeout();
-          requestpolicy.overlay._checkForBlockedContent(content.document);
+          requestpolicy.overlay
+              ._updateBlockedContentState(gBrowser.selectedBrowser);
         },
         // Though unnecessary for Gecko 2.0, I'm leaving in onSecurityChange for
         // SeaMonkey because of https://bugzilla.mozilla.org/show_bug.cgi?id=685466
@@ -1168,7 +886,7 @@ requestpolicy.overlay = (function() {
 
         OnHistoryGotoIndex : function(index, gotoURI) {
           RequestProcessor.registerHistoryRequest(gotoURI.asciiSpec);
-   return true;
+          return true;
         },
 
         OnHistoryNewEntry : function(newURI) {
@@ -1251,8 +969,9 @@ requestpolicy.overlay = (function() {
     onPopupHidden: function(event) {
       var rulesChanged = requestpolicy.menu.processQueuedRuleChanges();
       if (rulesChanged || self._needsReloadOnMenuClose) {
-        if (Prefs.prefs.getBoolPref("autoReload")) {
-          content.document.location.reload(false);
+        if (rpPrefBranch.getBoolPref("autoReload")) {
+          let mm = gBrowser.selectedBrowser.messageManager;
+          mm.sendAsyncMessage(MMID + ":reload");
         }
       }
       self._needsReloadOnMenuClose = false;
@@ -1278,19 +997,9 @@ requestpolicy.overlay = (function() {
      * Get the top-level document's uri.
      */
     getTopLevelDocumentUri: function() {
-      // We don't just retrieve the translations array once during init because
-      // we're not sure if it will be fully populated during init. This is
-      // especially a concern given the async addon manager API in Firefox 4.
-      var translations = rpService.getTopLevelDocTranslations();
-      if (translations.length) {
-        var docURI = content.document.documentURI;
-        for (var i = 0; i < translations.length; i++) {
-          if (docURI.indexOf(translations[i][0]) == 0) {
-            return translations[i][1];
-          }
-        }
-      }
-      return DomainUtil.stripFragment(content.document.documentURI);
+      let uri = gBrowser.selectedBrowser.currentURI.spec;
+      return rpService.getTopLevelDocTranslation(uri) ||
+          DomainUtil.stripFragment(uri);
     },
 
     /**
@@ -1457,32 +1166,6 @@ requestpolicy.overlay = (function() {
       popup.hidePopup();
     },
 
-    _performRedirect: function(document, redirectTargetUri) {
-      try {
-        if (redirectTargetUri[0] == '/') {
-          Logger.info(Logger.TYPE_INTERNAL,
-              "Redirecting to relative path <" + redirectTargetUri + "> from <"
-                  + document.documentURI + ">");
-          document.location.pathname = redirectTargetUri;
-        } else {
-          // If there is no scheme, treat it as relative to the current directory.
-          if (redirectTargetUri.indexOf(":") == -1) {
-            // TODO: Move this logic to DomainUtil.
-            var curDir = document.documentURI.split("/").slice(0, -1).join("/");
-            redirectTargetUri = curDir + "/" + redirectTargetUri;
-          }
-          Logger.info(Logger.TYPE_INTERNAL,
-              "Redirecting to <" + redirectTargetUri + "> from <"
-                  + document.documentURI + ">");
-          document.location.href = redirectTargetUri;
-        }
-      } catch (e) {
-        if (e.name != "NS_ERROR_FILE_NOT_FOUND") {
-          throw e;
-        }
-      }
-    },
-
     _openInNewTab: function(uri) {
       gBrowser.selectedTab = gBrowser.addTab(uri);
     },
@@ -1529,15 +1212,15 @@ requestpolicy.overlay = (function() {
     },
 
     openPrefs: function() {
-      self.openSettingsTab('chrome://requestpolicy/content/settings/basicprefs.html');
+      self.openSettingsTab('about:requestpolicy');
     },
 
     openPolicyManager: function() {
-      self.openSettingsTab('chrome://requestpolicy/content/settings/yourpolicy.html');
+      self.openSettingsTab('about:requestpolicy?yourpolicy');
     },
 
     openHelp: function() {
-      var tab = gBrowser.addTab('https://github.com/RequestPolicyContinued/requestpolicy/wiki#help-and-support-for-users-and-developers');
+      var tab = gBrowser.addTab('https://github.com/RequestPolicyContinued/requestpolicy/wiki/Help-and-Support');
       gBrowser.selectedTab = tab;
       var popup = document.getElementById('rp-popup');
       popup.hidePopup();
diff --git a/src/content/ui/request-log-tree-view.js b/src/content/ui/request-log-tree-view.js
index 1895a72..c5a0d2b 100644
--- a/src/content/ui/request-log-tree-view.js
+++ b/src/content/ui/request-log-tree-view.js
@@ -33,8 +33,8 @@ window.requestpolicy.requestLogTreeView = (function () {
 
   let mod = {};
   Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
-  mod.ScriptLoader.importModules(["utils"], mod);
-  let Utils = mod.Utils;
+  mod.ScriptLoader.importModules(["string-utils"], mod);
+  let StringUtils = mod.StringUtils;
 
 
 
@@ -57,8 +57,8 @@ window.requestpolicy.requestLogTreeView = (function () {
         .getService(Components.interfaces.nsIAtomService),
 
     init : function(e) {
-      var message = Utils.strbundle.GetStringFromName("requestLogIsEmpty");
-      var directions = Utils.strbundle
+      var message = StringUtils.strbundle.GetStringFromName("requestLogIsEmpty");
+      var directions = StringUtils.strbundle
           .GetStringFromName("requestLogDirections");
       self._visibleData.push([message, directions, false, ""]);
     },
diff --git a/src/content/ui/request-log.js b/src/content/ui/request-log.js
index 904172c..3818911 100644
--- a/src/content/ui/request-log.js
+++ b/src/content/ui/request-log.js
@@ -33,9 +33,10 @@ window.requestpolicy.requestLog = (function () {
   Cu.import("resource://gre/modules/Services.jsm", mod);
   mod.ScriptLoader.importModules([
     "domain-util",
-    "utils"
+    "string-utils"
   ], mod);
-  let Services = mod.Services, DomainUtil = mod.DomainUtil, Utils = mod.Utils;
+  let Services = mod.Services, DomainUtil = mod.DomainUtil,
+      StringUtils = mod.StringUtils;
 
 
   let initialized = false;
@@ -88,8 +89,8 @@ window.requestpolicy.requestLog = (function () {
       }
 
       if (forbidden) {
-        var alertTitle = Utils.strbundle.GetStringFromName("actionForbidden");
-        var alertText = Utils.strbundle
+        var alertTitle = StringUtils.strbundle.GetStringFromName("actionForbidden");
+        var alertText = StringUtils.strbundle
             .GetStringFromName("urlCanOnlyBeCopiedToClipboard");
         Services.prompt.alert(null, alertTitle, alertText);
         return;
diff --git a/src/install.rdf b/src/install.rdf
index 6dcef9b..70b8a62 100644
--- a/src/install.rdf
+++ b/src/install.rdf
@@ -15,7 +15,7 @@
     <em:multiprocessCompatible>false</em:multiprocessCompatible>
 
     <em:homepageURL>https://requestpolicycontinued.github.io/</em:homepageURL>
-    <em:optionsURL>chrome://requestpolicy/content/settings/basicprefs.html</em:optionsURL>
+    <em:optionsURL>about:requestpolicy</em:optionsURL>
     <em:optionsType>3</em:optionsType>
     <em:iconURL>chrome://requestpolicy/skin/requestpolicy-icon-32.png</em:iconURL>
 
diff --git a/tests/content/iframe_3.html b/tests/content/iframe_3.html
index 4a4724b..c3f6049 100644
--- a/tests/content/iframe_3.html
+++ b/tests/content/iframe_3.html
@@ -2,8 +2,8 @@
 <html>
 <head>
   <meta charset="utf-8" />
-  <script src="inc/jquery.min.js"></script>
-  <script src="inc/global.js"></script>
+  <script src="inc/jquery.min.js" class="rp-request"></script>
+  <script src="inc/global.js" class="rp-request"></script>
 </head>
 <body>
 
@@ -36,7 +36,7 @@
 
       var html = '<b>' + domain + ':</b><br />' +
           '<iframe id="' + id + '" src="' + src + '" ' +
-          ' width="'+width+'" height="'+height+'"></iframe><br />';
+          ' width="'+width+'" height="'+height+'" class="rp-request"></iframe><br />';
       $('body').append(html);
     }
 
diff --git a/tests/mozmill/lib/domain-util.js b/tests/mozmill/lib/domain-util.js
index 10db309..b8f5af3 100644
--- a/tests/mozmill/lib/domain-util.js
+++ b/tests/mozmill/lib/domain-util.js
@@ -51,5 +51,23 @@ var DomainUtil = exports.DomainUtil = (function() {
     return !self.isSameDomain(dom1, dom2);
   };
 
+  self.stripFilenameFromURL = function(aURL) {
+    return /^(.*\/)[^/]*$/.exec(aURL)[1];
+  };
+
+  self.isAbsoluteURL = function(aURL) {
+    return /^[a-zA-Z][a-zA-Z0-9+-.]*:\/\//.test(aURL);
+  };
+
+  self.getAbsoluteURL = function(aURL, aReferenceURL) {
+    if (self.isAbsoluteURL(aURL)) {
+      return aURL;
+    }
+
+    // this function does not consider all types of relative URIs !
+    // see http://tools.ietf.org/html/std66#appendix-A
+    return self.stripFilenameFromURL(aReferenceURL) + aURL;
+  };
+
   return self;
 }());
diff --git a/tests/mozmill/tests/testPolicy/lib/iframe.js b/tests/mozmill/tests/testPolicy/lib/iframe.js
index a48a380..0f33362 100644
--- a/tests/mozmill/tests/testPolicy/lib/iframe.js
+++ b/tests/mozmill/tests/testPolicy/lib/iframe.js
@@ -44,8 +44,12 @@ Iframe.prototype.doChecks = function() {
   assert.ok(hasRequestBeenAllowed === shouldRequestHaveBeenAllowed, text);
 };
 Iframe.prototype.accumulateNumRequests = function(numRequestCounter) {
+  let isAllowed = this.hasIframeRequestBeenAllowed();
   numRequestCounter.accumulate(this.ownerHostname, this.iframeSrcHostname, 1,
-      this.hasIframeRequestBeenAllowed());
+      isAllowed);
+  //if (isAllowed) {
+  //  NumRequestsCounter.accumulateAdditionalRequestsToSamedomain(doc);
+  //}
 };
 
 
@@ -64,6 +68,26 @@ function* allIframesOnDocument(doc) {
   }
 }
 
+function* recursivelyGetAllDocs(aDoc) {
+  yield aDoc;
+
+  for (let iframe of allIframesOnDocument(aDoc)) {
+    if (iframe.hasIframeRequestBeenAllowed()) {
+      yield* recursivelyGetAllDocs(iframe.getContentDocument());
+    }
+  }
+}
+
+function* recursivelyGetAllDOMNodesWithRequests(aDoc) {
+  for (let doc of allIframesOnDocument(aDoc)) {
+    for (let node of doc.querySelectorAll("[src]")) {
+      yield node;
+    }
+  }
+}
 
 exports.Iframe = Iframe;
 exports.allIframesOnDocument = allIframesOnDocument;
+exports.recursivelyGetAllDocs = recursivelyGetAllDocs;
+exports.recursivelyGetAllDOMNodesWithRequests =
+    recursivelyGetAllDOMNodesWithRequests;
diff --git a/tests/mozmill/tests/testPolicy/lib/num-request-counter.js b/tests/mozmill/tests/testPolicy/lib/num-request-counter.js
new file mode 100644
index 0000000..aba168c
--- /dev/null
+++ b/tests/mozmill/tests/testPolicy/lib/num-request-counter.js
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var rpRootDir = "../../../";
+var rpConst = require(rpRootDir + "lib/constants");
+var rootDir = rpRootDir + rpConst.mozmillTestsRootDir;
+
+var {assert, expect} = require(rootDir + "lib/assertions");
+var rpUtils = require(rpRootDir + "lib/rp-utils");
+
+
+
+function NumRequestsCounter(controller) {
+  let counter = {};
+
+  let self = this;
+
+  let rpMenuButton = findElement.ID(controller.window.document,
+                                    'requestpolicyToolbarButton');
+  let rpMenuPopup = findElement.ID(controller.window.document, 'rp-popup');
+
+
+  function initOriginAndDest(originHostname, destHostname) {
+    counter.$total = counter.$total ||
+        {allowed: 0, denied: 0, $total: 0};
+    counter[originHostname] = counter[originHostname] ||
+        {$total: {allowed: 0, denied: 0, $total: 0}};
+    counter[originHostname][destHostname] = counter[originHostname][destHostname] ||
+        {allowed: 0, denied: 0, $total: 0};
+  }
+
+  self.accumulate = function(originHostname, destHostname, numRequestsToAdd,
+      isAllowed) {
+    let action = isAllowed ? 'allowed' : 'denied';
+
+    initOriginAndDest(originHostname, destHostname);
+
+    numRequestsToAdd = parseInt(numRequestsToAdd);
+
+    counter[originHostname][destHostname][action] += numRequestsToAdd;
+    counter[originHostname][destHostname].$total += numRequestsToAdd;
+    counter[originHostname].$total[action] += numRequestsToAdd;
+    counter[originHostname].$total.$total += numRequestsToAdd;
+    counter.$total[action] += numRequestsToAdd;
+    counter.$total.$total += numRequestsToAdd;
+  };
+
+  self.accumulateNonIframeRequests = function(doc) {
+    var element = rpUtils.getElementById(doc,
+                                         'num-additional-samedomain-requests');
+    var numRequestsToAdd = element.textContent;
+    var hostname = doc.location.hostname;
+
+    self.accumulate(hostname, hostname, numRequestsToAdd, true);
+  };
+
+  self.reset = function() {
+    counter = {};
+  };
+
+  self.checkIfIsCorrect = function() {
+    // open the menu
+    rpMenuButton.click();
+    rpMenuPopup.waitForElement();
+
+    {
+      let rpOriginNumReq = rpUtils.getElementById(controller.window.document,
+                                                  "rp-origin-num-requests");
+      let total = counter.$total.$total;
+      let totalAllow = counter.$total.allowed;
+      let totalDeny = counter.$total.denied;
+
+      let re = /^\s*([0-9]+)\s*(?:\(\s*([0-9]+)\s*\+\s*([0-9]+)\s*\))?\s*$/;
+      let reResult = re.exec(rpOriginNumReq.value);
+      assert.ok(reResult !== null, "The numRequests field of #rp-origin " +
+                "has the correct format, either 'num' or 'num (num + num)'.");
+      assert.ok(reResult[1] > total, "The total number of requests" +
+                " is correct.");
+
+      /*
+      if (totalAllow == 0 || totalDeny == 0) {
+        re = new RegExp("^\s*" + total + "\s*$");
+      } else {
+        let addWhitespace = function() {
+          let str = "\s*";
+          for (let i = 0, len = arguments.length; i < len; ++i) {
+            str += arguments[i] + "\s*";
+          }
+        };
+
+        re = new RegExp("^" + addWhitespace(total, "\(", totalDeny, "\+",
+                                            totalAllow, "\)") + "$");
+      }
+      assert.ok(re.test(rpOriginNumReq.value), "The total number of requests" +
+                " is displayed correctly: " +
+                "'" + total + " ("+totalDeny+" + "+totalAllow+")'.");
+      */
+    }
+
+
+    /**
+     * check whether all requests are correct,
+     * whether the request counter is correct,
+     * ...
+     */
+
+    // close the menu
+    rpMenuPopup.keypress('VK_ESCAPE');
+  };
+
+  return self;
+}
+
+exports.NumRequestsCounter = NumRequestsCounter;
diff --git a/tests/mozmill/tests/testPolicy/lib/temporary-rule-iterator.js b/tests/mozmill/tests/testPolicy/lib/temporary-rule-iterator.js
new file mode 100644
index 0000000..5e7957e
--- /dev/null
+++ b/tests/mozmill/tests/testPolicy/lib/temporary-rule-iterator.js
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// TODO: implement
+let TemporaryRuleManager = (function() {
+  let self = {};
+
+  self.getCurrentBinaryValue = function() {
+  };
+  self.setPref = function(binaryValue) {
+  };
+
+  // initialize
+  self.setPref(0);
+
+  return self;
+}());
+
+function* makeTemporaryRules(doc) {
+  if (!doc) {
+    throw new Error("no doc specified.");
+  }
+
+  yield "this will be rule one";
+}
+
+exports.TemporaryRuleManager = TemporaryRuleManager;
+exports.makeTemporaryRules = makeTemporaryRules;
diff --git a/tests/mozmill/tests/testPolicy/manifest.ini b/tests/mozmill/tests/testPolicy/manifest.ini
index 9ebff53..41d26d9 100644
--- a/tests/mozmill/tests/testPolicy/manifest.ini
+++ b/tests/mozmill/tests/testPolicy/manifest.ini
@@ -1,3 +1,4 @@
 [parent:../manifest.ini]
 
-[include:testIframeTree/manifest.ini]
+[testPolicy.js]
+#[include:testIframeTree/manifest.ini]
diff --git a/tests/mozmill/tests/testPolicy/testIframeTree/manifest.ini b/tests/mozmill/tests/testPolicy/testIframeTree/manifest.ini
index df2c5c0..31911b7 100644
--- a/tests/mozmill/tests/testPolicy/testIframeTree/manifest.ini
+++ b/tests/mozmill/tests/testPolicy/testIframeTree/manifest.ini
@@ -1,3 +1,4 @@
 [parent:../manifest.ini]
 
 [testDefaultPolicies.js]
+#[testTemporaryRules.js]
diff --git a/tests/mozmill/tests/testPolicy/testIframeTree/testDefaultPolicies.js b/tests/mozmill/tests/testPolicy/testIframeTree/testDefaultPolicies.js
index d29024d..cf1dd25 100644
--- a/tests/mozmill/tests/testPolicy/testIframeTree/testDefaultPolicies.js
+++ b/tests/mozmill/tests/testPolicy/testIframeTree/testDefaultPolicies.js
@@ -38,8 +38,7 @@ var testDefaultPolicies = function() {
   controller.open(TEST_URL);
   controller.waitForPageLoad();
 
-  let itDefaultPolicy = makeDefaultPolicyIterator();
-  while (!itDefaultPolicy.next().done) {
+  for (let defaultPolicySetting of makeDefaultPolicyIterator()) {
     DefaultPolicyManager.dumpState();
 
     // reload the page with the new preferences
diff --git a/tests/mozmill/tests/testPolicy/testIframeTree/testDefaultPolicies.js b/tests/mozmill/tests/testPolicy/testIframeTree/testTemporaryRules.js
similarity index 50%
copy from tests/mozmill/tests/testPolicy/testIframeTree/testDefaultPolicies.js
copy to tests/mozmill/tests/testPolicy/testIframeTree/testTemporaryRules.js
index d29024d..f2770fc 100644
--- a/tests/mozmill/tests/testPolicy/testIframeTree/testDefaultPolicies.js
+++ b/tests/mozmill/tests/testPolicy/testIframeTree/testTemporaryRules.js
@@ -12,16 +12,27 @@ var {assert, expect} = require(rootDir + "lib/assertions");
 var prefs = require(rootDir + "firefox/lib/prefs");
 var tabs = require(rootDir + "firefox/lib/tabs");
 
+var rpUtils = require(rpRootDir + "lib/rp-utils");
+var {DomainUtil} = require(rpRootDir + "lib/domain-util");
+
 var {
   DefaultPolicyManager,
   makeDefaultPolicyIterator
 } = require("../lib/default-policy-iterator");
-var {Iframe, allIframesOnDocument} = require("../lib/iframe");
+var {
+  TemporaryRuleManager,
+  TemporaryRuleIterator
+} = require("../lib/temporary-rule-iterator");
+var {NumRequestsCounter} = require("../lib/num-request-counter");
+var {allIframesOnDocument, recursivelyGetAllDocs} = require("../lib/iframe");
 
 const TEST_URL = "http://www.maindomain.test/iframe_3.html";
 
 
 var setupModule = function(aModule) {
+  prefs.preferences.setPref(rpConst.PREF_MENU_SHOW_NUM_REQUESTS, true);
+  prefs.preferences.setPref(rpConst.PREF_AUTO_RELOAD, false);
+
   aModule.controller = mozmill.getBrowserController();
   aModule.tabBrowser = new tabs.tabBrowser(aModule.controller);
   aModule.tabBrowser.closeAllTabs();
@@ -30,11 +41,15 @@ var setupModule = function(aModule) {
 var teardownModule = function(aModule) {
   prefs.preferences.clearUserPref(rpConst.PREF_DEFAULT_ALLOW);
   prefs.preferences.clearUserPref(rpConst.PREF_DEFAULT_ALLOW_SAME_DOMAIN);
+  prefs.preferences.clearUserPref(rpConst.PREF_MENU_SHOW_NUM_REQUESTS);
+  prefs.preferences.clearUserPref(rpConst.PREF_AUTO_RELOAD);
   aModule.tabBrowser.closeAllTabs();
 }
 
 
-var testDefaultPolicies = function() {
+
+
+var testTemporaryRules = function() {
   controller.open(TEST_URL);
   controller.waitForPageLoad();
 
@@ -46,9 +61,26 @@ var testDefaultPolicies = function() {
     controller.refresh();
     controller.waitForPageLoad();
 
-    let doc = controller.window.content.document;
-    for (let iframe of allIframesOnDocument(doc)) {
-      iframe.doChecks();
-    }
+    let itTemporaryRules = new TemporaryRuleIterator(
+        controller.window.content.document);
+    itTemporaryRules.start();
+    do {
+      let numRequestsCounter = new NumRequestsCounter(controller);
+
+      // reload the page with the new preferences
+      controller.refresh();
+      controller.waitForPageLoad();
+
+      let mainDoc = controller.window.content.document;
+      for (let iframe of allIframesOnDocument(mainDoc)) {
+        iframe.doChecks();
+        iframe.accumulateNumRequests(numRequestsCounter);
+      }
+      for (let doc of recursivelyGetAllDocs(mainDoc)) {
+        numRequestsCounter.accumulateNonIframeRequests(doc);
+      }
+
+      numRequestsCounter.checkIfIsCorrect();
+    } while (itTemporaryRules.next());
   }
 }
diff --git a/tests/mozmill/tests/testRedirect/testAutoRedirect.js b/tests/mozmill/tests/testRedirect/testAutoRedirect.js
index 3eea50f..1d2da4e 100644
--- a/tests/mozmill/tests/testRedirect/testAutoRedirect.js
+++ b/tests/mozmill/tests/testRedirect/testAutoRedirect.js
@@ -61,10 +61,10 @@ var testAutoRedirect = function() {
       controller.waitFor(function() {
           return controller.window.content.document.location.href !== testURL;
       }, "The URL in the urlbar has changed.");
-      assert.ok(!panel.exists(), "The redirect has been allowed.");
+      expect.ok(!panel.exists(), "The redirect notification bar is hidden.");
     } else {
-      assert.ok(panel.exists(), "The redirect has been blocked.");
-      assert.ok(controller.window.content.document.location.href === testURL,
+      expect.ok(panel.exists(), "The redirect notification bar is displayed.");
+      expect.ok(controller.window.content.document.location.href === testURL,
                 "The URL in the urlbar hasn't changed.");
     }
 
diff --git a/tests/mozmill/tests/testRedirect/testLinkClickRedirect.js b/tests/mozmill/tests/testRedirect/testLinkClickRedirect.js
index f0d3a3c..31ec8cd 100644
--- a/tests/mozmill/tests/testRedirect/testLinkClickRedirect.js
+++ b/tests/mozmill/tests/testRedirect/testLinkClickRedirect.js
@@ -64,9 +64,9 @@ var testLinkClickRedirect = function() {
         controller.waitFor(function() {
             return controller.window.content.document.location.href !== url;
         }, "The URL in the urlbar has changed.");
-        assert.ok(!panel.exists(), "The redirect has been allowed.");
+        expect.ok(!panel.exists(), "The redirect notification bar is hidden.");
       } else {
-        assert.ok(panel.exists(), "The redirect has been blocked.");
+        expect.ok(panel.exists(), "The redirect notification bar is displayed.");
       }
 
       tabBrowser.closeAllTabs();

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



More information about the Pkg-mozext-commits mailing list