[Pkg-mozext-commits] [requestpolicy] 71/280: [refact] separate file for redirection-related stuff

David Prévot taffit at moszumanska.debian.org
Sat May 2 20:30:01 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 9b698099ec282d09e6606c31d38bc593f07bba5a
Author: Martin Kimmerle <dev at 256k.de>
Date:   Sat Dec 27 16:25:56 2014 +0100

    [refact] separate file for redirection-related stuff
---
 src/content/lib/domain-util.jsm                    |   3 +
 src/content/lib/request-processor.jsm              | 520 +++------------------
 src/content/lib/request-processor.redirects.js     | 481 +++++++++++++++++++
 src/content/lib/requestpolicy-service.jsm          |  23 -
 src/content/lib/utils.jsm                          |  47 +-
 src/content/ui/frame.dom-content-loaded.js         |   6 +-
 .../mozmill/tests/testRedirect/testAutoRedirect.js |  16 +-
 .../tests/testRedirect/testLinkClickRedirect.js    |  12 +-
 8 files changed, 628 insertions(+), 480 deletions(-)

diff --git a/src/content/lib/domain-util.jsm b/src/content/lib/domain-util.jsm
index 6860898..9bcc2f7 100644
--- a/src/content/lib/domain-util.jsm
+++ b/src/content/lib/domain-util.jsm
@@ -131,6 +131,9 @@ DomainUtil.getHost = function(uri) {
  *         exception if it is an invalid uri.
  */
 DomainUtil.getUriObject = function(uri) {
+  // fixme: if `uri` is relative, `newURI()` throws NS_ERROR_MALFORMED_URI.
+  // possible solution: use nsIURI.resolve() instead for relative uris
+
   // Throws an exception if uri is invalid.
   try {
     return Services.io.newURI(uri, null, null);
diff --git a/src/content/lib/request-processor.jsm b/src/content/lib/request-processor.jsm
index 7ed59f9..e34fca3 100644
--- a/src/content/lib/request-processor.jsm
+++ b/src/content/lib/request-processor.jsm
@@ -28,8 +28,6 @@ const Cu = Components.utils;
 
 let EXPORTED_SYMBOLS = ["RequestProcessor"];
 
-let globalScope = this;
-
 const CP_OK = Ci.nsIContentPolicy.ACCEPT;
 const CP_REJECT = Ci.nsIContentPolicy.REJECT_SERVER;
 
@@ -38,6 +36,7 @@ const CP_REJECT = Ci.nsIContentPolicy.REJECT_SERVER;
 const CP_MAPPEDDESTINATION = 0x178c40bf;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
 ScriptLoader.importModules([
@@ -48,17 +47,18 @@ ScriptLoader.importModules([
   "utils",
   "request",
   "request-result",
-  "request-set"
+  "request-set",
+  "bootstrap-manager"
 ], this);
 ScriptLoader.defineLazyModuleGetters({
   "content-policy": ["PolicyImplementation"],
   "requestpolicy-service": ["rpService"]
-}, globalScope);
+}, this);
 
 
 
-let RequestProcessor = (function() {
-  let self = {};
+let RequestProcessor = (function(self) {
+  let internal = Utils.moduleInternal(self);
 
 
   /**
@@ -83,273 +83,57 @@ let RequestProcessor = (function() {
     "result" : null
   };
 
-  /**
-   * These are redirects that the user allowed when presented with a redirect
-   * notification.
-   */
-  let userAllowedRedirects = {};
-
-  let allowedRedirectsReverse = {};
-
   let historyRequests = {};
 
-  let submittedForms = {};
-  let submittedFormsReverse = {};
-
-  let clickedLinks = {};
-  let clickedLinksReverse = {};
-
-  let faviconRequests = {};
-
-  let mappedDestinations = {};
-
-  let requestObservers = [];
-
-
-
-
-  function mapDestinations(origDestUri, newDestUri) {
-    origDestUri = DomainUtil.stripFragment(origDestUri);
-    newDestUri = DomainUtil.stripFragment(newDestUri);
-    Logger.info(Logger.TYPE_INTERNAL,
-        "Mapping destination <" + origDestUri + "> to <" + newDestUri + ">.");
-    if (!mappedDestinations[newDestUri]) {
-      mappedDestinations[newDestUri] = {};
-    }
-    mappedDestinations[newDestUri][origDestUri] =
-        DomainUtil.getUriObject(origDestUri);
-  }
-
-
-
-
-  /**
-   * Checks whether a request is initiated by a content window. If it's from a
-   * content window, then it's from unprivileged code.
-   */
-  function isContentRequest(channel) {
-    var callbacks = [];
-    if (channel.notificationCallbacks) {
-      callbacks.push(channel.notificationCallbacks);
-    }
-    if (channel.loadGroup && channel.loadGroup.notificationCallbacks) {
-      callbacks.push(channel.loadGroup.notificationCallbacks);
-    }
-
-    for (var i = 0; i < callbacks.length; i++) {
-      var callback = callbacks[i];
-      try {
-        return callback.getInterface(Ci.nsILoadContext).isContent;
-      } catch (e) {
-      }
-    }
-
-    return false;
-  }
-  function processRedirect(request, httpChannel) {
-    var originURI = request.originURI;
-    var destURI = request.destURI;
-    var headerType = request.httpHeader;
-
-    // Ignore redirects to javascript. The browser will ignore them, as well.
-    if (DomainUtil.getUriObject(destURI).schemeIs("javascript")) {
-      Logger.warning(Logger.TYPE_HEADER_REDIRECT,
-          "Ignoring redirect to javascript URI <" + destURI + ">");
-      return;
-    }
-
-    request.requestResult = checkRedirect(request);
-    if (true === request.requestResult.isAllowed) {
-      Logger.warning(Logger.TYPE_HEADER_REDIRECT, "** ALLOWED ** '"
-          + headerType + "' header to <" + destURI + "> " + "from <" + originURI
-          + ">. Same hosts or allowed origin/destination.");
-      recordAllowedRequest(originURI, destURI, false, request.requestResult);
-      allowedRedirectsReverse[destURI] = originURI;
-
-      // If this was a link click or a form submission, we register an
-      // additional click/submit with the original source but with a new
-      // destination of the target of the redirect. This is because future
-      // requests (such as using back/forward) might show up as directly from
-      // the initial origin to the ultimate redirected destination.
-      if (httpChannel.referrer) {
-        var realOrigin = httpChannel.referrer.spec;
-
-        if (clickedLinks[realOrigin] && clickedLinks[realOrigin][originURI]) {
-          Logger.warning(Logger.TYPE_HEADER_REDIRECT,
-              "This redirect was from a link click." +
-              " Registering an additional click to <" + destURI + "> " +
-              "from <" + realOrigin + ">");
-          self.registerLinkClicked(realOrigin, destURI);
-
-        } else if (submittedForms[realOrigin]
-            && submittedForms[realOrigin][originURI.split("?")[0]]) {
-          Logger.warning(Logger.TYPE_HEADER_REDIRECT,
-              "This redirect was from a form submission." +
-              " Registering an additional form submission to <" + destURI +
-              "> " + "from <" + realOrigin + ">");
-          self.registerFormSubmitted(realOrigin, destURI);
-        }
-      }
+  internal.submittedForms = {};
+  internal.submittedFormsReverse = {};
 
-      return;
-    }
+  internal.clickedLinks = {};
+  internal.clickedLinksReverse = {};
 
-    // The header isn't allowed, so remove it.
-    try {
-      if (!Prefs.isBlockingDisabled()) {
-        httpChannel.setResponseHeader(headerType, "", false);
+  internal.faviconRequests = {};
 
-        /* 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*/
+  internal.mappedDestinations = {};
 
+  internal.requestObservers = [];
 
-        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");
-          if (contentDisp.indexOf("attachment") != -1) {
-            try {
-              httpChannel.setResponseHeader("Content-Disposition", "", false);
-              Logger.warning(Logger.TYPE_HEADER_REDIRECT,
-                  "Removed 'Content-Disposition: attachment' header to " +
-                  "prevent display of about:neterror.");
-            } catch (e) {
-              Logger.warning(Logger.TYPE_HEADER_REDIRECT,
-                  "Unable to remove 'Content-Disposition: attachment' header " +
-                  "to prevent display of about:neterror. " + e);
-            }
-          }
-        } catch (e) {
-          // No Content-Disposition header.
-        }
-
-        // We try to trace the blocked redirect back to a link click or form
-        // submission if we can. It may indicate, for example, a link that
-        // was to download a file but a redirect got blocked at some point.
-        var initialOrigin = originURI;
-        var initialDest = destURI;
-        // To prevent infinite loops, bound the number of iterations.
-        // Note that an apparent redirect loop doesn't mean a problem with a
-        // website as the site may be using other information, such as cookies
-        // that get set in the redirection process, to stop the redirection.
-        var iterations = 0;
-        const ASSUME_REDIRECT_LOOP = 100; // Chosen arbitrarily.
-        while (initialOrigin in allowedRedirectsReverse &&
-               iterations++ < ASSUME_REDIRECT_LOOP) {
-          initialDest = initialOrigin;
-          initialOrigin = allowedRedirectsReverse[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).
-            var sourcePage = i;
-          }
 
-          notifyRequestObserversOfBlockedLinkClickRedirect(sourcePage,
-              originURI, destURI);
-
-          // Maybe we just record the clicked link and each step in between as
-          // an allowed request, and the final blocked one as a blocked request.
-          // That is, make it show up in the requestpolicy menu like anything
-          // else.
-          // We set the "isInsert" parameter so we don't clobber the existing
-          // info about allowed and deleted requests.
-          recordAllowedRequest(sourcePage, initialOrigin, true,
-                               request.requestResult);
-        }
 
-        // if (submittedFormsReverse[initialOrigin]) {
-        // // TODO: implement for form submissions whose redirects are blocked
-        // }
-
-        recordRejectedRequest(request);
-      }
-      Logger.warning(Logger.TYPE_HEADER_REDIRECT,
-          "** BLOCKED ** '" + headerType + "' header to <" + destURI + ">" +
-          " found in response from <" + originURI + ">");
-    } catch (e) {
-      Logger.severe(
-          Logger.TYPE_HEADER_REDIRECT, "Failed removing " +
-          "'" + headerType + "' header to <" + destURI + ">" +
-          "  in response from <" + originURI + ">." + e);
-    }
-  }
 
 
 
 
 
   function notifyRequestObserversOfBlockedRequest(request) {
-    for (var i = 0; i < requestObservers.length; i++) {
-      if (!requestObservers[i]) {
+    for (var i = 0; i < internal.requestObservers.length; i++) {
+      if (!internal.requestObservers[i]) {
         continue;
       }
-      requestObservers[i].observeBlockedRequest(request.originURI,
+      internal.requestObservers[i].observeBlockedRequest(request.originURI,
           request.destURI, request.requestResult);
     }
   }
 
   function notifyRequestObserversOfAllowedRequest(originUri,
       destUri, requestResult) {
-    for (var i = 0; i < requestObservers.length; i++) {
-      if (!requestObservers[i]) {
+    for (var i = 0; i < internal.requestObservers.length; i++) {
+      if (!internal.requestObservers[i]) {
         continue;
       }
-      requestObservers[i].observeAllowedRequest(originUri, destUri,
+      internal.requestObservers[i].observeAllowedRequest(originUri, destUri,
           requestResult);
     }
   }
 
-  function notifyRequestObserversOfBlockedLinkClickRedirect(sourcePageUri,
-      linkDestUri, blockedRedirectUri) {
-    for (var i = 0; i < requestObservers.length; i++) {
-      if (!requestObservers[i]) {
-        continue;
-      }
-      requestObservers[i].observeBlockedLinkClickRedirect(sourcePageUri,
-          linkDestUri, blockedRedirectUri);
-    }
-  }
-
   function notifyBlockedTopLevelDocRequest(originUri, destUri) {
     // TODO: this probably could be done async.
-    for (var i = 0; i < requestObservers.length; i++) {
-      if (!requestObservers[i]) {
+    for (var i = 0; i < internal.requestObservers.length; i++) {
+      if (!internal.requestObservers[i]) {
         continue;
       }
-      requestObservers[i].observeBlockedTopLevelDocRequest(originUri,
+      internal.requestObservers[i].observeBlockedTopLevelDocRequest(originUri,
           destUri);
     }
   }
@@ -358,68 +142,6 @@ let RequestProcessor = (function() {
 
 
 
-  function checkRedirect(request) {
-    // TODO: Find a way to get rid of repitition of code between this and
-    // shouldLoad().
-
-    // Note: If changing the logic here, also make necessary changes to
-    // shouldLoad().
-
-    // This is not including link clicks, form submissions, and user-allowed
-    // redirects.
-
-    var originURI = request.originURI;
-    var destURI = request.destURI;
-
-    var originURIObj = DomainUtil.getUriObject(originURI);
-    var destURIObj = DomainUtil.getUriObject(destURI);
-
-    var result = PolicyManager.checkRequestAgainstUserRules(originURIObj,
-        destURIObj);
-    // For now, we always give priority to deny rules.
-    if (result.denyRulesExist()) {
-      result.isAllowed = false;
-      return result;
-    }
-    if (result.allowRulesExist()) {
-      result.isAllowed = true;
-      return result;
-    }
-
-    var result = PolicyManager.checkRequestAgainstSubscriptionRules(
-        originURIObj, destURIObj);
-    // For now, we always give priority to deny rules.
-    if (result.denyRulesExist()) {
-      result.isAllowed = false;
-      return result;
-    }
-    if (result.allowRulesExist()) {
-      result.isAllowed = true;
-      return result;
-    }
-
-    if (destURI[0] && destURI[0] == '/'
-        || destURI.indexOf(":") == -1) {
-      // Redirect is to a relative url.
-      // ==> allow.
-      return new RequestResult(true, REQUEST_REASON_RELATIVE_URL);
-    }
-
-    let compatibilityRules = rpService.getCompatibilityRules();
-    for (var i = 0; i < compatibilityRules.length; i++) {
-      var rule = compatibilityRules[i];
-      var allowOrigin = rule[0] ? originURI.indexOf(rule[0]) == 0 : true;
-      var allowDest = rule[1] ? destURI.indexOf(rule[1]) == 0 : true;
-      if (allowOrigin && allowDest) {
-        return new RequestResult(true,
-            REQUEST_REASON_COMPATIBILITY);
-      }
-    }
-
-    var result = checkByDefaultPolicy(originURI, destURI);
-    return result;
-  }
-
 
 
   // We always call this from shouldLoad to reject a request.
@@ -436,7 +158,7 @@ let RequestProcessor = (function() {
     }
 
     cacheShouldLoadResult(CP_REJECT, request.originURI, request.destURI);
-    recordRejectedRequest(request);
+    internal.recordRejectedRequest(request);
 
     if (Ci.nsIContentPolicy.TYPE_DOCUMENT == request.aContentType) {
       // This was a blocked top-level document request. This may be due to
@@ -448,7 +170,7 @@ let RequestProcessor = (function() {
     return CP_REJECT;
   }
 
-  function recordRejectedRequest(request) {
+  internal.recordRejectedRequest = function(request) {
     self._rejectedRequests.addRequest(request.originURI, request.destURI,
         request.requestResult);
     self._allowedRequests.removeRequest(request.originURI, request.destURI);
@@ -476,8 +198,8 @@ let RequestProcessor = (function() {
       notifyRequestObserversOfAllowedRequest(request.originURI, request.destURI,
           request.requestResult);
     } else {
-      recordAllowedRequest(request.originURI, request.destURI, false,
-          request.requestResult);
+      internal.recordAllowedRequest(request.originURI, request.destURI, false,
+                                    request.requestResult);
     }
 
     return CP_OK;
@@ -497,6 +219,7 @@ let RequestProcessor = (function() {
     self._allowedRequests.addRequest(originUri, destUri, requestResult);
     notifyRequestObserversOfAllowedRequest(originUri, destUri, requestResult);
   }
+  internal.recordAllowedRequest = recordAllowedRequest;
 
   function cacheShouldLoadResult(result, originUri, destUri) {
     var date = new Date();
@@ -506,7 +229,7 @@ let RequestProcessor = (function() {
     lastShouldLoadCheck.result = result;
   }
 
-  function checkByDefaultPolicy(originUri, destUri) {
+  internal.checkByDefaultPolicy = function(originUri, destUri) {
     if (Prefs.isDefaultAllow()) {
       var result = new RequestResult(true,
           REQUEST_REASON_DEFAULT_POLICY);
@@ -528,7 +251,7 @@ let RequestProcessor = (function() {
         DomainUtil.LEVEL_SOP);
     return new RequestResult(originIdent == destIdent,
         REQUEST_REASON_DEFAULT_SAME_DOMAIN);
-  }
+  };
 
   /**
    * Determines if a request is a duplicate of the last call to shouldLoad(). If
@@ -855,7 +578,7 @@ let RequestProcessor = (function() {
 
         if (domNode && domNode.nodeName == "LINK" &&
             (domNode.rel == "icon" || domNode.rel == "shortcut icon")) {
-          faviconRequests[destURI] = true;
+          internal.faviconRequests[destURI] = true;
         }
       }
 
@@ -877,11 +600,11 @@ let RequestProcessor = (function() {
       // was opened in a new tab but that link would have been allowed
       // regardless of the link click. The original tab would then show it
       // in its menu.
-      if (clickedLinks[originURI] &&
-          clickedLinks[originURI][destURI]) {
+      if (internal.clickedLinks[originURI] &&
+          internal.clickedLinks[originURI][destURI]) {
         // Don't delete the clickedLinks item. We need it for if the user
         // goes back/forward through their history.
-        // delete clickedLinks[originURI][destURI];
+        // delete internal.clickedLinks[originURI][destURI];
 
         // We used to have this not be recorded so that it wouldn't cause us
         // to forget blocked/allowed requests. However, when a policy change
@@ -893,14 +616,14 @@ let RequestProcessor = (function() {
             REQUEST_REASON_LINK_CLICK);
         return accept("User-initiated request by link click", request);
 
-      } else if (submittedForms[originURI] &&
-          submittedForms[originURI][destURI.split("?")[0]]) {
+      } else if (internal.submittedForms[originURI] &&
+          internal.submittedForms[originURI][destURI.split("?")[0]]) {
         // Note: we dropped the query string from the destURI because form GET
         // requests will have that added on here but the original action of
         // the form may not have had it.
         // Don't delete the clickedLinks item. We need it for if the user
         // goes back/forward through their history.
-        // delete submittedForms[originURI][destURI.split("?")[0]];
+        // delete internal.submittedForms[originURI][destURI.split("?")[0]];
 
         // See the note above for link clicks and forgetting blocked/allowed
         // requests on refresh. I haven't tested if it's the same for forms
@@ -918,8 +641,8 @@ let RequestProcessor = (function() {
         request.requestResult = new RequestResult(true,
             REQUEST_REASON_HISTORY_REQUEST);
         return accept("History request", request, true);
-      } else if (userAllowedRedirects[originURI]
-          && userAllowedRedirects[originURI][destURI]) {
+      } else if (internal.userAllowedRedirects[originURI]
+          && internal.userAllowedRedirects[originURI][destURI]) {
         // shouldLoad is called by location.href in overlay.js as of Fx
         // 3.7a5pre and SeaMonkey 2.1a.
         request.requestResult = new RequestResult(true,
@@ -1097,9 +820,9 @@ let RequestProcessor = (function() {
       // request if the original destination would have been accepted.
       // Check aExtra against CP_MAPPEDDESTINATION to stop further recursion.
       if (request.aExtra != CP_MAPPEDDESTINATION &&
-          mappedDestinations[destURI]) {
-        for (var mappedDest in mappedDestinations[destURI]) {
-          var mappedDestUriObj = mappedDestinations[destURI][mappedDest];
+          internal.mappedDestinations[destURI]) {
+        for (var mappedDest in internal.mappedDestinations[destURI]) {
+          var mappedDestUriObj = internal.mappedDestinations[destURI][mappedDest];
           Logger.warning(Logger.TYPE_CONTENT,
               "Checking mapped destination: " + mappedDest);
           var mappedResult = PolicyImplementation.shouldLoad(
@@ -1111,7 +834,7 @@ let RequestProcessor = (function() {
         }
       }
 
-      request.requestResult = checkByDefaultPolicy(originURI, destURI);
+      request.requestResult = internal.checkByDefaultPolicy(originURI, destURI);
       if (request.requestResult.isAllowed) {
         return accept("Allowed by default policy", request);
       } else {
@@ -1140,107 +863,6 @@ let RequestProcessor = (function() {
 
 
   /**
-   * Called after a response has been received from the web server. Headers are
-   * available on the channel. The response can be accessed and modified via
-   * nsITraceableChannel.
-   */
-  self._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
-    // the menu and attempted the download again, they won't see the allowed
-    // request in the menu. Fixing that might be a pain and also runs the
-    // risk of making the menu cluttered and confusing with destinations of
-    // followed links from the current page.
-
-    // TODO: Make user aware of blocked headers so they can allow them if
-    // desired.
-
-    var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
-
-    var headerType;
-    var dest;
-
-    try {
-      // If there is no such header, getResponseHeader() will throw
-      // NS_ERROR_NOT_AVAILABLE. If there is more than header, the last one is
-      // the one that will be used.
-      headerType = "Location";
-      dest = httpChannel.getResponseHeader(headerType);
-    } catch (e) {
-      // No location header. Look for a Refresh header.
-      try {
-        headerType = "Refresh";
-        var refreshString = httpChannel.getResponseHeader(headerType);
-      } catch (e) {
-        // No Location header or Refresh header.
-        return;
-      }
-      try {
-        // 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 + ">");
-        if (!Prefs.isBlockingDisabled()) {
-          httpChannel.setResponseHeader(headerType, "", false);
-        }
-        return;
-      }
-    }
-
-    // For origins that are IDNs, this will always be in ACE format. We want
-    // it in UTF8 format if it's a TLD that Mozilla allows to be in UTF8.
-    var originURI = DomainUtil.formatIDNUri(httpChannel.name);
-
-    // Allow redirects of requests from privileged code.
-    if (!isContentRequest(httpChannel)) {
-      // However, favicon requests that are redirected appear as non-content
-      // requests. So, check if the original request was for a favicon.
-      var originPath = DomainUtil.getPath(httpChannel.name);
-      // We always have to check "/favicon.ico" because Firefox will use this
-      // as a default path and that request won't pass through shouldLoad().
-      if (originPath == "/favicon.ico" || faviconRequests[originURI]) {
-        // If the redirected request is allowed, we need to know that was a
-        // favicon request in case it is further redirected.
-        faviconRequests[dest] = true;
-        Logger.info(Logger.TYPE_HEADER_REDIRECT, "'" + headerType
-                + "' header to <" + dest + "> " + "from <" + originURI
-                + "> appears to be a redirected favicon request. "
-                + "This will be treated as a content request.");
-      } else {
-        Logger.warning(Logger.TYPE_HEADER_REDIRECT,
-            "** ALLOWED ** '" + headerType + "' header to <" + dest + "> " +
-            "from <" + originURI +
-            ">. Original request is from privileged code.");
-        return;
-      }
-    }
-
-    // If it's not a valid uri, the redirect is relative to the origin host.
-    // The way we have things written currently, without this check the full
-    // dest string will get treated as the destination and displayed in the
-    // menu because DomainUtil.getIdentifier() doesn't raise exceptions.
-    // We add this to fix issue #39:
-    // https://github.com/RequestPolicyContinued/requestpolicy/issues/39
-    if (!DomainUtil.isValidUri(dest)) {
-      var destAsUri = DomainUtil.determineRedirectUri(originURI, dest);
-      Logger.warning(
-          Logger.TYPE_HEADER_REDIRECT,
-          "Redirect destination is not a valid uri, assuming dest <" + dest
-              + "> from origin <" + originURI + "> is actually dest <" + destAsUri
-              + ">.");
-      dest = destAsUri;
-    }
-
-    var request = new RedirectRequest(originURI, dest, headerType);
-    processRedirect(request, httpChannel);
-  };
-
-  /**
    * Called as a http request is made. The channel is available to allow you to
    * modify headers and such.
    *
@@ -1296,21 +918,21 @@ let RequestProcessor = (function() {
     // we'll need to be dropping the query string there.
     destinationUrl = destinationUrl.split("?")[0];
 
-    if (submittedForms[originUrl] == undefined) {
-      submittedForms[originUrl] = {};
+    if (internal.submittedForms[originUrl] == undefined) {
+      internal.submittedForms[originUrl] = {};
     }
-    if (submittedForms[originUrl][destinationUrl] == undefined) {
+    if (internal.submittedForms[originUrl][destinationUrl] == undefined) {
       // TODO: See timestamp note for registerLinkClicked.
-      submittedForms[originUrl][destinationUrl] = true;
+      internal.submittedForms[originUrl][destinationUrl] = true;
     }
 
     // Keep track of a destination-indexed map, as well.
-    if (submittedFormsReverse[destinationUrl] == undefined) {
-      submittedFormsReverse[destinationUrl] = {};
+    if (internal.submittedFormsReverse[destinationUrl] == undefined) {
+      internal.submittedFormsReverse[destinationUrl] = {};
     }
-    if (submittedFormsReverse[destinationUrl][originUrl] == undefined) {
+    if (internal.submittedFormsReverse[destinationUrl][originUrl] == undefined) {
       // TODO: See timestamp note for registerLinkClicked.
-      submittedFormsReverse[destinationUrl][originUrl] = true;
+      internal.submittedFormsReverse[destinationUrl][originUrl] = true;
     }
   };
 
@@ -1322,10 +944,10 @@ let RequestProcessor = (function() {
     Logger.info(Logger.TYPE_INTERNAL,
         "Link clicked from <" + originUrl + "> to <" + destinationUrl + ">.");
 
-    if (clickedLinks[originUrl] == undefined) {
-      clickedLinks[originUrl] = {};
+    if (internal.clickedLinks[originUrl] == undefined) {
+      internal.clickedLinks[originUrl] = {};
     }
-    if (clickedLinks[originUrl][destinationUrl] == undefined) {
+    if (internal.clickedLinks[originUrl][destinationUrl] == undefined) {
       // TODO: Possibly set the value to a timestamp that can be used elsewhere
       // to determine if this is a recent click. This is probably necessary as
       // multiple calls to shouldLoad get made and we need a way to allow
@@ -1335,16 +957,16 @@ let RequestProcessor = (function() {
       // of time). This would have the advantage that we could delete items from
       // the clickedLinks object. One of these approaches would also reduce log
       // clutter, which would be good.
-      clickedLinks[originUrl][destinationUrl] = true;
+      internal.clickedLinks[originUrl][destinationUrl] = true;
     }
 
     // Keep track of a destination-indexed map, as well.
-    if (clickedLinksReverse[destinationUrl] == undefined) {
-      clickedLinksReverse[destinationUrl] = {};
+    if (internal.clickedLinksReverse[destinationUrl] == undefined) {
+      internal.clickedLinksReverse[destinationUrl] = {};
     }
-    if (clickedLinksReverse[destinationUrl][originUrl] == undefined) {
+    if (internal.clickedLinksReverse[destinationUrl][originUrl] == undefined) {
       // TODO: Possibly set the value to a timestamp, as described above.
-      clickedLinksReverse[destinationUrl][originUrl] = true;
+      internal.clickedLinksReverse[destinationUrl][originUrl] = true;
     }
   };
 
@@ -1356,19 +978,14 @@ let RequestProcessor = (function() {
     Logger.info(Logger.TYPE_INTERNAL, "User-allowed redirect from <" +
         originUrl + "> to <" + destinationUrl + ">.");
 
-    if (userAllowedRedirects[originUrl] == undefined) {
-      userAllowedRedirects[originUrl] = {};
+    if (internal.userAllowedRedirects[originUrl] == undefined) {
+      internal.userAllowedRedirects[originUrl] = {};
     }
-    if (userAllowedRedirects[originUrl][destinationUrl] == undefined) {
-      userAllowedRedirects[originUrl][destinationUrl] = true;
+    if (internal.userAllowedRedirects[originUrl][destinationUrl] == undefined) {
+      internal.userAllowedRedirects[originUrl][destinationUrl] = true;
     }
   };
 
-  self.isAllowedRedirect = function(originURI, destURI) {
-    var request = new RedirectRequest(originURI, destURI);
-    return (true === checkRedirect(request).isAllowed);
-  };
-
   /**
    * Add an observer to be notified of all blocked and allowed requests. TODO:
    * This should be made to accept instances of a defined interface.
@@ -1382,7 +999,7 @@ let RequestProcessor = (function() {
     }
     Logger.debug(Logger.TYPE_INTERNAL,
         "Adding request observer: " + observer.toString());
-    requestObservers.push(observer);
+    internal.requestObservers.push(observer);
   };
 
   /**
@@ -1391,11 +1008,11 @@ let RequestProcessor = (function() {
    * @param {Object} observer
    */
   self.removeRequestObserver = function(observer) {
-    for (var i = 0; i < requestObservers.length; i++) {
-      if (requestObservers[i] == observer) {
+    for (var i = 0; i < internal.requestObservers.length; i++) {
+      if (internal.requestObservers[i] == observer) {
         Logger.debug(Logger.TYPE_INTERNAL,
             "Removing request observer: " + observer.toString());
-        delete requestObservers[i];
+        delete internal.requestObservers[i];
         return;
       }
     }
@@ -1456,4 +1073,7 @@ let RequestProcessor = (function() {
   };
 
   return self;
-}());
+}(RequestProcessor || {}));
+
+Services.scriptloader.loadSubScript('chrome://requestpolicy/content/lib/' +
+                                    'request-processor.redirects.js');
diff --git a/src/content/lib/request-processor.redirects.js b/src/content/lib/request-processor.redirects.js
new file mode 100644
index 0000000..1db3922
--- /dev/null
+++ b/src/content/lib/request-processor.redirects.js
@@ -0,0 +1,481 @@
+/*
+ * ***** 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 HTTPS_EVERYWHERE_REWRITE_TOPIC = "https-everywhere-uri-rewrite";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ScriptLoader.importModules([
+  "logger",
+  "prefs",
+  "policy-manager",
+  "domain-util",
+  "utils",
+  "request",
+  "request-result"
+], this);
+ScriptLoader.defineLazyModuleGetters({
+  "requestpolicy-service": ["rpService"]
+}, this);
+
+
+
+let RequestProcessor = (function(self) {
+  let internal = Utils.moduleInternal(self);
+
+
+  /**
+   * These are redirects that the user allowed when presented with a redirect
+   * notification.
+   */
+  internal.userAllowedRedirects = {};
+
+  internal.allowedRedirectsReverse = {};
+
+
+
+  Utils.observeNotifications(
+    // the observer
+    {
+      observe: function(subject, topic, data) {
+        switch (topic) {
+          case "http-on-examine-response":
+            examineHttpResponse(subject);
+            break;
+
+          case HTTPS_EVERYWHERE_REWRITE_TOPIC:
+            handleHttpsEverywhereUriRewrite(subject, data);
+            break;
+        }
+      }
+    },
+    // observer topics
+    [
+      "http-on-examine-response",
+      HTTPS_EVERYWHERE_REWRITE_TOPIC
+    ]
+  );
+
+
+
+
+
+
+  function mapDestinations(origDestUri, newDestUri) {
+    origDestUri = DomainUtil.stripFragment(origDestUri);
+    newDestUri = DomainUtil.stripFragment(newDestUri);
+    Logger.info(Logger.TYPE_INTERNAL,
+        "Mapping destination <" + origDestUri + "> to <" + newDestUri + ">.");
+    if (!internal.mappedDestinations[newDestUri]) {
+      internal.mappedDestinations[newDestUri] = {};
+    }
+    internal.mappedDestinations[newDestUri][origDestUri] =
+        DomainUtil.getUriObject(origDestUri);
+  }
+
+  /**
+   * Handles observer notifications sent by the HTTPS Everywhere extension
+   * that inform us of URIs that extension has rewritten.
+   *
+   * @param nsIURI oldURI
+   * @param string newSpec
+   */
+  function handleHttpsEverywhereUriRewrite(oldURI, newSpec) {
+    oldURI = oldURI.QueryInterface(Ci.nsIURI);
+    mapDestinations(oldURI.spec, newSpec);
+  }
+
+  function checkRedirect(request) {
+    // TODO: Find a way to get rid of repitition of code between this and
+    // shouldLoad().
+
+    // Note: If changing the logic here, also make necessary changes to
+    // shouldLoad().
+
+    // This is not including link clicks, form submissions, and user-allowed
+    // redirects.
+
+    var originURI = request.originURI;
+    var destURI = request.destURI;
+
+    var originURIObj = DomainUtil.getUriObject(originURI);
+    var destURIObj = DomainUtil.getUriObject(destURI);
+
+    var result = PolicyManager.checkRequestAgainstUserRules(originURIObj,
+        destURIObj);
+    // For now, we always give priority to deny rules.
+    if (result.denyRulesExist()) {
+      result.isAllowed = false;
+      return result;
+    }
+    if (result.allowRulesExist()) {
+      result.isAllowed = true;
+      return result;
+    }
+
+    var result = PolicyManager.checkRequestAgainstSubscriptionRules(
+        originURIObj, destURIObj);
+    // For now, we always give priority to deny rules.
+    if (result.denyRulesExist()) {
+      result.isAllowed = false;
+      return result;
+    }
+    if (result.allowRulesExist()) {
+      result.isAllowed = true;
+      return result;
+    }
+
+    if (destURI[0] && destURI[0] == '/'
+        || destURI.indexOf(":") == -1) {
+      // Redirect is to a relative url.
+      // ==> allow.
+      return new RequestResult(true, REQUEST_REASON_RELATIVE_URL);
+    }
+
+    let compatibilityRules = rpService.getCompatibilityRules();
+    for (var i = 0; i < compatibilityRules.length; i++) {
+      var rule = compatibilityRules[i];
+      var allowOrigin = rule[0] ? originURI.indexOf(rule[0]) == 0 : true;
+      var allowDest = rule[1] ? destURI.indexOf(rule[1]) == 0 : true;
+      if (allowOrigin && allowDest) {
+        return new RequestResult(true, REQUEST_REASON_COMPATIBILITY);
+      }
+    }
+
+    var result = internal.checkByDefaultPolicy(originURI, destURI);
+    return result;
+  }
+
+
+  self.isAllowedRedirect = function(originURI, destURI) {
+    var request = new RedirectRequest(originURI, destURI);
+    return (true === checkRedirect(request).isAllowed);
+  };
+
+  function processRedirect(request, httpChannel) {
+    var originURI = request.originURI;
+    var destURI = request.destURI;
+    var headerType = request.httpHeader;
+
+    // Ignore redirects to javascript. The browser will ignore them, as well.
+    if (DomainUtil.getUriObject(destURI).schemeIs("javascript")) {
+      Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+          "Ignoring redirect to javascript URI <" + destURI + ">");
+      return;
+    }
+
+    request.requestResult = checkRedirect(request);
+    if (true === request.requestResult.isAllowed) {
+      Logger.warning(Logger.TYPE_HEADER_REDIRECT, "** ALLOWED ** '"
+          + headerType + "' header to <" + destURI + "> " + "from <" + originURI
+          + ">. Same hosts or allowed origin/destination.");
+      internal.recordAllowedRequest(originURI, destURI, false,
+                                    request.requestResult);
+      internal.allowedRedirectsReverse[destURI] = originURI;
+
+      // If this was a link click or a form submission, we register an
+      // additional click/submit with the original source but with a new
+      // destination of the target of the redirect. This is because future
+      // requests (such as using back/forward) might show up as directly from
+      // the initial origin to the ultimate redirected destination.
+      if (httpChannel.referrer) {
+        var realOrigin = httpChannel.referrer.spec;
+
+        if (internal.clickedLinks[realOrigin] &&
+            internal.clickedLinks[realOrigin][originURI]) {
+          Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+              "This redirect was from a link click." +
+              " Registering an additional click to <" + destURI + "> " +
+              "from <" + realOrigin + ">");
+          self.registerLinkClicked(realOrigin, destURI);
+
+        } else if (internal.submittedForms[realOrigin]
+            && internal.submittedForms[realOrigin][originURI.split("?")[0]]) {
+          Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+              "This redirect was from a form submission." +
+              " Registering an additional form submission to <" + destURI +
+              "> " + "from <" + realOrigin + ">");
+          self.registerFormSubmitted(realOrigin, destURI);
+        }
+      }
+
+      return;
+    }
+
+    // The header isn't allowed, so remove it.
+    try {
+      if (!Prefs.isBlockingDisabled()) {
+        httpChannel.setResponseHeader(headerType, "", false);
+
+        /* 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 {
+          let contentDisp = httpChannel.getResponseHeader("Content-Disposition");
+          if (contentDisp.indexOf("attachment") != -1) {
+            try {
+              httpChannel.setResponseHeader("Content-Disposition", "", false);
+              Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+                  "Removed 'Content-Disposition: attachment' header to " +
+                  "prevent display of about:neterror.");
+            } catch (e) {
+              Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+                  "Unable to remove 'Content-Disposition: attachment' header " +
+                  "to prevent display of about:neterror. " + e);
+            }
+          }
+        } catch (e) {
+          // No Content-Disposition header.
+        }
+
+        // We try to trace the blocked redirect back to a link click or form
+        // submission if we can. It may indicate, for example, a link that
+        // was to download a file but a redirect got blocked at some point.
+        var initialOrigin = originURI;
+        var initialDest = destURI;
+        // To prevent infinite loops, bound the number of iterations.
+        // Note that an apparent redirect loop doesn't mean a problem with a
+        // website as the site may be using other information, such as cookies
+        // that get set in the redirection process, to stop the redirection.
+        var iterations = 0;
+        const ASSUME_REDIRECT_LOOP = 100; // Chosen arbitrarily.
+        while (initialOrigin in internal.allowedRedirectsReverse &&
+               iterations++ < ASSUME_REDIRECT_LOOP) {
+          initialDest = initialOrigin;
+          initialOrigin = internal.allowedRedirectsReverse[initialOrigin];
+        }
+
+        if (initialOrigin in internal.clickedLinksReverse) {
+          for (var i in internal.clickedLinksReverse[initialOrigin]) {
+            // We hope there's only one possibility of a source page (that is,
+            // ideally there will be one iteration of this loop).
+            var sourcePage = i;
+          }
+
+          notifyRequestObserversOfBlockedLinkClickRedirect(sourcePage,
+              originURI, destURI);
+
+          // Maybe we just record the clicked link and each step in between as
+          // an allowed request, and the final blocked one as a blocked request.
+          // That is, make it show up in the requestpolicy menu like anything
+          // else.
+          // We set the "isInsert" parameter so we don't clobber the existing
+          // info about allowed and deleted requests.
+          internal.recordAllowedRequest(sourcePage, initialOrigin, true,
+                                        request.requestResult);
+        }
+
+        // if (internal.submittedFormsReverse[initialOrigin]) {
+        // // TODO: implement for form submissions whose redirects are blocked
+        // }
+
+        internal.recordRejectedRequest(request);
+      }
+      Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+          "** BLOCKED ** '" + headerType + "' header to <" + destURI + ">" +
+          " found in response from <" + originURI + ">");
+    } catch (e) {
+      Logger.severe(
+          Logger.TYPE_HEADER_REDIRECT, "Failed removing " +
+          "'" + headerType + "' header to <" + destURI + ">" +
+          "  in response from <" + originURI + ">." + e);
+    }
+  }
+
+  function notifyRequestObserversOfBlockedLinkClickRedirect(sourcePageUri,
+      linkDestUri, blockedRedirectUri) {
+    for (var i = 0; i < internal.requestObservers.length; i++) {
+      if (!internal.requestObservers[i]) {
+        continue;
+      }
+      internal.requestObservers[i].observeBlockedLinkClickRedirect(
+          sourcePageUri, linkDestUri, blockedRedirectUri);
+    }
+  }
+
+
+
+  /**
+   * Called after a response has been received from the web server. Headers are
+   * available on the channel. The response can be accessed and modified via
+   * nsITraceableChannel.
+   */
+  let 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
+    // the menu and attempted the download again, they won't see the allowed
+    // request in the menu. Fixing that might be a pain and also runs the
+    // risk of making the menu cluttered and confusing with destinations of
+    // followed links from the current page.
+
+    // TODO: Make user aware of blocked headers so they can allow them if
+    // desired.
+
+    var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+
+    var headerType;
+    var dest;
+
+    try {
+      // If there is no such header, getResponseHeader() will throw
+      // NS_ERROR_NOT_AVAILABLE. If there is more than header, the last one is
+      // the one that will be used.
+      headerType = "Location";
+      dest = httpChannel.getResponseHeader(headerType);
+    } catch (e) {
+      // No location header. Look for a Refresh header.
+      try {
+        headerType = "Refresh";
+        var refreshString = httpChannel.getResponseHeader(headerType);
+      } catch (e) {
+        // No Location header or Refresh header.
+        return;
+      }
+      try {
+        // 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 + ">");
+        if (!Prefs.isBlockingDisabled()) {
+          httpChannel.setResponseHeader(headerType, "", false);
+        }
+        return;
+      }
+    }
+
+    // For origins that are IDNs, this will always be in ACE format. We want
+    // it in UTF8 format if it's a TLD that Mozilla allows to be in UTF8.
+    var originURI = DomainUtil.formatIDNUri(httpChannel.name);
+
+    // Allow redirects of requests from privileged code.
+    if (!isContentRequest(httpChannel)) {
+      // However, favicon requests that are redirected appear as non-content
+      // requests. So, check if the original request was for a favicon.
+      var originPath = DomainUtil.getPath(httpChannel.name);
+      // We always have to check "/favicon.ico" because Firefox will use this
+      // as a default path and that request won't pass through shouldLoad().
+      if (originPath == "/favicon.ico" || internal.faviconRequests[originURI]) {
+        // If the redirected request is allowed, we need to know that was a
+        // favicon request in case it is further redirected.
+        internal.faviconRequests[dest] = true;
+        Logger.info(Logger.TYPE_HEADER_REDIRECT, "'" + headerType
+                + "' header to <" + dest + "> " + "from <" + originURI
+                + "> appears to be a redirected favicon request. "
+                + "This will be treated as a content request.");
+      } else {
+        Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+            "** ALLOWED ** '" + headerType + "' header to <" + dest + "> " +
+            "from <" + originURI +
+            ">. Original request is from privileged code.");
+        return;
+      }
+    }
+
+    // If it's not a valid uri, the redirect is relative to the origin host.
+    // The way we have things written currently, without this check the full
+    // dest string will get treated as the destination and displayed in the
+    // menu because DomainUtil.getIdentifier() doesn't raise exceptions.
+    // We add this to fix issue #39:
+    // https://github.com/RequestPolicyContinued/requestpolicy/issues/39
+    if (!DomainUtil.isValidUri(dest)) {
+      var destAsUri = DomainUtil.determineRedirectUri(originURI, dest);
+      Logger.warning(
+          Logger.TYPE_HEADER_REDIRECT,
+          "Redirect destination is not a valid uri, assuming dest <" + dest
+              + "> from origin <" + originURI + "> is actually dest <" + destAsUri
+              + ">.");
+      dest = destAsUri;
+    }
+
+    var request = new RedirectRequest(originURI, dest, headerType);
+    processRedirect(request, httpChannel);
+  };
+
+
+
+  /**
+   * Checks whether a request is initiated by a content window. If it's from a
+   * content window, then it's from unprivileged code.
+   */
+  function isContentRequest(channel) {
+    var callbacks = [];
+    if (channel.notificationCallbacks) {
+      callbacks.push(channel.notificationCallbacks);
+    }
+    if (channel.loadGroup && channel.loadGroup.notificationCallbacks) {
+      callbacks.push(channel.loadGroup.notificationCallbacks);
+    }
+
+    for (var i = 0; i < callbacks.length; i++) {
+      var callback = callbacks[i];
+      try {
+        return callback.getInterface(Ci.nsILoadContext).isContent;
+      } catch (e) {
+      }
+    }
+
+    return false;
+  }
+
+
+  return self;
+}(RequestProcessor || {}));
diff --git a/src/content/lib/requestpolicy-service.jsm b/src/content/lib/requestpolicy-service.jsm
index 83c6d06..dd5071a 100644
--- a/src/content/lib/requestpolicy-service.jsm
+++ b/src/content/lib/requestpolicy-service.jsm
@@ -27,8 +27,6 @@ const Cu = Components.utils;
 
 let EXPORTED_SYMBOLS = ["rpService"];
 
-const HTTPS_EVERYWHERE_REWRITE_TOPIC = "https-everywhere-uri-rewrite";
-
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/AddonManager.jsm");
@@ -385,11 +383,9 @@ let rpService = (function() {
 
   function register() {
     let obs = Services.obs;
-    obs.addObserver(self, "http-on-examine-response", false);
     obs.addObserver(self, "http-on-modify-request", false);
     obs.addObserver(self, "sessionstore-windows-restored", false);
     obs.addObserver(self, "private-browsing", false);
-    obs.addObserver(self, HTTPS_EVERYWHERE_REWRITE_TOPIC, false);
     obs.addObserver(self, SUBSCRIPTION_UPDATED_TOPIC, false);
     obs.addObserver(self, SUBSCRIPTION_ADDED_TOPIC, false);
     obs.addObserver(self, SUBSCRIPTION_REMOVED_TOPIC, false);
@@ -399,7 +395,6 @@ let rpService = (function() {
 
   function unregister() {
     let obs = Services.obs;
-    obs.removeObserver(self, "http-on-examine-response");
     obs.removeObserver(self, "http-on-modify-request");
     obs.removeObserver(self, "sessionstore-windows-restored");
     obs.removeObserver(self, SUBSCRIPTION_UPDATED_TOPIC);
@@ -509,18 +504,6 @@ let rpService = (function() {
       return topLevelDocTranslationRules[uri] || null;
     },
 
-    /**
-     * Handles observer notifications sent by the HTTPS Everywhere extension
-     * that inform us of URIs that extension has rewritten.
-     *
-     * @param nsIURI oldURI
-     * @param string newSpec
-     */
-    _handleHttpsEverywhereUriRewrite : function(oldURI, newSpec) {
-      oldURI = oldURI.QueryInterface(Ci.nsIURI);
-      RequestProcessor.mapDestinations(oldURI.spec, newSpec);
-    },
-
 
 
 
@@ -530,9 +513,6 @@ let rpService = (function() {
 
     observe: function(subject, topic, data) {
       switch (topic) {
-        case "http-on-examine-response" :
-          RequestProcessor._examineHttpResponse(subject);
-          break;
         case "http-on-modify-request" :
           RequestProcessor._examineHttpRequest(subject);
           break;
@@ -577,9 +557,6 @@ let rpService = (function() {
           var failures = PolicyManager.unloadSubscriptionRules(subInfo);
           break;
 
-        case HTTPS_EVERYWHERE_REWRITE_TOPIC :
-          self._handleHttpsEverywhereUriRewrite(subject, data);
-          break;
         case "sessionstore-windows-restored":
           showWelcomeWindow();
           break;
diff --git a/src/content/lib/utils.jsm b/src/content/lib/utils.jsm
index 5e54182..dc3adde 100644
--- a/src/content/lib/utils.jsm
+++ b/src/content/lib/utils.jsm
@@ -32,7 +32,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AddonManager.jsm");
 
 Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
-ScriptLoader.importModules(["prefs", "constants"], this);
+ScriptLoader.importModules(["prefs", "constants", "bootstrap-manager"], this);
 
 
 
@@ -149,5 +149,50 @@ let Utils = (function() {
     return scope;
   };
 
+
+
+  self.observeNotifications = function(observer, observerTopics) {
+    BootstrapManager.registerStartupFunction(function() {
+      for (let i = 0, len = observerTopics.length; i < len; ++i) {
+        Services.obs.addObserver(observer, observerTopics[i], false);
+      }
+    });
+    BootstrapManager.registerShutdownFunction(function(data, reason) {
+      for (let i = 0, len = observerTopics.length; i < len; ++i) {
+        Services.obs.removeObserver(observer, observerTopics[i], false);
+      }
+    });
+  };
+  self.unregisterObservers = function(observer, observerTopics) {
+  };
+
+
+
+  /**
+   * This function returns and eventually creates a module's `internal`
+   * variable. The `internal` can be accessed from all submodules of that
+   * module (which might be in different files).
+   *
+   * The `internal` is added to `self`, and as soon as all modules have been
+   * loaded, i.e. when the startup functions are called, the `internal` is
+   * removed from `self` (the module is „sealed“).
+   *
+   *   This function can be used as follows:
+   *   let MyModule = (function(self) {
+   *     let internal = Utils.moduleInternal(self);
+   *   }(MyModule || {}));
+   *
+   * @param {Object} aModuleScope
+   * @returns {Object} the module's `internal`
+   */
+  self.moduleInternal = function(aModuleScope) {
+    aModuleScope.internal = aModuleScope.internal || {};
+    let sealInternal = function() {
+      delete aModuleScope.internal;
+    };
+    BootstrapManager.registerStartupFunction(sealInternal);
+    return aModuleScope.internal;
+  };
+
   return self;
 }());
diff --git a/src/content/ui/frame.dom-content-loaded.js b/src/content/ui/frame.dom-content-loaded.js
index 9326949..894adfa 100644
--- a/src/content/ui/frame.dom-content-loaded.js
+++ b/src/content/ui/frame.dom-content-loaded.js
@@ -40,7 +40,7 @@ let ManagerForDOMContentLoaded = (function() {
     sendSyncMessage(MMID + ":notifyLinkClicked",
                     {origin: event.currentTarget.ownerDocument.URL,
                      dest: event.currentTarget.href});
-    dump("<a> clicked\n");
+    //dump("<a> clicked\n");
   }
 
 
@@ -76,7 +76,7 @@ let ManagerForDOMContentLoaded = (function() {
       // isn't set on new tab open when this is called.
       return;
     }*/
-    dump("onDOMContentLoaded called for " + doc.documentURI + "\n");
+    //dump("onDOMContentLoaded called for " + doc.documentURI + "\n");
 
     onDocumentLoaded(doc);
     let docID = DocManager.generateDocID(doc);
@@ -167,7 +167,7 @@ let ManagerForDOMContentLoaded = (function() {
     }
 
     if (metaRefreshes.length > 0) {
-      dump("meta refreshes found.\n");
+      //dump("meta refreshes found.\n");
 
       var docShell = document.defaultView
                              .QueryInterface(Ci.nsIInterfaceRequestor)
diff --git a/tests/mozmill/tests/testRedirect/testAutoRedirect.js b/tests/mozmill/tests/testRedirect/testAutoRedirect.js
index 1d2da4e..7a082df 100644
--- a/tests/mozmill/tests/testRedirect/testAutoRedirect.js
+++ b/tests/mozmill/tests/testRedirect/testAutoRedirect.js
@@ -70,7 +70,19 @@ var testAutoRedirect = function() {
 
     tabBrowser.closeAllTabs();
 
-    // the following sleep is a workaround against the error:
+    // It's necessary to wait for the notification panel to be closed. If we
+    // don't wait for that to happen, the next URL in urlsWithRedirect might
+    // already be displayed while the panel is still there.
+    controller.waitFor((() => !panel.exists()), "No panel is being displayed " +
+                       "because all tabs have been closed.");
+
+    //
+    // The `sleep` below has been a workaround against the following exception:
+    //
+    // --> Note: This workaround is probably not needed anymore -- if the
+    //           problem occurres again, uncomment the `sleep`. Otherwise,
+    //           remove this comment after some time.
+    //           (comment by @myrdd, 27.12.2014)
     // *************************
     // A coding exception was thrown in a Promise resolution callback.
     // See https://developer.mozilla.org/Mozilla/JavaScript_code_modules/Promise.jsm/Promise
@@ -80,6 +92,6 @@ var testAutoRedirect = function() {
     // BackgroundPageThumbs._processCaptureQueue at resource://gre/modules/BackgroundPageThumbs.jsm:222:5
     // BackgroundPageThumbs.capture at resource://gre/modules/BackgroundPageThumbs.jsm:73:5
     // (...)
-    controller.sleep(100);
+    //controller.sleep(100);
   }
 }
diff --git a/tests/mozmill/tests/testRedirect/testLinkClickRedirect.js b/tests/mozmill/tests/testRedirect/testLinkClickRedirect.js
index 31ec8cd..ed106a5 100644
--- a/tests/mozmill/tests/testRedirect/testLinkClickRedirect.js
+++ b/tests/mozmill/tests/testRedirect/testLinkClickRedirect.js
@@ -61,15 +61,25 @@ var testLinkClickRedirect = function() {
           '/{"value":"' + rpConst.REDIRECT_NOTIFICATION_VALUE + '"}');
 
       if (redirectShouldBeAllowed) {
+        // fixme: find a better waitFor-function that ensures that the part of
+        //        RP which is responsible for showing the panel *really* has
+        //        finished.
         controller.waitFor(function() {
             return controller.window.content.document.location.href !== url;
         }, "The URL in the urlbar has changed.");
         expect.ok(!panel.exists(), "The redirect notification bar is hidden.");
       } else {
-        expect.ok(panel.exists(), "The redirect notification bar is displayed.");
+        controller.waitFor((() => panel.exists()), "The redirect " +
+                           "notification bar has been displayed.");
       }
 
       tabBrowser.closeAllTabs();
+
+      // It's necessary to wait for the notification panel to be closed. If we
+      // don't wait for that to happen, the next URL in urlsWithRedirect might
+      // already be displayed while the panel is still there.
+      controller.waitFor((() => !panel.exists()), "No panel is being " +
+                         "displayed because all tabs have been closed.");
     }
   }
 }

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