Merge m-c to s-c
authorPhilipp von Weitershausen <philipp@weitershausen.de>
Tue, 27 Sep 2011 09:47:53 -0700
changeset 77756 f030249274c140f6d112deba7fcecee8277e8a89
parent 77755 1e00033f27c65cc357a23a410b5532978eed0803 (current diff)
parent 77708 c0983049bcaa9551e5f276d5a77ce154c151e0b0 (diff)
child 77757 cb4b93331e4f0052a35f305a959aaa23460c3a88
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
milestone9.0a1
Merge m-c to s-c
browser/base/content/domplate.jsm
browser/base/content/insideOutBox.js
browser/base/content/inspector.html
browser/base/content/inspector.js
browser/base/content/test/inspector/Makefile.in
browser/base/content/test/inspector/browser_inspector_bug_566084_location_changed.js
browser/base/content/test/inspector/browser_inspector_bug_665880.js
browser/base/content/test/inspector/browser_inspector_bug_674871.js
browser/base/content/test/inspector/browser_inspector_editor.js
browser/base/content/test/inspector/browser_inspector_highlighter.js
browser/base/content/test/inspector/browser_inspector_iframeTest.js
browser/base/content/test/inspector/browser_inspector_initialization.js
browser/base/content/test/inspector/browser_inspector_registertools.js
browser/base/content/test/inspector/browser_inspector_scrolling.js
browser/base/content/test/inspector/browser_inspector_store.js
browser/base/content/test/inspector/browser_inspector_tab_switch.js
browser/base/content/test/inspector/browser_inspector_treePanel_click.js
browser/base/content/test/inspector/browser_inspector_treePanel_input.html
browser/base/content/test/inspector/browser_inspector_treePanel_output.js
browser/base/content/test/inspector/browser_inspector_treePanel_result.html
browser/base/content/test/inspector/browser_inspector_treeSelection.js
browser/themes/gnomestripe/browser/reload-stop-go.png
browser/themes/pinstripe/browser/Go-arrow.png
browser/themes/pinstripe/browser/places/pageStarred.png
browser/themes/winstripe/browser/Go-arrow-aero.png
browser/themes/winstripe/browser/Go-arrow.png
build/autoconf/libIDL.m4
content/svg/content/src/nsSVGAnimatedTransformList.cpp
content/svg/content/src/nsSVGAnimatedTransformList.h
content/svg/content/src/nsSVGMatrix.cpp
content/svg/content/src/nsSVGMatrix.h
content/svg/content/src/nsSVGSMILTransform.h
content/svg/content/src/nsSVGTransform.cpp
content/svg/content/src/nsSVGTransform.h
content/svg/content/src/nsSVGTransformList.cpp
content/svg/content/src/nsSVGTransformList.h
content/svg/content/src/nsSVGTransformListParser.cpp
content/svg/content/src/nsSVGTransformListParser.h
content/svg/content/src/nsSVGTransformSMILAttr.cpp
content/svg/content/src/nsSVGTransformSMILAttr.h
content/svg/content/src/nsSVGTransformSMILType.cpp
content/svg/content/src/nsSVGTransformSMILType.h
dom/plugins/base/android_npapi.h
editor/libeditor/html/tests/test_bug612128.html
js/src/jsvalue.h
js/src/resource.h
js/src/t/3d-cube.js
js/src/t/3d-morph.js
js/src/t/3d-raytrace.js
js/src/t/access-binary-trees.js
js/src/t/access-fannkuch.js
js/src/t/access-nbody.js
js/src/t/access-nsieve.js
js/src/t/bitops-3bit-bits-in-byte.js
js/src/t/bitops-bits-in-byte.js
js/src/t/bitops-bitwise-and.js
js/src/t/bitops-nsieve-bits.js
js/src/t/controlflow-recursive.js
js/src/t/crypto-aes.js
js/src/t/crypto-md5.js
js/src/t/crypto-sha1.js
js/src/t/date-format-tofte.js
js/src/t/date-format-xparb.js
js/src/t/math-cordic.js
js/src/t/math-partial-sums.js
js/src/t/math-spectral-norm.js
js/src/t/regexp-dna.js
js/src/t/string-base64.js
js/src/t/string-fasta.js
js/src/t/string-tagcloud.js
js/src/t/string-unpack-code.js
js/src/t/string-validate-input.js
js/src/tests/js1_5/Function/15.3.4.4.js
js/src/xpconnect/tests/TestXPC.cpp
js/src/xpconnect/tests/components/Makefile.in
js/src/xpconnect/tests/components/xpctest_array.cpp
js/src/xpconnect/tests/components/xpctest_attributes.cpp
js/src/xpconnect/tests/components/xpctest_calljs.cpp
js/src/xpconnect/tests/components/xpctest_child.cpp
js/src/xpconnect/tests/components/xpctest_const.cpp
js/src/xpconnect/tests/components/xpctest_domstring.cpp
js/src/xpconnect/tests/components/xpctest_echo.cpp
js/src/xpconnect/tests/components/xpctest_in.cpp
js/src/xpconnect/tests/components/xpctest_inout.cpp
js/src/xpconnect/tests/components/xpctest_module.cpp
js/src/xpconnect/tests/components/xpctest_multiple.cpp
js/src/xpconnect/tests/components/xpctest_noisy.cpp
js/src/xpconnect/tests/components/xpctest_out.cpp
js/src/xpconnect/tests/components/xpctest_overloaded.cpp
js/src/xpconnect/tests/components/xpctest_private.h
js/src/xpconnect/tests/components/xpctest_string.cpp
js/src/xpconnect/tests/components/xpctest_variant.cpp
js/src/xpconnect/tests/idl/xpctest.idl
js/src/xpconnect/tests/idl/xpctest2.idl
js/src/xpconnect/tests/idl/xpctest_calljs.idl
js/src/xpconnect/tests/idl/xpctest_const.idl
js/src/xpconnect/tests/idl/xpctest_domstring.idl
js/src/xpconnect/tests/idl/xpctest_in.idl
js/src/xpconnect/tests/idl/xpctest_inout.idl
js/src/xpconnect/tests/idl/xpctest_multiple.idl
js/src/xpconnect/tests/idl/xpctest_out.idl
js/src/xpconnect/tests/js/readwriteattributes.js
layout/reftests/editor/readonly-editable-ref.html
layout/reftests/editor/readonly-editable.html
layout/reftests/editor/readonly-non-editable-ref.html
layout/reftests/editor/readonly-non-editable.html
layout/reftests/editor/readwrite-editable-ref.html
layout/reftests/editor/readwrite-editable.html
layout/reftests/editor/readwrite-non-editable-ref.html
layout/reftests/editor/readwrite-non-editable.html
media/libsydneyaudio/bug495558_alsa_endian.patch
media/libsydneyaudio/bug495794_closeAudio.patch
media/libsydneyaudio/bug525401_drain_deadlock.patch
media/libsydneyaudio/bug562488_oss_destroy_crash.patch
media/libsydneyaudio/bug564734-win32-drain.patch
media/libsydneyaudio/include-CoreServices.patch
media/libsydneyaudio/pause-resume.patch
media/libsydneyaudio/sydney_aix.patch
media/libsydneyaudio/sydney_android.patch
media/libsydneyaudio/sydney_os2_base.patch
media/libsydneyaudio/sydney_os2_moz.patch
media/libsydneyaudio/update.sh
mobile/components/WebappsSupport.idl
mobile/components/WebappsSupport.js
mobile/themes/core/honeycomb/images/endcap-active-bg.png
mobile/themes/core/honeycomb/images/endcap-default-bg.png
mobile/themes/core/honeycomb/images/endcap-ev-active-bg.png
mobile/themes/core/honeycomb/images/endcap-ev-default-bg.png
mobile/themes/core/honeycomb/images/endcap-ssl-active-bg.png
mobile/themes/core/honeycomb/images/endcap-ssl-default-bg.png
modules/libreg/Makefile.in
modules/libreg/include/NSReg.h
modules/libreg/include/VerReg.h
modules/libreg/src/Makefile.in
modules/libreg/src/VerReg.c
modules/libreg/src/nr_bufio.c
modules/libreg/src/reg.c
modules/libreg/src/reg.h
modules/libreg/src/vr_stubs.c
modules/libreg/src/vr_stubs.h
modules/zlib/standalone/Makefile.in
netwerk/test/unit/test_bug477578.js
toolkit/components/places/tests/head_common.js
widget/src/gtk2/nsAccessibilityHelper.cpp
widget/src/gtk2/nsAccessibilityHelper.h
xpcom/typelib/xpidl/README
xpcom/typelib/xpidl/xpidl.c
xpcom/typelib/xpidl/xpidl.h
xpcom/typelib/xpidl/xpidl_doc.c
xpcom/typelib/xpidl/xpidl_header.c
xpcom/typelib/xpidl/xpidl_idl.c
xpcom/typelib/xpidl/xpidl_java.c
xpcom/typelib/xpidl/xpidl_typelib.c
xpcom/typelib/xpidl/xpidl_util.c
--- a/accessible/public/ia2/Makefile.in
+++ b/accessible/public/ia2/Makefile.in
@@ -44,17 +44,17 @@ LIBRARY_NAME  = IA2Marshal
 MODULE        = accessibility
 GRE_MODULE    = 1
 DEFFILE       = $(win_srcdir)/IA2Marshal.def
 
 IA2DIR        = $(topsrcdir)/other-licenses/ia2
 
 include $(DEPTH)/config/autoconf.mk
 
-DEFINES       += -DREGISTER_PROXY_DLL -D_WIN32_WINNT=0x400
+DEFINES       += -DREGISTER_PROXY_DLL
 
 GARBAGE       += $(MIDL_GENERATED_FILES)
 
 FORCE_SHARED_LIB = 1
 
 SRCS_IN_OBJDIR   = 1
 
 MIDL_INTERFACES = \
--- a/accessible/public/msaa/Makefile.in
+++ b/accessible/public/msaa/Makefile.in
@@ -48,17 +48,17 @@ DEFFILE = $(win_srcdir)/AccessibleMarsha
 
 include $(DEPTH)/config/autoconf.mk
 
 XPIDLSRCS = \
       nsIAccessibleWin32Object.idl \
       nsIWinAccessNode.idl \
       $(NULL)
 
-DEFINES		+= -DREGISTER_PROXY_DLL -D_WIN32_WINNT=0x400
+DEFINES += -DREGISTER_PROXY_DLL
 
 GARBAGE += $(MIDL_GENERATED_FILES) done_gen dlldata.c
 
 FORCE_SHARED_LIB = 1
 
 SRCS_IN_OBJDIR	= 1
 
 CSRCS	= \
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,15 +1,14 @@
 dnl
 dnl Local autoconf macros used with mozilla
 dnl The contents of this file are under the Public Domain.
 dnl 
 
 builtin(include, build/autoconf/glib.m4)dnl
-builtin(include, build/autoconf/libIDL.m4)dnl
 builtin(include, build/autoconf/nspr.m4)dnl
 builtin(include, build/autoconf/nss.m4)dnl
 builtin(include, build/autoconf/pkg.m4)dnl
 builtin(include, build/autoconf/freetype2.m4)dnl
 builtin(include, build/autoconf/codeset.m4)dnl
 builtin(include, build/autoconf/altoptions.m4)dnl
 builtin(include, build/autoconf/mozprog.m4)dnl
 builtin(include, build/autoconf/mozheader.m4)dnl
--- a/browser/app/macbuild/Contents/Info.plist.in
+++ b/browser/app/macbuild/Contents/Info.plist.in
@@ -209,10 +209,12 @@
 	<string>10.5</string>
 	<key>LSMinimumSystemVersionByArchitecture</key>
 	<dict>
 		<key>i386</key>
 		<string>10.5.0</string>
 		<key>x86_64</key>
 		<string>10.6.0</string>
 	</dict>
+  <key>NSSupportsAutomaticGraphicsSwitching</key>
+  <true/>
 </dict>
 </plist>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -249,17 +249,17 @@ pref("browser.chrome.favicons", true);
 // browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
 pref("browser.warnOnQuit", true);
 pref("browser.warnOnRestart", false);
 // browser.showQuitWarning specifically controls the quit warning dialog. We
 // might still show the window closing dialog with showQuitWarning == false.
 pref("browser.showQuitWarning", false);
 pref("browser.fullscreen.autohide", true);
 pref("browser.fullscreen.animateUp", 1);
-pref("browser.overlink-delay", 70);
+pref("browser.overlink-delay", 80);
 
 #ifdef UNIX_BUT_NOT_MAC
 pref("browser.urlbar.clickSelectsAll", false);
 #else
 pref("browser.urlbar.clickSelectsAll", true);
 #endif
 #ifdef UNIX_BUT_NOT_MAC
 pref("browser.urlbar.doubleClickSelectsAll", true);
@@ -861,20 +861,16 @@ pref("browser.privatebrowsing.autostart"
 
 // Whether we should skip prompting before starting the private browsing mode
 pref("browser.privatebrowsing.dont_prompt_on_enter", false);
 
 // Don't try to alter this pref, it'll be reset the next time you use the
 // bookmarking dialog
 pref("browser.bookmarks.editDialog.firstEditField", "namePicker");
 
-// base url for the wifi geolocation network provider
-pref("geo.wifi.uri", "https://maps.googleapis.com/maps/api/browserlocation/json");
-pref("geo.wifi.protocol", 0);
-
 // Whether to use a panel that looks like an OS X sheet for customization
 #ifdef XP_MACOSX
 pref("toolbar.customization.usesheet", true);
 #else
 pref("toolbar.customization.usesheet", false);
 #endif
 
 // The default for this pref reflects whether the build is capable of IPC.
--- a/browser/base/Makefile.in
+++ b/browser/base/Makefile.in
@@ -51,17 +51,16 @@ CHROME_DEPS += $(abs_srcdir)/content/ove
 
 ifdef ENABLE_TESTS
 DIRS += content/test
 endif
 
 EXTRA_JS_MODULES = \
 	content/openLocationLastURL.jsm \
 	content/NetworkPrioritizer.jsm \
-	content/domplate.jsm \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 PRE_RELEASE_SUFFIX := ""
 
 DEFINES += \
 	-DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -16,16 +16,18 @@
 #
 # The Initial Developer of the Original Code is
 # Netscape Communications Corporation.
 # Portions created by the Initial Developer are Copyright (C) 2001
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
+#   Rob Campbell <rcampbell@mozilla.com>
+#   Panagiotis Astithas <past@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -335,8 +337,14 @@
       <menuitem hidden="true" id="context-bidi-text-direction-toggle"
                 label="&bidiSwitchTextDirectionItem.label;"
                 accesskey="&bidiSwitchTextDirectionItem.accesskey;"
                 command="cmd_switchTextDirection"/>
       <menuitem hidden="true" id="context-bidi-page-direction-toggle"
                 label="&bidiSwitchPageDirectionItem.label;"
                 accesskey="&bidiSwitchPageDirectionItem.accesskey;"
                 oncommand="gContextMenu.switchPageDirection();"/>
+      <menuseparator id="inspect-separator" hidden="true"/>
+      <menuitem id="context-inspect"
+                hidden="true"
+                label="&inspectContextMenu.label;"
+                accesskey="&inspectContextMenu.accesskey;"
+                oncommand="gContextMenu.inspectNode();"/>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -506,26 +506,30 @@ statuspanel[type=status] {
 
 @media all and (max-width: 800px) {
   statuspanel[type=status] {
     min-width: 33%;
   }
 }
 
 statuspanel[type=overLink] {
-  -moz-transition: opacity 100ms ease-out;
+  -moz-transition: opacity 120ms ease-out;
   direction: ltr;
 }
 
-statuspanel[label=""] {
+statuspanel[inactive] {
   -moz-transition: none;
   opacity: 0;
   pointer-events: none;
 }
 
+statuspanel[inactive][previoustype=overLink] {
+  -moz-transition: opacity 200ms ease-out;
+}
+
 .statuspanel-inner {
   height: 3em;
   width: 100%;
   -moz-box-align: end;
 }
 
 .styleInspector {
   min-width: 350px;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -168,24 +168,29 @@ XPCOMUtils.defineLazyGetter(this, "Popup
     return new tmp.PopupNotifications(gBrowser,
                                       document.getElementById("notification-popup"),
                                       document.getElementById("notification-popup-box"));
   } catch (ex) {
     Cu.reportError(ex);
   }
 });
 
+XPCOMUtils.defineLazyGetter(this, "InspectorUI", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/inspector.jsm", tmp);
+  return new tmp.InspectorUI(window);
+});
+
 let gInitialPages = [
   "about:blank",
   "about:privatebrowsing",
   "about:sessionrestore"
 ];
 
 #include browser-fullZoom.js
-#include inspector.js
 #include browser-places.js
 #include browser-tabPreviews.js
 #include browser-tabview.js
 
 #ifdef MOZ_SERVICES_SYNC
 #include browser-syncui.js
 #endif
 
@@ -1671,17 +1676,17 @@ function delayedStartup(isLoadingBlank, 
 #ifdef MOZ_SERVICES_SYNC
   // initialize the sync UI
   gSyncUI.init();
 #endif
 
   TabView.init();
 
   // Enable Inspector?
-  let enabled = gPrefService.getBoolPref(InspectorUI.prefEnabledName);
+  let enabled = gPrefService.getBoolPref("devtools.inspector.enabled");
   if (enabled) {
     document.getElementById("menu_pageinspect").hidden = false;
     document.getElementById("Tools:Inspect").removeAttribute("disabled");
 #ifdef MENUBAR_CAN_AUTOHIDE
     document.getElementById("appmenu_pageInspect").hidden = false;
 #endif
   }
 
@@ -1720,16 +1725,19 @@ function delayedStartup(isLoadingBlank, 
 
 function BrowserShutdown() {
   // In certain scenarios it's possible for unload to be fired before onload,
   // (e.g. if the window is being closed after browser.js loads but before the
   // load completes). In that case, there's nothing to do here.
   if (!gStartupRan)
     return;
 
+  if (!__lookupGetter__("InspectorUI"))
+    InspectorUI.destroy();
+
   // First clean up services initialized in BrowserStartup (or those whose
   // uninit methods don't depend on the services having been initialized).
   allTabs.uninit();
 
   CombinedStopReload.uninit();
 
   gGestureSupport.init(false);
 
@@ -4670,17 +4678,17 @@ var XULBrowserWindow = {
 };
 
 var LinkTargetDisplay = {
   get DELAY_SHOW() {
      delete this.DELAY_SHOW;
      return this.DELAY_SHOW = Services.prefs.getIntPref("browser.overlink-delay");
   },
 
-  DELAY_HIDE: 150,
+  DELAY_HIDE: 250,
   _timer: 0,
 
   get _isVisible () XULBrowserWindow.statusTextField.label != "",
 
   update: function () {
     clearTimeout(this._timer);
     window.removeEventListener("mousemove", this, true);
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -227,17 +227,16 @@
     <panel id="inspector-tree-panel"
            orient="vertical"
            hidden="true"
            ignorekeys="true"
            noautofocus="true"
            noautohide="true"
            titlebar="normal"
            close="true"
-           onpopuphiding="InspectorUI.closeInspectorUI();"
            label="&inspectPanelTitle.label;">
       <hbox id="tree-panel-resizer-box" align="end">
         <spacer flex="1" />
         <resizer dir="bottomend" />
       </hbox>
     </panel>
 
     <menupopup id="toolbar-context-menu"
@@ -952,17 +951,17 @@
     <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
     <vbox id="appcontent" flex="1">
       <tabbrowser id="content" disablehistory="true"
                   flex="1" contenttooltip="aHTMLTooltip"
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   onclick="return contentAreaClick(event, false);"/>
-      <statuspanel id="statusbar-display" label=""/>
+      <statuspanel id="statusbar-display" inactive="true"/>
     </vbox>
     <vbox id="browser-border-end" hidden="true" layer="true"/>
   </hbox>
 
   <vbox id="browser-bottombox" layer="true">
     <toolbar id="inspector-toolbar"
              nowindowdrag="true"
              hidden="true">
deleted file mode 100644
--- a/browser/base/content/insideOutBox.js
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Software License Agreement (BSD License)
- *
- * Copyright (c) 2007, Parakey Inc.
- * All rights reserved.
- * 
- * Redistribution and use of this software in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- * 
- * * Redistributions of source code must retain the above
- *   copyright notice, this list of conditions and the
- *   following disclaimer.
- * 
- * * Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the
- *   following disclaimer in the documentation and/or other
- *   materials provided with the distribution.
- * 
- * * Neither the name of Parakey Inc. nor the names of its
- *   contributors may be used to endorse or promote products
- *   derived from this software without specific prior
- *   written permission of Parakey Inc.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Creator:
- *  Joe Hewitt
- * Contributors
- *  John J. Barton (IBM Almaden)
- *  Jan Odvarko (Mozilla Corp.)
- *  Max Stepanov (Aptana Inc.)
- *  Rob Campbell (Mozilla Corp.)
- *  Hans Hillen (Paciello Group, Mozilla)
- *  Curtis Bartley (Mozilla Corp.)
- *  Mike Collins (IBM Almaden)
- *  Kevin Decker
- *  Mike Ratcliffe (Comartis AG)
- *  Hernan Rodríguez Colmeiro
- *  Austin Andrews
- *  Christoph Dorn
- *  Steven Roussey (AppCenter Inc, Network54)
- */
-
-///////////////////////////////////////////////////////////////////////////
-//// InsideOutBox
-
-/**
- * InsideOutBoxView is a simple interface definition for views implementing
- * InsideOutBox controls. All implementors must define these methods.
- * Implemented in InspectorUI.
- */
-
-/*
-InsideOutBoxView = {
-  //
-   * Retrieves the parent object for a given child object.
-   * @param aChild
-   *        The child node to retrieve the parent object for.
-   * @returns a DOM node | null
-   //
-  getParentObject: function(aChild) {},
-
-  //
-   * Retrieves a given child node.
-   *
-   * If both index and previousSibling are passed, the implementation
-   * may assume that previousSibling will be the return for getChildObject
-   * with index-1.
-   * @param aParent
-   *        The parent object of the child object to retrieve.
-   * @param aIndex
-   *        The index of the child object to retrieve from aParent.
-   * @param aPreviousSibling
-   *        The previous sibling of the child object to retrieve.
-   *        Supercedes aIndex.
-   * @returns a DOM object | null
-   //
-  getChildObject: function(aParent, aIndex, aPreviousSibling) {},
-
-  //
-   * Renders the HTML representation of the object. Should return an HTML
-   * object which will be displayed to the user.
-   * @param aObject
-   *        The object to create the box object for.
-   * @param aIsRoot
-   *        Is the object the root object. May not be used in all
-   *        implementations.
-   * @returns an object box | null
-   //
-  createObjectBox: function(aObject, aIsRoot) {},
-
-  //
-  * Convenience wrappers for classList API.
-  * @param aObject
-  *        DOM node to query/set.
-  * @param aClassName
-  *        String containing the class name to query/set.
-  //
-  hasClass: function(aObject, aClassName) {},
-  addClass: function(aObject, aClassName) {},
-  removeClass: function(aObject, aClassName) {}
-};
-*/
-
-/**
- * Creates a tree based on objects provided by a separate "view" object.
- *
- * Construction uses an "inside-out" algorithm, meaning that the view's job is
- * first to tell us the ancestry of each object, and secondarily its
- * descendants.
- *
- * Constructor
- * @param aView
- *        The view requiring the InsideOutBox.
- * @param aBox
- *        The box object containing the InsideOutBox. Required to add/remove
- *        children during box manipulation (toggling opened or closed).
- */
-function InsideOutBox(aView, aBox)
-{
-  this.view = aView;
-  this.box = aBox;
-
-  this.rootObject = null;
-
-  this.rootObjectBox = null;
-  this.selectedObjectBox = null;
-  this.highlightedObjectBox = null;
-  this.scrollIntoView = false;
-};
-
-InsideOutBox.prototype =
-{
-  /**
-   * Highlight the given object node in the tree.
-   * @param aObject
-   *        the object to highlight.
-   * @returns objectBox
-   */
-  highlight: function IOBox_highlight(aObject)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    this.highlightObjectBox(objectBox);
-    return objectBox;
-  },
-
-  /**
-   * Open the given object node in the tree.
-   * @param aObject
-   *        The object node to open.
-   * @returns objectBox
-   */
-  openObject: function IOBox_openObject(aObject)
-  {
-    let object = aObject;
-    let firstChild = this.view.getChildObject(object, 0);
-    if (firstChild)
-      object = firstChild;
-
-    return this.openToObject(object);
-  },
-
-  /**
-   * Open the tree up to the given object node.
-   * @param aObject
-   *        The object in the tree to open to.
-   * @returns objectBox
-   */
-  openToObject: function IOBox_openToObject(aObject)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    this.openObjectBox(objectBox);
-    return objectBox;
-  },
-
-  /**
-   * Select the given object node in the tree.
-   * @param aObject
-   *        The object node to select.
-   * @param makeBoxVisible
-   *        Boolean. Open the object box in the tree?
-   * @param forceOpen
-   *        Force the object box open by expanding all elements in the tree?
-   * @param scrollIntoView
-   *        Scroll the objectBox into view?
-   * @returns nsIDOMNode|null
-   *          A DOM node that represents the "object box", the element that
-   *          holds/displays the given aObject representation in the tree. If
-   *          the object cannot be selected, if it is a stale object, null is
-   *          returned.
-   */
-  select:
-  function IOBox_select(aObject, makeBoxVisible, forceOpen, scrollIntoView)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    if (!objectBox) {
-      return null;
-    }
-    this.selectObjectBox(objectBox, forceOpen);
-    if (makeBoxVisible) {
-      this.openObjectBox(objectBox);
-      if (scrollIntoView) {
-        objectBox.scrollIntoView(true);
-      }
-    }
-    return objectBox;
-  },
-
-  /**
-   * Expands/contracts the given object, depending on its state.
-   * @param aObject
-   *        The tree node to expand/contract.
-   */
-  toggleObject: function IOBox_toggleObject(aObject)
-  {
-    let box = this.createObjectBox(aObject);
-    if (!(this.view.hasClass(box, "open")))
-      this.expandObjectBox(box);
-    else
-      this.contractObjectBox(box);
-  },
-
-  /**
-   * Expand the given object in the tree.
-   * @param aObject
-   *        The tree node to expand.
-   */
-  expandObject: function IOBox_expandObject(aObject)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    if (objectBox)
-      this.expandObjectBox(objectBox);
-  },
-
-  /**
-   * Contract the given object in the tree.
-   * @param aObject
-   *        The tree node to contract.
-   */
-  contractObject: function IOBox_contractObject(aObject)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    if (objectBox)
-      this.contractObjectBox(objectBox);
-  },
-
-  /**
-   * General method for iterating over an object's ancestors and performing
-   * some function.
-   * @param aObject
-   *        The object whose ancestors we wish to iterate over.
-   * @param aCallback
-   *        The function to call with the object as argument.
-   */
-
-  iterateObjectAncestors: function IOBox_iterateObjectAncesors(aObject, aCallback)
-  {
-    let object = aObject;
-    if (!(aCallback && typeof(aCallback) == "function")) {
-      this.view._log("Illegal argument in IOBox.iterateObjectAncestors");
-      return;
-    }
-    while ((object = this.getParentObjectBox(object)))
-      aCallback(object);
-  },
-
-  /**
-   * Highlight the given objectBox in the tree.
-   * @param aObjectBox
-   *        The objectBox to highlight.
-   */
-  highlightObjectBox: function IOBox_highlightObjectBox(aObjectBox)
-  {
-    let self = this;
-
-    if (!aObjectBox)
-      return;
-
-    if (this.highlightedObjectBox) {
-      this.view.removeClass(this.highlightedObjectBox, "highlighted");
-      this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
-        self.view.removeClass(box, "highlightOpen");
-      });
-    }
-
-    this.highlightedObjectBox = aObjectBox;
-
-    this.view.addClass(aObjectBox, "highlighted");
-    this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
-      self.view.addClass(box, "highlightOpen");
-    });
-
-    aObjectBox.scrollIntoView(true);
-  },
-
-  /**
-   * Select the given objectBox in the tree, forcing it to be open if necessary.
-   * @param aObjectBox
-   *        The objectBox to select.
-   * @param forceOpen
-   *        Force the box (subtree) to be open?
-   */
-  selectObjectBox: function IOBox_selectObjectBox(aObjectBox, forceOpen)
-  {
-    let isSelected = this.selectedObjectBox &&
-      aObjectBox == this.selectedObjectBox;
-
-    // aObjectBox is already selected, return
-    if (isSelected)
-      return;
-
-    if (this.selectedObjectBox)
-      this.view.removeClass(this.selectedObjectBox, "selected");
-
-    this.selectedObjectBox = aObjectBox;
-
-    if (aObjectBox) {
-      this.view.addClass(aObjectBox, "selected");
-
-      // Force it open the first time it is selected
-      if (forceOpen)
-        this.expandObjectBox(aObjectBox, true);
-    }
-  },
-
-  /**
-   * Open the ancestors of the given object box.
-   * @param aObjectBox
-   *        The object box to open.
-   */
-  openObjectBox: function IOBox_openObjectBox(aObjectBox)
-  {
-    if (!aObjectBox)
-      return;
-
-    let self = this;
-    this.iterateObjectAncestors(aObjectBox, function (box) {
-      self.view.addClass(box, "open");
-      let labelBox = box.querySelector(".nodeLabelBox");
-      if (labelBox)
-        labelBox.setAttribute("aria-expanded", "true");
-   });
-  },
-
-  /**
-   * Expand the given object box.
-   * @param aObjectBox
-   *        The object box to expand.
-   */
-  expandObjectBox: function IOBox_expandObjectBox(aObjectBox)
-  {
-    let nodeChildBox = this.getChildObjectBox(aObjectBox);
-
-    // no children means nothing to expand, return
-    if (!nodeChildBox)
-      return;
-
-    if (!aObjectBox.populated) {
-      let firstChild = this.view.getChildObject(aObjectBox.repObject, 0);
-      this.populateChildBox(firstChild, nodeChildBox);
-    }
-    let labelBox = aObjectBox.querySelector(".nodeLabelBox");
-    if (labelBox)
-      labelBox.setAttribute("aria-expanded", "true");
-    this.view.addClass(aObjectBox, "open");
-  },
-
-  /**
-   * Contract the given object box.
-   * @param aObjectBox
-   *        The object box to contract.
-   */
-  contractObjectBox: function IOBox_contractObjectBox(aObjectBox)
-  {
-    this.view.removeClass(aObjectBox, "open");
-    let nodeLabel = aObjectBox.querySelector(".nodeLabel");
-    let labelBox = nodeLabel.querySelector(".nodeLabelBox");
-    if (labelBox)
-      labelBox.setAttribute("aria-expanded", "false");
-  },
-
-  /**
-   * Toggle the given object box, forcing open if requested.
-   * @param aObjectBox
-   *        The object box to toggle.
-   * @param forceOpen
-   *        Force the objectbox open?
-   */
-  toggleObjectBox: function IOBox_toggleObjectBox(aObjectBox, forceOpen)
-  {
-    let isOpen = this.view.hasClass(aObjectBox, "open");
-
-    if (!forceOpen && isOpen)
-      this.contractObjectBox(aObjectBox);
-    else if (!isOpen)
-      this.expandObjectBox(aObjectBox);
-  },
-
-  /**
-   * Creates all of the boxes for an object, its ancestors, and siblings.
-   * @param aObject
-   *        The tree node to create the object boxes for.
-   * @returns anObjectBox or null
-   */
-  createObjectBox: function IOBox_createObjectBox(aObject)
-  {
-    if (!aObject)
-      return null;
-
-    this.rootObject = this.getRootNode(aObject) || aObject;
-
-    // Get or create all of the boxes for the target and its ancestors
-    let objectBox = this.createObjectBoxes(aObject, this.rootObject);
-
-    if (!objectBox)
-      return null;
-
-    if (aObject == this.rootObject)
-      return objectBox;
-
-    return this.populateChildBox(aObject, objectBox.parentNode);
-  },
-
-  /**
-   * Creates all of the boxes for an object, its ancestors, and siblings up to
-   * a root.
-   * @param aObject
-   *        The tree's object node to create the object boxes for.
-   * @param aRootObject
-   *        The root object at which to stop building object boxes.
-   * @returns an object box or null
-   */
-  createObjectBoxes: function IOBox_createObjectBoxes(aObject, aRootObject)
-  {
-    if (!aObject)
-      return null;
-
-    if (aObject == aRootObject) {
-      if (!this.rootObjectBox || this.rootObjectBox.repObject != aRootObject) {
-        if (this.rootObjectBox) {
-          try {
-            this.box.removeChild(this.rootObjectBox);
-          } catch (exc) {
-            InspectorUI._log("this.box.removeChild(this.rootObjectBox) FAILS " +
-              this.box + " must not contain " + this.rootObjectBox);
-          }
-        }
-
-        this.highlightedObjectBox = null;
-        this.selectedObjectBox = null;
-        this.rootObjectBox = this.view.createObjectBox(aObject, true);
-        this.box.appendChild(this.rootObjectBox);
-      }
-      return this.rootObjectBox;
-    }
-
-    let parentNode = this.view.getParentObject(aObject);
-    let parentObjectBox = this.createObjectBoxes(parentNode, aRootObject);
-
-    if (!parentObjectBox)
-      return null;
-
-    let parentChildBox = this.getChildObjectBox(parentObjectBox);
-
-    if (!parentChildBox)
-      return null;
-
-    let childObjectBox = this.findChildObjectBox(parentChildBox, aObject);
-
-    return childObjectBox ? childObjectBox
-      : this.populateChildBox(aObject, parentChildBox);
-  },
-
-  /**
-   * Locate the object box for a given object node.
-   * @param aObject
-   *        The given object node in the tree.
-   * @returns an object box or null.
-   */
-  findObjectBox: function IOBox_findObjectBox(aObject)
-  {
-    if (!aObject)
-      return null;
-
-    if (aObject == this.rootObject)
-      return this.rootObjectBox;
-
-    let parentNode = this.view.getParentObject(aObject);
-    let parentObjectBox = this.findObjectBox(parentNode);
-    if (!parentObjectBox)
-      return null;
-
-    let parentChildBox = this.getChildObjectBox(parentObjectBox);
-    if (!parentChildBox)
-      return null;
-
-    return this.findChildObjectBox(parentChildBox, aObject);
-  },
-
-  getAncestorByClass: function IOBox_getAncestorByClass(node, className)
-  {
-    for (let parent = node; parent; parent = parent.parentNode) {
-      if (this.view.hasClass(parent, className))
-        return parent;
-    }
-
-    return null;
-  },
-
-  /**
-   * We want all children of the parent of repObject.
-   */
-  populateChildBox: function IOBox_populateChildBox(repObject, nodeChildBox)
-  {
-    if (!repObject)
-      return null;
-
-    let parentObjectBox = this.getAncestorByClass(nodeChildBox, "nodeBox");
-
-    if (parentObjectBox.populated)
-      return this.findChildObjectBox(nodeChildBox, repObject);
-
-    let lastSiblingBox = this.getChildObjectBox(nodeChildBox);
-    let siblingBox = nodeChildBox.firstChild;
-    let targetBox = null;
-    let view = this.view;
-    let targetSibling = null;
-    let parentNode = view.getParentObject(repObject);
-
-    for (let i = 0; 1; ++i) {
-      targetSibling = view.getChildObject(parentNode, i, targetSibling);
-      if (!targetSibling)
-        break;
-
-      // Check if we need to start appending, or continue to insert before
-      if (lastSiblingBox && lastSiblingBox.repObject == targetSibling)
-        lastSiblingBox = null;
-
-      if (!siblingBox || siblingBox.repObject != targetSibling) {
-        let newBox = view.createObjectBox(targetSibling);
-        if (newBox) {
-          if (lastSiblingBox)
-            nodeChildBox.insertBefore(newBox, lastSiblingBox);
-          else
-            nodeChildBox.appendChild(newBox);
-        }
-
-        siblingBox = newBox;
-      }
-
-      if (targetSibling == repObject)
-        targetBox = siblingBox;
-
-      if (siblingBox && siblingBox.repObject == targetSibling)
-        siblingBox = siblingBox.nextSibling;
-    }
-
-    if (targetBox)
-      parentObjectBox.populated = true;
-
-    return targetBox;
-  },
-
-  /**
-   * Get the parent object box of a given object box.
-   * @params aObjectBox
-   *         The object box of the parent.
-   * @returns an object box or null
-   */
-  getParentObjectBox: function IOBox_getParentObjectBox(aObjectBox)
-  {
-    let parent = aObjectBox.parentNode ? aObjectBox.parentNode.parentNode : null;
-    return parent && parent.repObject ? parent : null;
-  },
-
-  /**
-   * Get the child object box of a given object box.
-   * @param aObjectBox
-   *        The object box whose child you want.
-   * @returns an object box or null
-   */
-  getChildObjectBox: function IOBox_getChildObjectBox(aObjectBox)
-  {
-    return aObjectBox.querySelector(".nodeChildBox");
-  },
-
-  /**
-   * Find the child object box for a given repObject within the subtree
-   * rooted at aParentNodeBox.
-   * @param aParentNodeBox
-   *        root of the subtree in which to search for repObject.
-   * @param aRepObject
-   *        The object you wish to locate in the subtree.
-   * @returns an object box or null
-   */
-  findChildObjectBox: function IOBox_findChildObjectBox(aParentNodeBox, aRepObject)
-  {
-    let childBox = aParentNodeBox.firstChild;
-    while (childBox) {
-      if (childBox.repObject == aRepObject)
-        return childBox;
-      childBox = childBox.nextSibling;
-    }
-    return null; // not found
-  },
-
-  /**
-   * Determines if the given node is an ancestor of the current root.
-   * @param aNode
-   *        The node to look for within the tree.
-   * @returns boolean
-   */
-  isInExistingRoot: function IOBox_isInExistingRoot(aNode)
-  {
-    let parentNode = aNode;
-    while (parentNode && parentNode != this.rootObject) {
-      parentNode = this.view.getParentObject(parentNode);
-    }
-    return parentNode == this.rootObject;
-  },
-
-  /**
-   * Get the root node of a given node.
-   * @param aNode
-   *        The node whose root you wish to retrieve.
-   * @returns a root node or null
-   */
-  getRootNode: function IOBox_getRootNode(aNode)
-  {
-    let node = aNode;
-    let tmpNode;
-    while ((tmpNode = this.view.getParentObject(node)))
-      node = tmpNode;
-
-    return node;
-  },
-};
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -41,16 +41,17 @@
 #   Simon Bünzli <zeniko@gmail.com>
 #   Gijs Kruitbosch <gijskruitbosch@gmail.com>
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Dan Mosedale <dmose@mozilla.org>
 #   Justin Dolske <dolske@mozilla.com>
 #   Kathleen Brade <brade@pearlcrescent.com>
 #   Mark Smith <mcs@pearlcrescent.com>
 #   Kailas Patil <patilkr24@gmail.com>
