[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r12327: Code cleanup, minor reorg, syntactic normalization, semantic (in torbutton/trunk/src: chrome/content components)



Author: mikeperry
Date: 2007-11-02 02:12:48 -0400 (Fri, 02 Nov 2007)
New Revision: 12327

Modified:
   torbutton/trunk/src/chrome/content/preferences.js
   torbutton/trunk/src/chrome/content/torbutton.js
   torbutton/trunk/src/components/cssblocker.js
   torbutton/trunk/src/components/nsSessionStore.js
   torbutton/trunk/src/components/window-mapper.js
Log:

Code cleanup, minor reorg, syntactic normalization, semantic
fixes.



Modified: torbutton/trunk/src/chrome/content/preferences.js
===================================================================
--- torbutton/trunk/src/chrome/content/preferences.js	2007-11-02 06:06:40 UTC (rev 12326)
+++ torbutton/trunk/src/chrome/content/preferences.js	2007-11-02 06:12:48 UTC (rev 12327)
@@ -298,7 +298,7 @@
     var children;
     var i;
 
-    // XXX: change this to handle people with non-default proxy settings
+    // FIXME: change this to handle people with non-default proxy settings
     //  1. Clear proxy settings
     //  2. Restore saved prefs
     //  3. Clear torbutton settings

Modified: torbutton/trunk/src/chrome/content/torbutton.js
===================================================================
--- torbutton/trunk/src/chrome/content/torbutton.js	2007-11-02 06:06:40 UTC (rev 12326)
+++ torbutton/trunk/src/chrome/content/torbutton.js	2007-11-02 06:12:48 UTC (rev 12327)
@@ -15,6 +15,7 @@
         var pref_service = Components.classes["@mozilla.org/preferences-service;1"]
                                      .getService(Components.interfaces.nsIPrefBranchInternal);
         this._branch = pref_service.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
+        // FIXME: Narrow these topics
         this._branch.addObserver("", this, false);
     },
 
