Bug 681653 - Augment RegisterTools API in Highlighter to deregister tools; r=msucan, gavin
authorRob Campbell <rcampbell@mozilla.com>
Mon, 26 Sep 2011 13:43:37 -0300
changeset 78973 168f1c6b69dcea14020201a73a7b4671488df760
parent 78826 c722928d8b69b18daf5c6a29e1c66ea35cf7e230
child 78974 264e504e0ce9ca5ff70eb41bdb455614204433ed
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmsucan, gavin
bugs681653
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 681653 - Augment RegisterTools API in Highlighter to deregister tools; r=msucan, gavin
browser/devtools/highlighter/inspector.js
browser/devtools/highlighter/test/browser_inspector_registertools.js
--- a/browser/devtools/highlighter/inspector.js
+++ b/browser/devtools/highlighter/inspector.js
@@ -130,17 +130,16 @@ Highlighter.prototype = {
     stack.appendChild(this.highlighterContainer);
 
     this.browser.addEventListener("resize", this, true);
     this.browser.addEventListener("scroll", this, true);
 
     this.handleResize();
   },
 
-
   /**
    * Build the veil:
    *
    * <vbox id="highlighter-veil-container">
    *   <box id="highlighter-veil-topbox" class="highlighter-veil"/>
    *   <hbox id="highlighter-veil-middlebox">
    *     <box id="highlighter-veil-leftbox" class="highlighter-veil"/>
    *     <box id="highlighter-veil-transparentbox"/>
@@ -550,16 +549,17 @@ Highlighter.prototype = {
 //// InspectorUI
 
 /**
  * Main controller class for the Inspector.
  */
 var InspectorUI = {
   browser: null,
   tools: {},
+  toolEvents: {},
   showTextNodesWithWhitespace: false,
   inspecting: false,
   treeLoaded: false,
   get enabled()
   {
     return gPrefService.getBoolPref("devtools.inspector.enabled");
   },
   isDirty: false,
@@ -809,30 +809,39 @@ var InspectorUI = {
                                false);
     }
     // Start initialization.
     this.browser = gBrowser.selectedBrowser;
     this.win = this.browser.contentWindow;
     this.winID = this.getWindowID(this.win);
     this.toolbar = document.getElementById("inspector-toolbar");
 
+    this.initTools();
+
     if (!this.domplate) {
       Cu.import("resource:///modules/domplate.jsm", this);
       this.domplateUtils.setDOM(window);
     }
 
     this.openTreePanel();
 
     this.toolbar.hidden = false;
     this.inspectCmd.setAttribute("checked", true);
 
     gBrowser.addProgressListener(InspectorProgressListener);
   },
 
   /**
+   * Register and initialize any included tools.
+   */
+  initTools: function IUI_initTools()
+  {
+  },
+
+  /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
     this.highlighter = new Highlighter(this.browser);
     this.highlighterReady();
   },
 
@@ -898,16 +907,22 @@ var InspectorUI = {
       InspectorStore.setValue(this.winID, "inspecting", this.inspecting);
     }
 
     if (InspectorStore.isEmpty()) {
       gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
     }
 
     this.stopInspecting();