+#   Rob Campbell <rcampbell@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -239,18 +240,21 @@ nsContextMenu.prototype = {
                   this.isContentSelected);
     this.showItem("context-viewpartialsource-mathml",
                   this.onMathML && !this.isContentSelected);
 
     var shouldShow = !(this.isContentSelected ||
                        this.onImage || this.onCanvas ||
                        this.onVideo || this.onAudio ||
                        this.onLink || this.onTextInput);
+    var showInspect = gPrefService.getBoolPref("devtools.inspector.enabled");
     this.showItem("context-viewsource", shouldShow);
     this.showItem("context-viewinfo", shouldShow);
+    this.showItem("inspect-separator", showInspect);
+    this.showItem("context-inspect", showInspect);
 
     this.showItem("context-sep-viewsource", shouldShow);
 
     // Set as Desktop background depends on whether an image was clicked on,
     // and only works if we have a shell service.
     var haveSetDesktopBackground = false;
 #ifdef HAVE_SHELL_SERVICE
     // Only enable Set as Desktop Background if we can get the shell service.
@@ -424,16 +428,25 @@ nsContextMenu.prototype = {
       this.setItemAttr("context-media-showcontrols", "disabled", hasError);
       this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
       if (this.onVideo)
         this.setItemAttr("context-video-fullscreen",  "disabled", hasError);
     }
     this.showItem("context-media-sep-commands",  onMedia);
   },
 
+  inspectNode: function CM_inspectNode() {
+    if (InspectorUI.isTreePanelOpen) {
+      InspectorUI.inspectNode(this.target);
+      InspectorUI.stopInspecting();
+    } else {
+      InspectorUI.openInspectorUI(this.target);
+    }
+  },
+
   // Set various context menu attributes based on the state of the world.
   setTarget: function (aNode, aRangeParent, aRangeOffset) {
     const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     if (aNode.namespaceURI == xulNS ||
         aNode.nodeType == Node.DOCUMENT_NODE ||
         this.isTargetAFormControl(aNode)) {
       this.shouldDisplay = false;
       return;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4172,16 +4172,21 @@
           if (aTab.hasAttribute("busy")) {
             aMenuitem.setAttribute("busy", aTab.getAttribute("busy"));
             aMenuitem.removeAttribute("image");
           } else {
             aMenuitem.setAttribute("image", aTab.getAttribute("image"));
             aMenuitem.removeAttribute("busy");
           }
 
+          if (aTab.hasAttribute("pending"))
+            aMenuitem.setAttribute("pending", aTab.getAttribute("pending"));
+          else
+            aMenuitem.removeAttribute("pending");
+
           if (aTab.selected)
             aMenuitem.setAttribute("selected", "true");
           else
             aMenuitem.removeAttribute("selected");
         ]]></body>
       </method>
     </implementation>
 
@@ -4269,21 +4274,27 @@
             else
               this.removeAttribute("mirror");
           }
 
           this.style.minWidth = this.getAttribute("type") == "status" &&
                                 this.getAttribute("previoustype") == "status"
                                   ? getComputedStyle(this).width : "";
 
-          this.setAttribute("label", val);
+          if (val) {
+            this.setAttribute("label", val);
+            this.removeAttribute("inactive");
+          } else {
+            this.setAttribute("inactive", "true");
+          }
+
           return val;
         ]]></setter>
         <getter>