@@ -479,12 +480,18 @@
             }
         } else {
             try {
-                m_tb_prefs.clearUserPref("general.appname.override");
-                m_tb_prefs.clearUserPref("general.appversion.override");
-                m_tb_prefs.clearUserPref("general.useragent.override");
-                m_tb_prefs.clearUserPref("general.useragent.vendor");
-                m_tb_prefs.clearUserPref("general.useragent.vendorSub");
-                m_tb_prefs.clearUserPref("general.platform.override");
+                if(m_tb_prefs.prefHasUserValue("general.appname.override"))
+                    m_tb_prefs.clearUserPref("general.appname.override");
+                if(m_tb_prefs.prefHasUserValue("general.appversion.override"))
+                    m_tb_prefs.clearUserPref("general.appversion.override");
+                if(m_tb_prefs.prefHasUserValue("general.useragent.override"))
+                    m_tb_prefs.clearUserPref("general.useragent.override");
+                if(m_tb_prefs.prefHasUserValue("general.useragent.vendor"))
+                    m_tb_prefs.clearUserPref("general.useragent.vendor");
+                if(m_tb_prefs.prefHasUserValue("general.useragent.vendorSub"))
+                    m_tb_prefs.clearUserPref("general.useragent.vendorSub");
+                if(m_tb_prefs.prefHasUserValue("general.platform.override"))
+                    m_tb_prefs.clearUserPref("general.platform.override");
             } catch (e) {
                 // This happens because we run this from time to time
                 torbutton_log(3, "Prefs already cleared");
@@ -512,8 +519,10 @@
                 torprefs.getCharPref("spoof_language"));
     } else {
         try {
-            m_tb_prefs.clearUserPref("intl.accept_charsets");
-            m_tb_prefs.clearUserPref("intl.accept_languages");
+            if(m_tb_prefs.prefHasUserValue("intl.accept_charsets"))
+                m_tb_prefs.clearUserPref("intl.accept_charsets");
+            if(m_tb_prefs.prefHasUserValue("intl.accept_languages"))
+                m_tb_prefs.clearUserPref("intl.accept_languages");
         } catch (e) {
             // Can happen if english browser.
             torbutton_log(3, "Browser already english");
@@ -547,7 +556,7 @@
         m_tb_prefs.setBoolPref("security.enable_java", !mode);
     }
 
-    torbutton_toggle_jsplugins(!mode, 
+    torbutton_toggle_jsplugins(mode, 
             changed && torprefs.getBoolPref("isolate_content"),
             torprefs.getBoolPref("no_tor_plugins"));
 
@@ -562,7 +571,7 @@
         torbutton_clear_history();
     }
 
-    // XXX: This is kind of not so user friendly to people who like
+    // FIXME: This is kind of not so user friendly to people who like
     // to keep their own prefs.. Not sure what to do though..
     if(mode) {
         if(torprefs.getBoolPref('block_thwrite')) {
@@ -739,22 +748,22 @@
 
 // -------------- JS/PLUGIN HANDLING CODE ---------------------
 
-function torbutton_check_js_tag(browser, allowed, js_enabled) {
-    if (typeof(browser.__tb_js_state) == 'undefined') {
-        // XXX: the error console is still a navigator:browser
+function torbutton_check_js_tag(browser, tor_enabled, js_enabled) {
+    if (typeof(browser.__tb_tor_fetched) == 'undefined') {
+        // FIXME: the error console is still a navigator:browser
         // and triggers this.
         // Is there any way to otherwise detect it?
         torbutton_log(5, "UNTAGGED WINDOW!!!!!!!!!");
     }
 
-    if(browser.__tb_js_state == allowed) { // States match, js ok 
+    if(browser.__tb_tor_fetched == tor_enabled) { // States match, js ok 
         browser.docShell.allowJavascript = js_enabled;
     } else { // States differ or undefined, js not ok 
         browser.docShell.allowJavascript = false;
     }
 }
 
-function torbutton_toggle_win_jsplugins(win, allowed, js_enabled, isolate_dyn, 
+function torbutton_toggle_win_jsplugins(win, tor_enabled, js_enabled, isolate_dyn, 
                                         kill_plugins) {
     var browser = win.getBrowser();
     var browsers = browser.browsers;
@@ -762,15 +771,15 @@
     for (var i = 0; i < browsers.length; ++i) {
         var b = browser.browsers[i];
         if (b) {
-            // Only allow plugins if the tab load was from an allowed state 
-            // and the current tor state is off.
+            // Only allow plugins if the tab load was from an 
+            // non-tor state and the current tor state is off.
             if(kill_plugins) 
-                b.docShell.allowPlugins = allowed && b.__tb_js_state;
+                b.docShell.allowPlugins = !b.__tb_tor_fetched && !tor_enabled;
             else 
                 browser.docShell.allowPlugins = true;
             
             if(isolate_dyn) {
-                torbutton_check_js_tag(b, allowed, js_enabled);
+                torbutton_check_js_tag(b, tor_enabled, js_enabled);
                 // kill meta-refresh and existing page loading 
                 b.webNavigation.stop(b.webNavigation.STOP_ALL);
             }
@@ -780,15 +789,15 @@
 
 // This is an ugly beast.. But unfortunately it has to be so..
 // Looping over all tabs twice is not somethign we wanna do..
-function torbutton_toggle_jsplugins(allowed, isolate_dyn, kill_plugins) {
-    torbutton_log(1, "Plugins: "+allowed);
+function torbutton_toggle_jsplugins(tor_enabled, isolate_dyn, kill_plugins) {
+    torbutton_log(1, "Tor state: "+tor_enabled);
     var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                        .getService(Components.interfaces.nsIWindowMediator);
     var enumerator = wm.getEnumerator("navigator:browser");
     var js_enabled = m_tb_prefs.getBoolPref("javascript.enabled");
     while(enumerator.hasMoreElements()) {
         var win = enumerator.getNext();
-        torbutton_toggle_win_jsplugins(win, allowed, js_enabled, isolate_dyn, 
+        torbutton_toggle_win_jsplugins(win, tor_enabled, js_enabled, isolate_dyn, 
                                        kill_plugins);   
     }
 }
@@ -799,9 +808,9 @@
     }
 
     // Only tag new windows
-    if (typeof(browser.__tb_js_state) == 'undefined') {
+    if (typeof(browser.__tb_tor_fetched) == 'undefined') {
         torbutton_log(3, "Tagging new window: "+tor_tag);
-        browser.__tb_js_state = tor_tag;
+        browser.__tb_tor_fetched = !tor_tag;
     }
 }
 
@@ -1042,6 +1051,21 @@
     }
 
     if(win != win.top) {
+        // XXX: Same-origin policy may prevent our hooks from applying
+        // to inner iframes.. Test with frames, iframes, and
+        // popups:
+        //  - http://www.htmlbasix.com/popup.shtml
+        //  - http://msdn2.microsoft.com/en-us/library/ms531202.aspx
+        //  - Url-free: http://www.yourhtmlsource.com/javascript/popupwindows.html#accessiblepopups
+        //    - Blocked by default (tho perhaps only via onload). 
+        //      see popup blocker detectors:
+        //      - http://javascript.internet.com/snippets/popup-blocker-detection.html
+        //      - http://www.visitor-stats.com/articles/detect-popup-blocker.php 
+        //      - http://www.dynamicdrive.com/dynamicindex8/dhtmlwindow.htm
+        //  - popup blocker tests:
+        //    - http://swik.net/User:Staple/JavaScript+Popup+Windows+Generation+and+Testing+Tutorials
+        //  - pure javascript pages/non-text/html pages
+        //
         // Handle the iframe case
         torbutton_log(3, "Hook for non-toplevel window: "+doc.location);
         win = win.top;
@@ -1057,7 +1081,10 @@
     if(doc.doctype) {
         torbutton_log(2, "Hooking document: "+doc.doctype.name);
     }
-   
+  
+    // XXX: These alerts seem to happen with certain form posts via Tor that
+    // were originally loaded in non-tor 
+
     // We can't just tag the document here because it is possible
     // to hit reload at just the right point such that the document
     // has been cleared but the window remained.
@@ -1074,11 +1101,11 @@
         /* hrmm.. would doc.isSupported("javascript") 
          * or doc.implementation.hasFeature() work better? */
         if(doc.contentType.indexOf("text/html") != -1 && 
-                browser.__tb_js_state == false &&
+                browser.__tb_tor_fetched == true &&
                 !torbutton_check_flag(win.wrappedJSObject, 
                     "__tb_hooks_ran")) {
-            torbutton_log(5, "FALSE WIN HOOKING. Please report bug+website!");
-            win.alert("False win hooking. Please report bug+website!");
+            torbutton_log(5, "FALSE WIN HOOKING. Please report bug+website: "+doc.location);
+            win.alert("False win hooking. Please report bug+website: "+doc.location);
         }
         return; // Ran already
     }
@@ -1096,11 +1123,11 @@
         /* hrmm.. would doc.isSupported("javascript") 
          * or doc.implementation.hasFeature() work better? */
         if(doc.contentType.indexOf("text/html") != -1 && 
-                browser.__tb_js_state == false &&
+                browser.__tb_tor_fetched == true &&
                 !torbutton_check_flag(win.wrappedJSObject, 
                     "__tb_hooks_ran")) {
-            torbutton_log(5, "FALSE WIN HOOKING. Please report bug+website!");
-            win.alert("False win hooking. Please report bug+website!");
+            torbutton_log(5, "FALSE DOC HOOKING. Please report bug+website: "+doc.location);
+            win.alert("False doc hooking. Please report bug+website: "+doc.location);
         }
         return; // Ran already
     }
@@ -1121,7 +1148,7 @@
     var kill_plugins = m_tb_prefs.getBoolPref("extensions.torbutton.no_tor_plugins");
 
     torbutton_log(2, "Tagging browser for: " + doc.location);
-    browser.__tb_js_state = tor_tag;
+    browser.__tb_tor_fetched = !tor_tag;
     browser.docShell.allowPlugins = tor_tag || !kill_plugins;
     browser.docShell.allowJavascript = js_enabled;
 
@@ -1190,6 +1217,8 @@
                 && m_tb_prefs.getBoolPref("extensions.torbutton.no_tor_plugins")) {
             torbutton_eclog(2, 'LocChange: '+aRequest.contentType);
 
+            // XXX: This still does not work for pdfs :(
+            // NoScript manages to make it work though...
             if (aRequest.contentType in m_tb_plugin_mimetypes) {
                 aRequest.cancel(0x804b0002);
                 window.alert("Torbutton blocked direct Tor load of plugin content.\n\nUse Save-As instead.\n\n");

Modified: torbutton/trunk/src/components/cssblocker.js
===================================================================
--- torbutton/trunk/src/components/cssblocker.js	2007-11-02 06:06:40 UTC (rev 12326)
+++ torbutton/trunk/src/components/cssblocker.js	2007-11-02 06:12:48 UTC (rev 12327)
@@ -41,6 +41,7 @@
     return node.defaultView;
 }
 
+//FIXME: can we kill this noise?
 //HACKHACK: need a way to get an implicit wrapper for nodes because of bug 337095 (fixed in Gecko 1.8.0.5)
 var fakeFactory = {
 	createInstance: function(outer, iid) {
@@ -65,6 +66,8 @@
 	return fakeFactory.createInstance(insecNode, Components.interfaces.nsISupports);
 }
 
+
+
 // Unwraps jar:, view-source: and wyciwyg: URLs, returns the contained URL
 function unwrapURL(url) {
 	if (!url)
@@ -84,21 +87,32 @@
     "resource" : true, "x-jsd" : true, "addbook" : true, "cid" : true, 
     "mailbox" : true, "data" : true, "javascript" : true};
 
-var policy = {
-	init: function() {
-        this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
-            .getService(Components.interfaces.nsIPrefBranch);
-        this.wm = Components.classes["@torproject.org/content-window-mapper;1"]
-            .getService(Components.interfaces.nsISupports)
-            .wrappedJSObject;
-        // XXX: Ewww. torbutton.logger may not be loaded yet..
-        this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
-            .getService(Components.interfaces.nsISupports).wrappedJSObject;
+function ContentPolicy() {
+    this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
+        .getService(Components.interfaces.nsIPrefBranch);
+    this.wm = Components.classes["@torproject.org/content-window-mapper;1"]
+        .getService(Components.interfaces.nsISupports)
+        .wrappedJSObject;
+    
+    // XXX: Ewww. torbutton.logger may not be loaded yet..
+    this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
+        .getService(Components.interfaces.nsISupports).wrappedJSObject;
+        
+    this.isolate_content = this._prefs.getBoolPref("extensions.torbutton.isolate_content");
+    this.tor_enabled = this._prefs.getBoolPref("extensions.torbutton.tor_enabled");
+    this.no_tor_plugins = this._prefs.getBoolPref("extensions.torbutton.no_tor_plugins");
 
-        dump("Content policy component initialized\n");
-        return;
-    },
+    // Register observer: FIXME: Restrict this to extensions.torbutton branch?
+    var pref_service = Components.classes["@mozilla.org/preferences-service;1"]
+        .getService(Components.interfaces.nsIPrefBranchInternal);
+    this._branch = pref_service.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
+    this._branch.addObserver("", this, false);
 
+    dump("Content policy component initialized\n");
+    return;
+}
+
+ContentPolicy.prototype = {
     isLocalScheme: function(loc) {
         if (loc.indexOf(":") < 0)
             return false;
@@ -107,9 +121,6 @@
         return (scheme in localSchemes) || loc == "about:blank";
     },
 
-
-    // TODO: Optimize this with a pref observer so it doesn't
-    // have to continually query prefs
 	// nsIContentPolicy interface implementation
 	shouldLoad: function(contentType, contentLocation, requestOrigin, insecNode, mimeTypeGuess, extra) {
        
@@ -125,13 +136,13 @@
             return ok;
         }
 
-        if(!this._prefs.getBoolPref("extensions.torbutton.isolate_content")) {
+        if(!this.isolate_content) {
             this.logger.eclog(1, "Content policy disabled");
             return ok;
         }
         
         var node = wrapNode(insecNode);
-        var wind = getWindow(wrapNode(insecNode));
+        var wind = getWindow(node);
 
 		// Local stuff has to be eclog because otherwise debuglogger will
         // get into an infinite log-loop w/ its chrome updates
@@ -147,17 +158,15 @@
 		}
 
         // XXX: Something is rotten in denmark        
-        var torTag = !this._prefs.getBoolPref("extensions.torbutton.tor_enabled");
+        var tor_state = this.tor_enabled;
 
         if (contentType == 5) { // Object
             // Never seems to happen.. But it would be nice if we 
             // could handle it either here or shouldProcess, instead of in 
             // the webprogresslistener
-            if(!torTag) {
-                if(this._prefs.getBoolPref("extensions.torbutton.no_tor_plugins")) {
-                    this.logger.log(4, "Blocking object at "+contentLocation.spec);
-                    return block;
-                }
+            if(this.tor_enabled && this.no_tor_plugins) {
+                this.logger.log(4, "Blocking object at "+contentLocation.spec);
+                return block;
             }
         }
 
@@ -176,20 +185,18 @@
         var browser = this.wm.getBrowserForContentWindow(wind.top);
         if(!browser) {
             // This happens on the first load of a doc
-            // XXX: Other cases?
             this.logger.log(3, "No window found: "+contentLocation.spec);
             return ok; 
         }
 
-        if (typeof(browser.__tb_js_state) == 'undefined') {
+        if (typeof(browser.__tb_tor_fetched) == 'undefined') {
             this.logger.log(5, "UNTAGGED WINDOW2!!!!!!!!! "+contentLocation.spec);
             return block;
         }
 
-
-        if(browser.__tb_js_state == torTag)
+        if(browser.__tb_tor_fetched == tor_state) {
             return ok;
-        else {
+        } else {
             this.logger.log(3, "Blocking: "+contentLocation.spec);
             return block;
         }
@@ -203,12 +210,35 @@
         // See mozilla bugs 380556, 305699, 309524
         return ok;
 	},
+
+    // Pref observer interface implementation
+  
+    // topic:   what event occurred
+    // subject: what nsIPrefBranch we're observing
+    // data:    which pref has been changed (relative to subject)
+    observe: function(subject, topic, data)
+    {
+        if (topic != "nsPref:changed") return;
+        switch (data) {
+            case "extensions.torbutton.isolate_content":
+                this.isolate_content = this._prefs.getBoolPref("extensions.torbutton.isolate_content");
+                break;
+            case "extensions.torbutton.tor_enabled":
+                this.tor_enabled = this._prefs.getBoolPref("extensions.torbutton.tor_enabled");
+                break;
+            case "extensions.torbutton.no_tor_plugins":
+                this.no_tor_plugins = this._prefs.getBoolPref("extensions.torbutton.no_tor_plugins");
+                break;
+        }
+    }
 };
 
 /*
  * Factory object
  */
 
+var ContentPolicyInstance = null;
+
 const factory = {
 	// nsIFactory interface implementation
 	createInstance: function(outer, iid) {
@@ -223,8 +253,10 @@
             return null;
         }
 
-        policy.init();
-		return policy;
+        if(!ContentPolicyInstance)
+            ContentPolicyInstance = new ContentPolicy();
+
+		return ContentPolicyInstance;
 	},
 
 	// nsISupports interface implementation

Modified: torbutton/trunk/src/components/nsSessionStore.js
===================================================================
--- torbutton/trunk/src/components/nsSessionStore.js	2007-11-02 06:06:40 UTC (rev 12326)
+++ torbutton/trunk/src/components/nsSessionStore.js	2007-11-02 06:12:48 UTC (rev 12327)
@@ -785,8 +785,8 @@
       var tabData = { entries: [], index: 0 };
       
       var browser = browsers[i];
-      if(bypass_tor && typeof(browser.__tb_js_state) != "undefined" && 
-              !browser.__tb_js_state) {
+      if(bypass_tor && typeof(browser.__tb_tor_fetched) != "undefined" && 
+              browser.__tb_tor_fetched) {
           //dump("bypassing tor tab\n");
           //tabs.push(tabData);
           continue;

Modified: torbutton/trunk/src/components/window-mapper.js
===================================================================
--- torbutton/trunk/src/components/window-mapper.js	2007-11-02 06:06:40 UTC (rev 12326)
+++ torbutton/trunk/src/components/window-mapper.js	2007-11-02 06:12:48 UTC (rev 12327)
@@ -15,95 +15,25 @@
 const Ci = Components.interfaces;
 const EXPIRATION_TIME = 60000; // 60 seconds
 
+const nsISupports = Components.interfaces.nsISupports;
+const nsIClassInfo = Components.interfaces.nsIClassInfo;
+const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
+const nsIObserverService = Components.interfaces.nsIObserverService;
+
 function ContentWindowMapper() {
     this.cache = new Object();
     this.cache["bah"] = 0;
 
-    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-        .getService(Components.interfaces.nsIPrefBranch);
-
     this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
         .getService(Components.interfaces.nsISupports).wrappedJSObject;
         
 
-    this.checkCache = function(topContentWindow) {
-        if(typeof(this.cache[topContentWindow]) != "undefined") {
-            this.logger.log(1, "Found cached element");
-            return this.cache[topContentWindow].browser;
-        }
-
-        return null;
-    };
-
-    this.addCache = function(topContentWindow, browser) {
-        var insertion = new Object();
-        insertion.browser = browser;
-        insertion.time = Date.now();
-        this.cache[topContentWindow] = insertion; 
-        this.loggger.log(2, "Cached element: "+topContentWindow.location);
-    };
-
-    this.expireOldCache = function() {
-        var now = Date.now();
-
-        for(var elem in this.cache) {
-            if((now - this.cache[elem].time) > EXPIRATION_TIME) {
-                this.loggger.log(2, "Deleting cached element: "+elem.location);
-                delete this.cache[elem];
-            }
-        }
-        for(var elem in this.cache) {
-            if((now - this.cache[elem].time) > EXPIRATION_TIME) {
-                this.loggger.log(4, "ELEMENT STILL REMAINS: "+elem.location);
-                delete this.cache[elem];
-            }
-        }
-    };
-
-    this.getBrowserForContentWindow = function(topContentWindow) {
-        var cached = this.checkCache(topContentWindow);
-        if(cached != null) return cached;
-
-        var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-            .getService(Components.interfaces.nsIWindowMediator);
-        var enumerator = wm.getEnumerator("navigator:browser");
-        while(enumerator.hasMoreElements()) {
-            var win = enumerator.getNext();
-            var browser = win.getBrowser(); 
-            for (var i = 0; i < browser.browsers.length; ++i) {
-                var b = browser.browsers[i];
-                if (b && b.contentWindow == topContentWindow) {
-                    this.addCache(topContentWindow, browser);
-                    return browser;
-                }
-            }
-        }
-
-        if(topContentWindow && topContentWindow.location)
-            this.logger.log(3, "No browser found: "+topContentWindow.location);
-        else
-            this.logger.log(3, "No browser found!");
-
-        return null;
-    };
-
   // This JSObject is exported directly to chrome
   this.wrappedJSObject = this;
 
   dump("Window mapper component initialized\n");
 }
 
-/**
- * JS XPCOM component registration goop:
- *
- * Everything below is boring boilerplate and can probably be ignored.
- */
-
-const nsISupports = Components.interfaces.nsISupports;
-const nsIClassInfo = Components.interfaces.nsIClassInfo;
-const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
-const nsIObserverService = Components.interfaces.nsIObserverService;
-
 ContentWindowMapper.prototype =
 {
   QueryInterface: function(iid)
@@ -134,8 +64,75 @@
   // method of nsIClassInfo
   getHelperForLanguage: function(count) { return null; },
 
+
+  checkCache: function(topContentWindow) {
+      if(typeof(this.cache[topContentWindow]) != "undefined") {
+          this.logger.log(1, "Found cached element for: "+topContentWindow.location);
+          return this.cache[topContentWindow].browser;
+      }
+
+      return null;
+  },
+
+  addCache: function(topContentWindow, browser) {
+      var insertion = new Object();
+      insertion.browser = browser;
+      insertion.time = Date.now();
+      this.cache[topContentWindow] = insertion; 
+      this.logger.log(2, "Cached element: "+topContentWindow.location);
+  },
+
+  expireOldCache: function() {
+      var now = Date.now();
+
+      for(var elem in this.cache) {
+          if((now - this.cache[elem].time) > EXPIRATION_TIME) {
+              this.logger.log(2, "Deleting cached element: "+elem.location);
+              delete this.cache[elem];
+          }
+      }
+      for(var elem in this.cache) {
+          if((now - this.cache[elem].time) > EXPIRATION_TIME) {
+              this.logger.log(4, "ELEMENT STILL REMAINS: "+elem.location);
+              delete this.cache[elem];
+          }
+      }
+  },
+
+  getBrowserForContentWindow: function(topContentWindow) {
+      var cached = this.checkCache(topContentWindow);
+      if(cached != null) return cached;
+
+      var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+          .getService(Components.interfaces.nsIWindowMediator);
+      var enumerator = wm.getEnumerator("navigator:browser");
+      while(enumerator.hasMoreElements()) {
+          var win = enumerator.getNext();
+          var browser = win.getBrowser(); 
+          for (var i = 0; i < browser.browsers.length; ++i) {
+              var b = browser.browsers[i];
+              if (b && b.contentWindow == topContentWindow) {
+                  this.addCache(topContentWindow, browser);
+                  return browser;
+              }
+          }
+      }
+
+      if(topContentWindow && topContentWindow.location)
+          this.logger.log(3, "No browser found: "+topContentWindow.location);
+      else
+          this.logger.log(3, "No browser found!");
+
+      return null;
+  }
 }
 
+/**
+ * JS XPCOM component registration goop:
+ *
+ * Everything below is boring boilerplate and can probably be ignored.
+ */
+
 var ContentWindowMapperInstance = null;
 var ContentWindowMapperFactory = new Object();