+
+    this.saveToolState(this.winID);
+    this.toolsDo(function IUI_toolsHide(aTool) {
+      this.unregisterTool(aTool);
+    }.bind(this));
+
     if (this.highlighter) {
       this.highlighter.destroy();
       this.highlighter = null;
     }
 
     if (this.treePanelDiv) {
       this.treePanelDiv.ownerPanel = null;
       let parent = this.treePanelDiv.parentNode;
@@ -925,23 +940,16 @@ var InspectorUI = {
 
     if (this.domplate) {
       this.domplateUtils.setDOM(null);
       delete this.domplate;
       delete this.HTMLTemplates;
       delete this.domplateUtils;
     }
 
-    this.saveToolState(this.winID);
-    this.toolsDo(function IUI_toolsHide(aTool) {
-      if (aTool.panel) {
-        aTool.panel.hidePopup();
-      }
-    });
-
     this.inspectCmd.setAttribute("checked", false);
     this.browser = this.win = null; // null out references to browser and window
     this.winID = null;
     this.selection = null;
     this.treeLoaded = false;
 
     this.treePanel.addEventListener("popuphidden", function treePanelHidden() {
       this.removeEventListener("popuphidden", treePanelHidden, false);
@@ -1015,35 +1023,34 @@ var InspectorUI = {
 
     if (forceUpdate || aNode != this.selection) {
       this.selection = aNode;
       if (!this.inspecting) {
         this.highlighter.highlightNode(this.selection);
       }
       this.ioBox.select(this.selection, true, true, aScroll);
     }
-    this.toolsDo(function IUI_toolsOnSelect(aTool) {
-      if (aTool.panel.state == "open") {
-        aTool.onSelect.apply(aTool.context, [aNode]);
-      }
-    });
+
+    this.toolsSelect();
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
 
   highlighterReady: function IUI_highlighterReady()
   {
     // Setup the InspectorStore or restore state
     this.initializeStore();
 
     if (InspectorStore.getValue(this.winID, "inspecting")) {
       this.startInspecting();
     }
 
+    this.restoreToolState(this.winID);
+
     this.win.focus();
     Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.OPENED, null);
   },
 
   /**
    * Main callback handler for events.
    *
    * @param event
@@ -1068,17 +1075,16 @@ var InspectorUI = {
             Services.obs.addObserver(function reopenInspectorForTab() {
               Services.obs.removeObserver(reopenInspectorForTab,
                 INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
               InspectorUI.openInspectorUI();
             }, INSPECTOR_NOTIFICATIONS.CLOSED, false);
           } else {
             this.openInspectorUI();
-            this.restoreToolState(winID);
           }
         }
 
         if (InspectorStore.isEmpty()) {
           gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
         }
         break;
       case "pagehide":
@@ -1588,120 +1594,223 @@ var InspectorUI = {
         this._log("filename: " + frame.filename + " lineNumber: " + frame.lineNumber +
           " functionName: " + frame.name);
       }
     }
     this._log("END TRACE");
   },
 
   /**
+   * Get the toolbar button name for a given id string. Used by the
+   * registerTools API to retrieve a consistent name for toolbar buttons
+   * based on the ID of the tool.
+   * @param anId String
+   *        id of the tool to be buttonized
+   * @returns String
+   */
+  getToolbarButtonId: function IUI_createButtonId(anId)
+  {
+    return "inspector-" + anId + "-toolbutton";
+  },
+
+  /**
    * Register an external tool with the inspector.
    *
    * aRegObj = {
    *   id: "toolname",
    *   context: myTool,
    *   label: "Button label",
    *   icon: "chrome://somepath.png",
    *   tooltiptext: "Button tooltip",
    *   accesskey: "S",
+   *   isOpen: object.property, (getter) returning true if tool is open.
    *   onSelect: object.method,
-   *   onShow: object.method,
-   *   onHide: object.method,
+   *   show: object.method, called to show the tool when button is pressed.
+   *   hide: object.method, called to hide the tool when button is pressed.
+   *   unregister: object.method, called when tool should be destroyed.
    *   panel: myTool.panel
    * }
    *
-   * @param aRegObj
+   * @param aRegObj Object
+   *        The Registration Object used to register this tool described
+   *        above. The tool should cache this object for later deregistration.
    */
-  registerTool: function IUI_RegisterTool(aRegObj) {
-    if (this.tools[aRegObj.id]) {
+  registerTool: function IUI_registerTool(aRegObj)
+  {
+    if (this.toolRegistered(aRegObj.id)) {
       return;
-    } else {
-      let id = aRegObj.id;
-      let buttonId = "inspector-" + id + "-toolbutton";
-      aRegObj.buttonId = buttonId;
-
-      aRegObj.panel.addEventListener("popuphiding",
-        function IUI_toolPanelHiding() {
-          btn.setAttribute("checked", "false");
-        }, false);
-      aRegObj.panel.addEventListener("popupshowing",
-        function IUI_toolPanelShowing() {
-          btn.setAttribute("checked", "true");
-        }, false);
-
-      this.tools[id] = aRegObj;
     }
 
-    let toolbox = document.getElementById("inspector-tools");
+    this.tools[aRegObj.id] = aRegObj;
+
+    let buttonContainer = document.getElementById("inspector-tools");
     let btn = document.createElement("toolbarbutton");
-    btn.setAttribute("id", aRegObj.buttonId);
+    let buttonId = this.getToolbarButtonId(aRegObj.id);
+    btn.setAttribute("id", buttonId);
     btn.setAttribute("label", aRegObj.label);
     btn.setAttribute("tooltiptext", aRegObj.tooltiptext);
     btn.setAttribute("accesskey", aRegObj.accesskey);
-    btn.setAttribute("class", "toolbarbutton-text");
     btn.setAttribute("image", aRegObj.icon || "");
-    toolbox.appendChild(btn);
+    buttonContainer.appendChild(btn);
+
+    /**
+     * Save the registered tool's toolbar button's click handler so we can remove
+     * it at deregistration time.
+     * @param aButton XUL:toolbarbutton
+     * @param aCallback Function the click event handler for the button
+     */
+    function bindToolEvent(aWidget, aEvent, aCallback)
+    {
+      let toolEvent = aWidget.id + "_" + aEvent;
+      InspectorUI.toolEvents[toolEvent] = aCallback;
+      aWidget.addEventListener(aEvent, aCallback, false);
+    }
 
-    btn.addEventListener("click",
-      function IUI_ToolButtonClick(aEvent) {
-        if (btn.getAttribute("checked") == "true") {
-          aRegObj.onHide.apply(aRegObj.context);
+    bindToolEvent(btn, "click",
+      function IUI_toolButtonClick(aEvent) {
+        if (btn.checked) {
+          this.toolHide(aRegObj);
         } else {
-          aRegObj.onShow.apply(aRegObj.context, [InspectorUI.selection]);
-          aRegObj.onSelect.apply(aRegObj.context, [InspectorUI.selection]);
+          this.toolShow(aRegObj);
         }
-      }, false);
+      }.bind(this));
+
+    if (aRegObj.panel) {
+      bindToolEvent(aRegObj.panel, "popuphiding",
+        function IUI_toolPanelHiding() {
+          btn.checked = false;
+        });
+    }
+  },
+
+  /**
+   * Show the specified tool.
+   * @param aTool Object (see comment for IUI_registerTool)
+   */
+  toolShow: function IUI_toolShow(aTool)
+  {
+    aTool.show.call(aTool.context, this.selection);
+    document.getElementById(this.getToolbarButtonId(aTool.id)).checked = true;
   },
 
-/**
- * Save a list of open tools to the inspector store.
- *
- * @param aWinID The ID of the window used to save the associated tools
- */
+  /**
+   * Hide the specified tool.
+   * @param aTool Object (see comment for IUI_registerTool)
+   */
+  toolHide: function IUI_toolHide(aTool)
+  {
+    aTool.hide.call(aTool.context);
+    document.getElementById(this.getToolbarButtonId(aTool.id)).checked = false;
+  },
+
+  /**
+   * Unregister the registered tool, unbinding click events for the buttons
+   * and showing and hiding events for the panel.
+   * @param aRegObj Object
+   *        The registration object used to register the tool.
+   */
+  unregisterTool: function IUI_unregisterTool(aRegObj)
+  {
+    let button = document.getElementById(this.getToolbarButtonId(aRegObj.id));
+
+    /**
+     * Unregister the click handler for the registered tool's button.
+     * @param aButton XUL:toolbarbutton
+     */
+    function unbindToolEvent(aWidget, aEvent)
+    {
+      let toolEvent = aWidget.id + "_" + aEvent;
+      if (!InspectorUI.toolEvents[toolEvent]) {
+        return;
+      }
+
+      aWidget.removeEventListener(aEvent, InspectorUI.toolEvents[toolEvent], false);
+      delete InspectorUI.toolEvents[toolEvent]
+    }
+
+    let buttonContainer = document.getElementById("inspector-tools");
+    unbindToolEvent(button, "click");
+
+    if (aRegObj.panel)
+      unbindToolEvent(aRegObj.panel, "popuphiding");
+
+    buttonContainer.removeChild(button);
+
+    if (aRegObj.unregister)
+      aRegObj.unregister.call(aRegObj.context);
+
+    delete this.tools[aRegObj.id];
+  },
+
+  /**
+   * Save a list of open tools to the inspector store.
+   *
+   * @param aWinID The ID of the window used to save the associated tools
+   */
   saveToolState: function IUI_saveToolState(aWinID)
   {
     let openTools = {};
     this.toolsDo(function IUI_toolsSetId(aTool) {
-      if (aTool.panel.state == "open") {
+      if (aTool.isOpen) {
         openTools[aTool.id] = true;
       }
     });
     InspectorStore.setValue(aWinID, "openTools", openTools);
   },
 
-/**
- * Restore tools previously save using saveToolState().
- *
- * @param aWinID The ID of the window to which the associated tools are to be
- *               restored.
- */
+  /**
+   * Restore tools previously save using saveToolState().
+   *
+   * @param aWinID The ID of the window to which the associated tools are to be
+   *               restored.
+   */
   restoreToolState: function IUI_restoreToolState(aWinID)
   {
     let openTools = InspectorStore.getValue(aWinID, "openTools");
-    InspectorUI.selection = InspectorUI.selection;
     if (openTools) {
       this.toolsDo(function IUI_toolsOnShow(aTool) {
         if (aTool.id in openTools) {
-          aTool.onShow.apply(aTool.context, [InspectorUI.selection]);
+          this.toolShow(aTool);
         }
-      });
+      }.bind(this));
     }
   },
-  
+
+  /**
+   * For each tool in the tools collection select the current node that is
+   * selected in the highlighter
+   */
+  toolsSelect: function IUI_toolsSelect()
+  {
+    this.toolsDo(function IUI_toolsOnSelect(aTool) {
+      if (aTool.isOpen) {
+        aTool.onSelect.call(aTool.context, InspectorUI.selection);
+      }
+    });
+  },
+
   /**
    * Loop through all registered tools and pass each into the provided function
-   *
    * @param aFunction The function to which each tool is to be passed
    */
   toolsDo: function IUI_toolsDo(aFunction)
   {
     for each (let tool in this.tools) {
       aFunction(tool);
     }
   },
+
+  /**
+   * Check if a tool is registered?
+   * @param aId The id of the tool to check
+   */
+  toolRegistered: function IUI_toolRegistered(aId)
+  {
+    return aId in this.tools;
+  },
 };
 
 /**
  * The Inspector store is used for storing data specific to each tab window.
  */
 var InspectorStore = {
   store: {},
   length: 0,
--- a/browser/devtools/highlighter/test/browser_inspector_registertools.js
+++ b/browser/devtools/highlighter/test/browser_inspector_registertools.js
@@ -34,26 +34,30 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 let doc;
 let h1;
+let p2;
+let toolsLength = 0;
+let toolEvents = 0;
 let tool1;
 let tool2;
 let tool3;
+let initToolsMethod = InspectorUI.initTools;
 
 function createDocument()
 {
   let div = doc.createElement("div");
-  let h1 = doc.createElement("h1");
+  h1 = doc.createElement("h1");
   let p1 = doc.createElement("p");
-  let p2 = doc.createElement("p");
+  p2 = doc.createElement("p");
   let div2 = doc.createElement("div");
   let p3 = doc.createElement("p");
   doc.title = "Inspector Tree Selection Test";
   h1.textContent = "Inspector Tree Selection Test";
   p1.textContent = "This is some example text";
   p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
     "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
     "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
@@ -74,202 +78,215 @@ function createDocument()
   div2.appendChild(p3);
   doc.body.appendChild(div);
   doc.body.appendChild(div2);
   setupHighlighterTests();
 }
 
 function setupHighlighterTests()
 {
-  h1 = doc.querySelectorAll("h1")[0];
   ok(h1, "we have the header node");
-  Services.obs.addObserver(inspectorOpen, "inspector-opened", false);
+  Services.obs.addObserver(inspectorOpen, INSPECTOR_NOTIFICATIONS.OPENED, false);
+  registerTools();
   InspectorUI.toggleInspectorUI();
 }
 
 function inspectorOpen()
 {
   info("we received the inspector-opened notification");
-  Services.obs.removeObserver(inspectorOpen, "inspector-opened", false);
-  Services.obs.addObserver(startToolTests, "inspector-highlighting", false);
-  let rect = h1.getBoundingClientRect();
-  executeSoon(function() {
-    EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
-  });
+  Services.obs.removeObserver(inspectorOpen, INSPECTOR_NOTIFICATIONS.OPENED);
+  toolsLength = InspectorUI.tools.length;
+  toolEvents = InspectorUI.toolEvents.length;
+  info("tools registered");
+  Services.obs.addObserver(startToolTests, INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.inspectNode(h1);
 }
 
 function startToolTests(evt)
 {
-  info("we received the inspector-highlighting notification");
-  Services.obs.removeObserver(startToolTests, "inspector-highlighting", false);
+  Services.obs.removeObserver(startToolTests, INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
   InspectorUI.stopInspecting();
-
   info("Getting InspectorUI.tools");
   let tools = InspectorUI.tools;
+
   tool1 = InspectorUI.tools["tool_1"];
   tool2 = InspectorUI.tools["tool_2"];
   tool3 = InspectorUI.tools["tool_3"];
 
   info("Checking panel states 1");
-  ok(tool1.context.panelIsClosed, "Panel 1 is closed");
-  ok(tool2.context.panelIsClosed, "Panel 2 is closed");
-  ok(tool3.context.panelIsClosed, "Panel 3 is closed");
+  ok(!tool1.isOpen, "Panel 1 is closed");
+  ok(!tool2.isOpen, "Panel 2 is closed");
+  ok(!tool3.isOpen, "Panel 3 is closed");
 
   info("Calling show method for all tools");
-  tool1.onShow.apply(tool1.context, [h1]);
-  tool2.onShow.apply(tool2.context, [h1]);
-  tool3.onShow.apply(tool3.context, [h1]);
+  InspectorUI.toolShow(tool1);
+  InspectorUI.toolShow(tool2);
+  InspectorUI.toolShow(tool3);
 
   info("Checking panel states 2");
-  ok(tool1.context.panelIsOpen, "Panel 1 is open");
-  ok(tool2.context.panelIsOpen, "Panel 2 is open");
-  ok(tool3.context.panelIsOpen, "Panel 3 is open");
+  ok(tool1.isOpen, "Panel 1 is open");
+  ok(tool2.isOpen, "Panel 2 is open");
+  ok(tool3.isOpen, "Panel 3 is open");
 
-  info("Calling selectNode method for all tools");
-  tool1.onSelect.apply(tool1.context, [h1]);
-  tool2.onSelect.apply(tool2.context, [h1]);
-  tool3.onSelect.apply(tool3.context, [h1]);
+  info("Calling selectNode method for all tools, should see 3 selects");
+  InspectorUI.inspectNode(p2);
 
   info("Calling hide method for all tools");
-  tool1.onHide.apply(tool1.context, [h1]);
-  tool2.onHide.apply(tool2.context, [h1]);
-  tool3.onHide.apply(tool3.context, [h1]);
-
+  InspectorUI.toolHide(tool1);
+  InspectorUI.toolHide(tool2);
+  InspectorUI.toolHide(tool3);
+  
   info("Checking panel states 3");
-  ok(tool1.context.panelIsClosed, "Panel 1 is closed");
-  ok(tool2.context.panelIsClosed, "Panel 2 is closed");
-  ok(tool3.context.panelIsClosed, "Panel 3 is closed");
+  ok(!tool1.isOpen, "Panel 1 is closed");
+  ok(!tool2.isOpen, "Panel 2 is closed");
+  ok(!tool3.isOpen, "Panel 3 is closed");
 
   info("Showing tools 1 & 3");
-  tool1.onShow.apply(tool1.context, [h1]);
-  tool3.onShow.apply(tool3.context, [h1]);
+  InspectorUI.toolShow(tool1);
+  InspectorUI.toolShow(tool3);
 
   info("Checking panel states 4");
-  ok(tool1.context.panelIsOpen, "Panel 1 is open");
-  ok(tool2.context.panelIsClosed, "Panel 2 is closed");
-  ok(tool3.context.panelIsOpen, "Panel 3 is open");
+  ok(tool1.isOpen, "Panel 1 is open");
+  ok(!tool2.isOpen, "Panel 2 is closed");
+  ok(tool3.isOpen, "Panel 3 is open");
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     waitForFocus(testSecondTab, content);
   }, true);
 
   content.location = "data:text/html,registertool new tab test for inspector";
 }
 
 function testSecondTab()
 {
   info("Opened second tab");
   info("Checking panel states 5");
-  ok(tool1.context.panelIsClosed, "Panel 1 is closed");
-  ok(tool2.context.panelIsClosed, "Panel 2 is closed");
-  ok(tool3.context.panelIsClosed, "Panel 3 is closed");
+
+  let tools = InspectorUI.tools;
+  ok(!(tool1 in tools), "Panel 1 not in tools");
+  ok(!(tool2 in tools), "Panel 2 not in tools");
+  ok(!(tool3 in tools), "Panel 3 not in tools");
 
   info("Closing current tab");
+  Services.obs.addObserver(testOriginalTab, INSPECTOR_NOTIFICATIONS.OPENED, false);
   gBrowser.removeCurrentTab();
+}
 
+function testOriginalTab()
+{
+  Services.obs.removeObserver(testOriginalTab, INSPECTOR_NOTIFICATIONS.OPENED);
   info("Checking panel states 6");
-  ok(tool1.context.panelIsOpen, "Panel 1 is open");
-  ok(tool2.context.panelIsClosed, "Panel 2 is closed");
-  ok(tool3.context.panelIsOpen, "Panel 3 is open");
+
+  info("Tools: " + InspectorUI.tools);
+  // reacquaint ourselves with our tools
+  tool1 = InspectorUI.tools["tool_1"];
+  tool2 = InspectorUI.tools["tool_2"];
+  tool3 = InspectorUI.tools["tool_3"];
+
+  ok(tool1.isOpen, "Panel 1 is open after reactivation");
+  ok(!tool2.isOpen, "Panel 2 is closed after reactivation");
+  ok(tool3.isOpen, "Panel 3 is open after reactivation");
 
-  executeSoon(finishUp);
+  Services.obs.addObserver(unregisterTools, INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  InspectorUI.closeInspectorUI(true);
+}
+
+function unregisterTools()
+{
+  Services.obs.removeObserver(unregisterTools, INSPECTOR_NOTIFICATIONS.CLOSED);
+  let tools = InspectorUI.tools;
+
+  ok(!(tool1 in tools), "Tool 1 removed");
+  ok(!(tool2 in tools), "Tool 2 removed");
+  ok(!(tool3 in tools), "Tool 3 removed");
+  is(tools.length, toolsLength, "Number of Registered Tools matches original");
+  is(InspectorUI.toolEvents.length, toolEvents, "Number of tool events matches original");
+  finishUp();
 }
 
 function finishUp() {
-  InspectorUI.closeInspectorUI(true);
   gBrowser.removeCurrentTab();
+  InspectorUI.initTools = initToolsMethod;
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     doc = content.document;
-    waitForFocus(registerTools, content);
+    waitForFocus(createDocument, content);
   }, true);
   
   content.location = "data:text/html,registertool tests for inspector";
 }
 
 function registerTools()
 {
-  createDocument();
-  registerTool(new testTool("tool_1", "Tool 1", "Tool 1 tooltip", "I"));
-  registerTool(new testTool("tool_2", "Tool 2", "Tool 2 tooltip", "J"));
-  registerTool(new testTool("tool_3", "Tool 3", "Tool 3 tooltip", "K"));
+  InspectorUI.initTools = function() {
+    info("(re)registering tools");
+    registerTool(new testTool("tool_1", "Tool 1", "Tool 1 tooltip", "I"));
+    registerTool(new testTool("tool_2", "Tool 2", "Tool 2 tooltip", "J"));
+    registerTool(new testTool("tool_3", "Tool 3", "Tool 3 tooltip", "K"));
+  }
 }
 
 function registerTool(aTool)
 {
   InspectorUI.registerTool({
     id: aTool.id,
     label: aTool.label,
     tooltiptext: aTool.tooltip,
     accesskey: aTool.accesskey,
     context: aTool,
+    get isOpen() aTool.isOpen(),
     onSelect: aTool.selectNode,
-    onShow: aTool.show,
-    onHide: aTool.hide,
-    panel: aTool.panel
+    show: aTool.show,
+    hide: aTool.hide,
+    unregister: aTool.destroy,
   });
 }
 
 // Tool Object
 function testTool(aToolId, aLabel, aTooltip, aAccesskey)
 {
   this.id = aToolId;
   this.label = aLabel;
   this.tooltip = aTooltip;
-  this.accesskey = aAccesskey
-  this.panel = this.createPanel();
+  this.accesskey = aAccesskey;
+  this._isOpen = false;
 }
 
 testTool.prototype = {
-  get panelIsOpen()
-  {
-    return this.panel.state == "open" || this.panel.state == "showing";
-  },
-
-  get panelIsClosed()
-  {
-    return this.panel.state == "closed" || this.panel.state == "hiding";
+  isOpen: function BIR_isOpen() {
+    return this._isOpen;
   },
 
   selectNode: function BIR_selectNode(aNode) {
     is(InspectorUI.selection, aNode,
        "selectNode: currently selected node was passed: " + this.id);
   },
 
   show: function BIR_show(aNode) {
-    this.panel.openPopup(gBrowser.selectedBrowser,
-                         "end_before", 0, 20, false, false);
+    this._isOpen = true;
     is(InspectorUI.selection, aNode,
        "show: currently selected node was passed: " + this.id);
   },
 
   hide: function BIR_hide() {
     info(this.id + " hide");
-    this.panel.hidePopup();
+    this._isOpen = false;
   },
 
-  createPanel: function BIR_createPanel() {
-    let popupSet = document.getElementById("mainPopupSet");
-    let ns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-    let panel = this.panel = document.createElementNS(ns, "panel");
-    panel.setAttribute("orient", "vertical");
-    panel.setAttribute("noautofocus", "true");
-    panel.setAttribute("noautohide", "true");
-    panel.setAttribute("titlebar", "normal");
-    panel.setAttribute("close", "true");
-    panel.setAttribute("label", "Panel for " + this.id);
-    panel.setAttribute("width", 200);
-    panel.setAttribute("height", 400);
-    popupSet.appendChild(panel);
-
-    ok(panel.parentNode == popupSet, "Panel created and appended successfully");
-    return panel;
+  destroy: function BIR_destroy() {
+    info("tool destroyed " + this.id);
+    if (this.isOpen())
+      this.hide();
+    delete this.id;
+    delete this.label;
+    delete this.tooltip;
+    delete this.accesskey;
   },
 };