-          return this.getAttribute("label");
+          return this.hasAttribute("inactive") ? "" : this.getAttribute("label");
         </getter>
       </property>
 
       <method name="handleEvent">
         <parameter name="event"/>
         <body><![CDATA[
           if (event.type == "findbaropen" &&
               this.label)
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -487,40 +487,39 @@ GroupItem.prototype = Utils.extend(new I
   // ----------
   // Function: getContentBounds
   // Returns a <Rect> for the groupItem's content area (which doesn't include the title, etc).
   //
   // Parameters:
   //   options - an object with additional parameters, see below
   //
   // Possible options:
-  //   forceStacked - true to force content bounds for stacked mode
+  //   stacked - true to get content bounds for stacked mode
   getContentBounds: function GroupItem_getContentBounds(options) {
-    var box = this.getBounds();
-    var titleHeight = this.$titlebar.height();
+    let box = this.getBounds();
+    let titleHeight = this.$titlebar.height();
     box.top += titleHeight;
     box.height -= titleHeight;
 
     let appTabTrayContainer = iQ(this.$appTabTray[0].parentNode);
-    var appTabTrayWidth = appTabTrayContainer.width();
+    let appTabTrayWidth = appTabTrayContainer.width();
     if (appTabTrayWidth)
       appTabTrayWidth += parseInt(appTabTrayContainer.css(UI.rtl ? "left" : "right"));
 
     box.width -= appTabTrayWidth;
     if (UI.rtl) {
       box.left += appTabTrayWidth;
     }
 
     // Make the computed bounds' "padding" and expand button margin actually be
     // themeable --OR-- compute this from actual bounds. Bug 586546
     box.inset(6, 6);
 
-    // make some room for the expand button if we're stacked
-    let isStacked = (options && options.forceStacked) || this.isStacked();
-    if (isStacked)
+    // make some room for the expand button in stacked mode
+    if (options && options.stacked)
       box.height -= this.$expander.height() + 9; // the button height plus padding
 
     return box;
   },
 
   // ----------
   // Function: setBounds
   // Sets the bounds with the given <Rect>, animating unless "immediately" is false.
@@ -1082,17 +1081,18 @@ GroupItem.prototype = Utils.extend(new I
   // ----------
   // Function: _onChildClose
   // Handles "close" events from the group's children.
   //
   // Parameters:
   //   tabItem - The tabItem that is closed.
   _onChildClose: function GroupItem__onChildClose(tabItem) {
     let count = this._children.length;
-    let dontArrange = this.expanded || !this.shouldStack(count);
+    let dontArrange = tabItem.closedManually &&
+                      (this.expanded || !this.shouldStack(count));
     let dontClose = !tabItem.closedManually && gBrowser._numPinnedTabs > 0;
     this.remove(tabItem, {dontArrange: dontArrange, dontClose: dontClose});
 
     if (dontArrange)
       this._freezeItemSize(count);
 
     if (this._children.length > 0 && this._activeTab)
       UI.setActive(this);
@@ -1207,16 +1207,20 @@ GroupItem.prototype = Utils.extend(new I
     let self = this;
 
     let iconUrl = GroupItems.getAppTabFavIconUrl(xulTab);
     let $appTab = iQ("<img>")
       .addClass("appTabIcon")
       .attr("src", iconUrl)
       .data("xulTab", xulTab)
       .appendTo(this.$appTabTray)
+      .mousedown(function onAppTabMousedown(event) {
+        // stop mousedown propagation to disable group dragging on app tabs
+        event.stopPropagation();
+      })
       .click(function(event) {
         if (!Utils.isLeftClick(event))
           return;
 
         UI.setActive(self, { dontSetActiveTabInGroup: true });
         UI.goToTab(iQ(this).data("xulTab"));
       });
 
@@ -1291,18 +1295,18 @@ GroupItem.prototype = Utils.extend(new I
   // ----------
   // Function: shouldStack
   // Returns true if the groupItem, given "count", should stack (instead of 
   // grid).
   shouldStack: function GroupItem_shouldStack(count) {
     if (count <= 1)
       return false;
 
-    var bb = this.getContentBounds();
-    var options = {
+    let bb = this.getContentBounds();
+    let options = {
       return: 'widthAndColumns',
       count: count || this._children.length,
       hideTitle: false
     };
     let arrObj = Items.arrange(this._children, bb, options);
 
     let shouldStack = arrObj.childWidth < TabItems.minTabWidth * 1.35;
     this._columns = shouldStack ? null : arrObj.columns;
@@ -1398,30 +1402,30 @@ GroupItem.prototype = Utils.extend(new I
       else
         childrenToArrange.push(child);
     });
 
     if (GroupItems._arrangePaused) {
       GroupItems.pushArrange(this, options);
       return false;
     }
-    
+
     let shouldStack = this.shouldStack(childrenToArrange.length + (options.addTab ? 1 : 0));
     let shouldStackArrange = (shouldStack && !this.expanded);
     let box;
 
     // if we should stack and we're not expanded
     if (shouldStackArrange) {
       this.showExpandControl();
-      box = this.getContentBounds({forceStacked: true});
+      box = this.getContentBounds({stacked: true});
       this._stackArrange(childrenToArrange, box, options);
       return false;
     } else {
       this.hideExpandControl();
-      box = this.getContentBounds({forceStacked: false});
+      box = this.getContentBounds();
       // a dropIndex is returned
       return this._gridArrange(childrenToArrange, box, options);
     }
   },
 
   // ----------
   // Function: _stackArrange
   // Arranges the children in a stack.
@@ -2600,17 +2604,17 @@ let GroupItems = {
     // remove tab item from a groupItem
     if (tab._tabViewTabItem.parent)
       tab._tabViewTabItem.parent.remove(tab._tabViewTabItem);
 
     // add tab item to a groupItem
     if (groupItemId) {
       groupItem = GroupItems.groupItem(groupItemId);
       groupItem.add(tab._tabViewTabItem);
-      UI.setReorderTabItemsOnShow(groupItem);
+      groupItem.reorderTabsBasedOnTabItemOrder()
     } else {
       let pageBounds = Items.getPageBounds();
       pageBounds.inset(20, 20);
 
       let box = new Rect(pageBounds);
       box.width = 250;
       box.height = 200;
 
--- a/browser/base/content/tabview/tabitems.js
+++ b/browser/base/content/tabview/tabitems.js
@@ -223,17 +223,17 @@ TabItem.prototype = Utils.extend(new Ite
   // Function: getStorageData
   // Get data to be used for persistent storage of this object.
   getStorageData: function TabItem_getStorageData() {
     let data = {
       url: this.tab.linkedBrowser.currentURI.spec,
       groupID: (this.parent ? this.parent.id : 0),
       title: this.tab.label
     };
-    if (this.parent.getActiveTab() == this)
+    if (this.parent && this.parent.getActiveTab() == this)
       data.active = true;
 
     return data;
   },
 
   // ----------
   // Function: save
   // Store persistent for this object.
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -37,27 +37,28 @@
 DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = browser/base/content/test
 
 DIRS += \
 		tabview \
-		inspector \
 		$(NULL)
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_feed_discovery.html \
 		feed_discovery.html \
 		test_bug395533.html \
 		bug395533-data.txt \
+		test_contextmenu.html \
+		subtst_contextmenu.html \
 		ctxmenu-image.png \
 		video.ogg \
 		test_offlineNotification.html \
 		offlineChild.html \
 		offlineChild.cacheManifest \
 		offlineChild.cacheManifest^headers^ \
 		offlineChild2.html \
 		offlineChild2.cacheManifest \
@@ -70,26 +71,16 @@ include $(topsrcdir)/config/rules.mk
 		bug364677-data.xml^headers^ \
 		test_offline_gzip.html \
 		gZipOfflineChild.html \
 		gZipOfflineChild.html^headers^ \
 		gZipOfflineChild.cacheManifest \
 		gZipOfflineChild.cacheManifest^headers^ \
 		$(NULL)
 
-ifeq (,$(filter gtk2,$(MOZ_WIDGET_TOOLKIT)))
-_TEST_FILES += \
-		test_contextmenu.html \
-		subtst_contextmenu.html \
-		$(NULL)
-
-else
-$(warning test_contextmenu.html disabled because of intermittent failures; bug 513558)
-endif
-
 # The following tests are disabled because they are unreliable:
 #   browser_bug423833.js is bug 428712
 #   browser_sanitize-download-history.js is bug 432425
 #
 # browser_sanitizeDialog_treeView.js is disabled until the tree view is added
 # back to the clear recent history dialog (santize.xul), if it ever is (bug
 # 480169)
 
--- a/browser/base/content/test/browser_sanitizeDialog.js
+++ b/browser/base/content/test/browser_sanitizeDialog.js
@@ -733,25 +733,30 @@ function blankSlate() {
  *       this is a problem only across different connections.
  */
 function waitForAsyncUpdates(aCallback, aScope, aArguments)
 {
   let scope = aScope || this;
   let args = aArguments || [];
   let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
                               .DBConnection;
-  db.createAsyncStatement("BEGIN EXCLUSIVE").executeAsync();
-  db.createAsyncStatement("COMMIT").executeAsync({
+  let begin = db.createAsyncStatement("BEGIN EXCLUSIVE");
+  begin.executeAsync();
+  begin.finalize();
+
+  let commit = db.createAsyncStatement("COMMIT");
+  commit.executeAsync({
     handleResult: function() {},
     handleError: function() {},
     handleCompletion: function(aReason)
     {
       aCallback.apply(scope, args);
     }
   });
+  commit.finalize();
 }
 
 /**
  * Ensures that the given pref is the expected value.
  *
  * @param aPrefName
  *        The pref's sub-branch under the privacy branch
  * @param aExpectedVal
--- a/browser/base/content/test/browser_sanitizeDialog_treeView.js
+++ b/browser/base/content/test/browser_sanitizeDialog_treeView.js
@@ -537,25 +537,30 @@ function blankSlate() {
  *       this is a problem only across different connections.
  */
 function waitForAsyncUpdates(aCallback, aScope, aArguments)
 {
   let scope = aScope || this;
   let args = aArguments || [];
   let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
                               .DBConnection;
-  db.createAsyncStatement("BEGIN EXCLUSIVE").executeAsync();
-  db.createAsyncStatement("COMMIT").executeAsync({
+  let begin = db.createAsyncStatement("BEGIN EXCLUSIVE");
+  begin.executeAsync();
+  begin.finalize();
+
+  let commit = db.createAsyncStatement("COMMIT");
+  commit.executeAsync({
     handleResult: function() {},
     handleError: function() {},
     handleCompletion: function(aReason)
     {
       aCallback.apply(scope, args);
     }
   });
+  commit.finalize();
 }
 
 /**
  * Checks to see if the download with the specified ID exists.
  *
  * @param  aID
  *         The ID of the download to check
  * @return True if the download exists, false otherwise
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -153,16 +153,19 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug663421.js \
                  browser_tabview_bug665502.js \
                  browser_tabview_bug669694.js \
                  browser_tabview_bug673196.js \
                  browser_tabview_bug673729.js \
                  browser_tabview_bug677310.js \
                  browser_tabview_bug679853.js \
                  browser_tabview_bug681599.js \
+                 browser_tabview_bug685476.js \
+                 browser_tabview_bug685692.js \
+                 browser_tabview_bug686654.js \
                  browser_tabview_click_group.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_expander.js \
                  browser_tabview_firstrun_pref.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_multiwindow_search.js \
--- a/browser/base/content/test/tabview/browser_tabview_bug610208.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug610208.js
@@ -13,22 +13,26 @@ function test() {
       test();
       return;
     }
 
     win.close();
     finish();
   }
 
+  let closeTabItemManually = function (tabItem) {
+    EventUtils.synthesizeMouseAtCenter(tabItem.container, {button: 1}, cw);
+  }
+
   let prepareTest = function (testName) {
     let originalBounds = groupItem.getChild(0).getBounds();
 
     let tabItem = groupItem.getChild(1);
     let bounds = tabItem.getBounds();
-    tabItem.close();
+    closeTabItemManually(tabItem);
 
     ok(originalBounds.equals(groupItem.getChild(0).getBounds()), testName + ': tabs did not change their size');
     ok(bounds.equals(groupItem.getChild(1).getBounds()), testName + ': third tab is now on second tab\'s previous position');
     
     return originalBounds;
   }
 
   let cleanUpTest = function (testName, originalBounds, callback) {
@@ -175,17 +179,17 @@ function test() {
     });
 
     let onExpanded = function () {
       let originalBounds = groupItem.getChild(0).getBounds();
       let tabItem = groupItem.getChild(1);
       let bounds = tabItem.getBounds();
 
       while (groupItem.getChildren().length > 2)
-        groupItem.getChild(1).close();
+        closeTabItemManually(groupItem.getChild(1));
 
       ok(originalBounds.equals(groupItem.getChild(0).getBounds()), 'testExpandedMode: tabs did not change their size');
 
       // move the mouse over the expanded layer
       let trayBounds = groupItem.expanded.bounds;
       let target = groupItem.expanded.$tray[0];
       EventUtils.synthesizeMouse(target, trayBounds.right - 5, trayBounds.bottom -5, {type: 'mousemove'}, cw);
 
--- a/browser/base/content/test/tabview/browser_tabview_bug662266.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug662266.js
@@ -22,17 +22,17 @@ function test() {
       groupItem.removeSubscriber("expanded", onExpanded);
 
       ok(groupItem.expanded, "groupItem is expanded");
       let bounds = children[1].getBounds();
 
       // remove two tabs and see if the remaining tabs are re-arranged to fill
       // the resulting gaps
       for (let i = 0; i < 2; i++) {
-        children[1].close();
+        EventUtils.synthesizeMouseAtCenter(children[1].container, {button: 1}, cw);
         ok(bounds.equals(children[1].getBounds()), "tabItems were re-arranged");
       }
 
       waitForFocus(finish);
     });
 
     groupItem.expand();
   });
--- a/browser/base/content/test/tabview/browser_tabview_bug665502.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug665502.js
@@ -16,17 +16,17 @@ function test() {
     let groupItem = cw.GroupItems.groupItems[0];
 
     groupItem.setSize(400, 200, true);
 
     let tabItem = groupItem.getChild(0);
     let bounds = tabItem.getBounds();
 
     is(groupItem.getActiveTab(), tabItem, "the first tab is active");
-    tabItem.close();
+    EventUtils.synthesizeMouseAtCenter(tabItem.container, {button: 1}, cw);
 
     is(groupItem.getChildren().indexOf(tabItem), -1, "tabItem got removed");
     ok(bounds.equals(groupItem.getChild(0).getBounds()), "tabItem bounds didn't change");
 
     finish();
   };
 
   newWindowWithTabView(onShow, onLoad);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug685476.js
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  showTabView(function () {
+    let tab = gBrowser.addTab();
+    gBrowser.pinTab(tab);
+    registerCleanupFunction(function () gBrowser.removeTab(tab));
+
+    let cw = TabView.getContentWindow();
+    let body = cw.document.body;
+    let [appTabIcon] = cw.iQ(".appTabTray .appTabIcon");
+
+    EventUtils.synthesizeMouseAtCenter(appTabIcon, {type: "mousedown"}, cw);
+    EventUtils.synthesizeMouse(body, 500, 100, {type: "mousemove"}, cw);
+    EventUtils.synthesizeMouse(body, 500, 100, {type: "mouseup"}, cw);
+
+    ok(TabView.isVisible(), "tabview is still visible");
+
+    hideTabView(finish);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug685692.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  gBrowser.addTab("http://example.com/");
+  gBrowser.addTab("http://example.com/");
+
+  registerCleanupFunction(function () {
+    while (gBrowser.tabs.length > 1)
+      gBrowser.removeTab(gBrowser.tabs[1]);
+    hideTabView();
+  })
+
+  afterAllTabsLoaded(function() {
+    showTabView(function() {
+      let cw = TabView.getContentWindow();
+
+      let groupItemOne = cw.GroupItems.groupItems[0];
+      is(groupItemOne.getChildren().length, 3, "The number of tabs in group one is 3");
+ 
+      // create a group with a blank tab
+      let groupItemTwo = createGroupItemWithBlankTabs(window, 400, 400, 40, 1);
+      is(groupItemTwo.getChildren().length, 1, "The number of tabs in group two is 1");
+
+      cw.UI.setActive(groupItemOne);
+
+      moveTabToAnotherGroup(groupItemOne.getChild(2).tab, groupItemOne, groupItemTwo, function() {
+        moveTabToAnotherGroup(groupItemOne.getChild(1).tab, groupItemOne, groupItemTwo, function() {
+          cw.UI.setActive(groupItemOne);
+          hideTabView(finish);
+        });
+      });
+    });
+  });
+}
+
+function moveTabToAnotherGroup(targetTab, groupItemOne, groupItemTwo, callback) {
+  hideTabView(function() {
+    let tabCountInGroupItemOne = groupItemOne.getChildren().length;
+    let tabCountInGroupItemTwo = groupItemTwo.getChildren().length;
+
+    TabView.moveTabTo(targetTab, groupItemTwo.id);
+
+    showTabView(function() {
+      is(groupItemOne.getChildren().length, --tabCountInGroupItemOne, "The number of tab items in group one is decreased");
+      is(groupItemTwo.getChildren().length, ++tabCountInGroupItemTwo, "The number of tab items in group two is increased");
+      is(groupItemTwo.getChild(tabCountInGroupItemTwo-1).tab, targetTab, "The last tab is the moved tab");
+
+      callback();
+    });
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug686654.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  newWindowWithTabView(function(win) {
+    let cw = win.TabView.getContentWindow();
+    let groupItem = cw.GroupItems.groupItems[0];
+
+    // set the group to 180 x 100 so it shows two tab items without going into
+    // the stacked mode
+    groupItem.setBounds(new cw.Rect(100, 0, 180, 100));
+
+    hideTabView(function() {
+      groupItem.addSubscriber("childAdded", function onChildAdded(data) {
+        groupItem.removeSubscriber("childAdded", onChildAdded);
+
+        is(groupItem.getChildren().length, 3, "The number of children in group is 3");
+        ok(groupItem.isStacked(), "The group item is stacked after adding a new tab");
+
+        let tabItem = groupItem.getChild(2);
+        groupItem.addSubscriber("childRemoved", function onChildRemoved() {
+          tabItem.removeSubscriber("childRemoved", onChildRemoved);
+
+          is(groupItem.getChildren().length, 2, "The number of children in group is 2");
+          // give a delay for the active item to be set
+          executeSoon(function() {
+            showTabView(function() {
+              ok(!groupItem.isStacked(), "The group item is not stacked after removing a tab");
+              finish();
+            }, win);
+          });
+        });
+        win.BrowserCloseTabOrWindow();
+      });
+      win.gBrowser.loadOneTab("", {inBackground: true});
+    }, win);
+  }, function(win) {
+    registerCleanupFunction(function () {
+      win.close();
+    });
+
+    win.gBrowser.addTab();
+  });
+}
--- a/browser/base/content/test/test_contextmenu.html
+++ b/browser/base/content/test/test_contextmenu.html
@@ -246,125 +246,143 @@ function runTest(testNum) {
                           "context-bookmarkpage", true,
                           "context-savepage",     true,
                           "context-sendpage",     true,
                           "---",                  null,
                           "context-viewbgimage",  false,
                           "context-selectall",    true,
                           "---",                  null,
                           "context-viewsource",   true,
-                          "context-viewinfo",     true]);
+                          "context-viewinfo",     true,
+                          "---",                  null,
+                          "context-inspect",      true]);
         closeContextMenu();
         openContextMenuFor(link); // Invoke context menu for next test.
         break;
 
     case 3:
         // Context menu for text link
         checkContextMenu(["context-openlinkintab", true,
                           "context-openlink",      true,
                           "---",                   null,
                           "context-bookmarklink",  true,
                           "context-savelink",      true,
                           "context-sendlink",      true,
-                          "context-copylink",      true]);
+                          "context-copylink",      true,
+                          "---",                  null,
+                          "context-inspect",      true]);
         closeContextMenu();
         openContextMenuFor(mailto); // Invoke context menu for next test.
         break;
 
     case 4:
         // Context menu for text mailto-link
-        checkContextMenu(["context-copyemail", true]);
+        checkContextMenu(["context-copyemail", true,
+                          "---",                  null,
+                          "context-inspect",      true]);
         closeContextMenu();
         openContextMenuFor(input); // Invoke context menu for next test.
         break;
 
     case 5:
         // Context menu for text input field
         checkContextMenu(["context-undo",        false,
                           "---",                 null,
                           "context-cut",         false,
                           "context-copy",        false,
                           "context-paste",       null, // ignore clipboard state
                           "context-delete",      false,
                           "---",                 null,
-                          "context-selectall",   true,
+                          "context-selectall",   false,
                           "---",                 null,
-                          "spell-check-enabled", true]);
+                          "spell-check-enabled", true,
+                          "---",                  null,
+                          "context-inspect",      true]);
         closeContextMenu();
         openContextMenuFor(img); // Invoke context menu for next test.
         break;
 
     case 6:
         // Context menu for an image
         checkContextMenu(["context-viewimage",            true,
                           "context-copyimage-contents",   true,
                           "context-copyimage",            true,
                           "---",                          null,
                           "context-saveimage",            true,
                           "context-sendimage",            true,
                           "context-setDesktopBackground", true,
-                          "context-viewimageinfo",        true]);
+                          "context-viewimageinfo",        true,
+                          "---",                          null,
+                          "context-inspect",              true]);
         closeContextMenu();
         openContextMenuFor(canvas); // Invoke context menu for next test.
         break;
 
     case 7:
         // Context menu for a canvas
         checkContextMenu(["context-viewimage",    true,
                           "context-saveimage",    true,
                           "context-bookmarkpage", true,
-                          "context-selectall",    true]);
+                          "context-selectall",    true,
+                          "---",                  null,
+                          "context-inspect",      true]);
         closeContextMenu();
         openContextMenuFor(video_ok); // Invoke context menu for next test.
         break;
 
     case 8:
         // Context menu for a video (with a VALID media source)
         checkContextMenu(["context-media-play",         true,
                           "context-media-mute",         true,
                           "context-media-showcontrols", true,
                           "context-video-fullscreen",   true,
                           "---",                        null,
                           "context-viewvideo",          true,
                           "context-copyvideourl",       true,
                           "---",                        null,
                           "context-savevideo",          true,
-                          "context-sendvideo",          true]);
+                          "context-sendvideo",          true,
+                          "---",                        null,
+                          "context-inspect",            true]);
         closeContextMenu();
         openContextMenuFor(video_bad); // Invoke context menu for next test.
         break;
 
     case 9:
         // Context menu for a video (with an INVALID media source)
         checkContextMenu(["context-media-play",         false,
                           "context-media-mute",         false,
                           "context-media-showcontrols", false,
                           "context-video-fullscreen",   false,
                           "---",                        null,
                           "context-viewvideo",          true,
                           "context-copyvideourl",       true,
                           "---",                        null,
                           "context-savevideo",          true,
-                          "context-sendvideo",          true]);
+                          "context-sendvideo",          true,
+                          "---",                        null,
+                          "context-inspect",            true]);
         closeContextMenu();
         openContextMenuFor(video_bad2); // Invoke context menu for next test.
         break;
 
     case 10:
         // Context menu for a video (with an INVALID media source)
         checkContextMenu(["context-media-play",         false,
                           "context-media-mute",         false,
                           "context-media-showcontrols", false,
                           "context-video-fullscreen",   false,
                           "---",                        null,
                           "context-viewvideo",          false,
                           "context-copyvideourl",       false,
                           "---",                        null,
                           "context-savevideo",          false,
-                          "context-sendvideo",          false]);
+                          "context-sendvideo",          false,
+                          "---",                        null,
+                          "context-inspect",            true]);
         closeContextMenu();
         openContextMenuFor(iframe); // Invoke context menu for next test.
         break;
 
     case 11:
         // Context menu for an iframe
         checkContextMenu(["context-back",         false,
                           "context-forward",      false,
@@ -388,17 +406,19 @@ function runTest(testNum) {
                                "context-saveframe",         true,
                                "---",                       null,
                                "context-printframe",        true,
                                "---",                       null,
                                "context-viewframesource",   true,
                                "context-viewframeinfo",     true], null,
                           "---",                  null,
                           "context-viewsource",   true,
-                          "context-viewinfo",     true]);
+                          "context-viewinfo",     true,
+                          "---",                  null,
+                          "context-inspect",      true]);
         closeContextMenu();
         openContextMenuFor(textarea); // Invoke context menu for next test.
         break;
 
     case 12:
         // Context menu for textarea
         checkContextMenu(["*chubbiness",         true, // spelling suggestion
                           "spell-add-to-dictionary", true,
@@ -411,17 +431,19 @@ function runTest(testNum) {
                           "context-delete",      false,
                           "---",                 null,
                           "context-selectall",   true,
                           "---",                 null,
                           "spell-check-enabled", true,
                           "spell-dictionaries",  true,
                               ["spell-check-dictionary-en-US", true,
                                "---",                          null,
-                               "spell-add-dictionaries",       true], null]);
+                               "spell-add-dictionaries",       true], null,
+                          "---",                  null,
+                          "context-inspect",      true]);
 
         closeContextMenu();
         openContextMenuFor(contenteditable); // Invoke context menu for next test.
         break;
 
     case 13:
         // Context menu for contenteditable
         checkContextMenu(["spell-no-suggestions", false,
@@ -435,17 +457,19 @@ function runTest(testNum) {
                           "context-delete",      false,
                           "---",                 null,
                           "context-selectall",   true,
                           "---",                 null,
                           "spell-check-enabled", true,
                           "spell-dictionaries",  true,
                               ["spell-check-dictionary-en-US", true,
                                "---",                          null,
-                               "spell-add-dictionaries",       true], null]);
+                               "spell-add-dictionaries",       true], null,
+                          "---",                 null,
+                          "context-inspect",     true]);
 
         closeContextMenu();
         openContextMenuFor(inputspell); // Invoke context menu for next test.
         break;
 
     case 14:
         // Context menu for spell-check input
         checkContextMenu(["*prodigality",        true, // spelling suggestion
@@ -459,17 +483,19 @@ function runTest(testNum) {
                           "context-delete",      false,
                           "---",                 null,
                           "context-selectall",   true,
                           "---",                 null,
                           "spell-check-enabled", true,
                           "spell-dictionaries",  true,
                               ["spell-check-dictionary-en-US", true,
                                "---",                          null,
-                               "spell-add-dictionaries",       true], null]);
+                               "spell-add-dictionaries",       true], null,
+                          "---",                 null,
+                          "context-inspect",     true]);
 
         closeContextMenu();
         openContextMenuFor(link); // Invoke context menu for next test.
         break;
 
     case 15:
         executeCopyCommand("cmd_copyLink", "http://mozilla.com/");
         closeContextMenu();
@@ -506,17 +532,19 @@ function runTest(testNum) {
                           "context-bookmarkpage", true,
                           "context-savepage",     true,
                           "context-sendpage",     true,
                           "---",                  null,
                           "context-viewbgimage",  false,
                           "context-selectall",    true,
                           "---",                  null,
                           "context-viewsource",   true,
-                          "context-viewinfo",     true]);
+                          "context-viewinfo",     true,
+                          "---",                  null,
+                          "context-inspect",      true]);
 
         invokeItemAction("0");
         closeContextMenu();
         openContextMenuFor(pagemenu, true); // Invoke context menu for next test.
         break;
 
     case 17:
         // Context menu for element with assigned content context menu
@@ -529,17 +557,19 @@ function runTest(testNum) {
                           "context-bookmarkpage", true,
                           "context-savepage",     true,
                           "context-sendpage",     true,
                           "---",                  null,
                           "context-viewbgimage",  false,
                           "context-selectall",    true,
                           "---",                  null,
                           "context-viewsource",   true,
-                          "context-viewinfo",     true]);
+                          "context-viewinfo",     true,
+                          "---",                  null,
+                          "context-inspect",      true]);
 
         subwindow.close();
         SimpleTest.finish();
         return;
 
     /*
      * Other things that would be nice to test:
      *  - selected text
--- a/browser/base/content/test/test_offlineNotification.html
+++ b/browser/base/content/test/test_offlineNotification.html
@@ -33,18 +33,18 @@ window.addEventListener("message", funct
     is(event.data, "success", "Child was successfully cached.");
 
     if (++numFinished == 3) {
       // Clean up after ourself
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
       var pm = Components.classes["@mozilla.org/permissionmanager;1"].
                getService(Components.interfaces.nsIPermissionManager);
 
-      pm.remove(frames.testFrame.document.documentURIObject.host, "offline-app");
-      pm.remove(frames.testFrame3.document.documentURIObject.host, "offline-app");
+      pm.remove(frames.testFrame.location.hostname, "offline-app");
+      pm.remove(frames.testFrame3.location.hostname, "offline-app");
 
       SimpleTest.finish();
     }
   }, false);
 
 var count = 0;
 var expectedEvent = "";
 function eventHandler(evt) {
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -26,17 +26,16 @@ browser.jar:
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 *       content/browser/content.js                    (content/content.js)
 *       content/browser/fullscreen-video.xhtml        (content/fullscreen-video.xhtml)
-*       content/browser/inspector.html                (content/inspector.html)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
 *       content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
 *       content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
 *       content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
 *       content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
 *       content/browser/pageinfo/feeds.xml            (content/pageinfo/feeds.xml)
 *       content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
 *       content/browser/pageinfo/security.js          (content/pageinfo/security.js)
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -777,17 +777,17 @@ BrowserGlue.prototype = {
     var browser = win.gBrowser; // for closure in notification bar callback
     var notifyBox = browser.getNotificationBox();
 
     var browserBundle   = Services.strings.createBundle("chrome://browser/locale/browser.properties");
     var brandBundle     = Services.strings.createBundle("chrome://branding/locale/brand.properties");
 
     var productName        = brandBundle.GetStringFromName("brandFullName");
     var serverOwner        = Services.prefs.getCharPref(PREF_TELEMETRY_SERVER_OWNER);
-    var telemetryText      = browserBundle.formatStringFromName("telemetryText", [productName, serverOwner], 2);
+    var telemetryPrompt    = browserBundle.formatStringFromName("telemetryPrompt", [productName, serverOwner], 2);
 
     var buttons = [
                     {
                       label:     browserBundle.GetStringFromName("telemetryYesButtonLabel"),
                       accessKey: browserBundle.GetStringFromName("telemetryYesButtonAccessKey"),
                       popup:     null,
                       callback:  function(aNotificationBar, aButton) {
                         Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
@@ -799,31 +799,31 @@ BrowserGlue.prototype = {
                       popup:     null,
                       callback:  function(aNotificationBar, aButton) {}
                     }
                   ];
 
     // Set pref to indicate we've shown the notification.
     Services.prefs.setBoolPref(PREF_TELEMETRY_PROMPTED, true);
 
-    var notification = notifyBox.appendNotification(telemetryText, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
-    notification.persistence = 3; // arbitrary number, just so bar sticks around for a bit
+    var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+    notification.persistence = 6; // arbitrary number, just so bar sticks around for a bit
 
     let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     let link = notification.ownerDocument.createElementNS(XULNS, "label");
     link.className = "text-link telemetry-text-link";
     link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel"));
     link.addEventListener('click', function() {
       // Open the learn more url in a new tab
       browser.selectedTab = browser.addTab(Services.prefs.getCharPref(PREF_TELEMETRY_INFOURL));
       // Remove the notification on which the user clicked
       notification.parentNode.removeNotification(notification, true);
       // Add a new notification to that tab, with no "Learn more" link
       var notifyBox = browser.getNotificationBox();
-      notifyBox.appendNotification(telemetryText, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+      notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
     }, false);
     let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
     description.appendChild(link);
   },
 #endif
 
   _showPluginUpdatePage: function BG__showPluginUpdatePage() {
     Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -72,17 +72,17 @@ var gEditItemOverlay = {
   /**
    * Determines the initial data for the item edited or added by this dialog
    */
   _determineInfo: function EIO__determineInfo(aInfo) {
     // hidden rows
     if (aInfo && aInfo.hiddenRows)
       this._hiddenRows = aInfo.hiddenRows;
     else
-      this._hiddenRows.splice(0);
+      this._hiddenRows.splice(0, this._hiddenRows.length);
     // force-read-only
     this._readOnly = aInfo && aInfo.forceReadOnly;
   },
 
   _showHideRows: function EIO__showHideRows() {
     var isBookmark = this._itemId != -1 &&
                      this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK;
     var isQuery = false;
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -72,17 +72,17 @@ var PlacesOrganizer = {
     this._initFolderTree();
 
     var leftPaneSelection = "AllBookmarks"; // default to all-bookmarks
     if (window.arguments && window.arguments[0])
       leftPaneSelection = window.arguments[0];
 
     this.selectLeftPaneQuery(leftPaneSelection);
     // clear the back-stack
-    this._backHistory.splice(0);
+    this._backHistory.splice(0, this._backHistory.length);
     document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);
 
     var view = this._content.treeBoxObject.view;
     if (view.rowCount > 0)
       view.selection.select(0);
 
     this._content.focus();
 
@@ -157,17 +157,17 @@ var PlacesOrganizer = {
   },
 
   set location(aLocation) {
     if (!aLocation || this._location == aLocation)
       return aLocation;
 
     if (this.location) {
       this._backHistory.unshift(this.location);
-      this._forwardHistory.splice(0);
+      this._forwardHistory.splice(0, this._forwardHistory.length);
     }
 
     this._location = aLocation;
     this._places.selectPlaceURI(aLocation);
 
     if (!this._places.hasSelection) {
       // If no node was found for the given place: uri, just load it directly
       this._content.place = aLocation;
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -62,18 +62,23 @@ function waitForClearHistory(aCallback) 
  *       this is a problem only across different connections.
  */
 function waitForAsyncUpdates(aCallback, aScope, aArguments)
 {
   let scope = aScope || this;
   let args = aArguments || [];
   let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
                               .DBConnection;
-  db.createAsyncStatement("BEGIN EXCLUSIVE").executeAsync();
-  db.createAsyncStatement("COMMIT").executeAsync({
+  let begin = db.createAsyncStatement("BEGIN EXCLUSIVE");
+  begin.executeAsync();
+  begin.finalize();
+
+  let commit = db.createAsyncStatement("COMMIT");
+  commit.executeAsync({
     handleResult: function() {},
     handleError: function() {},
     handleCompletion: function(aReason)
     {
       aCallback.apply(scope, args);
     }
   });
+  commit.finalize();
 }
--- a/browser/components/places/tests/unit/test_clearHistory_shutdown.js
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -47,17 +47,16 @@ const URIS = [
 , "http://c.example3.com/"
 ];
 
 const TOPIC_CONNECTION_CLOSED = "places-connection-closed";
 
 let EXPECTED_NOTIFICATIONS = [
   "places-shutdown"
 , "places-will-close-connection"
-, "places-connection-closing"
 , "places-expiration-finished"
 , "places-connection-closed"
 ];
 
 const UNEXPECTED_NOTIFICATIONS = [
   "xpcom-shutdown"
 ];
 
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -239,20 +239,18 @@ Site.prototype = {
   },
 
   /**
    * Gets logins stored for the site.
    *
    * @return An array of the logins stored for the site.
    */
   get logins() {
-    // There could be more logins for different schemes/ports, but this covers
-    // the vast majority of cases.
-    let httpLogins = Services.logins.findLogins({}, this.httpURI.prePath, "", null);
-    let httpsLogins = Services.logins.findLogins({}, this.httpsURI.prePath, "", null);
+    let httpLogins = Services.logins.findLogins({}, this.httpURI.prePath, "", "");
+    let httpsLogins = Services.logins.findLogins({}, this.httpsURI.prePath, "", "");
     return httpLogins.concat(httpsLogins);
   },
 
   get loginSavingEnabled() {
     // Only say that login saving is blocked if it is blocked for both http and https.
     return Services.logins.getLoginSavingEnabled(this.httpURI.prePath) &&
            Services.logins.getLoginSavingEnabled(this.httpsURI.prePath);
   },
--- a/browser/components/preferences/permissionsutils.js
+++ b/browser/components/preferences/permissionsutils.js
@@ -36,17 +36,17 @@
 #
 # ***** END LICENSE BLOCK *****
 
 var gTreeUtils = {
   deleteAll: function (aTree, aView, aItems, aDeletedItems)
   {
     for (var i = 0; i < aItems.length; ++i)
       aDeletedItems.push(aItems[i]);
-    aItems.splice(0);
+    aItems.splice(0, aItems.length);
     var oldCount = aView.rowCount;
     aView._rowCount = 0;
     aTree.treeBoxObject.rowCountChanged(0, -oldCount);
   },
   
   deleteSelectedItems: function (aTree, aView, aItems, aDeletedItems)
   {
     var selection = aTree.view.selection;
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -624,17 +624,17 @@ SessionStoreService.prototype = {
       this._clearRestoringWindows();
       break;
     case "nsPref:changed": // catch pref changes
       switch (aData) {
       // if the user decreases the max number of closed tabs they want
       // preserved update our internal states to match that max
       case "sessionstore.max_tabs_undo":
         for (let ix in this._windows) {
-          this._windows[ix]._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"));
+          this._windows[ix]._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"), this._windows[ix]._closedTabs.length);
         }
         break;
       case "sessionstore.max_windows_undo":
         this._capClosedWindows();
         break;
       case "sessionstore.interval":
         this._interval = this._prefBranch.getIntPref("sessionstore.interval");
         // reset timer and save
@@ -1161,17 +1161,17 @@ SessionStoreService.prototype = {
       this._updateCrashReportURL(aWindow);
     }
   },
 
   onTabShow: function sss_onTabShow(aWindow, aTab) {
     // If the tab hasn't been restored yet, move it into the right _tabsToRestore bucket
     if (aTab.linkedBrowser.__SS_restoreState &&
         aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
-      this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab));
+      this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab), this._tabsToRestore.hidden.length);
       // Just put it at the end of the list of visible tabs;
       this._tabsToRestore.visible.push(aTab);
 
       // let's kick off tab restoration again to ensure this tab gets restored
       // with "restore_hidden_tabs" == false (now that it has become visible)
       this.restoreNextTab();
     }
 
@@ -1179,17 +1179,17 @@ SessionStoreService.prototype = {
     // events due to changing groups in Panorama.
     this.saveStateDelayed(aWindow);
   },
 
   onTabHide: function sss_onTabHide(aWindow, aTab) {
     // If the tab hasn't been restored yet, move it into the right _tabsToRestore bucket
     if (aTab.linkedBrowser.__SS_restoreState &&
         aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
-      this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab));
+      this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab), this._tabsToRestore.visible.length);
       // Just put it at the end of the list of hidden tabs;
       this._tabsToRestore.hidden.push(aTab);
     }
 
     // Default delay of 2 seconds gives enough time to catch multiple TabHide
     // events due to changing groups in Panorama.
     this.saveStateDelayed(aWindow);
   },
@@ -1545,17 +1545,17 @@ SessionStoreService.prototype = {
 
       // If there's a window already open that we can restore into, use that
       if (canUseWindow) {
         // Since we're not overwriting existing tabs, we want to merge _closedTabs,
         // putting existing ones first. Then make sure we're respecting the max pref.
         if (winState._closedTabs && winState._closedTabs.length) {
           let curWinState = this._windows[windowToUse.__SSi];
           curWinState._closedTabs = curWinState._closedTabs.concat(winState._closedTabs);
-          curWinState._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"));
+          curWinState._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"), curWinState._closedTabs.length);
         }
 
         // Restore into that window - pretend it's a followup since we'll already
         // have a focused window.
         //XXXzpao This is going to merge extData together (taking what was in
         //        winState over what is in the window already. The hack we have
         //        in _preWindowToRestoreInto will prevent most (all?) Panorama
         //        weirdness but we will still merge other extData.
@@ -2585,38 +2585,38 @@ SessionStoreService.prototype = {
       winData.tabs = [];
     }
     // don't restore a single blank tab when we've had an external
     // URL passed in for loading at startup (cf. bug 357419)
     else if (root._firstTabs && !aOverwriteTabs && winData.tabs.length == 1 &&
              (!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
       winData.tabs = [];
     }
-    
+
     var tabbrowser = aWindow.gBrowser;
     var openTabCount = aOverwriteTabs ? tabbrowser.browsers.length : -1;
     var newTabCount = winData.tabs.length;
     var tabs = [];
 
     // disable smooth scrolling while adding, moving, removing and selecting tabs
     var tabstrip = tabbrowser.tabContainer.mTabstrip;
     var smoothScroll = tabstrip.smoothScroll;
     tabstrip.smoothScroll = false;
-    
-    // make sure that the selected tab won't be closed in order to
-    // prevent unnecessary flickering
-    if (aOverwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
-      tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
 
     // unpin all tabs to ensure they are not reordered in the next loop
     if (aOverwriteTabs) {
       for (let t = tabbrowser._numPinnedTabs - 1; t > -1; t--)
         tabbrowser.unpinTab(tabbrowser.tabs[t]);
     }
 
+    // make sure that the selected tab won't be closed in order to
+    // prevent unnecessary flickering
+    if (aOverwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
+      tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
+
     for (var t = 0; t < newTabCount; t++) {
       tabs.push(t < openTabCount ?
                 tabbrowser.tabs[t] :
                 tabbrowser.addTab("about:blank", {skipAnimation: true}));
       // when resuming at startup: add additionally requested pages to the end
       if (!aOverwriteTabs && root._firstTabs) {
         tabbrowser.moveTabTo(tabs[t], t);
       }
@@ -2820,16 +2820,17 @@ SessionStoreService.prototype = {
         this.xulAttributes[name] = true;
 
       tabData._tabStillLoading = true;
 
       // keep the data around to prevent dataloss in case
       // a tab gets closed before it's been properly restored
       browser.__SS_data = tabData;
       browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
+      tab.setAttribute("pending", "true");
 
       // Make sure that set/getTabValue will set/read the correct data by
       // wiping out any current value in tab.__SS_extdata.
       delete tab.__SS_extdata;
 
       if (!tabData.entries || tabData.entries.length == 0) {
         // make sure to blank out this tab's content
         // (just purging the tab's history won't be enough)
@@ -3002,41 +3003,35 @@ SessionStoreService.prototype = {
     // Make sure that this tab is removed from _tabsToRestore
     this._removeTabFromTabsToRestore(aTab);
 
     // Increase our internal count.
     this._tabsRestoringCount++;
 
     // Set this tab's state to restoring
     browser.__SS_restoreState = TAB_STATE_RESTORING;
+    aTab.removeAttribute("pending");
 
     // Remove the history listener, since we no longer need it once we start restoring
     this._removeSHistoryListener(aTab);
 
     let activeIndex = (tabData.index || tabData.entries.length) - 1;
     if (activeIndex >= tabData.entries.length)
       activeIndex = tabData.entries.length - 1;
-
     // Reset currentURI.  This creates a new session history entry with a new
     // doc identifier, so we need to explicitly save and restore the old doc
     // identifier (corresponding to the SHEntry at activeIndex) below.
     browser.webNavigation.setCurrentURI(this._getURIFromString("about:blank"));
-
     // Attach data that will be restored on "load" event, after tab is restored.
     if (activeIndex > -1) {
-      let curSHEntry = browser.webNavigation.sessionHistory.
-                       getEntryAtIndex(activeIndex, false).
-                       QueryInterface(Ci.nsISHEntry);
-
       // restore those aspects of the currently active documents which are not
       // preserved in the plain history entries (mainly scroll state and text data)
       browser.__SS_restore_data = tabData.entries[activeIndex] || {};
       browser.__SS_restore_pageStyle = tabData.pageStyle || "";
       browser.__SS_restore_tab = aTab;
-
       didStartLoad = true;
       try {
         // In order to work around certain issues in session history, we need to
         // force session history to update its internal index and call reload
         // instead of gotoIndex. See bug 597315.
         browser.webNavigation.sessionHistory.getEntryAtIndex(activeIndex, true);
         browser.webNavigation.sessionHistory.reloadCurrentEntry();
       }
@@ -4139,17 +4134,17 @@ SessionStoreService.prototype = {
     let normalWindowIndex = 0;
     // try to find a non-popup window in this._closedWindows
     while (normalWindowIndex < this._closedWindows.length &&
            !!this._closedWindows[normalWindowIndex].isPopup)
       normalWindowIndex++;
     if (normalWindowIndex >= maxWindowsUndo)
       spliceTo = normalWindowIndex + 1;
 #endif
-    this._closedWindows.splice(spliceTo);
+    this._closedWindows.splice(spliceTo, this._closedWindows.length);
   },
 
   _clearRestoringWindows: function sss__clearRestoringWindows() {
     for (let i = 0; i < this._closedWindows.length; i++) {
       delete this._closedWindows[i]._shouldRestore;
     }
   },
 
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -147,16 +147,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_624727.js \
 	browser_625257.js \
 	browser_628270.js \
 	browser_635418.js \
 	browser_636279.js \
 	browser_645428.js \
 	browser_659591.js \
 	browser_662812.js \
+	browser_682507.js \
 	$(NULL)
 
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	browser_625016.js \
 	$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_682507.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
+  gBrowser.addTab("about:mozilla");
+
+  ss.setTabState(gBrowser.tabs[1], ss.getTabState(gBrowser.tabs[1]));
+  ok(gBrowser.tabs[1].hasAttribute("pending"), "second tab should have 'pending' attribute");
+
+  gBrowser.selectedTab = gBrowser.tabs[1];
+  ok(!gBrowser.tabs[1].hasAttribute("pending"), "second tab should have not 'pending' attribute");
+
+  gBrowser.removeTab(gBrowser.tabs[1]);
+  Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
+}
--- a/browser/devtools/Makefile.in
+++ b/browser/devtools/Makefile.in
@@ -42,16 +42,17 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/config.mk
 
 DIRS = \
+  highlighter \
   webconsole \
   scratchpad \
   sourceeditor \
   styleinspector \
   shared \
   $(NULL)
 
 ifdef ENABLE_TESTS
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/InsideOutBox.jsm
@@ -0,0 +1,663 @@
+/*
+ * Software License Agreement (BSD License)
+ *
+ * Copyright (c) 2007, Parakey Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use of this software in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 
+ * * Redistributions of source code must retain the above
+ *   copyright notice, this list of conditions and the
+ *   following disclaimer.
+ * 
+ * * Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the
+ *   following disclaimer in the documentation and/or other
+ *   materials provided with the distribution.
+ * 
+ * * Neither the name of Parakey Inc. nor the names of its
+ *   contributors may be used to endorse or promote products
+ *   derived from this software without specific prior
+ *   written permission of Parakey Inc.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Creator:
+ *  Joe Hewitt
+ * Contributors
+ *  John J. Barton (IBM Almaden)
+ *  Jan Odvarko (Mozilla Corp.)
+ *  Max Stepanov (Aptana Inc.)
+ *  Rob Campbell (Mozilla Corp.)
+ *  Hans Hillen (Paciello Group, Mozilla)
+ *  Curtis Bartley (Mozilla Corp.)
+ *  Mike Collins (IBM Almaden)
+ *  Kevin Decker
+ *  Mike Ratcliffe (Comartis AG)
+ *  Hernan Rodríguez Colmeiro
+ *  Austin Andrews
+ *  Christoph Dorn
+ *  Steven Roussey (AppCenter Inc, Network54)
+ */
+
+///////////////////////////////////////////////////////////////////////////
+//// InsideOutBox
+
+/**
+ * InsideOutBoxView is a simple interface definition for views implementing
+ * InsideOutBox controls. All implementors must define these methods.
+ * Implemented in InspectorUI.
+ */
+
+/*
+InsideOutBoxView = {
+  //
+   * Retrieves the parent object for a given child object.
+   * @param aChild
+   *        The child node to retrieve the parent object for.
+   * @returns a DOM node | null
+   //
+  getParentObject: function(aChild) {},
+
+  //
+   * Retrieves a given child node.
+   *
+   * If both index and previousSibling are passed, the implementation
+   * may assume that previousSibling will be the return for getChildObject
+   * with index-1.
+   * @param aParent
+   *        The parent object of the child object to retrieve.
+   * @param aIndex
+   *        The index of the child object to retrieve from aParent.
+   * @param aPreviousSibling
+   *        The previous sibling of the child object to retrieve.
+   *        Supercedes aIndex.
+   * @returns a DOM object | null
+   //
+  getChildObject: function(aParent, aIndex, aPreviousSibling) {},
+
+  //
+   * Renders the HTML representation of the object. Should return an HTML
+   * object which will be displayed to the user.
+   * @param aObject
+   *        The object to create the box object for.
+   * @param aIsRoot
+   *        Is the object the root object. May not be used in all
+   *        implementations.
+   * @returns an object box | null
+   //
+  createObjectBox: function(aObject, aIsRoot) {},
+
+  //
+  * Convenience wrappers for classList API.
+  * @param aObject
+  *        DOM node to query/set.
+  * @param aClassName
+  *        String containing the class name to query/set.
+  //
+  hasClass: function(aObject, aClassName) {},
+  addClass: function(aObject, aClassName) {},
+  removeClass: function(aObject, aClassName) {}
+};
+*/
+
+/**
+ * Creates a tree based on objects provided by a separate "view" object.
+ *
+ * Construction uses an "inside-out" algorithm, meaning that the view's job is
+ * first to tell us the ancestry of each object, and secondarily its
+ * descendants.
+ *
+ * Constructor
+ * @param aView
+ *        The view requiring the InsideOutBox.
+ * @param aBox
+ *        The box object containing the InsideOutBox. Required to add/remove
+ *        children during box manipulation (toggling opened or closed).
+ */
+
+var EXPORTED_SYMBOLS = ["InsideOutBox"];
+
+function InsideOutBox(aView, aBox)
+{
+  this.view = aView;
+  this.box = aBox;
+
+  this.rootObject = null;
+
+  this.rootObjectBox = null;
+  this.selectedObjectBox = null;
+  this.highlightedObjectBox = null;
+  this.scrollIntoView = false;
+};
+
+InsideOutBox.prototype =
+{
+  /**
+   * Highlight the given object node in the tree.
+   * @param aObject
+   *        the object to highlight.
+   * @returns objectBox
+   */
+  highlight: function IOBox_highlight(aObject)
+  {
+    let objectBox = this.createObjectBox(aObject);
+    this.highlightObjectBox(objectBox);
+    return objectBox;
+  },
+
+  /**
+   * Open the given object node in the tree.
+   * @param aObject
+   *        The object node to open.
+   * @returns objectBox
+   */
+  openObject: function IOBox_openObject(aObject)
+  {
+    let object = aObject;
+    let firstChild = this.view.getChildObject(object, 0);
+    if (firstChild)
+      object = firstChild;
+
+    return this.openToObject(object);
+  },
+
+  /**
+   * Open the tree up to the given object node.
+   * @param aObject
+   *        The object in the tree to open to.
+   * @returns objectBox
+   */
+  openToObject: function IOBox_openToObject(aObject)
+  {
+    let objectBox = this.createObjectBox(aObject);
+    this.openObjectBox(objectBox);
+    return objectBox;
+  },
+
+  /**
+   * Select the given object node in the tree.
+   * @param aObject
+   *        The object node to select.
+   * @param makeBoxVisible
+   *        Boolean. Open the object box in the tree?
+   * @param forceOpen
+   *        Force the object box open by expanding all elements in the tree?
+   * @param scrollIntoView
+   *        Scroll the objectBox into view?
+   * @returns nsIDOMNode|null
+   *          A DOM node that represents the "object box", the element that
+   *          holds/displays the given aObject representation in the tree. If
+   *          the object cannot be selected, if it is a stale object, null is
+   *          returned.
+   */
+  select:
+  function IOBox_select(aObject, makeBoxVisible, forceOpen, scrollIntoView)
+  {
+    let objectBox = this.createObjectBox(aObject);
+    if (!objectBox) {
+      return null;
+    }
+    this.selectObjectBox(objectBox, forceOpen);
+    if (makeBoxVisible) {
+      this.openObjectBox(objectBox);
+      if (scrollIntoView) {
+        objectBox.scrollIntoView(true);
+      }
+    }
+    return objectBox;
+  },
+
+  /**
+   * Expands/contracts the given object, depending on its state.
+   * @param aObject
+   *        The tree node to expand/contract.
+   */
+  toggleObject: function IOBox_toggleObject(aObject)
+  {
+    let box = this.createObjectBox(aObject);
+    if (!(this.view.hasClass(box, "open")))
+      this.expandObjectBox(box);
+    else
+      this.contractObjectBox(box);
+  },
+
+  /**
+   * Expand the given object in the tree.
+   * @param aObject
+   *        The tree node to expand.
+   */
+  expandObject: function IOBox_expandObject(aObject)
+  {
+    let objectBox = this.createObjectBox(aObject);
+    if (objectBox)
+      this.expandObjectBox(objectBox);
+  },
+
+  /**
+   * Contract the given object in the tree.
+   * @param aObject
+   *        The tree node to contract.
+   */
+  contractObject: function IOBox_contractObject(aObject)
+  {
+    let objectBox = this.createObjectBox(aObject);
+    if (objectBox)
+      this.contractObjectBox(objectBox);
+  },
+
+  /**
+   * General method for iterating over an object's ancestors and performing
+   * some function.
+   * @param aObject
+   *        The object whose ancestors we wish to iterate over.
+   * @param aCallback
+   *        The function to call with the object as argument.
+   */
+
+  iterateObjectAncestors: function IOBox_iterateObjectAncesors(aObject, aCallback)
+  {
+    let object = aObject;
+    if (!(aCallback && typeof(aCallback) == "function")) {
+      this.view._log("Illegal argument in IOBox.iterateObjectAncestors");
+      return;
+    }
+    while ((object = this.getParentObjectBox(object)))
+      aCallback(object);
+  },
+
+  /**
+   * Highlight the given objectBox in the tree.
+   * @param aObjectBox
+   *        The objectBox to highlight.
+   */
+  highlightObjectBox: function IOBox_highlightObjectBox(aObjectBox)
+  {
+    let self = this;
+
+    if (!aObjectBox)
+      return;
+
+    if (this.highlightedObjectBox) {
+      this.view.removeClass(this.highlightedObjectBox, "highlighted");
+      this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
+        self.view.removeClass(box, "highlightOpen");
+      });
+    }
+
+    this.highlightedObjectBox = aObjectBox;
+
+    this.view.addClass(aObjectBox, "highlighted");
+    this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
+      self.view.addClass(box, "highlightOpen");
+    });
+
+    aObjectBox.scrollIntoView(true);
+  },
+
+  /**
+   * Select the given objectBox in the tree, forcing it to be open if necessary.
+   * @param aObjectBox
+   *        The objectBox to select.
+   * @param forceOpen
+   *        Force the box (subtree) to be open?
+   */
+  selectObjectBox: function IOBox_selectObjectBox(aObjectBox, forceOpen)
+  {
+    let isSelected = this.selectedObjectBox &&
+      aObjectBox == this.selectedObjectBox;
+
+    // aObjectBox is already selected, return
+    if (isSelected)
+      return;
+
+    if (this.selectedObjectBox)
+      this.view.removeClass(this.selectedObjectBox, "selected");
+
+    this.selectedObjectBox = aObjectBox;
+
+    if (aObjectBox) {
+      this.view.addClass(aObjectBox, "selected");
+
+      // Force it open the first time it is selected
+      if (forceOpen)
+        this.expandObjectBox(aObjectBox, true);
+    }
+  },
+
+  /**
+   * Open the ancestors of the given object box.
+   * @param aObjectBox
+   *        The object box to open.
+   */
+  openObjectBox: function IOBox_openObjectBox(aObjectBox)
+  {
+    if (!aObjectBox)
+      return;
+
+    let self = this;
+    this.iterateObjectAncestors(aObjectBox, function (box) {
+      self.view.addClass(box, "open");
+      let labelBox = box.querySelector(".nodeLabelBox");
+      if (labelBox)
+        labelBox.setAttribute("aria-expanded", "true");
+   });
+  },
+
+  /**
+   * Expand the given object box.
+   * @param aObjectBox
+   *        The object box to expand.
+   */
+  expandObjectBox: function IOBox_expandObjectBox(aObjectBox)
+  {
+    let nodeChildBox = this.getChildObjectBox(aObjectBox);
+
+    // no children means nothing to expand, return
+    if (!nodeChildBox)
+      return;
+
+    if (!aObjectBox.populated) {
+      let firstChild = this.view.getChildObject(aObjectBox.repObject, 0);
+      this.populateChildBox(firstChild, nodeChildBox);
+    }
+    let labelBox = aObjectBox.querySelector(".nodeLabelBox");
+    if (labelBox)
+      labelBox.setAttribute("aria-expanded", "true");
+    this.view.addClass(aObjectBox, "open");
+  },
+
+  /**
+   * Contract the given object box.
+   * @param aObjectBox
+   *        The object box to contract.
+   */
+  contractObjectBox: function IOBox_contractObjectBox(aObjectBox)
+  {
+    this.view.removeClass(aObjectBox, "open");
+    let nodeLabel = aObjectBox.querySelector(".nodeLabel");
+    let labelBox = nodeLabel.querySelector(".nodeLabelBox");
+    if (labelBox)
+      labelBox.setAttribute("aria-expanded", "false");
+  },
+
+  /**
+   * Toggle the given object box, forcing open if requested.
+   * @param aObjectBox
+   *        The object box to toggle.
+   * @param forceOpen
+   *        Force the objectbox open?
+   */
+  toggleObjectBox: function IOBox_toggleObjectBox(aObjectBox, forceOpen)
+  {
+    let isOpen = this.view.hasClass(aObjectBox, "open");
+
+    if (!forceOpen && isOpen)
+      this.contractObjectBox(aObjectBox);
+    else if (!isOpen)
+      this.expandObjectBox(aObjectBox);
+  },
+
+  /**
+   * Creates all of the boxes for an object, its ancestors, and siblings.
+   * @param aObject
+   *        The tree node to create the object boxes for.
+   * @returns anObjectBox or null
+   */
+  createObjectBox: function IOBox_createObjectBox(aObject)
+  {
+    if (!aObject)
+      return null;
+
+    this.rootObject = this.getRootNode(aObject) || aObject;
+
+    // Get or create all of the boxes for the target and its ancestors
+    let objectBox = this.createObjectBoxes(aObject, this.rootObject);
+
+    if (!objectBox)
+      return null;
+
+    if (aObject == this.rootObject)
+      return objectBox;
+
+    return this.populateChildBox(aObject, objectBox.parentNode);
+  },
+
+  /**
+   * Creates all of the boxes for an object, its ancestors, and siblings up to
+   * a root.
+   * @param aObject
+   *        The tree's object node to create the object boxes for.
+   * @param aRootObject
+   *        The root object at which to stop building object boxes.
+   * @returns an object box or null
+   */
+  createObjectBoxes: function IOBox_createObjectBoxes(aObject, aRootObject)
+  {
+    if (!aObject)
+      return null;
+
+    if (aObject == aRootObject) {
+      if (!this.rootObjectBox || this.rootObjectBox.repObject != aRootObject) {
+        if (this.rootObjectBox) {
+          try {
+            this.box.removeChild(this.rootObjectBox);
+          } catch (exc) {
+            this.view._log("this.box.removeChild(this.rootObjectBox) FAILS " +
+              this.box + " must not contain " + this.rootObjectBox);
+          }
+        }
+
+        this.highlightedObjectBox = null;
+        this.selectedObjectBox = null;
+        this.rootObjectBox = this.view.createObjectBox(aObject, true);
+        this.box.appendChild(this.rootObjectBox);
+      }
+      return this.rootObjectBox;
+    }
+
+    let parentNode = this.view.getParentObject(aObject);
+    let parentObjectBox = this.createObjectBoxes(parentNode, aRootObject);
+
+    if (!parentObjectBox)
+      return null;
+
+    let parentChildBox = this.getChildObjectBox(parentObjectBox);
+
+    if (!parentChildBox)
+      return null;
+
+    let childObjectBox = this.findChildObjectBox(parentChildBox, aObject);
+
+    return childObjectBox ? childObjectBox
+      : this.populateChildBox(aObject, parentChildBox);
+  },
+
+  /**
+   * Locate the object box for a given object node.
+   * @param aObject
+   *        The given object node in the tree.
+   * @returns an object box or null.
+   */
+  findObjectBox: function IOBox_findObjectBox(aObject)
+  {
+    if (!aObject)
+      return null;
+
+    if (aObject == this.rootObject)
+      return this.rootObjectBox;
+
+    let parentNode = this.view.getParentObject(aObject);
+    let parentObjectBox = this.findObjectBox(parentNode);
+    if (!parentObjectBox)
+      return null;
+
+    let parentChildBox = this.getChildObjectBox(parentObjectBox);
+    if (!parentChildBox)
+      return null;
+
+    return this.findChildObjectBox(parentChildBox, aObject);
+  },
+
+  getAncestorByClass: function IOBox_getAncestorByClass(node, className)
+  {
+    for (let parent = node; parent; parent = parent.parentNode) {
+      if (this.view.hasClass(parent, className))
+        return parent;
+    }
+
+    return null;
+  },
+
+  /**
+   * We want all children of the parent of repObject.
+   */
+  populateChildBox: function IOBox_populateChildBox(repObject, nodeChildBox)
+  {
+    if (!repObject)
+      return null;
+
+    let parentObjectBox = this.getAncestorByClass(nodeChildBox, "nodeBox");
+
+    if (parentObjectBox.populated)
+      return this.findChildObjectBox(nodeChildBox, repObject);
+
+    let lastSiblingBox = this.getChildObjectBox(nodeChildBox);
+    let siblingBox = nodeChildBox.firstChild;
+    let targetBox = null;
+    let view = this.view;
+    let targetSibling = null;
+    let parentNode = view.getParentObject(repObject);
+
+    for (let i = 0; 1; ++i) {
+      targetSibling = view.getChildObject(parentNode, i, targetSibling);
+      if (!targetSibling)
+        break;
+
+      // Check if we need to start appending, or continue to insert before
+      if (lastSiblingBox && lastSiblingBox.repObject == targetSibling)
+        lastSiblingBox = null;
+
+      if (!siblingBox || siblingBox.repObject != targetSibling) {
+        let newBox = view.createObjectBox(targetSibling);
+        if (newBox) {
+          if (lastSiblingBox)
+            nodeChildBox.insertBefore(newBox, lastSiblingBox);
+          else
+            nodeChildBox.appendChild(newBox);
+        }
+
+        siblingBox = newBox;
+      }
+
+      if (targetSibling == repObject)
+        targetBox = siblingBox;
+
+      if (siblingBox && siblingBox.repObject == targetSibling)
+        siblingBox = siblingBox.nextSibling;
+    }
+
+    if (targetBox)
+      parentObjectBox.populated = true;
+
+    return targetBox;
+  },
+
+  /**
+   * Get the parent object box of a given object box.
+   * @params aObjectBox
+   *         The object box of the parent.
+   * @returns an object box or null
+   */
+  getParentObjectBox: function IOBox_getParentObjectBox(aObjectBox)
+  {
+    let parent = aObjectBox.parentNode ? aObjectBox.parentNode.parentNode : null;
+    return parent && parent.repObject ? parent : null;
+  },
+
+  /**
+   * Get the child object box of a given object box.
+   * @param aObjectBox
+   *        The object box whose child you want.
+   * @returns an object box or null
+   */
+  getChildObjectBox: function IOBox_getChildObjectBox(aObjectBox)
+  {
+    return aObjectBox.querySelector(".nodeChildBox");
+  },
+
+  /**
+   * Find the child object box for a given repObject within the subtree
+   * rooted at aParentNodeBox.
+   * @param aParentNodeBox
+   *        root of the subtree in which to search for repObject.
+   * @param aRepObject
+   *        The object you wish to locate in the subtree.
+   * @returns an object box or null
+   */
+  findChildObjectBox: function IOBox_findChildObjectBox(aParentNodeBox, aRepObject)
+  {
+    let childBox = aParentNodeBox.firstChild;
+    while (childBox) {
+      if (childBox.repObject == aRepObject)
+        return childBox;
+      childBox = childBox.nextSibling;
+    }
+    return null; // not found
+  },
+
+  /**
+   * Determines if the given node is an ancestor of the current root.
+   * @param aNode
+   *        The node to look for within the tree.
+   * @returns boolean
+   */
+  isInExistingRoot: function IOBox_isInExistingRoot(aNode)
+  {
+    let parentNode = aNode;
+    while (parentNode && parentNode != this.rootObject) {
+      parentNode = this.view.getParentObject(parentNode);
+    }
+    return parentNode == this.rootObject;
+  },
+
+  /**
+   * Get the root node of a given node.
+   * @param aNode
+   *        The node whose root you wish to retrieve.
+   * @returns a root node or null
+   */
+  getRootNode: function IOBox_getRootNode(aNode)
+  {
+    let node = aNode;
+    let tmpNode;
+    while ((tmpNode = this.view.getParentObject(node)))
+      node = tmpNode;
+
+    return node;
+  },
+
+  /**
+   * Clean up our mess.
+   */
+  destroy: function IOBox_destroy()
+  {
+    delete this.view;
+    delete this.box;
+    delete this.rootObject;
+    delete this.rootObjectBox;
+    delete this.selectedObjectBox;
+    delete this.highlightedObjectBox;
+    delete this.scrollIntoView;
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/Makefile.in
@@ -0,0 +1,58 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Highlighter Build Code.
+#
+# The Initial Developer of the Original Code is The Mozilla Foundation.
+#
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Rob Campbell <rcampbell@mozilla.com>
+#   Mihai Sucan <mihai.sucan@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# 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 *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+EXTRA_JS_MODULES = \
+	inspector.jsm \
+	domplate.jsm \
+	InsideOutBox.jsm \
+	TreePanel.jsm \
+	$(NULL)
+
+ifdef ENABLE_TESTS
+ 	DIRS += test
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/TreePanel.jsm
@@ -0,0 +1,779 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla Tree Panel.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Campbell <rcampbell@mozilla.com> (original author)
+ *   Mihai Șucan <mihai.sucan@gmail.com>
+ *   Julian Viereck <jviereck@mozilla.com>
+ *   Paul Rouget <paul@mozilla.com>
+ *   Kyle Simpson <getify@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+const Cu = Components.utils;
+
+Cu.import("resource:///modules/domplate.jsm");
+Cu.import("resource:///modules/InsideOutBox.jsm");
+Cu.import("resource:///modules/Services.jsm");
+
+var EXPORTED_SYMBOLS = ["TreePanel"];
+
+const INSPECTOR_URI = "chrome://browser/content/inspector.html";
+
+/**
+ * TreePanel
+ * A container for the Inspector's HTML Tree Panel widget constructor function.
+ * @param aContext nsIDOMWindow (xulwindow)
+ * @param aIUI global InspectorUI object
+ */
+function TreePanel(aContext, aIUI) {
+  this._init(aContext, aIUI);
+};
+
+TreePanel.prototype = {
+  showTextNodesWithWhitespace: false,
+  id: "treepanel", // DO NOT LOCALIZE
+  openInDock: true,
+
+  /**
+   * The tree panel container element.
+   * @returns xul:panel|xul:vbox|null
+   *          xul:panel is returned when the tree panel is not docked, or
+   *          xul:vbox when when the tree panel is docked.
+   *          null is returned when no container is available.
+   */
+  get container()
+  {
+    if (this.openInDock) {
+      return this.document.getElementById("inspector-tree-box");
+    }
+
+    return this.document.getElementById("inspector-tree-panel");
+  },
+
+  /**
+   * Main TreePanel boot-strapping method. Initialize the TreePanel with the
+   * originating context and the InspectorUI global.
+   * @param aContext nsIDOMWindow (xulwindow)
+   * @param aIUI global InspectorUI object
+   */
+  _init: function TP__init(aContext, aIUI)
+  {
+    this.IUI = aIUI;
+    this.window = aContext;
+    this.document = this.window.document;
+
+    domplateUtils.setDOM(this.window);
+
+    let isOpen = this.isOpen.bind(this);
+
+    this.registrationObject = {
+      id: this.id,
+      label: this.IUI.strings.GetStringFromName("htmlPanel.label"),
+      tooltiptext: this.IUI.strings.GetStringFromName("htmlPanel.tooltiptext"),
+      accesskey: this.IUI.strings.GetStringFromName("htmlPanel.accesskey"),
+      context: this,
+      get isOpen() isOpen(),
+      show: this.open,
+      hide: this.close,
+      onSelect: this.select,
+      panel: this.openInDock ? null : this.container,
+      unregister: this.destroy,
+    };
+    this.editingEvents = {};
+
+    if (!this.openInDock) {
+      this._boundClose = this.close.bind(this);
+      this.container.addEventListener("popuphiding", this._boundClose, false);
+    }
+
+    // Register the HTML panel with the highlighter
+    this.IUI.registerTool(this.registrationObject);
+  },
+
+  /**
+   * Initialization function for the TreePanel.
+   */
+  initializeIFrame: function TP_initializeIFrame()
+  {
+    if (!this.initializingTreePanel || this.treeLoaded) {
+      return;
+    }
+    this.treeBrowserDocument = this.treeIFrame.contentDocument;
+    this.treePanelDiv = this.treeBrowserDocument.createElement("div");
+    this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
+    this.treePanelDiv.ownerPanel = this;
+    this.ioBox = new InsideOutBox(this, this.treePanelDiv);
+    this.ioBox.createObjectBox(this.IUI.win.document.documentElement);
+    this.treeLoaded = true;
+    this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false);
+    this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false);
+    delete this.initializingTreePanel;
+    Services.obs.notifyObservers(null,
+      this.IUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null);
+    if (this.IUI.selection)
+      this.select(this.IUI.selection, true);
+  },
+
+  /**
+   * Open the inspector's tree panel and initialize it.
+   */
+  open: function TP_open()
+  {
+    if (this.initializingTreePanel && !this.treeLoaded) {
+      return;
+    }
+
+    this.initializingTreePanel = true;
+    if (!this.openInDock)
+      this.container.hidden = false;
+
+    this.treeIFrame = this.document.getElementById("inspector-tree-iframe");
+    if (!this.treeIFrame) {
+      this.treeIFrame = this.document.createElement("iframe");
+      this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
+      this.treeIFrame.flex = 1;
+      this.treeIFrame.setAttribute("type", "content");
+    }
+
+    if (this.openInDock) { // Create vbox
+      this.openDocked();
+      return;
+    }
+
+    let resizerBox = this.document.getElementById("tree-panel-resizer-box");
+    this.treeIFrame = this.container.insertBefore(this.treeIFrame, resizerBox);
+
+    let boundLoadedInitializeTreePanel = function loadedInitializeTreePanel()
+    {
+      this.treeIFrame.removeEventListener("load",
+        boundLoadedInitializeTreePanel, true);
+      this.initializeIFrame();
+    }.bind(this);
+
+    let boundTreePanelShown = function treePanelShown()
+    {
+      this.container.removeEventListener("popupshown",
+        boundTreePanelShown, false);
+
+      this.treeIFrame.addEventListener("load",
+        boundLoadedInitializeTreePanel, true);
+
+      let src = this.treeIFrame.getAttribute("src");
+      if (src != INSPECTOR_URI) {
+        this.treeIFrame.setAttribute("src", INSPECTOR_URI);
+      } else {
+        this.treeIFrame.contentWindow.location.reload();
+      }
+    }.bind(this);
+
+    this.container.addEventListener("popupshown", boundTreePanelShown, false);
+
+    const panelWidthRatio = 7 / 8;
+    const panelHeightRatio = 1 / 5;
+
+    let width = parseInt(this.IUI.win.outerWidth * panelWidthRatio);
+    let height = parseInt(this.IUI.win.outerHeight * panelHeightRatio);
+    let y = Math.min(this.document.defaultView.screen.availHeight - height,
+      this.IUI.win.innerHeight);
+
+    this.container.openPopup(this.browser, "overlap", 0, 0,
+      false, false);
+
+    this.container.moveTo(80, y);
+    this.container.sizeTo(width, height);
+  },
+
+  openDocked: function TP_openDocked()
+  {
+    let treeBox = null;
+    let toolbar = this.IUI.toolbar.nextSibling; // Addons bar, typically
+    let toolbarParent =
+      this.IUI.browser.ownerDocument.getElementById("browser-bottombox");
+    treeBox = this.document.createElement("vbox");
+    treeBox.id = "inspector-tree-box";
+    treeBox.state = "open"; // for the registerTools API.
+    treeBox.minHeight = 10;
+    treeBox.flex = 1;
+    toolbarParent.insertBefore(treeBox, toolbar);
+    this.createResizer();
+    treeBox.appendChild(this.treeIFrame);
+
+    let boundLoadedInitializeTreePanel = function loadedInitializeTreePanel()
+    {
+      this.treeIFrame.removeEventListener("load",
+        boundLoadedInitializeTreePanel, true);
+      this.initializeIFrame();
+    }.bind(this);
+
+    this.treeIFrame.addEventListener("load",
+      boundLoadedInitializeTreePanel, true);
+
+    let src = this.treeIFrame.getAttribute("src");
+    if (src != INSPECTOR_URI) {
+      this.treeIFrame.setAttribute("src", INSPECTOR_URI);
+    } else {
+      this.treeIFrame.contentWindow.location.reload();
+    }
+  },
+
+  /**
+   * Lame resizer on the toolbar.
+   */
+  createResizer: function TP_createResizer()
+  {
+    let resizer = this.document.createElement("resizer");
+    resizer.id = "inspector-horizontal-splitter";
+    resizer.setAttribute("dir", "top");
+    resizer.flex = 1;
+    resizer.setAttribute("element", "inspector-tree-box");
+    resizer.height = 24;
+    this.IUI.toolbar.appendChild(resizer);
+    this.resizer = resizer;
+  },
+
+  /**
+   * Close the TreePanel.
+   */
+  close: function TP_close()
+  {
+    if (this.openInDock) {
+      this.IUI.toolbar.removeChild(this.resizer);
+      let treeBox = this.container;
+      let treeBoxParent = treeBox.parentNode;
+      treeBoxParent.removeChild(treeBox);
+    } else {
+      this.container.hidePopup();
+    }
+
+    if (this.treePanelDiv) {
+      this.treePanelDiv.ownerPanel = null;
+      let parent = this.treePanelDiv.parentNode;
+      parent.removeChild(this.treePanelDiv);
+      delete this.treePanelDiv;
+      delete this.treeBrowserDocument;
+    }
+
+    this.treeLoaded = false;
+  },
+
+  /**
+   * Is the TreePanel open?
+   * @returns boolean
+   */
+  isOpen: function TP_isOpen()
+  {
+    if (this.openInDock)
+      return this.treeLoaded && this.container;
+
+    return this.treeLoaded && this.container.state == "open";
+  },
+
+  /**
+   * Create the ObjectBox for the given object.
+   * @param object nsIDOMNode
+   * @param isRoot boolean - Is this the root object?
+   * @returns InsideOutBox
+   */
+  createObjectBox: function TP_createObjectBox(object, isRoot)
+  {
+    let tag = domplateUtils.getNodeTag(object);
+    if (tag)
+      return tag.replace({object: object}, this.treeBrowserDocument);
+  },
+
+  getParentObject: function TP_getParentObject(node)
+  {
+    let parentNode = node ? node.parentNode : null;
+
+    if (!parentNode) {
+      // Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
+      // and Notation. top level windows have no parentNode
+      if (node && node == this.window.Node.DOCUMENT_NODE) {
+        // document type
+        if (node.defaultView) {
+          let embeddingFrame = node.defaultView.frameElement;
+          if (embeddingFrame)
+            return embeddingFrame.parentNode;
+        }
+      }
+      // a Document object without a parentNode or window
+      return null;  // top level has no parent
+    }
+
+    if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
+      if (parentNode.defaultView) {
+        return parentNode.defaultView.frameElement;
+      }
+      // parent is document element, but no window at defaultView.
+      return null;
+    }
+
+    if (!parentNode.localName)
+      return null;
+
+    return parentNode;
+  },
+
+  getChildObject: function TP_getChildObject(node, index, previousSibling)
+  {
+    if (!node)
+      return null;
+
+    if (node.contentDocument) {
+      // then the node is a frame
+      if (index == 0) {
+        return node.contentDocument.documentElement;  // the node's HTMLElement
+      }
+      return null;
+    }
+
+    if (node instanceof this.window.GetSVGDocument) {
+      let svgDocument = node.getSVGDocument();
+      if (svgDocument) {
+        // then the node is a frame
+        if (index == 0) {
+          return svgDocument.documentElement;  // the node's SVGElement
+        }
+        return null;
+      }
+    }
+
+    let child = null;
+    if (previousSibling)  // then we are walking
+      child = this.getNextSibling(previousSibling);
+    else
+      child = this.getFirstChild(node);
+
+    if (this.showTextNodesWithWhitespace)
+      return child;
+
+    for (; child; child = this.getNextSibling(child)) {
+      if (!domplateUtils.isWhitespaceText(child))
+        return child;
+    }
+
+    return null;  // we have no children worth showing.
+  },
+
+  getFirstChild: function TP_getFirstChild(node)
+  {
+    this.treeWalker = node.ownerDocument.createTreeWalker(node,
+      this.window.NodeFilter.SHOW_ALL, null, false);
+    return this.treeWalker.firstChild();
+  },
+
+  getNextSibling: function TP_getNextSibling(node)
+  {
+    let next = this.treeWalker.nextSibling();
+
+    if (!next)
+      delete this.treeWalker;
+
+    return next;
+  },
+
+  /////////////////////////////////////////////////////////////////////
+  // Event Handling
+
+  /**
+   * Handle click events in the html tree panel.
+   * @param aEvent
+   *        The mouse event.
+   */
+  onTreeClick: function TP_onTreeClick(aEvent)
+  {
+    let node;
+    let target = aEvent.target;
+    let hitTwisty = false;
+    if (this.hasClass(target, "twisty")) {
+      node = this.getRepObject(aEvent.target.nextSibling);
+      hitTwisty = true;
+    } else {
+      node = this.getRepObject(aEvent.target);
+    }
+
+    if (node) {
+      if (hitTwisty) {
+        this.ioBox.toggleObject(node);
+      } else {
+        if (this.IUI.inspecting) {
+          this.IUI.stopInspecting(true);
+        } else {
+          this.IUI.select(node, true, false);
+          this.IUI.highlighter.highlightNode(node);
+        }
+      }
+    }
+  },
+
+  /**
+   * Handle double-click events in the html tree panel.
+   * (double-clicking an attribute value allows it to be edited)
+   * @param aEvent
+   *        The mouse event.
+   */
+  onTreeDblClick: function TP_onTreeDblClick(aEvent)
+  {
+    // if already editing an attribute value, double-clicking elsewhere
+    // in the tree is the same as a click, which dismisses the editor
+    if (this.editingContext)
+      this.closeEditor();
+
+    let target = aEvent.target;
+
+    if (this.hasClass(target, "nodeValue")) {
+      let repObj = this.getRepObject(target);
+      let attrName = target.getAttribute("data-attributeName");
+      let attrVal = target.innerHTML;
+
+      this.editAttributeValue(target, repObj, attrName, attrVal);
+    }
+  },
+
+  /**
+   * Starts the editor for an attribute value.
+   * @param aAttrObj
+   *        The DOM object representing the attribute value in the HTML Tree
+   * @param aRepObj
+   *        The original DOM (target) object being inspected/edited
+   * @param aAttrName
+   *        The name of the attribute being edited
+   * @param aAttrVal
+   *        The current value of the attribute being edited
+   */
+  editAttributeValue:
+  function TP_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal)
+  {
+    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
+    let editorInput =
+      this.treeBrowserDocument.getElementById("attribute-editor-input");
+    let attrDims = aAttrObj.getBoundingClientRect();
+    // figure out actual viewable viewport dimensions (sans scrollbars)
+    let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
+    let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
+
+    // saves the editing context for use when the editor is saved/closed
+    this.editingContext = {
+      attrObj: aAttrObj,
+      repObj: aRepObj,
+      attrName: aAttrName
+    };
+
+    // highlight attribute-value node in tree while editing
+    this.addClass(aAttrObj, "editingAttributeValue");
+
+    // show the editor
+    this.addClass(editor, "editing");
+
+    // offset the editor below the attribute-value node being edited
+    let editorVeritcalOffset = 2;
+
+    // keep the editor comfortably within the bounds of the viewport
+    let editorViewportBoundary = 5;
+
+    // outer editor is sized based on the <input> box inside it
+    editorInput.style.width = Math.min(attrDims.width, viewportWidth -
+                                editorViewportBoundary) + "px";
+    editorInput.style.height = Math.min(attrDims.height, viewportHeight -
+                                editorViewportBoundary) + "px";
+    let editorDims = editor.getBoundingClientRect();
+
+    // calculate position for the editor according to the attribute node
+    let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
+                    // center the editor against the attribute value
+                    ((editorDims.width - attrDims.width) / 2);
+    let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY +
+                    attrDims.height + editorVeritcalOffset;
+
+    // but, make sure the editor stays within the visible viewport
+    editorLeft = Math.max(0, Math.min(
+                                      (this.treeIFrame.contentWindow.scrollX +
+                                          viewportWidth - editorDims.width),
+                                      editorLeft)
+                          );
+    editorTop = Math.max(0, Math.min(
+                                      (this.treeIFrame.contentWindow.scrollY +
+                                          viewportHeight - editorDims.height),
+                                      editorTop)
+                          );
+
+    // position the editor
+    editor.style.left = editorLeft + "px";
+    editor.style.top = editorTop + "px";
+
+    // set and select the text
+    editorInput.value = aAttrVal;
+    editorInput.select();
+
+    // listen for editor specific events
+    this.bindEditorEvent(editor, "click", function(aEvent) {
+      aEvent.stopPropagation();
+    });
+    this.bindEditorEvent(editor, "dblclick", function(aEvent) {
+      aEvent.stopPropagation();
+    });
+    this.bindEditorEvent(editor, "keypress",
+                          this.handleEditorKeypress.bind(this));
+
+    // event notification
+    Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED,
+                                  null);
+  },
+
+  /**
+   * Handle binding an event handler for the editor.
+   * (saves the callback for easier unbinding later)
+   * @param aEditor
+   *        The DOM object for the editor
+   * @param aEventName
+   *        The name of the event to listen for
+   * @param aEventCallback
+   *        The callback to bind to the event (and also to save for later
+   *          unbinding)
+   */
+  bindEditorEvent:
+  function TP_bindEditorEvent(aEditor, aEventName, aEventCallback)
+  {
+    this.editingEvents[aEventName] = aEventCallback;
+    aEditor.addEventListener(aEventName, aEventCallback, false);
+  },
+
+  /**
+   * Handle unbinding an event handler from the editor.
+   * (unbinds the previously bound and saved callback)
+   * @param aEditor
+   *        The DOM object for the editor
+   * @param aEventName
+   *        The name of the event being listened for
+   */
+  unbindEditorEvent: function TP_unbindEditorEvent(aEditor, aEventName)
+  {
+    aEditor.removeEventListener(aEventName, this.editingEvents[aEventName],
+                                  false);
+    this.editingEvents[aEventName] = null;
+  },
+
+  /**
+   * Handle keypress events in the editor.
+   * @param aEvent
+   *        The keyboard event.
+   */
+  handleEditorKeypress: function TP_handleEditorKeypress(aEvent)
+  {
+    if (aEvent.which == this.window.KeyEvent.DOM_VK_RETURN) {
+      this.saveEditor();
+    } else if (aEvent.keyCode == this.window.KeyEvent.DOM_VK_ESCAPE) {
+      this.closeEditor();
+    }
+  },
+
+  /**
+   * Close the editor and cleanup.
+   */
+  closeEditor: function TP_closeEditor()
+  {
+    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
+    let editorInput =
+      this.treeBrowserDocument.getElementById("attribute-editor-input");
+
+    // remove highlight from attribute-value node in tree
+    this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
+
+    // hide editor
+    this.removeClass(editor, "editing");
+
+    // stop listening for editor specific events
+    this.unbindEditorEvent(editor, "click");
+    this.unbindEditorEvent(editor, "dblclick");
+    this.unbindEditorEvent(editor, "keypress");
+
+    // clean up after the editor
+    editorInput.value = "";
+    editorInput.blur();
+    this.editingContext = null;
+    this.editingEvents = {};
+
+    // event notification
+    Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
+                                  null);
+  },
+
+  /**
+   * Commit the edits made in the editor, then close it.
+   */
+  saveEditor: function TP_saveEditor()
+  {
+    let editorInput =
+      this.treeBrowserDocument.getElementById("attribute-editor-input");
+
+    // set the new attribute value on the original target DOM element
+    this.editingContext.repObj.setAttribute(this.editingContext.attrName,
+                                              editorInput.value);
+
+    // update the HTML tree attribute value
+    this.editingContext.attrObj.innerHTML = editorInput.value;
+
+    this.IUI.isDirty = true;
+
+    // event notification
+    Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,
+                                  null);
+
+    this.closeEditor();
+  },
+
+  /**
+   * Simple tree select method.
+   * @param aNode the DOM node in the content document to select.
+   * @param aScroll boolean scroll to the visible node?
+   */
+  select: function TP_select(aNode, aScroll)
+  {
+    if (this.ioBox)
+      this.ioBox.select(aNode, true, true, aScroll);
+  },
+
+  ///////////////////////////////////////////////////////////////////////////
+  //// Utility functions
+
+  /**
+   * Does the given object have a class attribute?
+   * @param aNode
+   *        the DOM node.
+   * @param aClass
+   *        The class string.
+   * @returns boolean
+   */
+  hasClass: function TP_hasClass(aNode, aClass)
+  {
+    if (!(aNode instanceof this.window.Element))
+      return false;
+    return aNode.classList.contains(aClass);
+  },
+
+  /**
+   * Add the class name to the given object.
+   * @param aNode
+   *        the DOM node.
+   * @param aClass
+   *        The class string.
+   */
+  addClass: function TP_addClass(aNode, aClass)
+  {
+    if (aNode instanceof this.window.Element)
+      aNode.classList.add(aClass);
+  },
+
+  /**
+   * Remove the class name from the given object
+   * @param aNode
+   *        the DOM node.
+   * @param aClass
+   *        The class string.
+   */
+  removeClass: function TP_removeClass(aNode, aClass)
+  {
+    if (aNode instanceof this.window.Element)
+      aNode.classList.remove(aClass);
+  },
+
+  /**
+   * Get the "repObject" from the HTML panel's domplate-constructed DOM node.
+   * In this system, a "repObject" is the Object being Represented by the box
+   * object. It is the "real" object that we're building our facade around.
+   *
+   * @param element
+   *        The element in the HTML panel the user clicked.
+   * @returns either a real node or null
+   */
+  getRepObject: function TP_getRepObject(element)
+  {
+    let target = null;
+    for (let child = element; child; child = child.parentNode) {
+      if (this.hasClass(child, "repTarget"))
+        target = child;
+
+      if (child.repObject) {
+        if (!target && this.hasClass(child.repObject, "repIgnore"))
+          break;
+        else
+          return child.repObject;
+      }
+    }
+    return null;
+  },
+
+  /**
+   * Destructor function. Cleanup.
+   */
+  destroy: function TP_destroy()
+  {
+    if (this.isOpen()) {
+      this.close();
+    }
+
+    domplateUtils.setDOM(null);
+
+    delete this.resizer;
+    delete this.treeWalker;
+
+    if (this.treePanelDiv) {
+      this.treePanelDiv.ownerPanel = null;
+      let parent = this.treePanelDiv.parentNode;
+      parent.removeChild(this.treePanelDiv);
+      delete this.treePanelDiv;
+      delete this.treeBrowserDocument;
+    }
+
+    if (this.treeIFrame) {
+      this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false);
+      this.treeIFrame.removeEventListener("click", this.onTreeClick, false);
+      let parent = this.treeIFrame.parentNode;
+      parent.removeChild(this.treeIFrame);
+      delete this.treeIFrame;
+    }
+
+    if (this.ioBox) {
+      this.ioBox.destroy();
+      delete this.ioBox;
+    }
+
+    if (!this.openInDock) {
+      this.container.removeEventListener("popuphiding", this._boundClose, false);
+      delete this._boundClose;
+    }
+  }
+};
+
rename from browser/base/content/domplate.jsm
rename to browser/devtools/highlighter/domplate.jsm
rename from browser/base/content/inspector.html
rename to browser/devtools/highlighter/inspector.html
rename from browser/base/content/inspector.js
rename to browser/devtools/highlighter/inspector.jsm
--- a/browser/base/content/inspector.js
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -1,11 +1,10 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
-#ifdef 0
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -21,34 +20,40 @@
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Rob Campbell <rcampbell@mozilla.com> (original author)
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *   Julian Viereck <jviereck@mozilla.com>
  *   Paul Rouget <paul@mozilla.com>
- *   Kyle Simpson <ksimpson@mozilla.com> 
+ *   Kyle Simpson <ksimpson@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * 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 ***** */
-#endif
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+const Cr = Components.results;
 
-#include insideOutBox.js
+var EXPORTED_SYMBOLS = ["InspectorUI"];
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const INSPECTOR_INVISIBLE_ELEMENTS = {
   "head": true,
   "base": true,
   "basefont": true,
   "isindex": true,
   "link": true,
   "meta": true,
@@ -67,16 +72,19 @@ const INSPECTOR_NOTIFICATIONS = {
 
   // Fires once the Inspector completes the initialization and opens up on
   // screen.
   OPENED: "inspector-opened",
 
   // Fires once the Inspector is closed.
   CLOSED: "inspector-closed",
 
+  // Fires when the Tree Panel is opened and initialized.
+  TREEPANELREADY: "inspector-treepanel-ready",
+
   // Event notifications for the attribute-value editor
   EDITOR_OPENED: "inspector-editor-opened",
   EDITOR_CLOSED: "inspector-editor-closed",
   EDITOR_SAVED: "inspector-editor-saved",
 };
 
 ///////////////////////////////////////////////////////////////////////////
 //// Highlighter
@@ -85,40 +93,42 @@ const INSPECTOR_NOTIFICATIONS = {
  * A highlighter mechanism.
  *
  * The highlighter is built dynamically once the Inspector is invoked:
  * <stack id="highlighter-container">
  *   <vbox id="highlighter-veil-container">...</vbox>
  *   <box id="highlighter-controls>...</vbox>
  * </stack>
  *
- * @param nsIDOMNode aBrowser
- *        The xul:browser object for the content window being highlighted.
+ * @param object aInspector
+ *        The InspectorUI instance.
  */
-function Highlighter(aBrowser)
+function Highlighter(aInspector)
 {
-  this._init(aBrowser);
+  this.IUI = aInspector;
+  this._init();
 }
 
 Highlighter.prototype = {
+  _init: function Highlighter__init()
+  {
+    this.browser = this.IUI.browser;
+    this.chromeDoc = this.IUI.chromeDoc;
 
-  _init: function Highlighter__init(aBrowser)
-  {
-    this.browser = aBrowser;
     let stack = this.browser.parentNode;
     this.win = this.browser.contentWindow;
     this._highlighting = false;
 
-    this.highlighterContainer = document.createElement("stack");
+    this.highlighterContainer = this.chromeDoc.createElement("stack");
     this.highlighterContainer.id = "highlighter-container";
 
-    this.veilContainer = document.createElement("vbox");
+    this.veilContainer = this.chromeDoc.createElement("vbox");
     this.veilContainer.id = "highlighter-veil-container";
 
-    let controlsBox = document.createElement("box");
+    let controlsBox = this.chromeDoc.createElement("box");
     controlsBox.id = "highlighter-controls";
 
     // The veil will make the whole page darker except
     // for the region of the selected box.
     this.buildVeil(this.veilContainer);
 
     // The controlsBox will host the different interactive
     // elements of the highlighter (buttons, toolbars, ...).
@@ -130,17 +140,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"/>
@@ -148,42 +157,41 @@ Highlighter.prototype = {
    *   </hbox>
    *   <box id="highlighter-veil-bottombox" class="highlighter-veil"/>
    * </vbox>
    *
    * @param nsIDOMNode aParent
    */
   buildVeil: function Highlighter_buildVeil(aParent)
   {
-
     // We will need to resize these boxes to surround a node.
     // See highlightRectangle().
 
-    this.veilTopBox = document.createElement("box");
+    this.veilTopBox = this.chromeDoc.createElement("box");
     this.veilTopBox.id = "highlighter-veil-topbox";
     this.veilTopBox.className = "highlighter-veil";
 
-    this.veilMiddleBox = document.createElement("hbox");
+    this.veilMiddleBox = this.chromeDoc.createElement("hbox");
     this.veilMiddleBox.id = "highlighter-veil-middlebox";
 
-    this.veilLeftBox = document.createElement("box");
+    this.veilLeftBox = this.chromeDoc.createElement("box");
     this.veilLeftBox.id = "highlighter-veil-leftbox";
     this.veilLeftBox.className = "highlighter-veil";
 
-    this.veilTransparentBox = document.createElement("box");
+    this.veilTransparentBox = this.chromeDoc.createElement("box");
     this.veilTransparentBox.id = "highlighter-veil-transparentbox";
 
     // We don't need any references to veilRightBox and veilBottomBox.
     // These boxes are automatically resized (flex=1)
 
-    let veilRightBox = document.createElement("box");
+    let veilRightBox = this.chromeDoc.createElement("box");
     veilRightBox.id = "highlighter-veil-rightbox";
     veilRightBox.className = "highlighter-veil";
 
-    let veilBottomBox = document.createElement("box");
+    let veilBottomBox = this.chromeDoc.createElement("box");
     veilBottomBox.id = "highlighter-veil-bottombox";
     veilBottomBox.className = "highlighter-veil";
 
     this.veilMiddleBox.appendChild(this.veilLeftBox);
     this.veilMiddleBox.appendChild(this.veilTransparentBox);
     this.veilMiddleBox.appendChild(veilRightBox);
 
     aParent.appendChild(this.veilTopBox);
@@ -195,26 +203,26 @@ Highlighter.prototype = {
    * Build the controls:
    *
    * <box id="highlighter-close-button"/>
    *
    * @param nsIDOMNode aParent
    */
   buildControls: function Highlighter_buildControls(aParent)
   {
-    let closeButton = document.createElement("box");
+    let closeButton = this.chromeDoc.createElement("box");
     closeButton.id = "highlighter-close-button";
-    closeButton.appendChild(document.createElement("image"));
+    closeButton.appendChild(this.chromeDoc.createElement("image"));
 
-    closeButton.setAttribute("onclick", "InspectorUI.closeInspectorUI(false);");
+    closeButton.addEventListener("click",
+      this.IUI.closeInspectorUI.bind(this.IUI), false);
 
     aParent.appendChild(closeButton);
   },
 
-
   /**
    * Destroy the nodes.
    */
   destroy: function Highlighter_destroy()
   {
     this.browser.removeEventListener("scroll", this, true);
     this.browser.removeEventListener("resize", this, true);
     this._highlightRect = null;
@@ -224,17 +232,18 @@ Highlighter.prototype = {
     this.veilMiddleBox = null;
     this.veilTransparentBox = null;
     this.veilContainer = null;
     this.node = null;
     this.highlighterContainer.parentNode.removeChild(this.highlighterContainer);
     this.highlighterContainer = null;
     this.win = null
     this.browser = null;
-    this.toolbar = null;
+    this.chromeDoc = null;
+    this.IUI = null;
   },
 
   /**
    * Is the highlighter highlighting? Public method for querying the state
    * of the highlighter.
    */
   get isHighlighting() {
     return this._highlighting;
@@ -303,17 +312,17 @@ Highlighter.prototype = {
       }
 
       // We are in an iframe.
       // We take into account the parent iframe position and its
       // offset (borders and padding).
       let frameRect = frameWin.frameElement.getBoundingClientRect();
 
       let [offsetTop, offsetLeft] =
-        InspectorUI.getIframeContentOffset(frameWin.frameElement);
+        this.IUI.getIframeContentOffset(frameWin.frameElement);
 
       rect.top += frameRect.top + offsetTop;
       rect.left += frameRect.left + offsetLeft;
 
       frameWin = frameWin.parent;
     }
 
     this.highlightRectangle(rect);
@@ -428,29 +437,29 @@ Highlighter.prototype = {
     let b = {
       x: a.x + this._highlightRect.width,
       y: a.y + this._highlightRect.height
     };
 
     // Get midpoint of diagonal line.
     let midpoint = this.midPoint(a, b);
 
-    return InspectorUI.elementFromPoint(this.win.document, midpoint.x,
+    return this.IUI.elementFromPoint(this.win.document, midpoint.x,
       midpoint.y);
   },
 
   /**
    * Is this.node highlightable?
    *
    * @returns boolean
    *          True if the node is highlightable or false otherwise.
    */
   isNodeHighlightable: function Highlighter_isNodeHighlightable()
   {
-    if (!this.node || this.node.nodeType != Node.ELEMENT_NODE) {
+    if (!this.node || this.node.nodeType != this.node.ELEMENT_NODE) {
       return false;
     }
     let nodeName = this.node.nodeName.toLowerCase();
     return !INSPECTOR_INVISIBLE_ELEMENTS[nodeName];
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
@@ -510,35 +519,35 @@ Highlighter.prototype = {
    * @param nsIDOMEvent aEvent
    *        The DOM event.
    */
   handleClick: function Highlighter_handleClick(aEvent)
   {
     // Stop inspection when the user clicks on a node.
     if (aEvent.button == 0) {
       let win = aEvent.target.ownerDocument.defaultView;
-      InspectorUI.stopInspecting();
+      this.IUI.stopInspecting();
       win.focus();
     }
     aEvent.preventDefault();
     aEvent.stopPropagation();
   },
 
   /**
    * Handle mousemoves in panel when InspectorUI.inspecting is true.
    *
    * @param nsiDOMEvent aEvent
    *        The MouseEvent triggering the method.
    */
   handleMouseMove: function Highlighter_handleMouseMove(aEvent)
   {
-    let element = InspectorUI.elementFromPoint(aEvent.target.ownerDocument,
+    let element = this.IUI.elementFromPoint(aEvent.target.ownerDocument,
       aEvent.clientX, aEvent.clientY);
     if (element && element != this.node) {
-      InspectorUI.inspectNode(element);
+      this.IUI.inspectNode(element);
     }
   },
 
   /**
    * Handle window resize events.
    */
   handleResize: function Highlighter_handleResize()
   {
@@ -546,35 +555,50 @@ Highlighter.prototype = {
   },
 };
 
 ///////////////////////////////////////////////////////////////////////////
 //// InspectorUI
 
 /**
  * Main controller class for the Inspector.
+ *
+ * @constructor
+ * @param nsIDOMWindow aWindow
+ *        The chrome window for which the Inspector instance is created.
  */
-var InspectorUI = {
+function InspectorUI(aWindow)
+{
+  this.chromeWin = aWindow;
+  this.chromeDoc = aWindow.document;
+  this.tabbrowser = aWindow.gBrowser;
+  this.tools = {};
+  this.toolEvents = {};
+  this.store = new InspectorStore();
+  this.INSPECTOR_NOTIFICATIONS = INSPECTOR_NOTIFICATIONS;
+}
+
+InspectorUI.prototype = {
   browser: null,
-  tools: {},
-  showTextNodesWithWhitespace: false,
+  tools: null,
+  toolEvents: null,
   inspecting: false,
-  treeLoaded: false,
-  prefEnabledName: "devtools.inspector.enabled",
+  treePanelEnabled: true,
   isDirty: false,
+  store: null,
 
   /**
    * Toggle the inspector interface elements on or off.
    *
    * @param aEvent
    *        The event that requested the UI change. Toolbar button or menu.
    */
   toggleInspectorUI: function IUI_toggleInspectorUI(aEvent)
   {
-    if (this.isTreePanelOpen) {
+    if (this.isInspectorOpen) {
       this.closeInspectorUI();
     } else {
       this.openInspectorUI();
     }
   },
 
   /**
    * Toggle the status of the inspector, starting or stopping it. Invoked
@@ -585,258 +609,156 @@ var InspectorUI = {
     if (this.inspecting) {
       this.stopInspecting();
     } else {
       this.startInspecting();
     }
   },
 
   /**
-   * Is the tree panel open?
+   * Is the inspector UI open? Simply check if the toolbar is visible or not.
    *
    * @returns boolean
    */
-  get isTreePanelOpen()
+  get isInspectorOpen()
   {
-    return this.treePanel && this.treePanel.state == "open";
+    return this.toolbar && !this.toolbar.hidden && this.highlighter;
   },
 
   /**
    * Return the default selection element for the inspected document.
    */
   get defaultSelection()
   {
     let doc = this.win.document;
     return doc.documentElement ? doc.documentElement.lastElementChild : null;
   },
 
-  initializeTreePanel: function IUI_initializeTreePanel()
+  /**
+   * Open inspector UI and HTML tree. Add listeners for document scrolling,
+   * resize, tabContainer.TabSelect and others. If a node is provided, then
+   * start inspecting it.
+   *
+   * @param [optional] aNode
+   *        The node to inspect.
+   */
+  openInspectorUI: function IUI_openInspectorUI(aNode)
   {
-    this.treeBrowserDocument = this.treeIFrame.contentDocument;
-    this.treePanelDiv = this.treeBrowserDocument.createElement("div");
-    this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
-    this.treePanelDiv.ownerPanel = this;
-    this.ioBox = new InsideOutBox(this, this.treePanelDiv);
-    this.ioBox.createObjectBox(this.win.document.documentElement);
-    this.treeLoaded = true;
-    this.editingContext = null;
-    this.editingEvents = {};
+    // InspectorUI is already up and running. Lock a node if asked (via context).
+    if (this.isInspectorOpen && aNode) {
+      this.inspectNode(aNode);
+      this.stopInspecting();
+      return;
+    }
+
+    // Observer used to inspect the specified element from content after the
+    // inspector UI has been opened.
+    function inspectObserver(aElement) {
+      Services.obs.removeObserver(boundInspectObserver,
+                                  INSPECTOR_NOTIFICATIONS.OPENED,
+                                  false);
+      this.inspectNode(aElement);
+      this.stopInspecting();
+    };
+
+    var boundInspectObserver = inspectObserver.bind(this, aNode);
+
+    if (aNode) {
+      // Add the observer to inspect the node after initialization finishes.
+      Services.obs.addObserver(boundInspectObserver,
+                               INSPECTOR_NOTIFICATIONS.OPENED,
+                               false);
+    }
+    // Start initialization.
+    this.browser = this.tabbrowser.selectedBrowser;
+    this.win = this.browser.contentWindow;
+    this.winID = this.getWindowID(this.win);
+    this.toolbar = this.chromeDoc.getElementById("inspector-toolbar");
+    this.inspectMenuitem = this.chromeDoc.getElementById("Tools:Inspect");
+    this.inspectToolbutton =
+      this.chromeDoc.getElementById("inspector-inspect-toolbutton");
+
+    this.initTools();
+
+    if (!this.TreePanel && this.treePanelEnabled) {
+      Cu.import("resource:///modules/TreePanel.jsm", this);
+      this.treePanel = new this.TreePanel(this.chromeWin, this);
+    }
+
+    this.toolbar.hidden = false;
+    this.inspectMenuitem.setAttribute("checked", true);
+
+    this.isDirty = false;
+
+    this.progressListener = new InspectorProgressListener(this);
 
     // initialize the highlighter
     this.initializeHighlighter();
   },
 
   /**
-   * Open the inspector's tree panel and initialize it.
+   * Register and initialize any included tools.
    */
-  openTreePanel: function IUI_openTreePanel()
+  initTools: function IUI_initTools()
   {
-    if (!this.treePanel) {
-      this.treePanel = document.getElementById("inspector-tree-panel");
-      this.treePanel.hidden = false;
-    }
-
-    this.treeIFrame = document.getElementById("inspector-tree-iframe");
-    if (!this.treeIFrame) {
-      let resizerBox = document.getElementById("tree-panel-resizer-box");
-      this.treeIFrame = document.createElement("iframe");
-      this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
-      this.treeIFrame.setAttribute("flex", "1");
-      this.treeIFrame.setAttribute("type", "content");
-      this.treeIFrame.setAttribute("onclick", "InspectorUI.onTreeClick(event)");
-      this.treeIFrame.setAttribute("ondblclick", "InspectorUI.onTreeDblClick(event);");
-      this.treeIFrame = this.treePanel.insertBefore(this.treeIFrame, resizerBox);
-    }
-
-    this.treePanel.addEventListener("popupshown", function treePanelShown() {
-      InspectorUI.treePanel.removeEventListener("popupshown",
-        treePanelShown, false);
-
-        InspectorUI.treeIFrame.addEventListener("load",
-          function loadedInitializeTreePanel() {
-            InspectorUI.treeIFrame.removeEventListener("load",
-              loadedInitializeTreePanel, true);
-            InspectorUI.initializeTreePanel();
-          }, true);
-
-      let src = InspectorUI.treeIFrame.getAttribute("src");
-      if (src != "chrome://browser/content/inspector.html") {
-        InspectorUI.treeIFrame.setAttribute("src",
-          "chrome://browser/content/inspector.html");
-      } else {
-        InspectorUI.treeIFrame.contentWindow.location.reload();
-      }
-
-    }, false);
-
-    const panelWidthRatio = 7 / 8;
-    const panelHeightRatio = 1 / 5;
-
-    let width = parseInt(this.win.outerWidth * panelWidthRatio);
-    let height = parseInt(this.win.outerHeight * panelHeightRatio);
-    let y = Math.min(window.screen.availHeight - height, this.win.innerHeight);
-
-    this.treePanel.openPopup(this.browser, "overlap", 0, 0,
-      false, false);
-
-    this.treePanel.moveTo(80, y);
-    this.treePanel.sizeTo(width, height);
-  },
-
-  createObjectBox: function IUI_createObjectBox(object, isRoot)
-  {
-    let tag = this.domplateUtils.getNodeTag(object);
-    if (tag)
-      return tag.replace({object: object}, this.treeBrowserDocument);
-  },
-
-  getParentObject: function IUI_getParentObject(node)
-  {
-    let parentNode = node ? node.parentNode : null;
-
-    if (!parentNode) {
-      // Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
-      // and Notation. top level windows have no parentNode
-      if (node && node == Node.DOCUMENT_NODE) {
-        // document type
-        if (node.defaultView) {
-          let embeddingFrame = node.defaultView.frameElement;
-          if (embeddingFrame)
-            return embeddingFrame.parentNode;
-        }
-      }
-      // a Document object without a parentNode or window
-      return null;  // top level has no parent
+    // Style inspector
+    // XXX bug 689164, remove /false &&/ from below when bug 689160 fixed.
+    if (false && Services.prefs.getBoolPref("devtools.styleinspector.enabled") &&
+        !this.toolRegistered("styleinspector")) {
+      let stylePanel = StyleInspector.createPanel(true);
+      this.registerTool({
+        id: "styleinspector",
+        label: StyleInspector.l10n("style.highlighter.button.label"),
+        tooltiptext: StyleInspector.l10n("style.highlighter.button.tooltip"),
+        accesskey: StyleInspector.l10n("style.highlighter.accesskey"),
+        context: stylePanel,
+        get isOpen() stylePanel.isOpen(),
+        onSelect: stylePanel.selectNode,
+        show: stylePanel.showTool,
+        hide: stylePanel.hideTool,
+        dim: stylePanel.dimTool,
+        panel: stylePanel,
+        unregister: stylePanel.destroy,
+      });
+      this.stylePanel = stylePanel;
     }
-
-    if (parentNode.nodeType == Node.DOCUMENT_NODE) {
-      if (parentNode.defaultView) {
-        return parentNode.defaultView.frameElement;
-      }
-      // parent is document element, but no window at defaultView.
-      return null;
-    }
-    if (!parentNode.localName) {
-      return null;
-    }
-    return parentNode;
-  },
-
-  getChildObject: function IUI_getChildObject(node, index, previousSibling)
-  {
-    if (!node)
-      return null;
-
-    if (node.contentDocument) {
-      // then the node is a frame
-      if (index == 0) {
-        return node.contentDocument.documentElement;  // the node's HTMLElement
-      }
-      return null;
-    }
-
-    if (node instanceof GetSVGDocument) {
-      let svgDocument = node.getSVGDocument();
-      if (svgDocument) {
-        // then the node is a frame
-        if (index == 0) {
-          return svgDocument.documentElement;  // the node's SVGElement
-        }
-        return null;
-      }
-    }
-
-    let child = null;
-    if (previousSibling)  // then we are walking
-      child = this.getNextSibling(previousSibling);
-    else
-      child = this.getFirstChild(node);
-
-    if (this.showTextNodesWithWhitespace)
-      return child;
-
-    for (; child; child = this.getNextSibling(child)) {
-      if (!this.domplateUtils.isWhitespaceText(child))
-        return child;
-    }
-
-    return null;  // we have no children worth showing.
-  },
-
-  getFirstChild: function IUI_getFirstChild(node)
-  {
-    this.treeWalker = node.ownerDocument.createTreeWalker(node,
-      NodeFilter.SHOW_ALL, null, false);
-    return this.treeWalker.firstChild();
-  },
-
-  getNextSibling: function IUI_getNextSibling(node)
-  {
-    let next = this.treeWalker.nextSibling();
-
-    if (!next)
-      delete this.treeWalker;
-
-    return next;
-  },
-
-  /**
-   * Open inspector UI. tree. Add listeners for document scrolling,
-   * resize, tabContainer.TabSelect and others.
-   */
-  openInspectorUI: function IUI_openInspectorUI()
-  {
-    // initialization
-    this.browser = gBrowser.selectedBrowser;
-    this.win = this.browser.contentWindow;
-    this.winID = this.getWindowID(this.win);
-    this.toolbar = document.getElementById("inspector-toolbar");
-
-    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);
   },
 
   /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
-    this.highlighter = new Highlighter(this.browser);
+    this.highlighter = new Highlighter(this);
     this.highlighterReady();
   },
 
   /**
    * Initialize the InspectorStore.
    */
   initializeStore: function IUI_initializeStore()
   {
     // First time opened, add the TabSelect listener
-    if (InspectorStore.isEmpty())
-      gBrowser.tabContainer.addEventListener("TabSelect", this, false);
+    if (this.store.isEmpty()) {
+      this.tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
+    }
 
     // Has this windowID been inspected before?
-    if (InspectorStore.hasID(this.winID)) {
-      let selectedNode = InspectorStore.getValue(this.winID, "selectedNode");
+    if (this.store.hasID(this.winID)) {
+      let selectedNode = this.store.getValue(this.winID, "selectedNode");
       if (selectedNode) {
         this.inspectNode(selectedNode);
       }
+      this.isDirty = this.store.getValue(this.winID, "isDirty");
     } else {
       // First time inspecting, set state to no selection + live inspection.
-      InspectorStore.addStore(this.winID);
-      InspectorStore.setValue(this.winID, "selectedNode", null);
-      InspectorStore.setValue(this.winID, "inspecting", true);
+      this.store.addStore(this.winID);
+      this.store.setValue(this.winID, "selectedNode", null);
+      this.store.setValue(this.winID, "inspecting", true);
+      this.store.setValue(this.winID, "isDirty", this.isDirty);
       this.win.addEventListener("pagehide", this, true);
     }
   },
 
   /**
    * Close inspector UI and associated panels. Unhighlight and stop inspecting.
    * Remove event listeners for document scrolling, resize,
    * tabContainer.TabSelect and others.
@@ -845,181 +767,157 @@ var InspectorUI = {
    *        Tells if you want the store associated to the current tab/window to
    *        be cleared or not. Set this to true to not clear the store, or false
    *        otherwise.
    */
   closeInspectorUI: function IUI_closeInspectorUI(aKeepStore)
   {
     // if currently editing an attribute value, closing the
     // highlighter/HTML panel dismisses the editor
-    if (this.editingContext)
-      this.closeEditor();
+    if (this.treePanel && this.treePanel.editingContext)
+      this.treePanel.closeEditor();
 
     if (this.closing || !this.win || !this.browser) {
       return;
     }
 
     this.closing = true;
     this.toolbar.hidden = true;
 
-    gBrowser.removeProgressListener(InspectorProgressListener);
+    this.progressListener.destroy();
+    delete this.progressListener;
 
     if (!aKeepStore) {
-      InspectorStore.deleteStore(this.winID);
+      this.store.deleteStore(this.winID);
       this.win.removeEventListener("pagehide", this, true);
     } else {
       // Update the store before closing.
       if (this.selection) {
-        InspectorStore.setValue(this.winID, "selectedNode",
+        this.store.setValue(this.winID, "selectedNode",
           this.selection);
       }
-      InspectorStore.setValue(this.winID, "inspecting", this.inspecting);
+      this.store.setValue(this.winID, "inspecting", this.inspecting);
+      this.store.setValue(this.winID, "isDirty", this.isDirty);
     }
 
-    if (InspectorStore.isEmpty()) {
-      gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
+    if (this.store.isEmpty()) {
+      this.tabbrowser.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;
-      parent.removeChild(this.treePanelDiv);
-      delete this.treePanelDiv;
-      delete this.treeBrowserDocument;
-    }
-
-    if (this.treeIFrame) {
-      let parent = this.treeIFrame.parentNode;
-      parent.removeChild(this.treeIFrame);
-      delete this.treeIFrame;
-    }
-    delete this.ioBox;
-
-    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.inspectMenuitem.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);
+    this.closing = false;
+    this.isDirty = false;
 
-      InspectorUI.closing = false;
-      Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null);
-    }, false);
-
-    this.treePanel.hidePopup();
     delete this.treePanel;
+    delete this.stylePanel;
+    delete this.toolbar;
+    delete this.TreePanel;
+    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null);
   },
 
   /**
    * Begin inspecting webpage, attach page event listeners, activate
    * highlighter event listeners.
    */
   startInspecting: function IUI_startInspecting()
   {
     // if currently editing an attribute value, starting
     // "live inspection" mode closes the editor
-    if (this.editingContext)
-      this.closeEditor();
+    if (this.treePanel && this.treePanel.editingContext)
+      this.treePanel.closeEditor();
 
-    document.getElementById("inspector-inspect-toolbutton").checked = true;
+    this.inspectToolbutton.checked = true;
     this.attachPageListeners();
     this.inspecting = true;
+    this.toolsDim(true);
     this.highlighter.veilContainer.removeAttribute("locked");
   },
 
   /**
    * Stop inspecting webpage, detach page listeners, disable highlighter
    * event listeners.
    * @param aPreventScroll
    *        Prevent scroll in the HTML tree?
    */
   stopInspecting: function IUI_stopInspecting(aPreventScroll)
   {
     if (!this.inspecting) {
       return;
     }
 
-    document.getElementById("inspector-inspect-toolbutton").checked = false;
+    this.inspectToolbutton.checked = false;
     this.detachPageListeners();
     this.inspecting = false;
+    this.toolsDim(false);
     if (this.highlighter.node) {
       this.select(this.highlighter.node, true, true, !aPreventScroll);
     } else {
       this.select(null, true, true);
     }
     this.highlighter.veilContainer.setAttribute("locked", true);
   },
 
   /**
    * Select an object in the tree view.
    * @param aNode
    *        node to inspect
    * @param forceUpdate
    *        force an update?
-   * @param aScroll
-   *        force scroll?
+   * @param aScroll boolean
+   *        scroll the tree panel?
    */
   select: function IUI_select(aNode, forceUpdate, aScroll)
   {
     // if currently editing an attribute value, using the
     // highlighter dismisses the editor
-    if (this.editingContext)
-      this.closeEditor();
+    if (this.treePanel && this.treePanel.editingContext)
+      this.treePanel.closeEditor();
 
     if (!aNode)
       aNode = this.defaultSelection;
 
     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(aScroll);
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
 
   highlighterReady: function IUI_highlighterReady()
   {
     // Setup the InspectorStore or restore state
     this.initializeStore();
 
-    if (InspectorStore.getValue(this.winID, "inspecting")) {
+    if (this.store.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
@@ -1028,331 +926,75 @@ var InspectorUI = {
   handleEvent: function IUI_handleEvent(event)
   {
     let winID = null;
     let win = null;
     let inspectorClosed = false;
 
     switch (event.type) {
       case "TabSelect":
-        winID = this.getWindowID(gBrowser.selectedBrowser.contentWindow);
-        if (this.isTreePanelOpen && winID != this.winID) {
+        winID = this.getWindowID(this.tabbrowser.selectedBrowser.contentWindow);
+        if (this.isInspectorOpen && winID != this.winID) {
           this.closeInspectorUI(true);
           inspectorClosed = true;
         }
 
-        if (winID && InspectorStore.hasID(winID)) {
+        if (winID && this.store.hasID(winID)) {
           if (inspectorClosed && this.closing) {
             Services.obs.addObserver(function reopenInspectorForTab() {
               Services.obs.removeObserver(reopenInspectorForTab,
                 INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
-              InspectorUI.openInspectorUI();
-            }, INSPECTOR_NOTIFICATIONS.CLOSED, false);
+              this.openInspectorUI();
+            }.bind(this), INSPECTOR_NOTIFICATIONS.CLOSED, false);
           } else {
             this.openInspectorUI();
-            this.restoreToolState(winID);
           }
         }
 
-        if (InspectorStore.isEmpty()) {
-          gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
+        if (this.store.isEmpty()) {
+          this.tabbrowser.tabContainer.removeEventListener("TabSelect", this,
+                                                         false);
         }
         break;
       case "pagehide":
         win = event.originalTarget.defaultView;
         // Skip iframes/frames.
         if (!win || win.frameElement || win.top != win) {
           break;
         }
 
         win.removeEventListener(event.type, this, true);
 
         winID = this.getWindowID(win);
         if (winID && winID != this.winID) {
-          InspectorStore.deleteStore(winID);
+          this.store.deleteStore(winID);
         }
 
-        if (InspectorStore.isEmpty()) {
-          gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
+        if (this.store.isEmpty()) {
+          this.tabbrowser.tabContainer.removeEventListener("TabSelect", this,
+                                                         false);
         }
         break;
       case "keypress":
         switch (event.keyCode) {
-          case KeyEvent.DOM_VK_RETURN:
-          case KeyEvent.DOM_VK_ESCAPE:
+          case this.chromeWin.KeyEvent.DOM_VK_RETURN:
+          case this.chromeWin.KeyEvent.DOM_VK_ESCAPE:
             if (this.inspecting) {
               this.stopInspecting();
               event.preventDefault();
               event.stopPropagation();
             }
             break;
         }
         break;
     }
   },
 
   /**
-   * Handle click events in the html tree panel.
-   * @param aEvent
-   *        The mouse event.
-   */
-  onTreeClick: function IUI_onTreeClick(aEvent)
-  {
-    // if currently editing an attribute value, clicking outside
-    // the editor dismisses the editor
-    if (this.editingContext) {
-      this.closeEditor();
-
-      // clicking outside the editor ONLY closes the editor
-      // so, cancel the rest of the processing of this event
-      aEvent.preventDefault();
-      return;
-    }
-
-    let node;
-    let target = aEvent.target;
-    let hitTwisty = false;
-    if (this.hasClass(target, "twisty")) {
-      node = this.getRepObject(aEvent.target.nextSibling);
-      hitTwisty = true;
-    } else {
-      node = this.getRepObject(aEvent.target);
-    }
-
-    if (node) {
-      if (hitTwisty) {
-        this.ioBox.toggleObject(node);
-      } else {
-        if (this.inspecting) {
-          this.stopInspecting(true);
-        } else {
-          this.select(node, true, false);
-          this.highlighter.highlightNode(node);
-        }
-      }
-    }
-  },
-
-  /**
-   * Handle double-click events in the html tree panel.
-   * (double-clicking an attribute value allows it to be edited)
-   * @param aEvent
-   *        The mouse event.
-   */
-  onTreeDblClick: function IUI_onTreeDblClick(aEvent)
-  {
-    // if already editing an attribute value, double-clicking elsewhere
-    // in the tree is the same as a click, which dismisses the editor
-    if (this.editingContext)
-      this.closeEditor();
-
-    let target = aEvent.target;
-    if (this.hasClass(target, "nodeValue")) {
-      let repObj = this.getRepObject(target);
-      let attrName = target.getAttribute("data-attributeName");
-      let attrVal = target.innerHTML;
-
-      this.editAttributeValue(target, repObj, attrName, attrVal);
-    }
-  },
-
-  /**
-   * Starts the editor for an attribute value.
-   * @param aAttrObj
-   *        The DOM object representing the attribute value in the HTML Tree
-   * @param aRepObj
-   *        The original DOM (target) object being inspected/edited
-   * @param aAttrName
-   *        The name of the attribute being edited
-   * @param aAttrVal
-   *        The current value of the attribute being edited
-   */
-  editAttributeValue: 
-  function IUI_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal)
-  {
-    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
-    let editorInput = 
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-    let attrDims = aAttrObj.getBoundingClientRect();
-    // figure out actual viewable viewport dimensions (sans scrollbars)
-    let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
-    let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
-
-    // saves the editing context for use when the editor is saved/closed
-    this.editingContext = {
-      attrObj: aAttrObj,
-      repObj: aRepObj,
-      attrName: aAttrName
-    };
-
-    // highlight attribute-value node in tree while editing
-    this.addClass(aAttrObj, "editingAttributeValue");
-
-    // show the editor
-    this.addClass(editor, "editing");
-
-    // offset the editor below the attribute-value node being edited
-    let editorVeritcalOffset = 2;
-
-    // keep the editor comfortably within the bounds of the viewport
-    let editorViewportBoundary = 5;
-
-    // outer editor is sized based on the <input> box inside it
-    editorInput.style.width = Math.min(attrDims.width, viewportWidth - 
-                                editorViewportBoundary) + "px";
-    editorInput.style.height = Math.min(attrDims.height, viewportHeight - 
-                                editorViewportBoundary) + "px";
-    let editorDims = editor.getBoundingClientRect();
-
-    // calculate position for the editor according to the attribute node
-    let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
-                    // center the editor against the attribute value    
-                    ((editorDims.width - attrDims.width) / 2); 
-    let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY + 
-                    attrDims.height + editorVeritcalOffset;
-
-    // but, make sure the editor stays within the visible viewport
-    editorLeft = Math.max(0, Math.min(
-                                      (this.treeIFrame.contentWindow.scrollX + 
-                                          viewportWidth - editorDims.width),
-                                      editorLeft)
-                          );
-    editorTop = Math.max(0, Math.min(
-                                      (this.treeIFrame.contentWindow.scrollY + 
-                                          viewportHeight - editorDims.height),
-                                      editorTop)
-                          );
-
-    // position the editor
-    editor.style.left = editorLeft + "px";
-    editor.style.top = editorTop + "px";
-
-    // set and select the text
-    editorInput.value = aAttrVal;
-    editorInput.select();
-
-    // listen for editor specific events
-    this.bindEditorEvent(editor, "click", function(aEvent) {
-      aEvent.stopPropagation();
-    });
-    this.bindEditorEvent(editor, "dblclick", function(aEvent) {
-      aEvent.stopPropagation();
-    });
-    this.bindEditorEvent(editor, "keypress", 
-                          this.handleEditorKeypress.bind(this));
-
-    // event notification    
-    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, 
-                                  null);
-  },
-
-  /**
-   * Handle binding an event handler for the editor.
-   * (saves the callback for easier unbinding later)   
-   * @param aEditor
-   *        The DOM object for the editor
-   * @param aEventName
-   *        The name of the event to listen for
-   * @param aEventCallback
-   *        The callback to bind to the event (and also to save for later 
-   *          unbinding)
-   */
-  bindEditorEvent: 
-  function IUI_bindEditorEvent(aEditor, aEventName, aEventCallback)
-  {
-    this.editingEvents[aEventName] = aEventCallback;
-    aEditor.addEventListener(aEventName, aEventCallback, false);
-  },
-
-  /**
-   * Handle unbinding an event handler from the editor.
-   * (unbinds the previously bound and saved callback)   
-   * @param aEditor
-   *        The DOM object for the editor
-   * @param aEventName
-   *        The name of the event being listened for
-   */
-  unbindEditorEvent: function IUI_unbindEditorEvent(aEditor, aEventName)
-  {
-    aEditor.removeEventListener(aEventName, this.editingEvents[aEventName], 
-                                  false);
-    this.editingEvents[aEventName] = null;
-  },
-
-  /**
-   * Handle keypress events in the editor.
-   * @param aEvent
-   *        The keyboard event.
-   */
-  handleEditorKeypress: function IUI_handleEditorKeypress(aEvent)
-  {
-    if (aEvent.which == KeyEvent.DOM_VK_RETURN) {
-      this.saveEditor();
-    } else if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) {
-      this.closeEditor();
-    }
-  },
-
-  /**
-   * Close the editor and cleanup.
-   */
-  closeEditor: function IUI_closeEditor()
-  {
-    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
-    let editorInput = 
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-
-    // remove highlight from attribute-value node in tree
-    this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
-
-    // hide editor
-    this.removeClass(editor, "editing");
-
-    // stop listening for editor specific events
-    this.unbindEditorEvent(editor, "click");
-    this.unbindEditorEvent(editor, "dblclick");
-    this.unbindEditorEvent(editor, "keypress");
-
-    // clean up after the editor
-    editorInput.value = "";
-    editorInput.blur();
-    this.editingContext = null;
-    this.editingEvents = {};
-
-    // event notification    
-    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, 
-                                  null);
-  },
-
-  /**
-   * Commit the edits made in the editor, then close it.
-   */
-  saveEditor: function IUI_saveEditor()
-  {
-    let editorInput = 
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-
-    // set the new attribute value on the original target DOM element
-    this.editingContext.repObj.setAttribute(this.editingContext.attrName, 
-                                              editorInput.value);
-
-    // update the HTML tree attribute value
-    this.editingContext.attrObj.innerHTML = editorInput.value;
-
-    this.isDirty = true;
-
-    // event notification    
-    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, 
-                                  null);
-    
-    this.closeEditor();
-  },
-
-  /**
    * Attach event listeners to content window and child windows to enable
    * highlighting and click to stop inspection.
    */
   attachPageListeners: function IUI_attachPageListeners()
   {
     this.browser.addEventListener("keypress", this, true);
     this.highlighter.attachInspectListeners();
   },
@@ -1391,32 +1033,32 @@ var InspectorUI = {
    * @param integer aX
    * @param integer aY
    * @returns Node|null the element node found at the given coordinates.
    */
   elementFromPoint: function IUI_elementFromPoint(aDocument, aX, aY)
   {
     let node = aDocument.elementFromPoint(aX, aY);
     if (node && node.contentDocument) {
-      if (node instanceof HTMLIFrameElement) {
+      if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
         let rect = node.getBoundingClientRect();
 
         // Gap between the iframe and its content window.
         let [offsetTop, offsetLeft] = this.getIframeContentOffset(node);
 
         aX -= rect.left + offsetLeft;
         aY -= rect.top + offsetTop;
 
         if (aX < 0 || aY < 0) {
           // Didn't reach the content document, still over the iframe.
           return node;
         }
       }
-      if (node instanceof HTMLIFrameElement ||
-          node instanceof HTMLFrameElement) {
+      if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
+          node instanceof Ci.nsIDOMHTMLFrameElement) {
         let subnode = this.elementFromPoint(node.contentDocument, aX, aY);
         if (subnode) {
           node = subnode;
         }
       }
     }
     return node;
   },
@@ -1447,57 +1089,16 @@ var InspectorUI = {
 
     let borderTop = parseInt(style.getPropertyValue("border-top-width"));
     let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
 
     return [borderTop + paddingTop, borderLeft + paddingLeft];
   },
 
   /**
-   * Does the given object have a class attribute?
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   * @returns boolean
-   */
-  hasClass: function IUI_hasClass(aNode, aClass)
-  {
-    if (!(aNode instanceof Element))
-      return false;
-    return aNode.classList.contains(aClass);
-  },
-
-  /**
-   * Add the class name to the given object.
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   */
-  addClass: function IUI_addClass(aNode, aClass)
-  {
-    if (aNode instanceof Element)
-      aNode.classList.add(aClass);
-  },
-
-  /**
-   * Remove the class name from the given object
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   */
-  removeClass: function IUI_removeClass(aNode, aClass)
-  {
-    if (aNode instanceof Element)
-      aNode.classList.remove(aClass);
-  },
-
-  /**
    * Retrieve the unique ID of a window object.
    *
    * @param nsIDOMWindow aWindow
    * @returns integer ID
    */
   getWindowID: function IUI_getWindowID(aWindow)
   {
     if (!aWindow) {
@@ -1510,42 +1111,16 @@ var InspectorUI = {
       util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
         getInterface(Ci.nsIDOMWindowUtils);
     } catch (ex) { }
 
     return util.currentInnerWindowID;
   },
 
   /**
-   * Get the "repObject" from the HTML panel's domplate-constructed DOM node.
-   * In this system, a "repObject" is the Object being Represented by the box
-   * object. It is the "real" object that we're building our facade around.
-   *
-   * @param element
-   *        The element in the HTML panel the user clicked.
-   * @returns either a real node or null
-   */
-  getRepObject: function IUI_getRepObject(element)
-  {
-    let target = null;
-    for (let child = element; child; child = child.parentNode) {
-      if (this.hasClass(child, "repTarget"))
-        target = child;
-
-      if (child.repObject) {
-        if (!target && this.hasClass(child.repObject, "repIgnore"))
-          break;
-        else
-          return child.repObject;
-      }
-    }
-    return null;
-  },
-
-  /**
    * @param msg
    *        text message to send to the log
    */
   _log: function LOG(msg)
   {
     Services.console.logStringMessage(msg);
   },
 
@@ -1564,127 +1139,263 @@ 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.
+   *   dim: object.method, called to disable a tool during highlighting.
+   *   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");
-    let btn = document.createElement("toolbarbutton");
-    btn.setAttribute("id", aRegObj.buttonId);
+    this.tools[aRegObj.id] = aRegObj;
+
+    let buttonContainer = this.chromeDoc.getElementById("inspector-tools");
+    let btn = this.chromeDoc.createElement("toolbarbutton");
+    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 a registered tool's callback for a specified event.
+     * @param aWidget xul:widget
+     * @param aEvent a DOM event name
+     * @param aCallback Function the click event handler for the button
+     */
+    let toolEvents = this.toolEvents;
+    function bindToolEvent(aWidget, aEvent, aCallback) {
+      toolEvents[aWidget.id + "_" + aEvent] = 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);
+    this.chromeDoc.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);
+    this.chromeDoc.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 = this.chromeDoc.getElementById(this.getToolbarButtonId(aRegObj.id));
+
+    /**
+     * Unregister the events associated with the registered tool's widget.
+     * @param aWidget XUL:widget (toolbarbutton|panel).
+     * @param aEvent a DOM event.
+     */
+    let toolEvents = this.toolEvents;
+    function unbindToolEvent(aWidget, aEvent) {
+      let toolEvent = aWidget.id + "_" + aEvent;
+      aWidget.removeEventListener(aEvent, toolEvents[toolEvent], false);
+      delete toolEvents[toolEvent]
+    };
+
+    let buttonContainer = this.chromeDoc.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);
+    this.store.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;
+    let openTools = this.store.getValue(aWinID, "openTools");
     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
+   * @param aScroll boolean
+   *        Do you want to scroll the treepanel?
+   */
+  toolsSelect: function IUI_toolsSelect(aScroll)
+  {
+    let selection = this.selection;
+    this.toolsDo(function IUI_toolsOnSelect(aTool) {
+      if (aTool.isOpen) {
+        aTool.onSelect.call(aTool.context, selection, aScroll);
+      }
+    });
+  },
+
+  /**
+   * Dim or undim each tool in the tools collection
+   * @param aState true = dim, false = undim
+   */
+  toolsDim: function IUI_toolsDim(aState)
+  {
+    this.toolsDo(function IUI_toolsOnSelect(aTool) {
+      if (aTool.isOpen && "dim" in aTool) {
+        aTool.dim.call(aTool.context, aState);
+      }
+    });
+  },
+
   /**
    * 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;
+  },
+
+  /**
+   * Destroy the InspectorUI instance. This is called by the InspectorUI API
+   * "user", see BrowserShutdown() in browser.js.
+   */
+  destroy: function IUI_destroy()
+  {
+    if (this.isInspectorOpen) {
+      this.closeInspectorUI();
+    }
+
+    delete this.store;
+    delete this.chromeDoc;
+    delete this.chromeWin;
+    delete this.tabbrowser;
+  },
 };
 
 /**
  * The Inspector store is used for storing data specific to each tab window.
+ * @constructor
  */
-var InspectorStore = {
-  store: {},
+function InspectorStore()
+{
+  this.store = {};
+}
+InspectorStore.prototype = {
   length: 0,
 
   /**
    * Check if there is any data recorded for any tab/window.
    *
    * @returns boolean True if there are no stores for any window/tab, or false
    * otherwise.
    */
@@ -1803,58 +1514,68 @@ var InspectorStore = {
 };
 
 /**
  * The InspectorProgressListener object is an nsIWebProgressListener which
  * handles onStateChange events for the inspected browser. If the user makes
  * changes to the web page and he tries to navigate away, he is prompted to
  * confirm page navigation, such that he's given the chance to prevent the loss
  * of edits.
+ *
+ * @constructor
+ * @param object aInspector
+ *        InspectorUI instance object.
  */
-var InspectorProgressListener = {
+function InspectorProgressListener(aInspector)
+{
+  this.IUI = aInspector;
+  this.IUI.tabbrowser.addProgressListener(this);
+}
+
+InspectorProgressListener.prototype = {
   onStateChange:
   function IPL_onStateChange(aProgress, aRequest, aFlag, aStatus)
   {
     // Remove myself if the Inspector is no longer open.
-    if (!InspectorUI.isTreePanelOpen) {
-      gBrowser.removeProgressListener(InspectorProgressListener);
+    if (!this.IUI.isInspectorOpen) {
+      this.destroy();
       return;
     }
 
     // Skip non-start states.
     if (!(aFlag & Ci.nsIWebProgressListener.STATE_START)) {
       return;
     }
 
     // If the request is about to happen in a new window, we are not concerned
     // about the request.
-    if (aProgress.DOMWindow != InspectorUI.win) {
+    if (aProgress.DOMWindow != this.IUI.win) {
       return;
     }
 
-    if (InspectorUI.isDirty) {
+    if (this.IUI.isDirty) {
       this.showNotification(aRequest);
     } else {
-      InspectorUI.closeInspectorUI();
+      this.IUI.closeInspectorUI();
     }
   },
 
   /**
    * Show an asynchronous notification which asks the user to confirm or cancel
    * the page navigation request.
    *
    * @param nsIRequest aRequest
    *        The request initiated by the user or by the page itself.
    * @returns void
    */
   showNotification: function IPL_showNotification(aRequest)
   {
     aRequest.suspend();
 
-    let notificationBox = gBrowser.getNotificationBox(InspectorUI.browser);
+    let notificationBox = this.IUI.tabbrowser.getNotificationBox(this.IUI.browser);
     let notification = notificationBox.
       getNotificationWithValue("inspector-page-navigation");
 
     if (notification) {
       notificationBox.removeNotification(notification, true);
     }
 
     let cancelRequest = function onCancelRequest() {
@@ -1869,55 +1590,76 @@ var InspectorProgressListener = {
       if (aEvent == "removed") {
         cancelRequest();
       }
     };
 
     let buttons = [
       {
         id: "inspector.confirmNavigationAway.buttonLeave",
-        label: InspectorUI.strings.
+        label: this.IUI.strings.
           GetStringFromName("confirmNavigationAway.buttonLeave"),
-        accessKey: InspectorUI.strings.
+        accessKey: this.IUI.strings.
           GetStringFromName("confirmNavigationAway.buttonLeaveAccesskey"),
         callback: function onButtonLeave() {
           if (aRequest) {
             aRequest.resume();
             aRequest = null;
-            InspectorUI.closeInspectorUI();
+            this.IUI.closeInspectorUI();
           }
-        },
+        }.bind(this),
       },
       {
         id: "inspector.confirmNavigationAway.buttonStay",
-        label: InspectorUI.strings.
+        label: this.IUI.strings.
           GetStringFromName("confirmNavigationAway.buttonStay"),
-        accessKey: InspectorUI.strings.
+        accessKey: this.IUI.strings.
           GetStringFromName("confirmNavigationAway.buttonStayAccesskey"),
         callback: cancelRequest
       },
     ];
 
-    let message = InspectorUI.strings.
+    let message = this.IUI.strings.
       GetStringFromName("confirmNavigationAway.message");
 
     notification = notificationBox.appendNotification(message,
       "inspector-page-navigation", "chrome://browser/skin/Info.png",
       notificationBox.PRIORITY_WARNING_HIGH, buttons, eventCallback);
 
     // Make sure this not a transient notification, to avoid the automatic
     // transient notification removal.
     notification.persistence = -1;
   },
+
+  /**
+   * Destroy the progress listener instance.
+   */
+  destroy: function IPL_destroy()
+  {
+    this.IUI.tabbrowser.removeProgressListener(this);
+
+    let notificationBox = this.IUI.tabbrowser.getNotificationBox(this.IUI.browser);
+    let notification = notificationBox.
+      getNotificationWithValue("inspector-page-navigation");
+
+    if (notification) {
+      notificationBox.removeNotification(notification, true);
+    }
+
+    delete this.IUI;
+  },
 };
 
 /////////////////////////////////////////////////////////////////////////
 //// Initializers
 
-XPCOMUtils.defineLazyGetter(InspectorUI, "inspectCmd", function () {
-  return document.getElementById("Tools:Inspect");
+XPCOMUtils.defineLazyGetter(InspectorUI.prototype, "strings",
+  function () {
+    return Services.strings.
+           createBundle("chrome://browser/locale/inspector.properties");
+  });
+
+XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () {
+  var obj = {};
+  Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj);
+  return obj.StyleInspector;
 });
 
-XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
-  return Services.strings.
-         createBundle("chrome://browser/locale/inspector.properties");
-});
-
rename from browser/base/content/test/inspector/Makefile.in
rename to browser/devtools/highlighter/test/Makefile.in
--- a/browser/base/content/test/inspector/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -29,21 +29,21 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # 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 *****
 
-DEPTH		= ../../../../..
+DEPTH     = ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
-relativesrcdir  = browser/base/content/test/inspector
+relativesrcdir  = browser/devtools/highlighter/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
 		browser_inspector_initialization.js \
 		browser_inspector_treeSelection.js \
 		browser_inspector_highlighter.js \
@@ -56,10 +56,13 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_treePanel_result.html \
 		browser_inspector_registertools.js \
 		browser_inspector_bug_665880.js \
 		browser_inspector_bug_674871.js \
 		browser_inspector_editor.js \
 		browser_inspector_bug_566084_location_changed.js \
 		$(NULL)
 
+# Disabled due to constant failures
+# 		browser_inspector_treePanel_click.js \
+
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
rename from browser/base/content/test/inspector/browser_inspector_bug_566084_location_changed.js
rename to browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js
--- a/browser/base/content/test/inspector/browser_inspector_bug_566084_location_changed.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js
@@ -1,28 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let notificationBox = null;
 
 function startLocationTests() {
   ok(window.InspectorUI, "InspectorUI variable exists");
-  Services.obs.addObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null);
+  Services.obs.addObserver(runInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, null);
   InspectorUI.toggleInspectorUI();
 }
 
 function runInspectorTests() {
-  Services.obs.removeObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null);
+  Services.obs.removeObserver(runInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, null);
 
   let para = content.document.querySelector("p");
   ok(para, "found the paragraph element");
   is(para.textContent, "init", "paragraph content is correct");
 
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
+  ok(InspectorUI.isInspectorOpen, "Inspector is open");
 
   InspectorUI.isDirty = true;
 
   notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
   notificationBox.addEventListener("AlertActive", alertActive1, false);
 
   gBrowser.selectedBrowser.addEventListener("load", onPageLoad, true);
 
@@ -51,29 +51,29 @@ function onPageLoad() {
   isnot(content.location.href.indexOf("test2"), -1,
         "page navigated to the correct location");
 
   let para = content.document.querySelector("p");
   ok(para, "found the paragraph element, third time");
   is(para.textContent, "test2", "paragraph content is correct");
 
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(!InspectorUI.isTreePanelOpen, "Inspector Panel is not open");
+  ok(!InspectorUI.isInspectorOpen, "Inspector Panel is not open");
 
   testEnd();
 }
 
 function locationTest2() {
   // Location did not change.
   let para = content.document.querySelector("p");
   ok(para, "found the paragraph element, second time");
   is(para.textContent, "init", "paragraph content is correct");
 
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
+  ok(InspectorUI.isInspectorOpen, "Inspector Panel is open");
 
   notificationBox.addEventListener("AlertActive", alertActive2, false);
 
   content.location = "data:text/html,<div>location change test 2 for " +
     "inspector</div><p>test2</p>";
 }
 
 function alertActive2() {
@@ -97,17 +97,16 @@ function alertActive2() {
   // Accept page navigation.
   executeSoon(function(){
     buttonLeave.doCommand();
   });
 }
 
 function testEnd() {
   notificationBox = null;
-  InspectorUI.isDirty = false;
   gBrowser.removeCurrentTab();
   executeSoon(finish);
 }
 
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
rename from browser/base/content/test/inspector/browser_inspector_bug_665880.js
rename to browser/devtools/highlighter/test/browser_inspector_bug_665880.js
--- a/browser/base/content/test/inspector/browser_inspector_bug_665880.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_665880.js
@@ -18,45 +18,45 @@ function test()
 
   content.location = "data:text/html,<object style='padding: 100px'><p>foobar</p></object>";
 
   function setupObjectInspectionTest()
   {
     objectNode = doc.querySelector("object");
     ok(objectNode, "we have the object node");
     Services.obs.addObserver(runObjectInspectionTest,
-      INSPECTOR_NOTIFICATIONS.OPENED, false);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
     InspectorUI.toggleInspectorUI();
   }
 
   function runObjectInspectionTest()
   {
     Services.obs.removeObserver(runObjectInspectionTest,
-      INSPECTOR_NOTIFICATIONS.OPENED);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
 
     executeSoon(function() {
       Services.obs.addObserver(performTestComparison,
-        INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
       InspectorUI.inspectNode(objectNode);
     });
   }
 
   function performTestComparison()
   {
     Services.obs.removeObserver(performTestComparison,
-      INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
 
     is(InspectorUI.selection, objectNode, "selection matches node");
 
     Services.obs.addObserver(finishUp,
-      INSPECTOR_NOTIFICATIONS.CLOSED, false);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
     InspectorUI.closeInspectorUI();
   }
 
 
   function finishUp() {
-    Services.obs.removeObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED);
+    Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
     doc = objectNode = null;
     gBrowser.removeCurrentTab();
     finish();
   }
 }
rename from browser/base/content/test/inspector/browser_inspector_bug_674871.js
rename to browser/devtools/highlighter/test/browser_inspector_bug_674871.js
--- a/browser/base/content/test/inspector/browser_inspector_bug_674871.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_674871.js
@@ -42,67 +42,67 @@ function test()
 
   function setupTest()
   {
     iframeNode = doc.querySelector("iframe");
     iframeBodyNode = iframeNode.contentDocument.querySelector("body");
     ok(iframeNode, "we have the iframe node");
     ok(iframeBodyNode, "we have the body node");
     Services.obs.addObserver(runTests,
-      INSPECTOR_NOTIFICATIONS.OPENED, false);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
     InspectorUI.toggleInspectorUI();
   }
 
   function runTests()
   {
     Services.obs.removeObserver(runTests,
-      INSPECTOR_NOTIFICATIONS.OPENED);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
 
     executeSoon(function() {
       Services.obs.addObserver(isTheIframeSelected,
-        INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
       moveMouseOver(iframeNode, 1, 1);
     });
   }
 
   function isTheIframeSelected()
   {
     Services.obs.removeObserver(isTheIframeSelected,
-      INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
 
     is(InspectorUI.selection, iframeNode, "selection matches node");
     iframeNode.style.marginBottom = doc.defaultView.innerHeight + "px";
     doc.defaultView.scrollBy(0, 40);
 
     executeSoon(function() {
       Services.obs.addObserver(isTheIframeContentSelected,
-        INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
       moveMouseOver(iframeNode, 40, 40);
     });
   }
 
   function isTheIframeContentSelected()
   {
     Services.obs.removeObserver(isTheIframeContentSelected,
-      INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
 
     is(InspectorUI.selection, iframeBodyNode, "selection matches node");
     // 184 == 200 + 11(border) + 13(padding) - 40(scroll)
     is(InspectorUI.highlighter._highlightRect.height, 184,
       "highlighter height");
 
     Services.obs.addObserver(finishUp,
-      INSPECTOR_NOTIFICATIONS.CLOSED, false);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
     InspectorUI.closeInspectorUI();
   }
 
   function finishUp() {
-    Services.obs.removeObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED);
+    Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
     doc = iframeNode = iframeBodyNode = null;
     gBrowser.removeCurrentTab();
     finish();
   }
 
 
   function moveMouseOver(aElement, x, y)
   {
rename from browser/base/content/test/inspector/browser_inspector_editor.js
rename to browser/devtools/highlighter/test/browser_inspector_editor.js
--- a/browser/base/content/test/inspector/browser_inspector_editor.js
+++ b/browser/devtools/highlighter/test/browser_inspector_editor.js
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* ***** BEGIN LICENSE BLOCK *****
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
- * 
+ *
  * Contributor(s):
  *   Rob Campbell <rcampbell@mozilla.com>
  *   Mihai Sucan <mihai.sucan@gmail.com>
- *   Kyle Simpson <ksimpson@mozilla.com> 
+ *   Kyle Simpson <ksimpson@mozilla.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 let doc;
 let div;
 let editorTestSteps;
 
 function doNextStep() {
@@ -20,61 +20,69 @@ function doNextStep() {
 }
 
 function setupEditorTests()
 {
   div = doc.createElement("div");
   div.setAttribute("id", "foobar");
   div.setAttribute("class", "barbaz");
   doc.body.appendChild(div);
-  
-  Services.obs.addObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+  Services.obs.addObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
+function setupHTMLPanel()
+{
+  Services.obs.removeObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+  Services.obs.addObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+  InspectorUI.treePanel.open();
+}
+
 function runEditorTests()
 {
-  Services.obs.removeObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
+  Services.obs.removeObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
   InspectorUI.stopInspecting();
-  
+
   // setup generator for async test steps
   editorTestSteps = doEditorTestSteps();
-  
+
   // add step listeners
-  Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
-  Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
-  Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
+  Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
+  Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
+  Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
 
   // start the tests
   doNextStep();
 }
 
 function doEditorTestSteps()
 {
-  let editor = InspectorUI.treeBrowserDocument.getElementById("attribute-editor");
-  let editorInput = InspectorUI.treeBrowserDocument.getElementById("attribute-editor-input");
+  let treePanel = InspectorUI.treePanel;
+  let editor = treePanel.treeBrowserDocument.getElementById("attribute-editor");
+  let editorInput = treePanel.treeBrowserDocument.getElementById("attribute-editor-input");
 
   // Step 1: grab and test the attribute-value nodes in the HTML panel, then open editor
-  let attrValNode_id = InspectorUI.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0];
-  let attrValNode_class = InspectorUI.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='class']")[0];
+  let attrValNode_id = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0];
+  let attrValNode_class = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='class']")[0];
 
   is(attrValNode_id.innerHTML, "foobar", "Step 1: we have the correct `id` attribute-value node in the HTML panel");
   is(attrValNode_class.innerHTML, "barbaz", "we have the correct `class` attribute-value node in the HTML panel");
-  
+
   // double-click the `id` attribute-value node to open the editor
   executeSoon(function() {
     // firing 2 clicks right in a row to simulate a double-click
     EventUtils.synthesizeMouse(attrValNode_id, 2, 2, {clickCount: 2}, attrValNode_id.ownerDocument.defaultView);
   });
 
   yield; // End of Step 1
 
 
   // Step 2: validate editing session, enter new attribute value into editor, and save input
-  ok(InspectorUI.editingContext, "Step 2: editor session started");
+  ok(InspectorUI.treePanel.editingContext, "Step 2: editor session started");
 
   let editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup visible");
 
   // check if the editor popup is "near" the correct position
   let editorDims = editor.getBoundingClientRect();
   let attrValNodeDims = attrValNode_id.getBoundingClientRect();
   let editorPositionOK = (editorDims.left >= (attrValNodeDims.left - editorDims.width - 5)) &&
@@ -83,35 +91,35 @@ function doEditorTestSteps()
                           (editorDims.bottom <= (attrValNodeDims.bottom + editorDims.height + 5));
 
   ok(editorPositionOK, "editor position acceptable");
 
   // check to make sure the attribute-value node being edited is properly highlighted
   let attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue");
   ok(attrValNodeHighlighted, "`id` attribute-value node is editor-highlighted");
 
-  is(InspectorUI.editingContext.repObj, div, "editor session has correct reference to div");
-  is(InspectorUI.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel");
-  is(InspectorUI.editingContext.attrName, "id", "editor session knows correct attribute-name");
+  is(treePanel.editingContext.repObj, div, "editor session has correct reference to div");
+  is(treePanel.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel");
+  is(treePanel.editingContext.attrName, "id", "editor session knows correct attribute-name");
 
   editorInput.value = "Hello World";
   editorInput.focus();
-  
+
   // hit <enter> to save the inputted value
   executeSoon(function() {
     EventUtils.synthesizeKey("VK_RETURN", {}, attrValNode_id.ownerDocument.defaultView);
   });
 
   // two `yield` statements, to trap both the "SAVED" and "CLOSED" events that will be triggered
   yield;
   yield; // End of Step 2
 
 
   // Step 3: validate that the previous editing session saved correctly, then open editor on `class` attribute value
-  ok(!InspectorUI.editingContext, "Step 3: editor session ended");
+  ok(!treePanel.editingContext, "Step 3: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue");
   ok(!attrValNodeHighlighted, "`id` attribute-value node is no longer editor-highlighted");
   is(div.getAttribute("id"), "Hello World", "`id` attribute-value successfully updated");
   is(attrValNode_id.innerHTML, "Hello World", "attribute-value node in HTML panel successfully updated");
 
   // double-click the `class` attribute-value node to open the editor
@@ -119,99 +127,99 @@ function doEditorTestSteps()
     // firing 2 clicks right in a row to simulate a double-click
     EventUtils.synthesizeMouse(attrValNode_class, 2, 2, {clickCount: 2}, attrValNode_class.ownerDocument.defaultView);
   });
 
   yield; // End of Step 3
 
 
   // Step 4: enter value into editor, then hit <escape> to discard it
-  ok(InspectorUI.editingContext, "Step 4: editor session started");
+  ok(treePanel.editingContext, "Step 4: editor session started");
   editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup visible");
-  
-  is(InspectorUI.editingContext.attrObj, attrValNode_class, "editor session has correct reference to `class` attribute-value node in HTML panel");
-  is(InspectorUI.editingContext.attrName, "class", "editor session knows correct attribute-name");
+
+  is(treePanel.editingContext.attrObj, attrValNode_class, "editor session has correct reference to `class` attribute-value node in HTML panel");
+  is(treePanel.editingContext.attrName, "class", "editor session knows correct attribute-name");
 
   editorInput.value = "Hello World";
   editorInput.focus();
-  
+
   // hit <escape> to discard the inputted value
   executeSoon(function() {
     EventUtils.synthesizeKey("VK_ESCAPE", {}, attrValNode_class.ownerDocument.defaultView);
   });
 
   yield; // End of Step 4
 
 
   // Step 5: validate that the previous editing session discarded correctly, then open editor on `id` attribute value again
-  ok(!InspectorUI.editingContext, "Step 5: editor session ended");
+  ok(!treePanel.editingContext, "Step 5: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   is(div.getAttribute("class"), "barbaz", "`class` attribute-value *not* updated");
   is(attrValNode_class.innerHTML, "barbaz", "attribute-value node in HTML panel *not* updated");
 
   // double-click the `id` attribute-value node to open the editor
   executeSoon(function() {
     // firing 2 clicks right in a row to simulate a double-click
     EventUtils.synthesizeMouse(attrValNode_id, 2, 2, {clickCount: 2}, attrValNode_id.ownerDocument.defaultView);
   });
 
   yield; // End of Step 5
 
 
   // Step 6: validate that editor opened again, then test double-click inside of editor (should do nothing)
-  ok(InspectorUI.editingContext, "Step 6: editor session started");
+  ok(treePanel.editingContext, "Step 6: editor session started");
   editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup visible");
-  
+
   // double-click on the editor input box
   executeSoon(function() {
     // firing 2 clicks right in a row to simulate a double-click
     EventUtils.synthesizeMouse(editorInput, 2, 2, {clickCount: 2}, editorInput.ownerDocument.defaultView);
-    
+
     // since the previous double-click is supposed to do nothing,
     // wait a brief moment, then move on to the next step
     executeSoon(function() {
       doNextStep();
     });
   });
 
   yield; // End of Step 6
 
 
-  // Step 7: validate that editing session is still correct, then enter a value and try a click 
+  // Step 7: validate that editing session is still correct, then enter a value and try a click
   //         outside of editor (should cancel the editing session)
-  ok(InspectorUI.editingContext, "Step 7: editor session still going");
+  ok(treePanel.editingContext, "Step 7: editor session still going");
   editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup still visible");
-  
+
   editorInput.value = "all your base are belong to us";
 
   // single-click the `class` attribute-value node
   executeSoon(function() {
     EventUtils.synthesizeMouse(attrValNode_class, 2, 2, {}, attrValNode_class.ownerDocument.defaultView);
   });
 
   yield; // End of Step 7
 
 
   // Step 8: validate that the editor was closed and that the editing was not saved
-  ok(!InspectorUI.editingContext, "Step 8: editor session ended");
+  ok(!treePanel.editingContext, "Step 8: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   is(div.getAttribute("id"), "Hello World", "`id` attribute-value *not* updated");
   is(attrValNode_id.innerHTML, "Hello World", "attribute-value node in HTML panel *not* updated");
-  
+
   // End of Step 8
 
   // end of all steps, so clean up
-  Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
-  Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
-  Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
+  Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
+  Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
+  Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
 
   executeSoon(finishUp);
 }
 
 function finishUp() {
   doc = div = null;
   InspectorUI.closeInspectorUI();
   gBrowser.removeCurrentTab();
rename from browser/base/content/test/inspector/browser_inspector_highlighter.js
rename to browser/devtools/highlighter/test/browser_inspector_highlighter.js
--- a/browser/base/content/test/inspector/browser_inspector_highlighter.js
+++ b/browser/devtools/highlighter/test/browser_inspector_highlighter.js
@@ -75,36 +75,36 @@ function createDocument()
   setupHighlighterTests();
 }
 
 function setupHighlighterTests()
 {
   h1 = doc.querySelectorAll("h1")[0];
   ok(h1, "we have the header node");
   Services.obs.addObserver(runSelectionTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function runSelectionTests()
 {
   Services.obs.removeObserver(runSelectionTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   executeSoon(function() {
     Services.obs.addObserver(performTestComparisons,
-      INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
     EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
   });
 }
 
 function performTestComparisons(evt)
 {
   Services.obs.removeObserver(performTestComparisons,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
   InspectorUI.stopInspecting();
   ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting");
   is(InspectorUI.highlighter.highlitNode, h1, "highlighter matches selection")
   is(InspectorUI.selection, h1, "selection matches node");
   is(InspectorUI.selection, InspectorUI.highlighter.highlitNode, "selection matches highlighter");
 
   doc = h1 = null;
rename from browser/base/content/test/inspector/browser_inspector_iframeTest.js
rename to browser/devtools/highlighter/test/browser_inspector_iframeTest.js
--- a/browser/base/content/test/inspector/browser_inspector_iframeTest.js
+++ b/browser/devtools/highlighter/test/browser_inspector_iframeTest.js
@@ -38,17 +38,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 let doc;
 let div1;
 let div2;
 let iframe1;
 let iframe2;
-let highlighterFrame;
 
 function createDocument()
 {
   doc.title = "Inspector iframe Tests";
 
   iframe1 = doc.createElement('iframe');
 
   iframe1.addEventListener("load", function () {
@@ -82,49 +81,48 @@ function moveMouseOver(aElement)
 {
   EventUtils.synthesizeMouse(aElement, 2, 2, {type: "mousemove"},
     aElement.ownerDocument.defaultView);
 }
 
 function setupIframeTests()
 {
   Services.obs.addObserver(runIframeTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.openInspectorUI();
 }
 
 function runIframeTests()
 {
   Services.obs.removeObserver(runIframeTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   Services.obs.addObserver(performTestComparisons1,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
-  highlighterFrame = InspectorUI.highlighter.iframe;
   executeSoon(moveMouseOver.bind(this, div1));
 }
 
 function performTestComparisons1()
 {
   Services.obs.removeObserver(performTestComparisons1,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
   Services.obs.addObserver(performTestComparisons2,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
   is(InspectorUI.selection, div1, "selection matches div1 node");
   is(InspectorUI.highlighter.highlitNode, div1, "highlighter matches selection");
 
   executeSoon(moveMouseOver.bind(this, div2));
 }
 
 function performTestComparisons2()
 {
   Services.obs.removeObserver(performTestComparisons2,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
   is(InspectorUI.selection, div2, "selection matches div2 node");
   is(InspectorUI.highlighter.highlitNode, div2, "highlighter matches selection");
 
   finish();
 }
 
 function test() {
rename from browser/base/content/test/inspector/browser_inspector_initialization.js
rename to browser/devtools/highlighter/test/browser_inspector_initialization.js
--- a/browser/base/content/test/inspector/browser_inspector_initialization.js
+++ b/browser/devtools/highlighter/test/browser_inspector_initialization.js
@@ -31,62 +31,166 @@
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * 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 salutation;
+let closing;
+
+function createDocument()
+{
+  doc.body.innerHTML = '<div id="first" style="{ margin: 10em; ' +
+    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA}">\n' +
+    '<h1>Some header text</h1>\n' +
+    '<p id="salutation" style="{font-size: 12pt}">hi.</p>\n' +
+    '<p id="body" style="{font-size: 12pt}">I am a test-case. This text exists ' +
+    'solely to provide some things to test the inspector initialization.</p>\n' +
+    'If you are reading this, you should go do something else instead. Maybe ' +
+    'read a book. Or better yet, write some test-cases for another bit of code. ' +
+    '<span style="{font-style: italic}">Maybe more inspector test-cases!</span></p>\n' +
+    '<p id="closing">end transmission</p>\n' +
+    '</div>';
+  doc.title = "Inspector Initialization Test";
+  startInspectorTests();
+}
 
 function startInspectorTests()
 {
   ok(InspectorUI, "InspectorUI variable exists");
   Services.obs.addObserver(runInspectorTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function runInspectorTests()
 {
   Services.obs.removeObserver(runInspectorTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
-  Services.obs.addObserver(finishInspectorTests,
-    INSPECTOR_NOTIFICATIONS.CLOSED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+  Services.obs.addObserver(treePanelTests,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
 
+  ok(InspectorUI.toolbar, "we have the toolbar.");
   ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
-  let iframe = document.getElementById("inspector-tree-iframe");
-  is(InspectorUI.treeIFrame, iframe, "Inspector IFrame matches");
   ok(InspectorUI.inspecting, "Inspector is inspecting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
   ok(InspectorUI.highlighter, "Highlighter is up");
 
+  InspectorUI.treePanel.open();
+}
+
+function treePanelTests()
+{
+  Services.obs.removeObserver(treePanelTests,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
+  Services.obs.addObserver(runContextMenuTest,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+
+  ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
+
   executeSoon(function() {
     InspectorUI.closeInspectorUI();
   });
 }
 
+function runContextMenuTest()
+{
+  Services.obs.removeObserver(runContextMenuTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  Services.obs.addObserver(inspectNodesFromContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  salutation = doc.getElementById("salutation");
+  ok(salutation, "hello, context menu test!");
+  let eventDeets = { type : "contextmenu", button : 2 };
+  let contextMenu = document.getElementById("contentAreaContextMenu");
+  ok(contextMenu, "we have the context menu");
+  let contextInspectMenuItem = document.getElementById("context-inspect");
+  ok(contextInspectMenuItem, "we have the inspect context menu item");
+  EventUtils.synthesizeMouse(salutation, 2, 2, eventDeets);
+  is(contextMenu.state, "showing", "context menu is open");
+  is(!contextInspectMenuItem.hidden, gPrefService.getBoolPref("devtools.inspector.enabled"), "is context menu item enabled?");
+  contextMenu.hidePopup();
+  executeSoon(function() {
+    InspectorUI.openInspectorUI(salutation);
+  });
+}
+
+function inspectNodesFromContextTest()
+{
+  Services.obs.removeObserver(inspectNodesFromContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  Services.obs.addObserver(openInspectorForContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  ok(!InspectorUI.inspecting, "Inspector is not actively highlighting");
+  is(InspectorUI.selection, salutation, "Inspector is highlighting salutation");
+  ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
+  // TODO: These tests depend on the style inspector patches.
+  todo(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
+  executeSoon(function() {
+    InspectorUI.closeInspectorUI(true);
+  });
+}
+
+function openInspectorForContextTest()
+{
+  Services.obs.removeObserver(openInspectorForContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
+  Services.obs.addObserver(inspectNodesFromContextTestWhileOpen, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  executeSoon(function() {
+    InspectorUI.openInspectorUI(salutation);
+  });
+}
+
+function inspectNodesFromContextTestWhileOpen()
+{
+  Services.obs.removeObserver(inspectNodesFromContextTestWhileOpen, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+  Services.obs.addObserver(inspectNodesFromContextTestTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  Services.obs.addObserver(inspectNodesFromContextTestHighlight, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  is(InspectorUI.selection, salutation, "Inspector is highlighting salutation");
+  closing = doc.getElementById("closing");
+  ok(closing, "we have the closing statement");
+  executeSoon(function() {
+    InspectorUI.openInspectorUI(closing);
+  });
+}
+
+function inspectNodesFromContextTestHighlight()
+{
+  Services.obs.removeObserver(inspectNodesFromContextTestHighlight, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+  Services.obs.addObserver(finishInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  is(InspectorUI.selection, closing, "InspectorUI.selection is header");
+  executeSoon(function() {
+    InspectorUI.closeInspectorUI(true);
+  });
+}
+
+function inspectNodesFromContextTestTrap()
+{
+  Services.obs.removeObserver(inspectNodesFromContextTestTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+  ok(false, "Inspector UI has been opened again. We Should Not Be Here!");
+}
+
 function finishInspectorTests()
 {
   Services.obs.removeObserver(finishInspectorTests,
-    INSPECTOR_NOTIFICATIONS.CLOSED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
 
   ok(!InspectorUI.highlighter, "Highlighter is gone");
-  ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
+  ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
   ok(!InspectorUI.inspecting, "Inspector is not inspecting");
-  ok(InspectorUI.toolbar.hidden, "toolbar is hidden");
+  ok(!InspectorUI.toolbar, "toolbar is hidden");
 
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-    waitForFocus(startInspectorTests, content);
+    doc = content.document;
+    waitForFocus(createDocument, content);
   }, true);
 
   content.location = "data:text/html,basic tests for inspector";
 }
 
rename from browser/base/content/test/inspector/browser_inspector_registertools.js
rename to browser/devtools/highlighter/test/browser_inspector_registertools.js
--- a/browser/base/content/test/inspector/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, InspectorUI.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, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+  toolsLength = InspectorUI.tools.length;
+  toolEvents = InspectorUI.toolEvents.length;
+  info("tools registered");
+  Services.obs.addObserver(startToolTests, InspectorUI.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, InspectorUI.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, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   gBrowser.removeCurrentTab();
+}
 
+function testOriginalTab()
+{
+  Services.obs.removeObserver(testOriginalTab, InspectorUI.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, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  InspectorUI.closeInspectorUI(true);
+}
+
+function unregisterTools()
+{
+  Services.obs.removeObserver(unregisterTools, InspectorUI.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;
   },
 };
rename from browser/base/content/test/inspector/browser_inspector_scrolling.js
rename to browser/devtools/highlighter/test/browser_inspector_scrolling.js
--- a/browser/base/content/test/inspector/browser_inspector_scrolling.js
+++ b/browser/devtools/highlighter/test/browser_inspector_scrolling.js
@@ -60,36 +60,36 @@ function createDocument()
   }, false);
 
   iframe.src = "data:text/html,foo bar";
   doc.body.appendChild(iframe);
 }
 
 function toggleInspector()
 {
-  Services.obs.addObserver(inspectNode, INSPECTOR_NOTIFICATIONS.OPENED, false);
+  Services.obs.addObserver(inspectNode, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function inspectNode()
 {
   Services.obs.removeObserver(inspectNode,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   Services.obs.addObserver(performScrollingTest,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
   executeSoon(function() {
     InspectorUI.inspectNode(div);
   });
 }
 
 function performScrollingTest()
 {
   Services.obs.removeObserver(performScrollingTest,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
   EventUtils.synthesizeMouseScroll(div, 10, 10,
     {axis:"vertical", delta:50, type:"MozMousePixelScroll"},
     iframe.contentWindow);
 
   gBrowser.selectedBrowser.addEventListener("scroll", function() {
     gBrowser.selectedBrowser.removeEventListener("scroll", arguments.callee,
       false);
rename from browser/base/content/test/inspector/browser_inspector_store.js
rename to browser/devtools/highlighter/test/browser_inspector_store.js
--- a/browser/base/content/test/inspector/browser_inspector_store.js
+++ b/browser/devtools/highlighter/test/browser_inspector_store.js
@@ -34,18 +34,18 @@
  * 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 ***** */
 
 function test()
 {
-  ok(window.InspectorUI, "InspectorUI variable exists");
-  ok(!InspectorUI.inspecting, "Inspector is not highlighting");
+  let InspectorStore = InspectorUI.store;
+
   is(InspectorStore.length, 0, "InspectorStore is empty");
   ok(InspectorStore.isEmpty(), "InspectorStore is empty (confirmed)");
   is(typeof InspectorStore.store, "object",
     "InspectorStore.store is an object");
 
   ok(InspectorStore.addStore("foo"), "addStore('foo') returns true");
 
   is(InspectorStore.length, 1, "InspectorStore.length = 1");
rename from browser/base/content/test/inspector/browser_inspector_tab_switch.js
rename to browser/devtools/highlighter/test/browser_inspector_tab_switch.js
--- a/browser/base/content/test/inspector/browser_inspector_tab_switch.js
+++ b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
@@ -42,33 +42,33 @@ let div;
 let tab1;
 let tab2;
 let tab1window;
 
 function inspectorTabOpen1()
 {
   ok(window.InspectorUI, "InspectorUI variable exists");
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(InspectorStore.isEmpty(), "InspectorStore is empty");
+  ok(InspectorUI.store.isEmpty(), "Inspector.store is empty");
 
   Services.obs.addObserver(inspectorUIOpen1,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.openInspectorUI();
 }
 
 function inspectorUIOpen1()
 {
   Services.obs.removeObserver(inspectorUIOpen1,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is open.
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
-  ok(!InspectorStore.isEmpty(), "InspectorStore is not empty");
-  is(InspectorStore.length, 1, "InspectorStore.length = 1");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  ok(!InspectorUI.store.isEmpty(), "InspectorUI.store is not empty");
+  is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
 
   // Highlight a node.
   div = content.document.getElementsByTagName("div")[0];
   InspectorUI.inspectNode(div);
   is(InspectorUI.selection, div, "selection matches the div element");
 
   // Open the second tab.
   tab2 = gBrowser.addTab();
@@ -82,92 +82,141 @@ function inspectorUIOpen1()
 
   content.location = "data:text/html,<p>tab 2: the inspector should close now";
 }
 
 function inspectorTabOpen2()
 {
   // Make sure the inspector is closed.
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(!InspectorUI.isPanelOpen, "Inspector Tree Panel is closed");
-  is(InspectorStore.length, 1, "InspectorStore.length = 1");
+  ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
+  is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
 
   // Activate the inspector again.
   executeSoon(function() {
     Services.obs.addObserver(inspectorUIOpen2,
-      INSPECTOR_NOTIFICATIONS.OPENED, false);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
     InspectorUI.openInspectorUI();
   });
 }
 
 function inspectorUIOpen2()
 {
   Services.obs.removeObserver(inspectorUIOpen2,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is open.
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
-  is(InspectorStore.length, 2, "InspectorStore.length = 2");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  is(InspectorUI.store.length, 2, "Inspector.store.length = 2");
 
   // Disable highlighting.
   InspectorUI.toggleInspection();
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
 
   // Switch back to tab 1.
   executeSoon(function() {
     Services.obs.addObserver(inspectorFocusTab1,
-      INSPECTOR_NOTIFICATIONS.OPENED, false);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
     gBrowser.selectedTab = tab1;
   });
 }
 
 function inspectorFocusTab1()
 {
   Services.obs.removeObserver(inspectorFocusTab1,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is still open.
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
-  is(InspectorStore.length, 2, "InspectorStore.length = 2");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  is(InspectorUI.store.length, 2, "Inspector.store.length = 2");
+  is(InspectorUI.selection, div, "selection matches the div element");
+
+  Services.obs.addObserver(inspectorOpenTreePanelTab1,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+
+  InspectorUI.treePanel.open();
+}
+
+function inspectorOpenTreePanelTab1()
+{
+  Services.obs.removeObserver(inspectorOpenTreePanelTab1,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
+  is(InspectorUI.store.length, 2, "Inspector.store.length = 2");
   is(InspectorUI.selection, div, "selection matches the div element");
 
   // Switch back to tab 2.
   Services.obs.addObserver(inspectorFocusTab2,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   gBrowser.selectedTab = tab2;
 }
 
 function inspectorFocusTab2()
 {
   Services.obs.removeObserver(inspectorFocusTab2,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is still open.
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
-  is(InspectorStore.length, 2, "InspectorStore.length = 2");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  is(InspectorUI.store.length, 2, "Inspector.store.length is 2");
+  isnot(InspectorUI.selection, div, "selection does not match the div element");
+
+  // Switch back to tab 1.
+  Services.obs.addObserver(inspectorSecondFocusTab1,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+  gBrowser.selectedTab = tab1;
+}
+
+function inspectorSecondFocusTab1()
+{
+  Services.obs.removeObserver(inspectorSecondFocusTab1,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
+  is(InspectorUI.store.length, 2, "Inspector.store.length = 2");
+  is(InspectorUI.selection, div, "selection matches the div element");
+
+  // Switch back to tab 2.
+  Services.obs.addObserver(inspectorSecondFocusTab2,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  gBrowser.selectedTab = tab2;
+}
+
+function inspectorSecondFocusTab2()
+{
+  Services.obs.removeObserver(inspectorSecondFocusTab2,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+
+  // Make sure the inspector is still open.
+  ok(!InspectorUI.inspecting, "Inspector is not highlighting");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  is(InspectorUI.store.length, 2, "Inspector.store.length is 2");
   isnot(InspectorUI.selection, div, "selection does not match the div element");
 
   // Remove tab 1.
   tab1window = gBrowser.getBrowserForTab(tab1).contentWindow;
   tab1window.addEventListener("pagehide", inspectorTabUnload1, false);
   gBrowser.removeTab(tab1);
 }
 
 function inspectorTabUnload1(evt)
 {
   tab1window.removeEventListener(evt.type, arguments.callee, false);
   tab1window = tab1 = tab2 = div = null;
 
   // Make sure the Inspector is still open and that the state is correct.
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
-  is(InspectorStore.length, 1, "InspectorStore.length = 1");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
 
   InspectorUI.closeInspectorUI();
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
rename from browser/base/content/test/inspector/browser_inspector_treePanel_click.js
rename to browser/devtools/highlighter/test/browser_inspector_treePanel_click.js
--- a/browser/base/content/test/inspector/browser_inspector_treePanel_click.js
+++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js
@@ -12,50 +12,54 @@ function test() {
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     doc = content.document;
     waitForFocus(setupTest, content);
   }, true);
 
-  content.location = "data:text/html,<div><p></p></div>";
+  content.location = 'data:text/html,<div style="width: 200px; height: 200px"><p></p></div>';
 
   function setupTest() {
     node1 = doc.querySelector("div");
     node2 = doc.querySelector("p");
-    Services.obs.addObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
+    Services.obs.addObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
     InspectorUI.toggleInspectorUI();
   }
 
   function runTests() {
-    Services.obs.removeObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED);
-    testNode1();
+    Services.obs.removeObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+    Services.obs.addObserver(testNode1, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+    InspectorUI.select(node1, true, true, true);
+    InspectorUI.openTreePanel();
   }
 
   function testNode1() {
-    let box = InspectorUI.ioBox.createObjectBox(node1);
-    box.click();
-    executeSoon(function() {
-      is(InspectorUI.selection, node1, "selection matches node");
-      is(InspectorUI.highlighter.node, node1, "selection matches node");
-      testNode2();
-    });
+    dump("testNode1\n");
+    Services.obs.removeObserver(testNode1, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
+    is(InspectorUI.selection, node1, "selection matches node");
+    is(InspectorUI.highlighter.node, node1, "selection matches node");
+    testNode2();
   }
 
   function testNode2() {
-    let box = InspectorUI.ioBox.createObjectBox(node2);
-    box.click();
-    executeSoon(function() {
-      is(InspectorUI.selection, node2, "selection matches node");
-      is(InspectorUI.highlighter.node, node2, "selection matches node");
-      Services.obs.addObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED, false);
-      InspectorUI.closeInspectorUI();
-    });
+    dump("testNode2\n")
+    Services.obs.addObserver(testHighlightingNode2, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.treePanelSelect("node2");
+  }
+
+  function testHighlightingNode2() {
+    dump("testHighlightingNode2\n")
+    Services.obs.removeObserver(testHighlightingNode2, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    is(InspectorUI.selection, node2, "selection matches node");
+    is(InspectorUI.highlighter.node, node2, "selection matches node");
+    Services.obs.addObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+    InspectorUI.closeInspectorUI();
   }
 
   function finishUp() {
-    Services.obs.removeObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED);
+    Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
     doc = node1 = node2 = null;
     gBrowser.removeCurrentTab();
     finish();
   }
 }
rename from browser/base/content/test/inspector/browser_inspector_treePanel_input.html
rename to browser/devtools/highlighter/test/browser_inspector_treePanel_input.html
rename from browser/base/content/test/inspector/browser_inspector_treePanel_output.js
rename to browser/devtools/highlighter/test/browser_inspector_treePanel_output.js
--- a/browser/base/content/test/inspector/browser_inspector_treePanel_output.js
+++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_output.js
@@ -37,18 +37,18 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 let doc = null;
 let xhr = null;
 let expectedResult = "";
 
-const TEST_URI = "http://mochi.test:8888/browser/browser/base/content/test/inspector/browser_inspector_treePanel_input.html";
-const RESULT_URI = "http://mochi.test:8888/browser/browser/base/content/test/inspector/browser_inspector_treePanel_result.html";
+const TEST_URI = "http://mochi.test:8888/browser/browser/devtools/highlighter/test/browser_inspector_treePanel_input.html";
+const RESULT_URI = "http://mochi.test:8888/browser/browser/devtools/highlighter/test/browser_inspector_treePanel_result.html";
 
 function tabFocused()
 {
   xhr = new XMLHttpRequest();
   xhr.onreadystatechange = xhr_onReadyStateChange;
   xhr.open("GET", RESULT_URI, true);
   xhr.send(null);
 }
@@ -59,60 +59,69 @@ function xhr_onReadyStateChange() {
   }
 
   is(xhr.status, 200, "xhr.status is 200");
   ok(!!xhr.responseText, "xhr.responseText is available");
   expectedResult = xhr.responseText.replace(/^\s+|\s+$/mg, '');
   xhr = null;
 
   Services.obs.addObserver(inspectorOpened,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.openInspectorUI();
 }
 
 function inspectorOpened()
 {
   Services.obs.removeObserver(inspectorOpened,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+
+  Services.obs.addObserver(treePanelOpened, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+  InspectorUI.treePanel.open();
+}
+
+function treePanelOpened()
+{
+  Services.obs.removeObserver(treePanelOpened,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
 
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
   InspectorUI.stopInspecting();
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
 
   let elements = doc.querySelectorAll("meta, script, style, p[unknownAttribute]");
   for (let i = 0; i < elements.length; i++) {
     InspectorUI.inspectNode(elements[i]);
   }
 
   let iframe = doc.querySelector("iframe");
   ok(iframe, "Found the iframe tag");
   ok(iframe.contentDocument, "Found the iframe.contentDocument");
 
   let iframeDiv = iframe.contentDocument.querySelector("div");
   ok(iframeDiv, "Found the div element inside the iframe");
   InspectorUI.inspectNode(iframeDiv);
 
-  ok(InspectorUI.treePanelDiv, "InspectorUI.treePanelDiv is available");
-  is(InspectorUI.treePanelDiv.innerHTML.replace(/^\s+|\s+$/mg, ''),
+  ok(InspectorUI.treePanel.treePanelDiv, "InspectorUI.treePanelDiv is available");
+  is(InspectorUI.treePanel.treePanelDiv.innerHTML.replace(/^\s+|\s+$/mg, ''),
     expectedResult, "treePanelDiv.innerHTML is correct");
   expectedResult = null;
 
   Services.obs.addObserver(inspectorClosed,
-    INSPECTOR_NOTIFICATIONS.CLOSED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
   InspectorUI.closeInspectorUI();
 }
 
 function inspectorClosed()
 {
   Services.obs.removeObserver(inspectorClosed,
-    INSPECTOR_NOTIFICATIONS.CLOSED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is not open");
+  ok(!InspectorUI.treePanel, "Inspector Tree Panel is not open");
 
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
rename from browser/base/content/test/inspector/browser_inspector_treePanel_result.html
rename to browser/devtools/highlighter/test/browser_inspector_treePanel_result.html
rename from browser/base/content/test/inspector/browser_inspector_treeSelection.js
rename to browser/devtools/highlighter/test/browser_inspector_treeSelection.js
--- a/browser/base/content/test/inspector/browser_inspector_treeSelection.js
+++ b/browser/devtools/highlighter/test/browser_inspector_treeSelection.js
@@ -63,35 +63,35 @@ function createDocument()
   setupSelectionTests();
 }
 
 function setupSelectionTests()
 {
   h1 = doc.querySelectorAll("h1")[0];
   ok(h1, "we have the header node");
   Services.obs.addObserver(runSelectionTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.openInspectorUI();
 }
 
 function runSelectionTests()
 {
   Services.obs.removeObserver(runSelectionTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   Services.obs.addObserver(performTestComparisons,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
   executeSoon(function() {
     InspectorUI.inspectNode(h1);
   });
 }
 
 function performTestComparisons(evt)
 {
   Services.obs.removeObserver(performTestComparisons,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
   is(h1, InspectorUI.selection, "selection matches node");
   ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting");
   is(InspectorUI.highlighter.highlitNode, h1, "highlighter highlighting correct node");
 
   finishUp();
 }
 
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -1,7 +1,9 @@
 browser.jar:
+*   content/browser/inspector.html                (highlighter/inspector.html)
     content/browser/NetworkPanel.xhtml            (webconsole/NetworkPanel.xhtml)
 *   content/browser/scratchpad.xul                (scratchpad/scratchpad.xul)
 *   content/browser/scratchpad.js                 (scratchpad/scratchpad.js)
     content/browser/csshtmltree.xhtml             (styleinspector/csshtmltree.xhtml)
     content/browser/orion.js                      (sourceeditor/orion/orion.js)
     content/browser/orion.css                     (sourceeditor/orion/orion.css)
+*   content/browser/inspector.html                (highlighter/inspector.html)
--- a/browser/devtools/scratchpad/Makefile.in
+++ b/browser/devtools/scratchpad/Makefile.in
@@ -7,21 +7,21 @@
 # the License. You may obtain a copy of the License at
 # http://www.mozilla.org/MPL/
 #
 # Software distributed under the License is distributed on an "AS IS" basis,
 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 # for the specific language governing rights and limitations under the
 # License.
 #
-# The Original Code is  HUDService code.
+# The Original Code is Scratchpad Build Code.
 #
-# The Initial Developer of the Original Code is Mozilla Corporation.
-# 
-# Portions created by the Initial Developer are Copyright (C) 2010
+# The Initial Developer of the Original Code is The Mozilla Foundation.
+#
+# Portions created by the Initial Developer are Copyright (C) 2011
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Rob Campbell <rcampbell@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -39,14 +39,12 @@
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 ifdef ENABLE_TESTS
-ifneq (mobile,$(MOZ_BUILD_APP))
 	DIRS += test
 endif
-endif
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm
+++ b/browser/devtools/styleinspector/CssHtmlTree.jsm
@@ -16,19 +16,20 @@
  * The Original Code is the Mozilla Inspector Module.
  *
  * The Initial Developer of the Original Code is
  * The Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Joe Walker (jwalker@mozilla.com) (original author)
+ *   Joe Walker (jwalker@mozilla.com) (Original Author)
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *   Michael Ratcliffe <mratcliffe@mozilla.com>
+ *   Rob Campbell <rcampbell@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
--- a/browser/devtools/styleinspector/StyleInspector.jsm
+++ b/browser/devtools/styleinspector/StyleInspector.jsm
@@ -16,17 +16,18 @@
  * The Original Code is the Mozilla Inspector Module.
  *
  * The Initial Developer of the Original Code is
  * The Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Mike Ratcliffe <mratcliffe@mozilla.com>
+ *   Mike Ratcliffe <mratcliffe@mozilla.com> (Original Author)
+ *   Rob Campbell <rcampbell@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -104,31 +105,33 @@ var StyleInspector = {
     popupSet.appendChild(panel);
 
     /**
      * Iframe's onload event
      */
     let iframeReady = false;
     function SI_iframeOnload() {
       iframe.removeEventListener("load", SI_iframeOnload, true);
-      panel.cssLogic = new CssLogic();
-      panel.cssHtmlTree = new CssHtmlTree(iframe, panel.cssLogic, panel);
       iframeReady = true;
       if (panelReady) {
         SI_popupShown.call(panel);
       }
     }
 
     /**
      * Initialize the popup when it is first shown
      */
     let panelReady = false;
     function SI_popupShown() {
       panelReady = true;
       if (iframeReady) {
+        if (!this.cssLogic) {
+          this.cssLogic = new CssLogic();
+          this.cssHtmlTree = new CssHtmlTree(iframe, this.cssLogic, this);
+        }
         let selectedNode = this.selectedNode || null;
         this.cssLogic.highlight(selectedNode);
         this.cssHtmlTree.highlight(selectedNode);
         Services.obs.notifyObservers(null, "StyleInspector-opened", null);
       }
     }
 
     /**
@@ -157,39 +160,71 @@ var StyleInspector = {
     /**
      * Select a node to inspect in the Style Inspector panel
      *
      * @param aNode The node to inspect
      */
     panel.selectNode = function SI_selectNode(aNode)
     {
       this.selectedNode = aNode;
-      if (this.isOpen()) {
+      if (this.isOpen() && !this.hasAttribute("dimmed")) {
         this.cssLogic.highlight(aNode);
         this.cssHtmlTree.highlight(aNode);
-      } else {
-        let win = Services.wm.getMostRecentWindow("navigator:browser");
-        this.openPopup(win.gBrowser.selectedBrowser, "end_before", 0, 0, false, false);
       }
     };
 
     /**
      * Destroy the style panel, remove listeners etc.
      */
     panel.destroy = function SI_destroy()
     {
+      if (!this.cssLogic)
+        return;
+      if (this.isOpen())
+        this.hideTool();
       this.cssLogic = null;
       this.cssHtmlTree = null;
       this.removeEventListener("popupshown", SI_popupShown);
       this.removeEventListener("popuphidden", SI_popupHidden);
       this.parentNode.removeChild(this);
       Services.obs.notifyObservers(null, "StyleInspector-closed", null);
     };
 
     /**
+     * Dim or undim a panel by setting or removing a dimmed attribute.
+     *
+     * @param aState
+     *        true = dim, false = undim
+     */
+    panel.dimTool = function SI_dimTool(aState)
+    {
+      if (!this.isOpen())
+        return;
+
+      if (aState) {
+        this.setAttribute("dimmed", "true");
+      } else if (this.hasAttribute("dimmed")) {
+        this.removeAttribute("dimmed");
+      }
+    };
+
+    panel.showTool = function SI_showTool(aSelection)
+    {
+      this.selectNode(aSelection);
+      let win = Services.wm.getMostRecentWindow("navigator:browser");
+      this.openPopup(win.gBrowser.selectedBrowser, "end_before", 0, 0,
+        false, false);
+    };
+
+    panel.hideTool = function SI_hideTool()
+    {
+      this.hidePopup();
+    };
+
+    /**
      * Is the Style Inspector initialized?
      * @returns {Boolean} true or false
      */
     function isInitialized()
     {
       return panel.cssLogic && panel.cssHtmlTree;
     }
 
--- a/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js
+++ b/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js
@@ -169,31 +169,23 @@ function styleInspectorClosedByHide()
                            "StyleInspector-closed", false);
   closeConsole();
 }
 
 function styleInspectorClosedFromConsole1()
 {
   Services.obs.removeObserver(styleInspectorClosedFromConsole1,
                               "StyleInspector-closed", false);
-  info("Style Inspector 1 closed");
-  Services.obs.addObserver(styleInspectorClosedFromConsole2,
-                           "StyleInspector-closed", false);
-}
-
-function styleInspectorClosedFromConsole2()
-{
-  Services.obs.removeObserver(styleInspectorClosedFromConsole2,
-                              "StyleInspector-closed", false);
-  info("Style Inspector 2 closed");
+  info("Style Inspector 1 and 2 closed");
   executeSoon(cleanUp);
 }
 
 function cleanUp()
 {
-  let popupSet = document.getElementById("mainPopupSet");
-  ok(!popupSet.lastChild.hasAttribute("hudToolId"),
+  let panels = document.querySelector("panel[hudToolId]");
+  ok(!panels,
      "all style inspector panels are now detached and ready for garbage collection");
 
   info("cleaning up");
+
   doc = hudBox = stylePanels = jsterm = null;
   finishTest();
 }
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -160,17 +160,20 @@ const SEVERITY_LOG = 3;
 // A mapping from the console API log event levels to the Web Console
 // severities.
 const LEVELS = {
   error: SEVERITY_ERROR,
   warn: SEVERITY_WARNING,
   info: SEVERITY_INFO,
   log: SEVERITY_LOG,
   trace: SEVERITY_LOG,
-  dir: SEVERITY_LOG
+  dir: SEVERITY_LOG,
+  group: SEVERITY_LOG,
+  groupCollapsed: SEVERITY_LOG,
+  groupEnd: SEVERITY_LOG
 };
 
 // The lowest HTTP response code (inclusive) that is considered an error.
 const MIN_HTTP_ERROR_CODE = 400;
 // The highest HTTP response code (exclusive) that is considered an error.
 const MAX_HTTP_ERROR_CODE = 600;
 
 // HTTP status codes.
@@ -247,16 +250,19 @@ const TYPEOF_FUNCTION = "function";
 
 const ERRORS = { LOG_MESSAGE_MISSING_ARGS:
                  "Missing arguments: aMessage, aConsoleNode and aMessageNode are required.",
                  CANNOT_GET_HUD: "Cannot getHeads Up Display with provided ID",
                  MISSING_ARGS: "Missing arguments",
                  LOG_OUTPUT_FAILED: "Log Failure: Could not append messageNode to outputNode",
 };
 
+// The indent of a console group in pixels.
+const GROUP_INDENT = 12;
+
 /**
  * Implements the nsIStreamListener and nsIRequestObserver interface. Used
  * within the HS_httpObserverFactory function to get the response body of
  * requests.
  *
  * The code is mostly based on code listings from:
  *
  *   http://www.softwareishard.com/blog/firebug/
@@ -2000,25 +2006,40 @@ HUD_SERVICE.prototype =
 
       case "dir":
         body = unwrap(args[0]);
         clipboardText = body.toString();
         sourceURL = aMessage.filename;
         sourceLine = aMessage.lineNumber;
         break;
 
+      case "group":
+      case "groupCollapsed":
+        clipboardText = body = formatResult(args);
+        sourceURL = aMessage.filename;
+        sourceLine = aMessage.lineNumber;
+        hud.groupDepth++;
+        break;
+
+      case "groupEnd":
+        if (hud.groupDepth > 0) {
+          hud.groupDepth--;
+        }
+        return;
+
       default:
         Cu.reportError("Unknown Console API log level: " + level);
         return;
     }
 
     let node = ConsoleUtils.createMessageNode(hud.outputNode.ownerDocument,
                                               CATEGORY_WEBDEV,
                                               LEVELS[level],
                                               body,
+                                              aHUDId,
                                               sourceURL,
                                               sourceLine,
                                               clipboardText,
                                               level);
 
     // Make the node bring up the property panel, to allow the user to inspect
     // the stack trace.
     if (level == "trace") {
@@ -2071,17 +2092,18 @@ HUD_SERVICE.prototype =
    */
   logWarningAboutReplacedAPI:
   function HS_logWarningAboutReplacedAPI(aHUDId)
   {
     let hud = this.hudReferences[aHUDId];
     let chromeDocument = hud.HUDBox.ownerDocument;
     let message = stringBundle.GetStringFromName("ConsoleAPIDisabled");
     let node = ConsoleUtils.createMessageNode(chromeDocument, CATEGORY_JS,
-                                              SEVERITY_WARNING, message);
+                                              SEVERITY_WARNING, message,
+                                              aHUDId);
     ConsoleUtils.outputMessageNode(node, aHUDId);
   },
 
   /**
    * Reports an error in the page source, either JavaScript or CSS.
    *
    * @param number aCategory
    *        The category of the message; either CATEGORY_CSS or CATEGORY_JS.
@@ -2111,16 +2133,17 @@ HUD_SERVICE.prototype =
       if (hudId) {
         let outputNode = this.hudReferences[hudId].outputNode;
         let chromeDocument = outputNode.ownerDocument;
 
         let node = ConsoleUtils.createMessageNode(chromeDocument,
                                                   aCategory,
                                                   severity,
                                                   aScriptError.errorMessage,
+                                                  hudId,
                                                   aScriptError.sourceName,
                                                   aScriptError.lineNumber);
 
         ConsoleUtils.outputMessageNode(node, hudId);
       }
     }
   },
 
@@ -2571,16 +2594,17 @@ HUD_SERVICE.prototype =
     linkNode.appendChild(statusNode);
 
     let clipboardText = aActivityObject.method + " " + aActivityObject.url;
 
     let messageNode = ConsoleUtils.createMessageNode(chromeDocument,
                                                      CATEGORY_NETWORK,
                                                      SEVERITY_LOG,
                                                      msgNode,
+                                                     hudId,
                                                      null,
                                                      null,
                                                      clipboardText);
 
     ConsoleUtils.outputMessageNode(messageNode, aActivityObject.hudId);
     return messageNode;
   },
 
@@ -3076,16 +3100,21 @@ function HeadsUpDisplay(aConfig)
   // A cache for tracking repeated CSS Nodes.
   this.cssNodes = {};
 }
 
 HeadsUpDisplay.prototype = {
 
   consolePanel: null,
 
+  /**
+   * The nesting depth of the currently active console group.
+   */
+  groupDepth: 0,
+
   get mainPopupSet()
   {
     return this.chromeDocument.getElementById("mainPopupSet");
   },
 
   /**
    * Get the tab associated to the HeadsUpDisplay object.
    */
@@ -4433,17 +4462,17 @@ function JSTermHelper(aJSTerm)
       errstr = HUDService.getStr("inspectStyle.mustBeDomNode");
     } else if (!(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration)) {
       errstr = HUDService.getStr("inspectStyle.nodeHasNoStyleProps");
     }
 
     if (!errstr) {
       let stylePanel = StyleInspector.createPanel();
       stylePanel.setAttribute("hudToolId", aJSTerm.hudId);
-      stylePanel.selectNode(aNode);
+      stylePanel.showTool(aNode);
     } else {
       aJSTerm.writeOutput(errstr + "\n", CATEGORY_OUTPUT, SEVERITY_ERROR);
     }
   };
 
   /**
    * Prints aObject to the output.
    *
@@ -4747,17 +4776,18 @@ JSTerm.prototype = {
    * @param object aOutputString
    *        The output string to be written to the outputNode.
    */
   writeOutputJS: function JST_writeOutputJS(aEvalString, aOutputObject, aOutputString)
   {
     let node = ConsoleUtils.createMessageNode(this.parentNode.ownerDocument,
                                               CATEGORY_OUTPUT,
                                               SEVERITY_LOG,
-                                              aOutputString);
+                                              aOutputString,
+                                              this.hudId);
 
     let linkNode = node.querySelector(".webconsole-msg-body");
 
     linkNode.classList.add("hud-clickable");
     linkNode.setAttribute("aria-haspopup", "true");
 
     // Make the object bring up the property panel.
     node.addEventListener("mousedown", function(aEvent) {
@@ -4794,17 +4824,17 @@ JSTerm.prototype = {
    * @param number aSeverity
    *        The severity of message: one of the SEVERITY_ constants.
    * @returns void
    */
   writeOutput: function JST_writeOutput(aOutputMessage, aCategory, aSeverity)
   {
     let node = ConsoleUtils.createMessageNode(this.parentNode.ownerDocument,
                                               aCategory, aSeverity,
-                                              aOutputMessage);
+                                              aOutputMessage, this.hudId);
 
     ConsoleUtils.outputMessageNode(node, this.hudId);
   },
 
   /**
    * Format the jsterm execution result based on its type.
    *
    * @param mixed aResult
@@ -5535,16 +5565,18 @@ ConsoleUtils = {
    * @param nsIDOMDocument aDocument
    *        The document in which to create the node.
    * @param number aCategory
    *        The category of the message: one of the CATEGORY_* constants.
    * @param number aSeverity
    *        The severity of the message: one of the SEVERITY_* constants;
    * @param string|nsIDOMNode aBody
    *        The body of the message, either a simple string or a DOM node.
+   * @param number aHUDId
+   *        The HeadsUpDisplay ID.
    * @param string aSourceURL [optional]
    *        The URL of the source file that emitted the error.
    * @param number aSourceLine [optional]
    *        The line number on which the error occurred. If zero or omitted,
    *        there is no line number associated with this message.
    * @param string aClipboardText [optional]
    *        The text that should be copied to the clipboard when this node is
    *        copied. If omitted, defaults to the body text. If `aBody` is not
@@ -5552,28 +5584,31 @@ ConsoleUtils = {
    * @param number aLevel [optional]
    *        The level of the console API message.
    * @return nsIDOMNode
    *         The message node: a XUL richlistitem ready to be inserted into
    *         the Web Console output node.
    */
   createMessageNode:
   function ConsoleUtils_createMessageNode(aDocument, aCategory, aSeverity,
-                                          aBody, aSourceURL, aSourceLine,
-                                          aClipboardText, aLevel) {
+                                          aBody, aHUDId, aSourceURL,
+                                          aSourceLine, aClipboardText, aLevel) {
     if (aBody instanceof Ci.nsIDOMNode && aClipboardText == null) {
       throw new Error("HUDService.createMessageNode(): DOM node supplied " +
                       "without any clipboard text");
     }
 
     // Make the icon container, which is a vertical box. Its purpose is to
     // ensure that the icon stays anchored at the top of the message even for
     // long multi-line messages.
     let iconContainer = aDocument.createElementNS(XUL_NS, "vbox");
     iconContainer.classList.add("webconsole-msg-icon-container");
+    // Apply the curent group by indenting appropriately.
+    let hud = HUDService.getHudReferenceById(aHUDId);
+    iconContainer.style.marginLeft = hud.groupDepth * GROUP_INDENT + "px";
 
     // Make the icon node. It's sprited and the actual region of the image is
     // determined by CSS rules.
     let iconNode = aDocument.createElementNS(XUL_NS, "image");
     iconNode.classList.add("webconsole-msg-icon");
     iconContainer.appendChild(iconNode);
 
     // Make the spacer that positions the icon.
@@ -6664,16 +6699,17 @@ ConsoleProgressListener.prototype = {
     }, false);
 
     msgNode.appendChild(linkNode);
 
     let messageNode = ConsoleUtils.createMessageNode(chromeDocument,
                                                      CATEGORY_NETWORK,
                                                      SEVERITY_LOG,
                                                      msgNode,
+                                                     this.hudId,
                                                      null,
                                                      null,
                                                      uri.spec);
 
     ConsoleUtils.outputMessageNode(messageNode, this.hudId);
   },
 
   onLocationChange: function() {},
--- a/browser/devtools/webconsole/test/browser/Makefile.in
+++ b/browser/devtools/webconsole/test/browser/Makefile.in
@@ -140,16 +140,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_webconsole_bug_585991_autocomplete_popup.js \
 	browser_webconsole_bug_585991_autocomplete_keys.js \
 	browser_webconsole_bug_663443_panel_title.js \
 	browser_webconsole_bug_660806_history_nav.js \
 	browser_webconsole_bug_651501_document_body_autocomplete.js \
 	browser_webconsole_bug_653531_highlighter_console_helper.js \
 	browser_webconsole_bug_659907_console_dir.js \
 	browser_webconsole_bug_678816.js \
+	browser_webconsole_bug_664131_console_group.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	test-console.html \
 	test-network.html \
 	test-network-request.html \
 	test-mutation.html \
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_580400_groups.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_580400_groups.js
@@ -77,13 +77,13 @@ function testGroups() {
     outputNode.getItemAtIndex(i).timestamp = 0;   // a "far past" value
   }
 
   jsterm.execute("2");
   is(outputNode.querySelectorAll(".webconsole-new-group").length, 1,
      "one group divider exists after the third console message");
 
   jsterm.clearOutput();
-  jsterm.history.splice(0);   // workaround for bug 592552
+  jsterm.history.splice(0, jsterm.history.length);   // workaround for bug 592552
 
   finishTest();
 }
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_642108_pruneTest.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_642108_pruneTest.js
@@ -20,30 +20,32 @@ function test() {
 
 function populateConsoleRepeats(aHudRef) {
   let hud = aHudRef.HUDBox;
 
   for (i = 0; i < 5; i++) {
     let node = ConsoleUtils.createMessageNode(hud.ownerDocument,
                                               CATEGORY_CSS,
                                               SEVERITY_WARNING,
-                                              "css log x");
+                                              "css log x",
+                                              aHudRef.hudId);
     ConsoleUtils.outputMessageNode(node, aHudRef.hudId);
   }
 }
 
 
 function populateConsole(aHudRef) {
   let hud = aHudRef.HUDBox;
 
   for (i = 0; i < LOG_LIMIT + 5; i++) {
     let node = ConsoleUtils.createMessageNode(hud.ownerDocument,
                                               CATEGORY_CSS,
                                               SEVERITY_WARNING,
-                                              "css log " + i);
+                                              "css log " + i,
+                                              aHudRef.hudId);
     ConsoleUtils.outputMessageNode(node, aHudRef.hudId);
   }
 }
 
 function testCSSPruning() {
   let prefBranch = Services.prefs.getBranch("devtools.hud.loglimit.");
   prefBranch.setIntPref("cssparser", LOG_LIMIT);
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_653531_highlighter_console_helper.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_653531_highlighter_console_helper.js
@@ -77,36 +77,36 @@ function createDocument()
   setupHighlighterTests();
 }
 
 function setupHighlighterTests()
 {
   h1 = doc.querySelectorAll("h1")[0];
   ok(h1, "we have the header node");
   Services.obs.addObserver(runSelectionTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function runSelectionTests()
 {
   Services.obs.removeObserver(runSelectionTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   executeSoon(function() {
     Services.obs.addObserver(performTestComparisons,
-      INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
     EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
   });
 }
 
 function performTestComparisons(evt)
 {
   Services.obs.removeObserver(performTestComparisons,
-    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
   InspectorUI.stopInspecting();
   ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting");
   is(InspectorUI.selection, h1, "selection matches node");
 
   HUDService.activateHUDForContext(gBrowser.selectedTab);
   let hudId = HUDService.getHudIdByWindow(content);
   let hud = HUDService.hudReferences[hudId];
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_664131_console_group.js
@@ -0,0 +1,48 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that console.group/groupEnd works as intended.
+const GROUP_INDENT = 12;
+
+function test() {
+  addTab("data:text/html,Web Console test for bug 664131: Expand console " +
+         "object with group methods");
+  browser.addEventListener("load", onLoad, true);
+}
+
+function onLoad(aEvent) {
+  browser.removeEventListener(aEvent.type, arguments.callee, true);
+
+  openConsole();
+  let hudId = HUDService.getHudIdByWindow(content);
+  let hud = HUDService.hudReferences[hudId];
+  outputNode = hud.outputNode;
+
+  content.console.group("a");
+  findLogEntry("a");
+  let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container");
+  is(msg.length, 1, "one message node displayed");
+  is(msg[0].style.marginLeft, GROUP_INDENT + "px", "correct group indent found");
+  content.console.log("inside");
+  findLogEntry("inside");
+  let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container");
+  is(msg.length, 2, "two message nodes displayed");
+  is(msg[1].style.marginLeft, GROUP_INDENT + "px", "correct group indent found");
+  content.console.groupEnd("a");
+  content.console.log("outside");
+  findLogEntry("outside");
+  let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container");
+  is(msg.length, 3, "three message nodes displayed");
+  is(msg[2].style.marginLeft, "0px", "correct group indent found");
+  content.console.groupCollapsed("b");
+  findLogEntry("b");
+  let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container");
+  is(msg.length, 4, "four message nodes displayed");
+  is(msg[3].style.marginLeft, GROUP_INDENT + "px", "correct group indent found");
+
+  finishTest();
+}
+
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_completion.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_completion.js
@@ -84,17 +84,17 @@ function testCompletion() {
   is(jsterm.completeNode.value, "                entsByClassName", "'document.getElem' another tab completion");
 
   // Test pressing shift_tab.
   jsterm.complete(jsterm.COMPLETE_BACKWARD);
   is(input.value, "document.getElem", "'document.getElem' untab completion");
   is(jsterm.completeNode.value, "                entById", "'document.getElem' completion");
 
   jsterm.clearOutput();
-  jsterm.history.splice(0);   // workaround for bug 592552
+  jsterm.history.splice(0, jsterm.history.length);   // workaround for bug 592552
 
   input.value = "docu";
   jsterm.complete(jsterm.COMPLETE_HINT_ONLY);
   is(jsterm.completeNode.value, "    ment", "'docu' completion");
   jsterm.execute();
   is(jsterm.completeNode.value, "", "clear completion on execute()");
 
   // Test multi-line completion works
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_execution_scope.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_execution_scope.js
@@ -63,13 +63,13 @@ function testExecutionScope() {
 
   is(/location;/.test(nodes[0].textContent), true,
      "'location;' written to output");
 
   ok(nodes[0].textContent.indexOf(TEST_URI),
     "command was executed in the window scope");
 
   jsterm.clearOutput();
-  jsterm.history.splice(0);   // workaround for bug 592552
+  jsterm.history.splice(0, jsterm.history.length);   // workaround for bug 592552
 
   finishTest();
 }
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_history.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_history.js
@@ -94,13 +94,13 @@ function testHistory() {
 
   is (input.value, "", "check input is still empty");
 
   let idxLast = executeList.length - 1;
   jsterm.historyPeruse(HISTORY_BACK);
   is (input.value, executeList[idxLast], "check history next idx:" + idxLast);
 
   jsterm.clearOutput();
-  jsterm.history.splice(0);   // workaround for bug 592552
+  jsterm.history.splice(0, jsterm.history.length);   // workaround for bug 592552
 
   finishTest();
 }
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_js_input_and_output_styling.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_js_input_and_output_styling.js
@@ -70,13 +70,13 @@ function testJSInputAndOutputStyling() {
   let jsOutputNodes = jsterm.outputNode.
                       querySelectorAll(".webconsole-msg-output");
   isnot(jsOutputNodes[0].textContent.indexOf("4"), -1,
         "JS output node contains '4'");
   ok(jsOutputNodes[0].classList.contains("webconsole-msg-output"),
      "JS output node is of the CSS class 'webconsole-msg-output'");
 
   jsterm.clearOutput();
-  jsterm.history.splice(0);   // workaround for bug 592552
+  jsterm.history.splice(0, jsterm.history.length);   // workaround for bug 592552
 
   finishTest();
 }
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_null_and_undefined_output.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_null_and_undefined_output.js
@@ -68,13 +68,13 @@ function testNullAndUndefinedOutput() {
   jsterm.clearOutput();
   jsterm.execute("undefined;");
 
   nodes = outputNode.querySelectorAll(".hud-msg-node");
   is(nodes.length, 2, "2 nodes in output");
   ok(nodes[1].textContent.indexOf("undefined") > -1, "'undefined' printed to output");
 
   jsterm.clearOutput();
-  jsterm.history.splice(0);   // workaround for bug 592552
+  jsterm.history.splice(0, jsterm.history.length);   // workaround for bug 592552
 
   finishTest();
 }
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_output_order.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_output_order.js
@@ -66,13 +66,13 @@ function testOutputOrder() {
     /console\.log\('foo', 'bar'\);/.test(nodes[0].textContent);
 
   let outputSecond =
     /foo bar/.test(nodes[1].textContent);
 
   ok(executedStringFirst && outputSecond, "executed string comes first");
 
   jsterm.clearOutput();
-  jsterm.history.splice(0);   // workaround for bug 592552
+  jsterm.history.splice(0, jsterm.history.length);   // workaround for bug 592552
 
   finishTest();
 }
 
--- a/browser/devtools/webconsole/test/browser/test-console-extras.html
+++ b/browser/devtools/webconsole/test/browser/test-console-extras.html
@@ -5,19 +5,16 @@
       function test() {
         console.log("start");
         console.time();
         console.timeEnd()
         console.exception()
         console.assert()
         console.clear()
         console.dirxml()
-        console.group()
-        console.groupCollapsed()
-        console.groupEnd()
         console.profile()
         console.profileEnd()
         console.count()
         console.table()
         console.log("end");
       }
     </script>
   </head>
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -189,16 +189,19 @@ can reach it easily. -->
 <!ENTITY webConsoleCmd.label          "Web Console">
 <!ENTITY webConsoleCmd.accesskey      "W">
 <!ENTITY webConsoleCmd.commandkey     "k">
 
 <!ENTITY inspectMenu.label            "Inspect">
 <!ENTITY inspectMenu.accesskey        "T">
 <!ENTITY inspectMenu.commandkey       "I">
 
+<!ENTITY inspectContextMenu.label     "Inspect Element">
+<!ENTITY inspectContextMenu.accesskey "Q">
+
 <!-- LOCALIZATION NOTE (scratchpad.label): This menu item label appears
   -  in the Tools menu. See bug 653093.
   -  The Scratchpad is intended to provide a simple text editor for creating
   -  and evaluating bits of JavaScript code for the purposes of function
   -  prototyping, experimentation and convenient scripting.
   -
   -  It's quite possible that you won't have a good analogue for the word
   -  "Scratchpad" in your locale. You should feel free to find a close
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -322,16 +322,16 @@ browser.menu.showCharacterEncoding=false
 syncPromoNotification.bookmarks.description=You can access your bookmarks on all your devices with %S.\u0020
 # LOCALIZATION NOTE (syncPromoNotification.passwords.label): This appears in
 # the remember password panel.  %S will be replaced by syncBrandShortName.
 # The final space separates this text from the Learn More link.
 syncPromoNotification.passwords.description=You can access your passwords on all your devices with %S.\u0020
 syncPromoNotification.learnMoreLinkText=Learn More
 
 # Telemetry prompt
-# LOCALIZATION NOTE (telemetryText): %1$S will be replaced by brandFullName,
+# LOCALIZATION NOTE (telemetryPrompt): %1$S will be replaced by brandFullName,
 # and %2$S by the value of the toolkit.telemetry.server_owner preference.
-telemetryText = Would you like to help improve %1$S by automatically reporting memory usage, performance, and responsiveness to %2$S?
+telemetryPrompt = Will you help improve %1$S by sending anonymous information about performance, hardware characteristics, feature usage, and browser customizations to %2$S?
 telemetryLinkLabel = Learn More
 telemetryYesButtonLabel = Yes
 telemetryYesButtonAccessKey = Y
 telemetryNoButtonLabel = No
 telemetryNoButtonAccessKey = N
--- a/browser/locales/en-US/chrome/browser/inspector.properties
+++ b/browser/locales/en-US/chrome/browser/inspector.properties
@@ -2,8 +2,23 @@
 # the user tries to navigate away from a web page, to confirm the change of
 # page.
 confirmNavigationAway.message=Leaving this page will close the Inspector and the changes you have made will be lost.
 confirmNavigationAway.buttonLeave=Leave Page
 confirmNavigationAway.buttonLeaveAccesskey=L
 confirmNavigationAway.buttonStay=Stay on Page
 confirmNavigationAway.buttonStayAccesskey=S
 
+# LOCALIZATION NOTE (htmlPanel): Used in the Inspector tool's openInspectorUI
+# method when registering the HTML panel.
+
+# LOCALIZATION NOTE (htmlPanel.label): This is a lable for a button that
+# activates the Web Developer->Inspect UI's HTML Tree Panel.
+htmlPanel.label=HTML
+
+# LOCALIZATION NOTE (htmlPanel.tooltiptext): The text that appears when a user
+# hovers over the HTML panel's toolbar button.
+htmlPanel.tooltiptext=HTML panel
+
+# LOCALIZATION NOTE (htmlPanel.accesskey): The key bound to the HTML panel's
+# toolbar button.
+htmlPanel.accesskey=H
+
--- a/browser/locales/en-US/chrome/browser/styleinspector.properties
+++ b/browser/locales/en-US/chrome/browser/styleinspector.properties
@@ -36,8 +36,14 @@ rule.sourceElement=element
 # these are the category names.
 group.Text_Fonts_and_Color=Text, Fonts & Color
 group.Background=Background
 group.Dimensions=Dimensions
 group.Positioning_and_Page_Flow=Positioning and Page Flow
 group.Borders=Borders
 group.Lists=Lists
 group.Effects_and_Other=Effects and Other
+
+# LOCALIZATION NOTE (style.highlighter.button): These strings are used inside
+# html tree of the highlighter for the style inspector button
+style.highlighter.button.label=Style
+style.highlighter.accesskey=S
+style.highlighter.button.tooltip=Inspect element styles
--- a/browser/locales/shipped-locales
+++ b/browser/locales/shipped-locales
@@ -73,11 +73,12 @@ sq
 sr
 sv-SE
 ta
 ta-LK
 te
 th
 tr
 uk
+vi
 zh-CN
 zh-TW
 zu
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -598,47 +598,28 @@ toolbar[mode="full"] .toolbarbutton-1 > 
   list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar");
 }
 #back-button[disabled="true"]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled");
 }
 
 #forward-button {
   list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar");
-  -moz-transition: 250ms ease-out;
 }
+#forward-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
+}
+
 #forward-button:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar");
 }
-
-toolbar:not([mode=icons]) #forward-button[disabled="true"] {
-  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
-}
-toolbar:not([mode=icons]) #forward-button[disabled="true"]:-moz-locale-dir(rtl) {
+#forward-button[disabled="true"]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled");
 }
 
-toolbar[mode=icons] #forward-button[disabled="true"] {
-  -moz-transform: scale(0);
-  opacity: 0;
-  pointer-events: none;
-}
-toolbar[mode=icons] #forward-button[disabled="true"]:-moz-locale-dir(ltr) {
-  margin-left: -36px;
-}
-toolbar[mode=icons] #forward-button[disabled="true"]:-moz-locale-dir(rtl) {
-  margin-right: -36px;
-}
-toolbar[mode=icons][iconsize=small] #forward-button[disabled="true"]:-moz-locale-dir(ltr) {
-  margin-left: -28px;
-}
-toolbar[mode=icons][iconsize=small] #forward-button[disabled="true"]:-moz-locale-dir(rtl) {
-  margin-right: -28px;
-}
-
 #reload-button {
   list-style-image: url("moz-icon://stock/gtk-refresh?size=toolbar");
 }
 #reload-button[disabled="true"] {
   list-style-image: url("moz-icon://stock/gtk-refresh?size=toolbar&state=disabled");
 }
 
 #stop-button {
@@ -800,27 +781,27 @@ toolbar[iconsize="small"] #back-button[d
 }
 
 toolbar[iconsize="small"] #forward-button {
   list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu");
 }
 .unified-nav-forward[_moz-menuactive] {
   list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu") !important;
 }
-toolbar[iconsize="small"]:not([mode=icons]) #forward-button[disabled="true"] {
+toolbar[iconsize="small"] #forward-button[disabled="true"] {
   list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu&state=disabled");
 }
 
 toolbar[iconsize="small"] #forward-button:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu");
 }
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu") !important;
 }
-toolbar[iconsize="small"]:not([mode=icons]) #forward-button[disabled="true"]:-moz-locale-dir(rtl) {
+toolbar[iconsize="small"] #forward-button[disabled="true"]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu&state=disabled");
 }
 
 toolbar[iconsize="small"] #stop-button {
   list-style-image: url("moz-icon://stock/gtk-stop?size=menu");
 }
 toolbar[iconsize="small"] #stop-button[disabled="true"] {
   list-style-image: url("moz-icon://stock/gtk-stop?size=menu&state=disabled");
@@ -951,21 +932,21 @@ toolbar[iconsize="small"] #feed-button {
 
 #urlbar-container {
   -moz-box-orient: horizontal;
   -moz-box-align: stretch;
 }
 
 #urlbar-icons {
   -moz-box-align: center;
-  -moz-padding-end: 2px;
 }
 
 .urlbar-icon {
   cursor: pointer;
+  padding: 0 3px;
 }
 
 #urlbar-search-splitter {
   -moz-appearance: none;
   width: 8px;
   -moz-margin-start: -4px;
 }
 
@@ -1310,93 +1291,47 @@ richlistitem[type~="action"][actiontype=
   color: GrayText;
   font-size: smaller;
 }
 
 .autocomplete-treebody::-moz-tree-cell(suggesthint) {
   border-top: 1px solid GrayText;
 }
 
-/* Go button */
+/* Combined go/reload/stop button in location bar */
 
 #go-button {
-  padding: 3px 2px 2px 2px;
-  list-style-image: url("chrome://browser/skin/Go-arrow.png");
+  padding-top: 2px;
+  padding-bottom: 2px;
 }
 
-#go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
-  -moz-transform: scaleX(-1);
-}
-
-/* Combined go/reload/stop button in location bar */
-
 #urlbar > toolbarbutton {
   -moz-appearance: none;
-  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-  margin: -1px;
-  -moz-margin-start: 0;
-  padding: 0 3px;
-  background-origin: border-box;
+  padding: 0;
   border: none;
-  -moz-border-start: 1px solid rgba(0,0,0,.35);
-  box-shadow: 0 1px 0 rgba(0,0,0,.1) inset,
-              -1px -1px 1px rgba(255,255,255,.2) inset;
-}
-
-#urlbar:-moz-locale-dir(ltr) > toolbarbutton {
-  border-top-right-radius: 2px;
-  border-bottom-right-radius: 2px;
+  cursor: pointer;
+  width: 22px;
 }
 
-#urlbar:-moz-locale-dir(rtl) > toolbarbutton {
-  border-top-left-radius: 2px;
-  border-bottom-left-radius: 2px;
+#go-button,
+#urlbar-go-button {
+  list-style-image: url("chrome://browser/skin/Go-arrow.png");
 }
 
-#urlbar > toolbarbutton:not([disabled]):active:hover,
-#urlbar-reload-button:not(:hover) {
-  -moz-border-start-style: none;
-  -moz-padding-start: 4px;
-  box-shadow: none;
-}
-
-#urlbar > toolbarbutton:not([disabled]):active:hover {
-  box-shadow: 0 0 6.5px rgba(0,0,0,.4) inset,
-              0 0 2px rgba(0,0,0,.4) inset;
-}
-
-#urlbar-go-button {
-  -moz-image-region: rect(0px, 56px, 14px, 42px);
-  background-image: -moz-linear-gradient(rgb(143,219,69), rgb(115,177,57));
-}
-
+#go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #urlbar-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   -moz-transform: scaleX(-1);
 }
 
-#urlbar-go-button:hover {
-  background-image: -moz-linear-gradient(rgb(163,232,92), rgb(137,196,81));
-}
-
 #urlbar-reload-button {
-  -moz-image-region: rect(0px, 14px, 14px, 0px);
-}
-
-#urlbar-reload-button:not([disabled]):hover {
-  -moz-image-region: rect(0px, 28px, 14px, 14px);
-  background-image: -moz-linear-gradient(rgb(137,183,233), rgb(79,130,195));
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu");
 }
 
 #urlbar-stop-button {
-  -moz-image-region: rect(0px, 42px, 14px, 28px);
-  background-image: -moz-linear-gradient(rgb(226,99,99), rgb(199,68,68));
-}
-
-#urlbar-stop-button:hover {
-  background-image: -moz-linear-gradient(rgb(237,120,120), rgb(216,92,92));
+  list-style-image: url("moz-icon://stock/gtk-stop?size=menu");
 }
 
 /* Popup blocker button */
 #page-report-button {
   list-style-image: url("chrome://browser/skin/Info.png");
 }
 
 /* Star button */
@@ -1983,8 +1918,17 @@ panel[dimmed="true"] {
   outline: 1px dashed rgba(255,255,255,0.5);
   outline-offset: -1px;
 }
 
 #highlighter-veil-container[locked] > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
   box-shadow: 0 0 0 1px black;
   outline-color: white;
 }
+
+/*
+ * need a "bumpy" background image for this!
+ */
+#inspector-horizontal-splitter {
+  background: none !important;
+  -moz-appearance: none;
+  cursor: n-resize;
+}
--- a/browser/themes/gnomestripe/browser/inspector.css
+++ b/browser/themes/gnomestripe/browser/inspector.css
@@ -197,26 +197,30 @@ code {
 }
 
 .nodeText {
   color: #333333;
 }
 
 .docType {
   position: absolute;
+  /* position DOCTYPE element above/outside the "nodeBox" that contains it */
+  /* Note: to be fixed in Bug #688439 */
   top: -16px;
   font-family: Menlo, Andale Mono, monospace;
   padding-left: 8px;
   color: #999;
   white-space: nowrap;
   font-style: italic;
 }
 
 .htmlNodeBox {
-  top: 16px;
+  /* make room for DOCTYPE element to be rendered above/outside "nodeBox" */
+  /* Note: to be fixed in Bug #688439 */
+  margin-top: 16px;
 }
 
 .nodeWhiteSpace {
   border: 1px solid LightGray;
   white-space: pre; /* otherwise the border will be collapsed around zero pixels */
   margin-left: 1px;
   color: gray;
 }
--- a/browser/themes/gnomestripe/browser/jar.mn
+++ b/browser/themes/gnomestripe/browser/jar.mn
@@ -22,17 +22,16 @@ browser.jar:
   skin/classic/browser/KUI-close.png
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
 * skin/classic/browser/pageInfo.css
   skin/classic/browser/pageInfo.png
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/Privacy-48.png
-  skin/classic/browser/reload-stop-go.png
   skin/classic/browser/searchbar.css                  (searchbar.css)
   skin/classic/browser/section_collapsed.png
   skin/classic/browser/section_collapsed-rtl.png
   skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure.png
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/Toolbar.png
deleted file mode 100644
index 922a4c4c62e8ecd1bd380a62673dbc0b429b1756..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/gnomestripe/browser/tabview/tabview.css
+++ b/browser/themes/gnomestripe/browser/tabview/tabview.css
@@ -653,15 +653,17 @@ html[dir=rtl] #otherresults {
 .inlineMatch:hover {
   opacity: 1.0;
 }
 
 .inlineMatch > img {
   -moz-margin-end: 5px;
   position: relative;
   top: 2px;
+  width: 16px;
+  height: 16px;
 }
 
 .inlineMatch > span {
   max-width: 200px;
   height: 15px;
 }
 
deleted file mode 100644
index 9fc4e1b26fd0bef86053ba1f060f75cb425fb328..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -831,23 +831,20 @@ toolbar[mode="icons"] #zoom-in-button {
 
 #urlbar[focused="true"],
 .searchbar-textbox[focused="true"] {
   border-color: -moz-mac-focusring;
   box-shadow: @focusRingShadow@;
 }
 
 #urlbar {
+  -moz-padding-end: 4px;
   border-radius: @toolbarbuttonCornerRadius@;
 }
 
-#urlbar-container:not([combined]) > #urlbar {
-  -moz-padding-end: 3px;
-}
-
 #identity-box {
   background-image: -moz-linear-gradient(hsl(0,0%,98%), hsl(0,0%,92%));
   box-shadow: 0 1px 0 hsla(0,0%,0%,.05) inset;
   -moz-border-end: 1px solid hsla(0,0%,0%,.1);
   -moz-margin-end: 3px;
   padding: 1px 4px;
 }
 
@@ -905,20 +902,41 @@ toolbar[mode="icons"] #zoom-in-button {
   -moz-margin-start: 6px;
 }
 
 .urlbar-input-box {
   -moz-margin-start: 0;
   padding: 3px 0 2px;
 }
 
+.urlbar-history-dropmarker {
+  padding: 0 3px;
+  list-style-image: url("chrome://browser/skin/urlbar-history-dropmarker.png");
+  -moz-image-region: rect(0px, 11px, 14px, 0px);
+}
+
+.urlbar-history-dropmarker[open="true"],
+.urlbar-history-dropmarker:hover:active {
+  -moz-image-region: rect(0px, 22px, 14px, 11px);
+  background-image: -moz-radial-gradient(center, circle closest-side, hsla(205,100%,70%,.3), hsla(205,100%,70%,0));
+}
+
 #urlbar-icons {
   -moz-box-align: center;
 }
 
+.urlbar-icon {
+  padding: 2px 3px;
+}
+
+.urlbar-icon[open="true"],
+.urlbar-icon:hover:active {
+  background-image: -moz-radial-gradient(center, circle closest-side, hsla(205,100%,70%,.3), hsla(205,100%,70%,0));
+}
+
 #urlbar-search-splitter {
   min-width: 8px;
   width: 8px;
   background-image: none;
   margin: 0 -4px;
   position: relative;
   height: 22px;
 }
@@ -970,33 +988,32 @@ toolbar[mode="icons"] #zoom-in-button {
 /* ----- AUTOCOMPLETE ----- */
 
 #treecolAutoCompleteImage {
   max-width: 36px;
 }
 
 .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
-  list-style-image: url("chrome://browser/skin/places/pageStarred.png");
-  width: 16px;
-  height: 16px;
+  list-style-image: url("chrome://browser/skin/places/star-icons.png");
+  -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
 .ac-result-type-keyword,
 .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
   list-style-image: url(chrome://global/skin/icons/search-textbox.png);
   margin: 2px;
   width: 12px;
   height: 12px;
 }
 
 richlistitem[selected="true"][current="true"] > hbox > .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
+  -moz-image-region: rect(0, 64px, 16px, 48px);
 }
 
 .ac-result-type-tag,
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/tag.png");
   width: 16px;
   height: 16px;
 }
@@ -1031,134 +1048,95 @@ richlistitem[type~="action"][actiontype=
   color: GrayText;
   font-size: smaller;
 }
 
 .autocomplete-treebody::-moz-tree-cell(suggesthint) {
   border-top: 1px solid GrayText;
 }
 
-/* ----- GO BUTTON ----- */
-
-#go-button {
-  list-style-image: url("chrome://browser/skin/Go-arrow.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-#go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
-  -moz-transform: scaleX(-1);
-}
-
-#go-button:hover {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-#go-button:hover:active {
-  -moz-image-region: rect(0px, 48px, 16px, 32px);
-}
 
 /* ----- COMBINED GO/RELOAD/STOP BUTTON IN LOCATION BAR ----- */
 
+#go-button,
 #urlbar > toolbarbutton {
-  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
   margin: 0;
-  -moz-margin-start: 2px;
-  padding: 0 3px;
+  -moz-padding-start: 2px; 
+  -moz-padding-end: 1px; 
   background-origin: border-box;
-  border: none;
-  -moz-border-start: 1px solid rgba(0,0,0,.25);
-  box-shadow: 0 1px 0 rgba(0,0,0,.1) inset,
-              -1px -1px 1px rgba(255,255,255,.15) inset;
-}
-
-#urlbar:-moz-locale-dir(ltr) > toolbarbutton {
-  border-top-right-radius: 2px;
-  border-bottom-right-radius: 2px;
-}
-
-#urlbar:-moz-locale-dir(rtl) > toolbarbutton {
-  border-top-left-radius: 2px;
-  border-bottom-left-radius: 2px;
-}
-
-#urlbar > toolbarbutton:not([disabled]):active:hover,
-#urlbar-reload-button:not(:hover) {
-  -moz-border-start-style: none;
-  -moz-padding-start: 4px;
-  box-shadow: none;
-}
-
-#urlbar > toolbarbutton:not([disabled]):active:hover {
-  box-shadow: @toolbarbuttonPressedInnerShadow@;
-}
-
+  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
+}
+
+#urlbar > toolbarbutton:not([disabled]):hover:active {
+  background-image: -moz-radial-gradient(center, circle closest-side, hsla(205,100%,70%,.3), hsla(205,100%,70%,0));
+}
+
+#go-button {
+  padding: 0 3px;
+}
+
+#go-button,
 #urlbar-go-button {
-  -moz-image-region: rect(0px, 56px, 14px, 42px);
-  background-image: -moz-linear-gradient(rgb(184,221,142), rgb(154,201,111) 49%, rgb(130,187,92) 51%, rgb(114,171,79));
-}
-
+  -moz-image-region: rect(0, 42px, 14px, 28px);
+}
+
+#go-button:hover:active,
+#urlbar-go-button:hover:active {
+  -moz-image-region: rect(14px, 42px, 28px, 28px);
+}
+
+#go-button:-moz-locale-dir(rtl),
 #urlbar-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   -moz-transform: scaleX(-1);
 }
 
 #urlbar-reload-button {
-  -moz-image-region: rect(0px, 14px, 14px, 0px);
-}
-
-#urlbar-reload-button:not([disabled]):hover {
-  -moz-image-region: rect(0px, 28px, 14px, 14px);
-  background-image: -moz-linear-gradient(rgb(162,207,241), rgb(111,178,225) 49%, rgb(91,159,217) 51%, rgb(62,138,200));
+  -moz-image-region: rect(0, 14px, 14px, 0);
+}
+
+#urlbar-reload-button:not([disabled]):hover:active {
+  -moz-image-region: rect(14px, 14px, 28px, 0);
+}
+
+#urlbar-reload-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
+  -moz-transform: scaleX(-1);
 }
 
 #urlbar-stop-button {
-  -moz-image-region: rect(0px, 42px, 14px, 28px);
-  background-image: -moz-linear-gradient(rgb(231,162,140), rgb(209,119,100) 49%, rgb(193,92,78) 51%, rgb(173,72,58));
+  -moz-image-region: rect(0, 28px, 14px, 14px);
+}
+
+#urlbar-stop-button:hover:active {
+  -moz-image-region: rect(14px, 28px, 28px, 14px);
 }
 
 /* POPUP BLOCKER BUTTON */
 #page-report-button {
   list-style-image: url("chrome://browser/skin/urlbar-popup-blocked.png");
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-#page-report-button:hover {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
 #page-report-button:hover:active,
 #page-report-button[open="true"] {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
+  -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
 /* STAR BUTTON */
 #star-button {
-  padding: 1px;
-  -moz-padding-start: 4px;
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-#star-button:hover {
+#star-button:hover:active,
+#star-button[starred="true"] {
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
-#star-button:hover:active {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
-}
-
-#star-button[starred="true"] {
-  -moz-image-region: rect(16px, 16px, 32px, 0);
-}
-
-#star-button:hover[starred="true"] {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-
 #star-button:hover:active[starred="true"] {
-  -moz-image-region: rect(16px, 48px, 32px, 32px);
+  -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 /* BOOKMARKING PANEL */
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
 }
@@ -2619,8 +2597,18 @@ panel[dimmed="true"] {
   background: -moz-linear-gradient(hsla(220,6%,10%,.6), hsla(210,11%,18%,.45) 75%, hsla(210,11%,30%,.4));
   box-shadow: 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15);
 }
 
 #inspector-inspect-toolbutton[checked]:hover:active,
 #inspector-tools > toolbarbutton[checked]:hover:active {
   background-color: hsla(210,8%,5%,.2);
 }
+
+/*
+ * need a "bumpy" background image for this!
+ */
+#inspector-horizontal-splitter {
+  background: none !important;
+  -moz-appearance: none;
+  cursor: n-resize;
+}
+
--- a/browser/themes/pinstripe/browser/inspector.css
+++ b/browser/themes/pinstripe/browser/inspector.css
@@ -197,26 +197,30 @@ code {
 }
 
 .nodeText {
   color: #333333;
 }
 
 .docType {
   position: absolute;
+  /* position DOCTYPE element above/outside the "nodeBox" that contains it */
+  /* Note: to be fixed in Bug #688439 */
   top: -16px;
   font-family: Menlo, Andale Mono, monospace;
   padding-left: 8px;
   color: #999;
   white-space: nowrap;
   font-style: italic;
 }
 
 .htmlNodeBox {
-  top: 16px;
+  /* make room for DOCTYPE element to be rendered above/outside "nodeBox" */
+  /* Note: to be fixed in Bug #688439 */
+  margin-top: 16px;
 }
 
 .nodeWhiteSpace {
   border: 1px solid LightGray;
   white-space: pre; /* otherwise the border will be collapsed around zero pixels */
   margin-left: 1px;
   color: gray;
 }
--- a/browser/themes/pinstripe/browser/jar.mn
+++ b/browser/themes/pinstripe/browser/jar.mn
@@ -9,17 +9,16 @@ browser.jar:
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                          (browser.css)
 * skin/classic/browser/engineManager.css                    (engineManager.css)
   skin/classic/browser/fullscreen-video.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
-  skin/classic/browser/Go-arrow.png
   skin/classic/browser/home.png
   skin/classic/browser/hud-style-check-box-checked.png
   skin/classic/browser/hud-style-check-box-empty.png
   skin/classic/browser/hud-style-dropmarker-double-arrows.png
   skin/classic/browser/hud-style-expander-closed.png
   skin/classic/browser/hud-style-expander-open.png
   skin/classic/browser/hud-style-new-folder-plus-sign.png
   skin/classic/browser/hud-style-twisties.png
@@ -40,16 +39,17 @@ browser.jar:
   skin/classic/browser/Search.png
   skin/classic/browser/section_collapsed.png
   skin/classic/browser/section_collapsed-rtl.png
   skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure-Glyph-White.png
   skin/classic/browser/keyhole-circle.png
   skin/classic/browser/Toolbar.png
   skin/classic/browser/toolbarbutton-dropmarker.png
+  skin/classic/browser/urlbar-history-dropmarker.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css               (feeds/subscribe-ui.css)
   skin/classic/browser/feeds/feedIcon.png                   (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                 (feeds/feedIcon16.png)
   skin/classic/browser/feeds/videoFeedIcon.png              (feeds/videoFeedIcon.png)
   skin/classic/browser/feeds/videoFeedIcon16.png            (feeds/videoFeedIcon16.png)
@@ -71,17 +71,16 @@ browser.jar:
   skin/classic/browser/places/toolbarDropMarker.png         (places/toolbarDropMarker.png)
   skin/classic/browser/places/folderDropArrow.png           (places/folderDropArrow.png)
   skin/classic/browser/places/editBookmarkOverlay.css       (places/editBookmarkOverlay.css)
   skin/classic/browser/places/minus.png                     (places/minus.png)
   skin/classic/browser/places/minus-active.png              (places/minus-active.png)
   skin/classic/browser/places/plus.png                      (places/plus.png)
   skin/classic/browser/places/plus-active.png               (places/plus-active.png)
   skin/classic/browser/places/starPage.png                  (places/starPage.png)
-  skin/classic/browser/places/pageStarred.png               (places/pageStarred.png)
   skin/classic/browser/places/searching_16.png              (places/searching_16.png)
   skin/classic/browser/places/starred48.png                 (places/starred48.png)
   skin/classic/browser/places/unstarred48.png               (places/unstarred48.png)
   skin/classic/browser/places/unfiledBookmarks.png          (places/unfiledBookmarks.png)
   skin/classic/browser/places/twisty-open.gif               (places/twisty-open.gif)
   skin/classic/browser/places/twisty-closed.gif             (places/twisty-closed.gif)
   skin/classic/browser/places/tag.png                       (places/tag.png)
   skin/classic/browser/places/downloads.png                 (places/downloads.png)
deleted file mode 100644
index 9f025f744eff57c84c1fc948670962941cec09f8..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
index 30d73dd79991103380627a80e3557b8bf56dc7f0..e5610bace4e1ce8b011df55b348bea29a4ecf61f
GIT binary patch
literal 1906
zc$@)p2aWiNP)<h;3K|Lk000e1NJLTq002M$000mO1^@s6rssJn000L$Nkl<Zc-ozp
zeNdFw6~?z?AiA&!h>%@HVj3+mp`gT7Obrp@+C<c3hN`2f8o#ojNoZt}n&Oy5{3swq
zKxJ7lM1g?CAOwsERzwl35f%Zvl;uOj;sVaX3Q_py>ACORrAzwmHj$a%aORxn4D+1(
z@m?hH@$sS@K73e`l$4Z5WKe8V=eDuL9(hEDJq-YxJf&3U5g9CgLUM_8dm2$w2?+_3
zBS(%XFJ8Qe^73*}96&ko0c<F;h4T66%Xscjmq2lVEgb++9F&ntr<KWYTJa?ulo0_>
zV17RFBgg+24InWwQIeaRTi)5(iH?pA<mBWO+u8-L?|duH`hv0$Z9A4|450X|4V_H`
z(vX-kw`~{TwzUk>C7+&sf&mm0CeCSFI?>fnPEJ<V)YQOiHe+yb5ZA6<v$+dg*PRwa
z`Rk~1tSTSDTg1!{%0RgQ5uNzzFNjpiR-b@JbOStg=^*<<CMXy13s--D=;fGg?Sh}0
zpFn3mu(-JR?_FJ8c>MUW0E-I?3oo!5(~KLy+FWZU5P`01vMWN;{)OO@VSy#b^l)99
zbDq_>#-bm8=I3vpKxaPC`OO0rQ+8Z}*I!x$+#@eS`bO+|R%04F##wD1@f~3#dKf$|
z;<a)8?4pa%YPCiA`T4CSB_*h?u7<&25LSO=WQ4(DmJ@1BLt1@$dU_G{ZkfFLWCT$}
zw7RZ2g_%1_u=KbdtIj{dto%XDCf=f$azc%1NUP^PTb^2=j&NN3QPCvTXRXq(c*s_3
z;jTUp&)rRUW^Wrqh~3oR3pMHTI80pnnV$7f?-l^tiN6tz#1Jva4R;fFIo&)r$Td3N
zvq_Sfnb}icUyttYZuIr_VPIeY!^6YE%JTuRnB|08Z*Q-#BkJnvK!fX;9G-FS^`lK#
zU(^qE<p?%aJjSYX!+1W&gy*saFpFZ!v6`S+SJ;Qp)J9ORVd87i_gpvS!6T{?o*!SQ
z6>lKZddR<MgM42*gJJ{Au^Q8m_7Yq-=HlsBKQaJlA?_1hM90shi}&1;kdY0wwYBC4
z4<1-d+l6WR`};w6go%3BIlb~h=;XK4O#bmYEG{r(X4V6{FpYm)J*cPO>6Q0`SA47u
zb=z>t<h4f!#ld!>(>|o}iqXOCk6HaIKhXvQ@FSXuHlp=s($06x90>qtolduz%nZ>A
z|6lValL<F&+%PvZG;9LkB#}rQq>FZ{Tvn!-Up!QY-{lYB*^E9>SZ&UMA3$1@G$=BZ
z^#X8M{z<0FecNfXd|xxB#or;iY;cEmLlfM$=9yOR{wkF9^0VyEhtNXYwg=bTB3`oG
z0TKYRn>TN&o12?|92y$3IRI8`Y-}8217m|@y)G-hl<*;hxvWSU3Od;b-^@PDNHdOO
z&WS!O{MrbYuq3l|VT6kHuwHJ!VOexWn8$~uL(}5hF+J&?DAVHF;IZ>NbJ(9xs4V8K
zc$ph(<iEveB6QYY1a|XVhTwAf^5sI>-Zlro?ug3D$~-nO>JfTfmd6XwkE2ZWMb@0X
z)tLWPKfF_V#<Ae*e)z^zL8kgVjhW|t)&?A>Z`Ncf4*rwrMUfw9g7>?}(=6si*w2mD
ziNR}otb44#{gTqs(ki;-Y!1L^G=e^n6-;Y+6Ug2;_+Ne>TfQjq7W~qT!ixRoXb)zm
z_6YHBHAVy_=pb7bTWZxa&VbvR#BxR4ZMI`XQT&y@2CEZFxn7KHZuB2w@LHDWJAfWK
zl8TCof78Q9F#G7yBevxkEM__3{FjxL8Av<UCtJ2}4B7#AgEgIaIsHC@GK`p;a2xXy
z^+Ft|xsSz14QvPKt$M}}U}O5-87ZB@ec_XCgwOGA;vSyW8iky9vH|WJkL&FW;3{#Q
zs3B@u%;0eik1vQ0ASETmnQnH$FuM%f+uK3+LMvU4o%9?Qat|Lq#MP@;M@j2AHh`sj
zEjYWqxgW1)_QEf=4%0WEf-E$qN%s1l7VmA}!2jSa1ZVVsbfbi$r4N9>)31H#JY{D-
ze6&57ajcU*e>G6-I@_d(E^1N4)(SZvO*f`Q=8TfoQN+9xzvQa9;d<g*qL{CI4X-s4
zj{kE6G#bskD_5?dt*wnccsj}G=lo)sUsY9gh=$X}hC=rQNc)222snqv5JbqQbEmIO
zCga(lxJ9~9t){a+mGG1<ij1P1!Fy{C<l%{MUb5Sl_l+5lsiNn)M`lB@zZPCQvs(gp
z98&|BN_hIcmlEamey%}r;5yvZC*h)s_7xd`A2;^{Q9^`S)xwEVUTY?1S!!ed`qB$;
z_L(zhs+d{+miA0dO_eYJJTp2vx{%Dr6%-Uy(OofPYycs<1*Aa{K2E{W=cGaEC}x_N
zWU0}Q^j8xn%c-_Y&@9+4=e02dzUyOsriRC!^HZlpvA<1BvN9mOV>HQMdM|m`)bM@h
s0^U6&7a0J{r7^R1p2=&t#>0~T56HDsY7y&<qW}N^07*qoM6N<$g4uqwqW}N^
index 0406477f52e20d343d5d7903aee28d6d05bcbc63..699ef2326e0561a10742dff28711cfc4c77edd56
GIT binary patch
literal 912
zc$@){18@9^P)<h;3K|Lk000e1NJLTq001fg000~a1^@s67vL@H000A5Nkl<Zc-rOG
z&x;dh9LMoqdmV`TXl&|eN<k0=uRRHS68awqLQkFwUMdQ9!|pQ6?y^6~{t8%nXjd$@
z7O(yda}pkS>a>=pq_j<I8wnKxg-_pyQIpLxlUZb;(!=YJZ=U&nznNubKW|BTBOL1R
z-rinvREu;vo%49V(xN*%J2l+KQwR<n7$i`^kjpTEdW4TE+<>#_p^Lb5(XFkmS@fWQ
zA%uxVcXxNMK(H_)^$011kINR#Z-j&3Q4cP<xw%<G55hj0dA)%Q%1tb~y}fPVAXpeg
zNFjK{)9z}!-8QfgGSnv)U0+}CK-eGslFtb=Dl~*2VBjEFq!2uUOP2JpBYn)+qN}T`
zT?h?HFKP*NP|=XJwKW3=<vik&^Qyq2_HxnX<>ev%d7Y=0>m#I4&cYO(Us+jE1sUou
z7fq7HLuej)NpM|+6oQ9?U}38ESz21E;Sq!k^|3$w;^JZ(!o5%f>Tee{Usza(kV5c?
zVc;NGOkI{41{Q+D<ij*SKVL^5!hM{}>lg3<9v_AVv|6pJ5Io|Mx`Bgmy=+N(8(0Vq
z;n!2%_#y^SKp!6z4Vs&qyM{PWHz-{)pFN8q9O|Jn2OBrA0f*;!h!*mTj}9{~!Ib?A
z{ZILSE!zBMaAfJ}y>I_Mk80`Z%vaB9xQ(X}96B&apn@TnVFL9CQ`1xP>%m#{&_#KA
zI{oEev*<wqLkOA}a&K@2f`u8WM@S)*rl&J^pBXp^9^u#1sn7qYp$B0f&Ai?~2IVFe
zJ$<Kd;2>BSL`WfcObsUS7k?U92pJ|{`0sXl9SCFTiO&f%Dl~*2VBjEFq!2tRr>9e&
z{cd0(Sd6EqciO*nAv7ets3p)rMME09zZp0v=Mfj5|5X)Oyh=|e*SbTzR(d+Q{&R#B
z%2}Ag^BbI*F2k$zbYjVS2!~Hk;JOGY1P=$nqH=nwFaK1-BM2GB)6?<!Cv6D9^hCWd
zJ(<>%2q^@Q7zPf4#nfewFZ^g=AvjDvOf$E>ucHs)KF;O!3wQvJV0xOlnO%k85s%aj
z9E8$fVm|%Bz(Q~+PEW^fJidql6wpUudOG&Wch?XH>IPHO)69*>vlzl*dU~R10~>I7
mj)!RB$kP)uF5%5jPyYfEhJw@=xn~am0000<MNUMnLSTZ9DVa|I
--- a/browser/themes/pinstripe/browser/tabview/tabview.css
+++ b/browser/themes/pinstripe/browser/tabview/tabview.css
@@ -643,15 +643,17 @@ html[dir=rtl] #otherresults {
 .inlineMatch:hover {
   opacity: 1.0;
 }
 
 .inlineMatch > img {
   -moz-margin-end: 5px;
   position: relative;
   top: 2px;
+  width: 16px;
+  height: 16px;
 }
 
 .inlineMatch > span {
-  max-width:200px;
+  max-width: 200px;
   height: 15px;
 }
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..53bd1cb0ca3a69eb5846f44d5ab2c7bacdc70e62
GIT binary patch
literal 389
zc$@)+0eb$4P)<h;3K|Lk000e1NJLTq000&M000gM1^@s6D>3ZN0003@Nkl<Zc-rmL
zy-LGS7zW_hic$)?skEpF1*cv>cb%QR1h2q?gOfOjgNx7$@B%Cv!Of<mttPFMBS~Xg
zn$(2oV!<i|3$I@a(SYfXgW%xsa5!*y;rlru0Keit_$&A{mrHZpMUO8OAPB{8@;r~T
zbIzf6spafbn)C>Ua7fBcpDC8gCyJs#*L4_%;pyLHSw^XRodpmhp`~McpJ0g0XDhnh
z*Hx)fZ5T!ywrzX*;gVEgj^nI&B`zLWQ=~=^Kp+u+k0%<<<|boz@1|+?L*0`x_SVw%
z4FF+({}Ko76mbZAUPv-%crj6`WHn8LWm!<wdsM5phXA5M!-?;;G6X(b1GmC0nZj9J
zRUwMf4S@KdsW0p{B19k{m!sA#?3R9&=N}(TbA8lG&F`p5qLaipdmMf4bk+b&kF&3H
j+x6L>e2~dM`|~~kic*Ihb0ca%00000NkvXXu0mjf+@!4A
index 0bbc27e2294d83c7fce14973966bd4817074e46a..8c4f4d96f06ed2067f62d012d1f91ab1b98503f8
GIT binary patch
literal 758
zc$@+D0tx+zP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^0008LNkl<Zc-p1a
z-%C?r7zgm?Hh&b+mA_JFOKnk67u`i5NP-~<g8xAfT?lm}X%V_H*IL?ILt8-@k`Y4P
zMi=2lbZ1>Ch4aT0J4aJHy3(6nJ$;|?yz6xgZFTYCeV_L{XYcnM&dxbNME~UKM4Al>
zzzz;@Lg5~q(r1_RZ0R41#bWJX0qsZn+Oz=n6K{4p>R<0zK!3qPe><O-2{1f7YyjoK
z(5~K5H~z~6%7ZC)q97e$XlRJ?rLG>F$6qEXUwmFBKr|X9(a3c!($}PV^#JbCO=6z0
zt!C84P^2kVJTJCdq<43KNF+jx=len56<F5;9DDYIm~1Eyo5%`LH)35w@5ZhG;c%Eh
z51`fy!3$r2c$B(I4^SIhBPN@xr`*vvzJ193WZjQrUx9c;-G64pa!UYvdZU4X0b=^*
zs7qiHD2K^lBz8~*sEMu;Q-3pd{`ux%tbGE?=8CR<zA*OlNG3oi6e6YvK<$_HIk*U=
zV8|NW_p1Qa4-8`Z?RSOEyRtrC_h_Z*+|!L}%uUtH$2n+vvtTetOb>uMA?r<03R6-W
zGA2}js(Z`Cv<I&c^&*ga&}cdPc&!@qY3Va%T+9Rr1Og;lP^-0+thr%I<}=`uc}fMS
z>|Y{Q8(MBt4;$(fkSA=O#5gTIanHk*2AQY$-1KICzkd}-<AMZ`c5DVJpjqmKdJ$E4
z7rp~2p22So7#D%GWARgAv(^<@YL)uW9q{>l%`gWfI-yIelTZ(BV8~jR`X^L*6PL^H
z%+CX{qrD3^Dti}tq-M$b;PUAjU!ntZ69~t<)kBz**Xy;zd!QWNf<60VB%G}E)pkkw
z?b(U4TeFl6>+dd&ZWXEbz@Ga0`a0nQSTc6X*HE#E0!9BiR`kqXFYk_jEV~)Ec;s4p
oSJ?jdbF8!JR1o1%^iMJS4R$)?zL<C|I{*Lx07*qoM6N<$f*~7VO8@`>
deleted file mode 100644
index 1f76506488a6613146fc335961b739efc66eca10..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 851ae1615dd175ade57fd2b2ac0caa9b7c4b5b17..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -875,26 +875,24 @@ toolbar[mode="full"] .toolbarbutton-1 > 
   border-radius: 10000px;
   padding: 5px;
   border: none;
   background-image: -moz-linear-gradient(rgba(251,252,253,.97), rgba(246,247,248,.5) 49%, 
                                          rgba(231,232,233,.45) 51%, rgba(225,226,229,.2));
   box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
               0 0 0 2px rgba(255,255,255,.1) inset,
               0 0 0 1px rgba(0,0,0,.15),
-              0 1px 0 rgba(0,0,0,.4),
-              0 1px 1px rgba(0,0,0,.3);
+              0 1px 0 rgba(0,0,0,.4);
 }
 
 @navbarLargeIcons@ #back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
   box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
               0 0 0 2px rgba(255,255,255,.1) inset,
               0 0 0 1px hsla(190,50%,40%,.3),
               0 1px 0 rgba(0,0,0,.4),
-              0 1px 1px rgba(0,0,0,.3),
               0 0 5px 1px hsl(190,90%,80%);
 }
 
 @navbarLargeIcons@ #back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
 @navbarLargeIcons@ #back-button[open="true"] > .toolbarbutton-icon {
   box-shadow: 0 0 6.5px rgba(0,0,0,.4) inset,
               0 0 2px rgba(0,0,0,.4) inset,
               0 0 0 1px rgba(0,0,0,.65),
@@ -1178,31 +1176,32 @@ toolbar[mode="full"] .toolbarbutton-1 > 
   -moz-image-region: rect(16px, 48px, 32px, 32px);
 }
 #close-button:hover:active {
   -moz-image-region: rect(32px, 48px, 48px, 32px);
 }
 
 /* ::::: Location Bar ::::: */
 
-#urlbar {
-  width: 7em;
-  min-width: 7em;
-}
-
 #urlbar,
 .searchbar-textbox {
   -moz-appearance: none;
   margin: 1px 3px;
-  padding: 2px;
+  padding: 0;
   background-clip: padding-box;
   border: 1px solid ThreeDShadow;
   border-radius: 2.5px;
 }
 
+#urlbar {
+  width: 7em;
+  min-width: 7em;
+  -moz-padding-end: 2px;
+}
+
 @media all and (-moz-windows-default-theme) {
   #urlbar,
   .searchbar-textbox {
     @navbarTextboxCustomBorder@
   }
 }
 
 #urlbar:-moz-lwtheme,
@@ -1227,44 +1226,55 @@ html|*.urlbar-input:-moz-lwtheme:-moz-pl
   -moz-box-align: stretch;
 }
 
 .urlbar-textbox-container {
   -moz-box-align: stretch;
 }
 
 #urlbar-icons {
-  height: 18px;
   -moz-box-align: center;
 }
 
 .urlbar-icon {
-  padding: 0 2px !important;
+  padding: 3px 3px;
+}
+
+.searchbar-engine-button,
+.search-go-container {
+  padding: 2px 2px;
 }
 
 .urlbar-icon:-moz-system-metric(touch-enabled) {
   -moz-margin-end: 1px !important;
   padding: 0 3px !important;
 }
 
+.urlbar-icon:hover {
+  background-image: -moz-radial-gradient(center, circle closest-side, hsla(200,100%,70%,.3), hsla(200,100%,70%,0));
+}
+
+.urlbar-icon[open="true"],
+.urlbar-icon:hover:active {
+  background-image: -moz-radial-gradient(center, circle closest-side, hsla(200,100%,70%,.1), hsla(200,100%,70%,0));
+}
+
 #urlbar-search-splitter {
   min-width: 6px;
   -moz-margin-start: -3px;
   border: none;
   background: transparent;
 }
 
 #urlbar-search-splitter + #urlbar-container > #urlbar ,
 #urlbar-search-splitter + #search-container > #searchbar > .searchbar-textbox {
   -moz-margin-start: 0;
 }
 
 #urlbar-display-box {
-  margin-top: -2px;
-  margin-bottom: -2px;
   -moz-border-end: 1px solid #AAA;
   -moz-margin-end: 3px;
 }
 
 #urlbar-display {
   margin-top: 0;
   margin-bottom: 0;
   color: GrayText;
@@ -1272,18 +1282,16 @@ html|*.urlbar-input:-moz-lwtheme:-moz-pl
 
 /* identity box */
 
 #identity-box {
   background-image: -moz-linear-gradient(hsl(0,0%,98%), hsl(0,0%,92%));
   box-shadow: 0 1px 0 hsla(0,0%,0%,.05) inset;
   -moz-border-end: 1px solid hsla(0,0%,0%,.1);
   padding: 2px;
-  margin: -2px;
-  -moz-margin-end: 0;
 }
 
 #identity-box:-moz-locale-dir(ltr) {
   border-top-left-radius: 1.5px;
   border-bottom-left-radius: 1.5px;
 }
 
 #identity-box:-moz-locale-dir(rtl) {
@@ -1347,31 +1355,37 @@ html|*.urlbar-input:-moz-lwtheme:-moz-pl
   -moz-margin-start: 1px;
   -moz-margin-end: 3px;
 }
 
 /* Location bar dropmarker */
 
 .urlbar-history-dropmarker {
   -moz-appearance: none;
-  padding: 0 1px;
+  padding: 0 3px;
   background-color: transparent;
   border: none;
   width: auto;
-  list-style-image: url(mainwindow-dropdown-arrow.png);
-  -moz-image-region: rect(0, 13px, 11px, 0);
+  list-style-image: url("chrome://browser/skin/urlbar-history-dropmarker.png");
+  -moz-image-region: rect(0px, 11px, 14px, 0px);
 }
 
 .urlbar-history-dropmarker:-moz-system-metric(touch-enabled) {
   min-width: 6.4mozmm;
 }
 
+.urlbar-history-dropmarker:hover {
+  background-image: -moz-radial-gradient(center, circle closest-side, hsla(205,100%,70%,.3), hsla(205,100%,70%,0));
+  -moz-image-region: rect(0px, 22px, 14px, 11px);
+}
+
 .urlbar-history-dropmarker:hover:active,
 .urlbar-history-dropmarker[open="true"] {
-  -moz-image-region: rect(0, 26px, 11px, 13px);
+  background-image: -moz-radial-gradient(center, circle closest-side, hsla(205,100%,70%,.1), hsla(205,100%,70%,0));
+  -moz-image-region: rect(0px, 33px, 14px, 22px);
 }
 
 /* page proxy icon */
 
 #page-proxy-favicon,
 #urlbar-throbber {
   width: 16px;
   height: 16px;
@@ -1464,97 +1478,83 @@ richlistitem[type~="action"][actiontype=
   color: GrayText;
   font-size: smaller;
 }
 
 .autocomplete-treebody::-moz-tree-cell(suggesthint) {
   border-top: 1px solid GrayText;
 }
 
-/* go button */
-
-#go-button {
-  list-style-image: url("chrome://browser/skin/Go-arrow.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-#go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
-  -moz-transform: scaleX(-1);
-}
-
-#go-button:hover {
-  -moz-image-region: rect(16px, 16px, 32px, 0px);
-}
-
 /* combined go/reload/stop button in location bar */
 
+#go-button,
 #urlbar > toolbarbutton {
   -moz-appearance: none;
-  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-  margin: -2px;
-  -moz-margin-start: 0;
-  padding: 0 3px;
+  padding: 0 2px;
   background-origin: border-box;
   border: none;
-  -moz-border-start: 1px solid rgba(0,0,0,.25);
-  box-shadow: 0 1px 0 rgba(0,0,0,.1) inset,
-              -1px -1px 1px rgba(255,255,255,.25) inset;
-}
-
-#urlbar:-moz-locale-dir(ltr) > toolbarbutton {
-  border-top-right-radius: 1.5px;