Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Mon, 14 May 2012 12:10:12 -0700
changeset 109779 50177d59c0e173e03d93ef7a3b67a704f1a1cb34
parent 109778 1ac14ac500373b60e22eb3cf13d301913f7de986 (current diff)
parent 96130 498d2784a240243e991e1b5f12c33a6657b6520f (diff)
child 109780 2c498ebb0feb8a1e4e25fcba6ff42db16f2e18ef
push id2248
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 19:23:44 +0000
treeherdermozilla-aurora@118a3b748323 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone15.0a1
Merge from mozilla-central.
accessible/accessible-docs.html
accessible/src/atk/ApplicationAccessibleWrap.h
accessible/src/base/nsAccessNode.h
accessible/src/base/nsDocAccessible.cpp
accessible/src/generic/ApplicationAccessible.h
browser/base/content/browser.js
browser/devtools/jar.mn
browser/devtools/webconsole/HUDService.jsm
browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
browser/devtools/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
caps/src/nsSecurityManagerFactory.cpp
configure.in
content/base/src/nsDOMFile.cpp
content/base/src/nsDOMParser.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsWebSocket.cpp
content/canvas/src/CustomQS_WebGL.h
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
content/html/content/src/nsHTMLCanvasElement.cpp
content/xtf/src/nsXTFElementWrapper.cpp
content/xtf/src/nsXTFElementWrapper.h
dom/base/nsDOMClassInfo.cpp
dom/ipc/ContentParent.cpp
dom/plugins/ipc/PluginInstanceParent.h
dom/src/json/nsJSON.cpp
dom/wifi/WifiWorker.js
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerScope.cpp
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextProviderCGL.mm
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/gl/GLContextProviderOSMesa.cpp
gfx/gl/GLContextProviderWGL.cpp
gfx/thebes/gfxBlur.cpp
gfx/thebes/gfxPlatform.cpp
js/jsd/jsd_val.c
js/jsd/jsd_xpc.cpp
js/src/configure.in
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi.cpp
js/src/jscompartment.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jstypedarray.cpp
js/src/jstypedarray.h
js/src/jstypedarrayinlines.h
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/src/Makefile.in
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/XPCDebug.cpp
js/xpconnect/src/XPCQuickStubs.cpp
js/xpconnect/src/XPCVariant.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/wrappers/AccessCheck.cpp
layout/reftests/selection/reftest.list
memory/jemalloc/jemalloc.c
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Makefile.in
modules/libpref/src/init/all.js
mozglue/android/nsGeckoUtils.cpp
netwerk/base/public/nsNetUtil.h
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsDNSService2.h
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
netwerk/protocol/viewsource/nsViewSourceChannel.h
netwerk/test/TestProtocols.cpp
testing/mochitest/ssltunnel/ssltunnel.cpp
toolkit/mozapps/installer/packager.mk
uriloader/prefetch/OfflineCacheUpdateParent.cpp
widget/gtk2/nsAppShell.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/threads/nsThread.cpp
deleted file mode 100644
--- a/accessible/accessible-docs.html
+++ /dev/null
@@ -1,1007 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
-  <title>Implementing an MSAA Server - How Mozilla Does It, and
-Practical Tips for Developers</title>
-</head>
-<body>
-<h1>Implementing an MSAA Server</h1>
-<h2>Practical Tips for Developers, and How Mozilla Does It<br>
-</h2>
-<h2>Contents</h2>
-<div style="margin-left: 40px;">
-<p>This document is for people working to support MSAA in an
-application in order to make it accessible with 3rd party assistive
-technologies, as well as for hackers wishing to be involved in Mozilla's
-MSAA support specifically.<br>
-You may also wish to read <a
- href="http://www.mozilla.org/projects/ui/accessibility/vendors-win.html">Gecko
-Info for Windows Accessibility Vendors</a>, a primer for vendors of 3rd
-party accessibility software, on how MSAA clients can utilize Gecko's
-MSAA support.</p>
-<a href="#intro">1. Intro: What is MSAA</a><br>
-<br>
-</div>
-<div style="margin-left: 40px;"><a href="#cheatsheets">2. Deciding
-Which MSAA Features to Support</a><br>
-<div style="margin-left: 40px;"><big></big><a href="#methods">Methods</a><br>
-<a href="#events">Events</a><br>
-<a href="#states">States</a><br>
-<a href="#roles">Roles</a><br>
-<a href="#objid">Object Identifiers</a><br>
-<br>
-</div>
-<a href="#quirks">3. </a><a href="#quirks">MSAA's Quirks and
-Workarounds</a><br>
-<div style="margin-left: 40px;"><a href="#Crash_prone">MSAA can be
-crash prone</a><br>
-<a href="#Hacky_caret_tracking_not_working">Hacky caret tracking not
-working</a><br>
-<a href="#Event_window_confusion">Event window confusion</a><br>
-<a href="#Confusion_with_system-generated_events">Confusion with
-system-generated events</a><br>
-<a href="#Hacky_caret_tracking_not_working">No unique child ID for
-object in window</a><br>
-<a href="#Not_all_MSAA_features_utilized_by_3rd">Not all MSAA features
-utilized by 3rd party vendors</a><br>
-<a href="#Missing_functionality_in_MSAA">Missing functionality in MSAA</a><br>
-<a href="#Dueling_text_equivalents">Dueling text equivalents</a><br>
-<a href="#Issues_with_Links">Issues with Links</a><br>
-<a href="#MSAA_Implementation_is_Not_Performant">Performance Problems</a><br>
-<a href="#Differing_client_implementations">Differing client
-implementations</a><br>
-<a href="#Undocumented_Window_Class_Usage">Undocumented Window Class
-Usage</a><br>
-<a href="#Vendor_quirks">Vendor quirks</a><br>
-<br>
-</div>
-<a href="#geckoimpl">4.
-Example: How Gecko and Mozilla Implement MSAA</a><br>
-<div style="margin-left: 40px;"><a
- href="#Creation_of_IAccessible_Objects">Creation
-of IAccessible Objects</a><br>
-<a
- href="#The_Accessible_Tree_vs._the_DOM_Tree">The
-Accessible Tree vs. the DOM Tree</a><br>
-<a
- href="#The_Implementations_Behind_IAccessible">The
-Various Implementations of IAccessible</a><br>
-<a
- href="#Generating_MSAA_Events">Generating
-MSAA Events</a><br>
-<br>
-</div>
-<a href="#feedback">5. Feedback</a><br>
-</div>
-<h2><a name="intro"></a>1. Intro: What is MSAA?</h2>
-<ul>
-  <p>MSAA is the <a
- href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaastart_9w2t.asp?frame=true">Microsoft
-Active Accessibility (MSAA) API</a> , used on Windows operating systems.
-to support assistive technologies for users with disabilities. <br>
-  </p>
-  <p>Third party assistive technology, such as screen readers, screen
-magnifiers and voice input software, want to track what's happening
-inside Mozilla. They needs to know about focus changes and other events,
-and it needs to know what objects are contained in the current document
-or dialog box. Using this information, a screen reader will speak out
-loud important changes to the document or UI, and allow the user to
-track where they navigate. The screen reader user can navigate the web
-page using screen reader commands or browser commands, and the two
-pieces of software must remain in sync. Some screen readers can even
-show information on a <a href="http://www.deafblind.com/display.html">refreshable
-braille display</a>. Screen magnifiers will zoom to the focus, keeping
-it on the screen at all times, or even allow the user to enter a special
-low vision document reading mode, with a variety of features such as
-ticker mode where text is streamed on a single line.&nbsp; Finally,
-voice dictation software needs to know what's in the current document or
-UI in order to implement "say what you see" kinds of features.<br>
-  <br>
-On Microsoft Windows, these kinds of assistive technology acquire this
-necessary information via a combination of&nbsp; hacks, MSAA and
-proprietary DOMs. MSAA is supposed to be the "right way" for
-accessibility aids to get information, but sometimes the hacks are more
-effective. For example, screen readers look for screen draws of a
-vertical blinking line, to determine the location of the caret. Without
-doing this, screen readers would not be able to let the user know where
-there caret has moved to in most programs, because so many applications
-do not use the system caret (Gecko does not). This is so commonly done,
-that no one even bothers to support the MSAA caret, after all the hack
-is general solution works with pretty much all applications.</p>
-  <p>MSAA provides information in several different ways: </p>
-  <ol>
-    <li>A COM interface (IAccessible) that allows applications to
-expose the tree of data nodes that make up each window in the user
-interface currently being interacted with and</li>
-    <li>Custom interface extensions via interfaces via QueryInterface
-and QueryService. This can provide assistive technology with contextual
-information specific to your object model. For example, Gecko support
-ISimpleDOMNode to provide information about the DOM node for an
-accessible object.<br>
-    </li>
-    <li>A set of system messages  that confer accessibility-related
-events such as focus changes, changes to document content and state
-changes in UI objects like checkboxes.<br>
-    </li>
-  </ol>
-  <p></p>
-  <p> To really learn about MSAA, you need to download the entire <a
- href="http://msdn.microsoft.com/library/default.asp?URL=/downloads/list/accessibility.asp">MSAA
-SDK</a>. Without downloading the SDK, you won't get the extremely
-useful tools, which help a great deal in the learning process. The
-Accessible Event Watcher shows what accessible events are being
-generated by a given piece of software. The Accessible Explorer and
-Inspect Object tools show the tree of data nodes the Accessible object
-is exposing through COM, and what the screen boundaries of each object
-are. In addition, MSDN has improved their <a
- href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaastart_9w2t.asp">MSAA
-documentation</a>.<br>
-  </p>
-</ul>
-<h2><a name="cheatsheets"></a>2. Deciding Which MSAA Features to Support<br>
-</h2>
-<h2 style="margin-left: 40px;"><a name="methods"></a>MSAA Methods -
-Cheat Sheet for Developers</h2>
-<div style="margin-left: 40px;"> </div>
-<ul style="margin-left: 40px;">
-  <p> The IAccessible interface is used in a tree of IAccessible's, each
-one representing a data node, similar to a DOM. </p>
-  <p> Here are the methods supported in IAccessible - a minimal
-implementation would contain those marked "<span
- style="font-weight: bold;">[important]</span>" :<br>
-  </p>
-  <ul>
-    <li>get_accParent: Get the parent of an IAccessible. <span
- style="font-weight: bold;">[important]</span><br>
-    </li>
-    <li>get_accChildCount: Get the number of children of an
-IAccessible. <span style="font-weight: bold;">[important]</span></li>
-    <li>get_accChild: Get the child of an IAccessible. <span
- style="font-weight: bold;">[important]</span></li>
-    <li>get_accName: Get the "name" of the IAccessible, for example the
-name of a button, checkbox or menu item. <span
- style="font-weight: bold;">[important]</span></li>
-    <li>get_accValue: Get the "value" of the IAccessible, for example a
-number in a slider, a URL for a link, the text a user entered in a
-field. <span style="font-weight: bold;">[important]</span></li>
-    <li>get_accDescription: Get a long description of the current
-IAccessible. This is not really too useful.</li>
-    <li>get_accRole: Get an enumerated value representing what this
-IAccessible is used for, for example. <br>
-    </li>
-	  is it a link, static text, editable text, a checkbox, or a table
-cell, etc. <span style="font-weight: bold;">[important]</span><span
- style="font-weight: bold;"></span><li>get_accState: a 32 bit field
-representing possible on/off states, such as focused, focusable,
-selected, selectable, visible, protected (for passwords), checked, etc. <span
- style="font-weight: bold;">[important]</span> </li>
-    <li>get_accHelp: Get context sensitive help for the IAccessible.</li>
-    <li>get_accHelpTopic: We don't use this, it's only if the Windows
-help system is used.</li>
-    <li>get_accKeyboardShortcut: What is the keyboard shortcut for this
-IAccessible (underlined alt+combo mnemonic)<br>
-    </li>
-    <li>get_accFocus: Which child is focused? <span
- style="font-weight: bold;">[important]</span></li>
-    <li>get_accSelection: Which children of this item are selected?</li>
-    <li>get_accDefaultAction: Get a description or name of the default
-action for this component, such as "jump" for links.</li>
-    <li>accSelect: Select the item associated with this IAccessible. <span
- style="font-weight: bold;">[important]</span></li>
-    <li>accLocation: Get the x,y coordinates, and the height and width
-of this IAccessible node. <span style="font-weight: bold;">[important]<br>
-      </span></li>
-    <li>accNavigate: Navigate to the first/last child, previous/next
-sibling, up, down, left or right from this IAccessible. <span
- style="font-weight: bold;">[important, </span><span
- style="font-weight: bold;">but no need to implement up/down/left/right</span><span
- style="font-weight: bold;">]</span></li>
-    <li>accHitTest: Find out what IAccessible exists and a specific
-coordinate.</li>
-    <li>accDoDefaultAction: Perform the action described by
-get_accDefaultAction.</li>
-    <li>put_accName: Change the name.</li>
-    <li>put_accValue: Change the value.</li>
-  </ul>
-</ul>
-<div style="margin-left: 40px;"> </div>
-<h2 style="margin-left: 40px;"><a name="events"></a>MSAA Events Cheat
-Sheet<br>
-</h2>
-<div style="margin-left: 40px;"> </div>
-<ul style="margin-left: 40px;">
-  <p>For information on what each event does, see the <a
- href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp">MSDN
-Event Constants page</a>.</p>
-  <p>Check with your assistive technology partners to find out what
-events you need to support. There's a very good chance they won't ask
-for more than the events marked <span style="font-weight: bold;">[important]</span>:<br>
-  </p>
-</ul>
-<div style="margin-left: 40px;"> </div>
-<table
- style="text-align: left; width: 75%; margin-right: auto; margin-left: auto;"
- border="0" cellspacing="2" cellpadding="2">
-  <tbody>
-    <tr>
-      <td style="vertical-align: top;">EVENT_SYSTEM_SOUND<br>
-EVENT_SYSTEM_ALERT<br>
-EVENT_SYSTEM_FOREGROUND<br>
-EVENT_SYSTEM_MENUSTART<br>
-EVENT_SYSTEM_MENUEND<br>
-EVENT_SYSTEM_MENUPOPUPSTART <span style="font-weight: bold;">[important]</span><br>
-EVENT_SYSTEM_MENUPOPUPEND <span style="font-weight: bold;">[important]</span><br>
-EVENT_SYSTEM_CAPTURESTART<br>
-EVENT_SYSTEM_CAPTUREEND<br>
-EVENT_SYSTEM_MOVESIZESTART<br>
-EVENT_SYSTEM_MOVESIZEEND<br>
-EVENT_SYSTEM_CONTEXTHELPSTART<br>
-EVENT_SYSTEM_CONTEXTHELPEND<br>
-EVENT_SYSTEM_DRAGDROPSTART<br>
-EVENT_SYSTEM_DRAGDROPEND<br>
-EVENT_SYSTEM_DIALOGSTART<br>
-EVENT_SYSTEM_DIALOGEND<br>
-EVENT_SYSTEM_SCROLLINGSTART<br>
-EVENT_SYSTEM_SCROLLINGEND <span style="font-weight: bold;">[possibly
-important, talk to AT vendor]</span><br>
-EVENT_SYSTEM_SWITCHSTART<br>
-EVENT_SYSTEM_SWITCHEND<br>
-EVENT_SYSTEM_MINIMIZESTART<br>
-EVENT_SYSTEM_MINIMIZEEND<br>
-      </td>
-      <td style="vertical-align: top;">EVENT_OBJECT_CREATE <span
- style="font-weight: bold;">[don't implement, watching system generated
-versions of this event causes </span><span style="font-weight: bold;">assistive
-technology </span><span style="font-weight: bold;">crashes]</span><br>
-EVENT_OBJECT_DESTROY <span style="font-weight: bold;">[don't
-implement, watching system generated versions of this event causes
-assistive technology crashes]</span><br>
-EVENT_OBJECT_SHOW<br>
-EVENT_OBJECT_HIDE<br>
-EVENT_OBJECT_REORDER <span style="font-weight: bold;">[important for
-mutating docs in future, but not yet]</span><br>
-EVENT_OBJECT_FOCUS <span style="font-weight: bold;">[important]</span><br>
-EVENT_OBJECT_SELECTION<br>
-EVENT_OBJECT_SELECTIONADD<br>
-EVENT_OBJECT_SELECTIONREMOVE<br>
-EVENT_OBJECT_SELECTIONWITHIN<br>
-EVENT_OBJECT_STATECHANGE <span style="font-weight: bold;">[important
-for checkboxes and radio buttons]</span><br>
-EVENT_OBJECT_LOCATIONCHANGE<br>
-EVENT_OBJECT_NAMECHANGE<br>
-EVENT_OBJECT_DESCRIPTIONCHANGE<br>
-EVENT_OBJECT_VALUECHANGE<br>
-EVENT_OBJECT_PARENTCHANGE<br>
-EVENT_OBJECT_HELPCHANGE<br>
-EVENT_OBJECT_DEFACTIONCHANGE<br>
-EVENT_OBJECT_ACCELERATORCHANGE<br>
-      </td>
-    </tr>
-  </tbody>
-</table>
-<div style="margin-left: 40px;"> </div>
-<h2 style="margin-left: 40px;"><a name="states"></a>MSAA States Cheat
-Sheet<br>
-</h2>
-<div style="margin-left: 40px;"> </div>
-<ul style="margin-left: 40px;">
-  <p>For information on what each state does, see the <a
- href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp">MSDN
-State Constants page</a>.</p>
-  <p>Check with your assistive technology partners to find out what
-states you need to support. There's a very good chance they won't ask
-for more than the states marked <span style="font-weight: bold;">[important]</span>:</p>
-</ul>
-<div style="margin-left: 40px;"></div>
-<table
- style="text-align: left; width: 75%; margin-right: auto; margin-left: auto;"
- border="0" cellspacing="2" cellpadding="2">
-  <tbody>
-    <tr>
-      <td style="vertical-align: top;">STATE_UNAVAILABLE <span
- style="font-weight: bold;">[important]</span><br>
-STATE_SELECTED <span style="font-weight: bold;">[important]</span><br>
-STATE_FOCUSED <span style="font-weight: bold;">[important]</span><br>
-STATE_PRESSED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
-STATE_CHECKED <span style="font-weight: bold;">[important]</span><br>
-STATE_MIXED<br>
-STATE_READONLY <span style="font-weight: bold;">[important]</span><br>
-STATE_HOTTRACKED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>
-STATE_DEFAULT <span style="font-weight: bold;">[important]</span><br>
-STATE_EXPANDED <span style="font-weight: bold;">[important]</span><br>
-STATE_COLLAPSED <span style="font-weight: bold;">[important]</span><br>
-STATE_BUSY <span style="font-weight: bold;">[important]</span><br>
-STATE_FLOATING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
-STATE_MARQUEED&nbsp; <br>
-STATE_ANIMATED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>
-STATE_INVISIBLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>
-      <br>
-      </td>
-      <td style="vertical-align: top;">STATE_OFFSCREEN <span
- style="font-weight: bold;">[important]</span><br>
-STATE_SIZEABLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br>
-STATE_MOVEABLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br>
-STATE_SELFVOICING&nbsp;&nbsp; &nbsp;<br>
-STATE_FOCUSABLE <span style="font-weight: bold;">[important]</span><br>
-STATE_SELECTABLE <span style="font-weight: bold;">[important]</span><br>
-STATE_LINKED <span style="font-weight: bold;">[important]</span><br>
-STATE_TRAVERSED <span style="font-weight: bold;">[important]</span><br>
-STATE_MULTISELECTABLE <span style="font-weight: bold;">[important]</span><br>
-STATE_EXTSELECTABLE &nbsp;<br>
-STATE_ALERT_LOW&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br>
-STATE_ALERT_MEDIUM&nbsp; &nbsp;<br>
-STATE_ALERT_HIGH&nbsp;&nbsp;&nbsp; &nbsp;<br>
-STATE_PROTECTED <span style="font-weight: bold;">[important]</span><br>
-STATE_HASPOPUP <br>
-      </td>
-    </tr>
-  </tbody>
-</table>
-<h2 style="margin-left: 40px;"><a name="roles"></a>MSAA Roles Cheat
-Sheet<br>
-</h2>
-<div style="margin-left: 40px;"> </div>
-<ul style="margin-left: 40px;">
-  <p>For information on what each role does, see the <a
- href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp">MSDN
-Role Constants page</a>.</p>
-  <p>Check with your assistive technology partners to find out what
-roles you need to support. There's a very good chance they won't ask for
-more than the roles marked <span style="font-weight: bold;">[important]</span>:<br>
-There is no need to support the objects marked <span
- style="font-weight: bold;">[inserted by system]. </span>Windows will
-add those objects to your hierarchy for you.<br>
-  </p>
-</ul>
-<div style="margin-left: 40px;"> </div>
-<table
- style="text-align: left; width: 75%; margin-right: auto; margin-left: auto;"
- border="0" cellspacing="2" cellpadding="2">
-  <tbody>
-    <tr>
-      <td style="vertical-align: top;">ROLE_TITLEBAR <span
- style="font-weight: bold;">[inserted by system]</span><br>
-ROLE_MENUBAR <span style="font-weight: bold;">[important if you don't
-use native menus]</span><br>
-ROLE_SCROLLBAR<br>
-ROLE_GRIP<br>
-ROLE_SOUND<br>
-ROLE_CURSOR<br>
-ROLE_CARET<br>
-ROLE_ALERT<br>
-ROLE_WINDOW <span style="font-weight: bold;">[inserted by system]</span><br>
-ROLE_CLIENT <span style="font-weight: bold;">[important]</span><br>
-ROLE_MENUPOPUP <span style="font-weight: bold;">[important]</span><br>
-ROLE_MENUITEM <span style="font-weight: bold;">[important]</span><br>
-ROLE_TOOLTIP<br>
-ROLE_APPLICATION<br>
-ROLE_DOCUMENT<br>
-ROLE_PANE <span style="font-weight: bold;">[important]</span><br>
-ROLE_CHART<br>
-ROLE_DIALOG<br>
-ROLE_BORDER<br>
-ROLE_GROUPING<br>
-ROLE_SEPARATOR <span style="font-weight: bold;">[important]</span><br>
-ROLE_TOOLBAR<br>
-ROLE_STATUSBAR <span style="font-weight: bold;">[important]</span><br>
-ROLE_TABLE <span style="font-weight: bold;">[important]</span><br>
-ROLE_COLUMNHEADER<br>
-ROLE_ROWHEADER<br>
-ROLE_COLUMN<br>
-ROLE_ROW<br>
-ROLE_CELL <span style="font-weight: bold;">[important]</span><br>
-ROLE_LINK <span style="font-weight: bold;">[important]</span><br>
-ROLE_HELPBALLOON<br>
-ROLE_CHARACTER<br>
-      </td>
-      <td style="vertical-align: top;">ROLE_LIST <span
- style="font-weight: bold;">[important]</span><br>
-ROLE_LISTITEM <span style="font-weight: bold;">[important]</span><br>
-ROLE_OUTLINE <span style="font-weight: bold;">[important]</span><br>
-ROLE_OUTLINEITEM <span style="font-weight: bold;">[important]</span><br>
-ROLE_PAGETAB <span style="font-weight: bold;">[important]</span><br>
-ROLE_PROPERTYPAGE <span style="font-weight: bold;">[important]</span><br>
-ROLE_INDICATOR<br>
-ROLE_GRAPHIC <span style="font-weight: bold;">[important]</span><br>
-ROLE_STATICTEXT <span style="font-weight: bold;">[important]</span><br>
-ROLE_TEXT <span style="font-weight: bold;">[important]</span><br>
-ROLE_PUSHBUTTON <span style="font-weight: bold;">[important]</span><br>
-ROLE_CHECKBUTTON <span style="font-weight: bold;">[important]</span><br>
-ROLE_RADIOBUTTON <span style="font-weight: bold;">[important]</span><br>
-ROLE_COMBOBOX <span style="font-weight: bold;">[important]</span><br>
-ROLE_DROPLIST <span style="font-weight: bold;">[important]</span><br>
-ROLE_PROGRESSBAR <span style="font-weight: bold;">[important]</span><br>
-ROLE_DIAL<br>
-ROLE_HOTKEYFIELD<br>
-ROLE_SLIDER<br>
-ROLE_SPINBUTTON<br>
-ROLE_DIAGRAM<br>
-ROLE_ANIMATION<br>
-ROLE_EQUATION<br>
-ROLE_BUTTONDROPDOWN<br>
-ROLE_BUTTONMENU<br>
-ROLE_BUTTONDROPDOWNGRID<br>
-ROLE_WHITESPACE<br>
-ROLE_PAGETABLIST <span style="font-weight: bold;">[important]</span><br>
-ROLE_CLOCK<br>
-ROLE_SPLITBUTTON<br>
-ROLE_IPADDRESS<br>
-ROLE_NOTHING<br>
-      <br>
-      </td>
-    </tr>
-  </tbody>
-</table>
-<div style="margin-left: 40px;"></div>
-<h2 style="margin-left: 40px;"><a name="objid"></a>MSAA Object
-Identifiers Cheat Sheet<br>
-</h2>
-<div style="margin-left: 40px;"> </div>
-<p style="margin-left: 80px;">For information on what each object
-identifier does, see the <a
- href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp">MSDN
-Object Identifiers Constants page</a>.</p>
-<div style="margin-left: 80px;">Check with <big><big></big></big>our
-assistive technology partners to find out what object identifiers you
-need to support. There's a very good chance they won't ask for more than
-the object itentifiers marked <span style="font-weight: bold;">[important]</span>:<br>
-</div>
-<dl style="margin-left: 120px;">
-  <dt>OBJID_ALERT<br>
-OBJID_CARET<br>
-OBJID_CLIENT <span style="font-weight: bold;">[important]</span><br>
-OBJID_CURSOR<br>
-OBJID_HSCROLL<br>
-OBJID_NATIVEOM <span style="font-weight: bold;">[important? might be
-useful for supporting custom interfaces, need to research]</span><br>
-OBJID_MENU<br>
-OBJID_QUERYCLASSNAMEIDX<br>
-OBJID_SIZEGRIP<br>
-OBJID_SOUND<br>
-OBJID_SYSMENU<br>
-OBJID_TITLEBAR<br>
-OBJID_VSCROLL<br>
-OBJID_WINDOW<br>
-  </dt>
-</dl>
-<h2><a name="quirks"></a>3. Dealing with the Quirks of MSAA</h2>
-<div style="margin-left: 40px;"> </div>
-<p style="margin-left: 40px;">MSAA has a well deseved reputation for
-quirkiness. It is not "plug and play", and will take a lot of
-testing/refinement before your solution works with any product. Here are
-some of its quirks and some solutions/workarounds:<br>
-</p>
-<div style="margin-left: 40px;"><big><a name="Crash_prone"></a>MSAA can
-be crash prone</big><br>
-</div>
-<div style="margin-left: 80px;"><br>
-<span style="text-decoration: underline;">Problem</span>: Many of
-MSAA's crash occur because more than one process is refcounting the same
-objects, and because pointers are being shared between processes. When
-your application closes, different signals are typically broadcast. For
-example, the application window closes and the window is blurred. It is
-impossible to know if and when the 3rd party assistive technology will
-use one of these signals to release the objects of yours that is is
-refcounting. This can lead to crashes where it releases something and
-the wrong time, when some of your dll's are unloaded but not others,
-and a destructor is called in an unloaded DLL.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: Create a
-"shutdown" method for each internal accessible object, to remove any
-references to other internal objects before any of your dll's are
-unloaded. In order to do this effectively, you will have to keep track
-of every accessible object that you create. The shutdown method for an
-accessibility object should be called whenever the document or UI object
-it refers to goes away. The easiest way to do that is to keep a pointer
-to an accessible in each internal UI object. If that pointer is
-non-null, then there is an accessible object for it. Whenever the UI
-object is destroyed, shutdown its accessible object as well. In
-Gecko/Mozilla we are not allowed to keep this extra pointer for each
-accessible object, so when accessibility is turned on we use a hash
-table to cache these objects. Such a cache must be kept in perfect sync
-with the tree of UI and document objects, which is difficult. Therefore,
-unless 4 bytes extra on each object is criticial in your application,
-just keep the extra pointer around instead of using a hash table.<br>
-<br>
-Also, don't implement EVENT_OBJECT_CREATE or EVENT_OBJECT_DESTROY.
-Vendors have found that watching these events causes crashes.<br>
-<br>
-</div>
-<div style="margin-left: 40px;"><big><a
- name="Hacky_caret_tracking_not_working"></a>Hacky caret tracking
-causes problems<br>
-</big></div>
-<div style="margin-left: 80px;"><br>
-<span style="text-decoration: underline;">Problem</span>: Assistive
-technologies do not use the MSAA caret. They follow screen draws,
-looking for a vertical blinking line. Unfortunately, some products can
-get confused by the vertical lines on other objects, such as list boxes,
-even though those lines are not blinking. The assistive technology may
-not see your caret at all.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: Make sure
-there is a configuration file for each assistive technology specific to
-your application. Read the manual or help, and find the keystroke or
-commands for training the caret, and save this information in the
-configuration file. Don't support the MSAA caret, none of the vendors
-use it.<br>
-<br>
-</div>
-<div style="margin-left: 40px;"> <big><a name="Event_window_confusion"></a>Event
-window handle is incorrect</big><br>
-<br>
-<div style="margin-left: 40px;"><span
- style="text-decoration: underline;">Problem</span>: The screen reader
-or other assistive technology does not track the focus or other events
-correctly.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: This may be
-because you are reporting that the events in a different window from the
-current system focused. The assistive technology may be asking
-GetGUIThreadInfo for its hwndFocus, and throwing away MSAA events that
-are not in the currently focused window. Even if you are visibly showing
-window focus on the correct window, you must also tell the operating
-system to focus this window before any other accessibility events get
-fired in it. </div>
-<br>
-<big><a name="Confusion_with_system-generated_events"></a>Confusion
-with system-generated events</big><br>
-<br>
-<div style="margin-left: 40px;"> <span
- style="text-decoration: underline;">Problem</span>: When you test with
-Accessible Event Watcher in the MSAA SDK, you will see many events that
-your application did not generate. Microsoft Windows generates some
-events for you. This is extremely annoying. The assistive technology has
-no way to tell whether the event came from your application or from
-Windows. For example, when you set window focus, Windows will generate
-an EVENT_OBJECT_FOCUS event an a ROLE_WINDOW object it also generated
-for you. If you happen to set window focus after you fired your own
-EVENT_OBJECT_FOCUS event on an object in the widnow, your event will be
-ignored, because assistive technology software tends to pay attention
-only to the last focus event.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: When an
-object is about to get focused in a different window, make sure you
-focus a window before you fire your own focus events for objects inside
-it. Test using Accessible Event Watcher in the MSAA SDK, and use the
-settings panel to watch subsets of accessibility events. Count on the
-assistive technology to make sense out the jumble of extra
-system-generated events, it's not your problem.<br>
-</div>
-<br>
-<big><a name="No_unique_child_ID_for_object_in_window"></a>No unique
-child ID for event target in window</big><br>
-<br>
-<div style="margin-left: 40px;"> <span
- style="text-decoration: underline;">Problem</span>: MSAA expects
-events to be reported using NotifyWinEvent(eventNum, hWnd, worldID,
-childID), and there may not be an obvious way to get a window handle and
-a 32 bit childID for an object. You may be using windowless controls, or
-have an entire document with lots of objects in a given window. The
-child ID must be unique, because this is what the assistive technology
-will use to retrieve the accessible via AccessibleObjectFromEvent()
-which ends up using get_accChild on the accessible for the given window.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: In
-Gecko/Mozilla, we did not want to store an extra 32 bit unique ID value
-on every object. Instead, we hand back a 32 bit value derived from the
-UI object's pointer, which is unique. We ensure that the value we hand
-back is always negative. When the get_accChild call comes back, we check
-our hash table cache for that window to see if there's an accessible
-object still associated with that unique value. This means the client
-must use AccessibleObjectFromEvent immediately, because there is a
-danger that the object will go away, and another different object will
-be created with the same pointer value.That case seems extremely remote,
-because information from events is generally retrieved right after the
-event.<br>
-<br>
-If you're not using a hash table to keep track of unique ID's, store
-the child ID's and objects for the last 50 or so events in a circular
-array. In practice, this is enough to keep AccessibleObjectFromEvent()
-happy.<br>
-</div>
-<br>
-<big><a name="Not_all_MSAA_features_utilized_by_3rd"></a>Not all MSAA
-features utilized by 3rd party vendors</big><br>
-<br>
-<div style="margin-left: 40px;"> <span
- style="text-decoration: underline;">Problem</span>: The assistive
-technology does not use 50% of what's available in MSAA, e.g. MSAA
-caret, a lot of events, roles, states and methods. It's difficult to
-know which things to support.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: Use this
-document to see what is generally considered important by assistive
-technology manufacturers. Contact the the top vendors early and often as
-you plan and implement your architecture, to see what's important to
-them. Implement only what's needed -- supporting everything would take
-too long for zero results.<br>
-</div>
-<br>
-<big><a name="Missing_functionality_in_MSAA"></a>Missing functionality
-in MSAA</big><br>
-<br>
-<div style="margin-left: 40px;"><span
- style="text-decoration: underline;">Problem and solutions:</span>
-Assistive technology vendors need some things which MSAA does not
-provide, such as:<br>
-</div>
-</div>
-<ul style="margin-left: 40px;">
-  <ul>
-    <li>No way of signifying that a document has finished
-loading.&nbsp; Fire EVENT_OBJECT_STATECHANGE for a window/client/pane
-object when it starts to load a new document. Use STATE_BUSY to indicate
-that a new document is being loaded. When the loading has finished, fire
-another EVENT_OBJECT_STATECHANGE event and clear the STATE_BUSY
-flag.&nbsp; </li>
-    <li>No method to get clipped/unclipped bounds of a piece of text
-within a text object. This is needed by screen magnifiers. No scrollTo
-method, also needed by screen magnifiers. Implement a custom interface
-for text objects, and support it through QueryInterface or QueryService
-if it's being implemented on a different object than IAccessible is.
-Support a scrollTo method which takes a text index, and a
-getClippedBounds and getUnclippedBounds method which takes a start and
-end index. Publish your custom interface.</li>
-    <li>No way for assistive technology to know when scrolling has
-stopped. Fire the EVENT_SYSTEM_SCROLLINGEND event to indicate when
-scrolling has ended (try not to fire too many of these, wait until
-scrolling has truly stopped). There is no need to support
-EVENT_SYSTEM_SCROLLINGSTART, it is not used by assistive technology.<br>
-    </li>
-  </ul>
-  <ul>
-    <li>No support for document formatting or "DOM" as requested by
-some vendors: support a custom interface that gives them the formatting
-information they are requesting.<br>
-    </li>
-  </ul>
-</ul>
-<div style="margin-left: 40px;"><big><a name="Dueling_text_equivalents"></a>Dueling
-text equivalents</big><br>
-<br>
-<div style="margin-left: 40px;"><span
- style="text-decoration: underline;"> Problem</span>: There are three
-kinds of text equivalents, and it is difficult to know when to use each.
-Different applications behave differently in this regard. For example,
-Acrobat uses accessible value for text labels where as most programs use
-accessible name. There are different roles for text objects,
-ROLE_STATICTEXT and ROLE_TEXT (editable text), which seems to be used
-for non-editable text in many places.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: Be as
-consistent with Internet Explorer as possible. Use accessible name for
-most text equivalents, and accessible value for URL's. Don't use
-accessible description unless you really do have a long description for
-the object you need to expose -- most assistive technology makes little
-use of it. Use ROLE_STATICTEXT for labels specific to dialog and UI
-controls, and always use ROLE_TEXT for document text even if the text is
-not editable (in that case use ROLE_TEXT with STATE_READONLY).<br>
-</div>
-<br>
-<big><a name="Issues_with_Links"></a>Issues with Links</big><br>
-<br>
-<div style="margin-left: 40px;"> <span
- style="text-decoration: underline;">Problem</span>: The assistive
-technology has inflexible heuristics when it comes to reading links.
-First, it won't read the object unless the states are correctly set.
-Second, it can mishandle the object if it cannot parse the whitespace
-according to its own rules.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: Make sure
-the ROLE_LINK object and its child ROLE_TEXT objects all have
-STATE_LINKED set. For multi-line links with a line break in the middle,
-make sure there is no whitespace at the beginning or end of any of the
-accessible names, and make sure there is a \r\n where the line breaks
-occur in the accessible name for the ROLE_LINK. For an example of how to
-do this properly, see Internet Explorer or Gecko. Again, if it's not
-done exactly this way, some links will not be read.<br>
-</div>
-<br>
-<big><a name="MSAA_Implementation_is_Not_Performant"></a>MSAA
-Implementation is Not Performant</big><br>
-<br>
-<div style="margin-left: 40px;"><span
- style="text-decoration: underline;"> Problem</span>: The assistive
-technology may interact slowly with your application.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: Try not to
-calculate the same things more than once or create the same objects more
-than once. For example, create and cache an object's children when you
-look for them in get_accChildCount(), so that you can just hand them
-back when asked for using get_accChild() or accNavigate(). Support
-IEnumVARIANT so that the MSAA client can ask for a number of children in
-one call. In custom interfaces, create methods that hand back a lot of
-data in one call, rather than requiring a large number of calls. Fewer
-calls are much better better because COM Marshaling is slow.<br>
-</div>
-<br>
-<big><a name="Differing_client_implementations"></a>Differing client
-implementations</big><br>
-<br>
-<div style="margin-left: 40px;"> <span
- style="text-decoration: underline;">Problem</span>: Every assistive
-technology uses MSAA differently.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: We don't
-know of any outright conflicts in the differing uses of MSAA (yet).
-However, be on guard. If a vendors asks you to do something different
-from the spec, it's better to check with the other vendors before moving
-forward. Check to see what applications from Microsoft do in a similar
-situation.<br>
-</div>
-<br>
-<big><a name="Undocumented_Window_Class_Usage"></a>Undocumented Window
-Class Usage</big><br>
-<br>
-<div style="margin-left: 40px;"> <span
- style="text-decoration: underline;">Problem</span>: most assistive
-technologies won't use your MSAA implementation out of the box. They
-must list your window classes somewhere in their implementation, and
-then turn on MSAA support when a window of that class receives focus.
-The window class is also used to determine a host of hard-coded
-behaviors, such as whether or not a screen reader will load the entire
-MSAA tree into a special buffer for the user to navigate with screen
-reader commands. This is only supposed to occur for document navigation,
-not for UI/dialogs. where your application's keyboard commands will be
-solely used to navigate.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: Contact each
-vendor and let them know what window classes you will be using MSAA for.
-If possible, use a different window class name for documents/content
-than you use for UI/dialogs. Or, do what Mozilla does&nbsp; - expose a
-control ID (GWL_ID) of 1 for content, and 0 for UI. Consistent window
-class names are important for the assistive technology vendors, so that
-they can determine what code to run for a given window. Don't change
-window class names after you have shipped a version.<br>
-</div>
-<br>
-<big><a name="Vendor_quirks"></a>Vendor quirks</big><br>
-<br>
-<div style="margin-left: 40px;"> <span
- style="text-decoration: underline;">Problem</span>: Because assistive
-technology tends to utilize MSAA as an additional solution resting on
-top of old hacks, rather than a completely clean and separate way to
-deal with an application, and because of the quirky nature of MSAA and
-of the inflexible heuristics that screen readers use, we do not have a
-"plug and play solution". In addition, assistive technology vendors are
-tiny companies, often scrambling to keep up with changes in the
-applications they already support, or new products other than yours
-which they need to support. It's very difficult to get vendors to spend
-time testing an MSAA implementation, send feedback or help find out why
-things aren't working. Time and version commitments often fall through.
-There is always a belated new version due around corner, after which you
-will be promised to be the next priority.<br>
-<br>
-<span style="text-decoration: underline;">Solution</span>: Try to reach
-out in a friendly manner to the assistive technology company. Be as easy
-to work with as you possibly can -- this includes being extremely
-responsive to their bug reports with new test builds, as well as being
-very communicative about what you have changed and when. Do as much work
-as you possibly can without their help. See if your organization can
-offer something they can't get for themselves. Be patient, and set your
-expectations to a reasonable level. Realize that it's about both pride
-and revenue for these companies, and that they need to sell a lot of
-copies of their software to make up the work they put in to support
-your app. Remember that no matter how small they are, you need them more
-than they need you, unless your application's accessibility is being
-demanded by end-users.</div>
-</div>
-<h2><a name="geckoimpl"></a>4. Example: How Gecko and Mozilla Implement
-MSAA<br>
-</h2>
-<p style="margin-left: 40px;">The <a
- href="http://lxr.mozilla.org/seamonkey/source/accessible/">Accessible
-module</a> is where the Mozilla MSAA implementation lives. Feel free to <a
- href="http://lxr.mozilla.org/seamonkey/source/accessible/">peruse the
-source code in the accessible module</a> whenever you want to see how
-something can be implemented.<br>
-</p>
-<p style="margin-left: 40px;">The accessible module is also where
-support for Sun's <a
- href="http://wwws.sun.com/software/star/gnome/accessibility/architecture.html">ATK</a>
-accessibility API for Linux and UNIX is implemented. For documentation
-specific to the Mozilla ATK effort, supported by Sun Microsystems, see
-the <a
- href="http://www.mozilla.org/projects/ui/accessibility/unix/index.html">Mozilla
-accessibility on Unix</a> page.</p>
-<h3 style="margin-left: 40px;"><a name="Creation_of_IAccessible_Objects"></a>Creation
-of IAccessible Objects<br>
-</h3>
-<ul style="margin-left: 40px;">
-  <p> The first thing that happens when an assistive technology wants to
-watch our application is that calls the Windows API function
-AccessibleObjectFromWindow(). This usually happens right after a window
-gets focused.<br>
-  </p>
-  <p>When the WIN32 API function AccessibleObjectFromWindow() is
-called, Windows sends the window in question a <a
- href="http://lxr.mozilla.org/seamonkey/search?string=WM_GETOBJECT">WM_GETOBJECT</a>
-message requesting an IAccessible for your root object in the window. In
-our case, this event is received in <a
- href="http://lxr.mozilla.org/mozilla-central/source/widget/windows/nsWindow.cpp#4370">mozilla/widget/windows/nsWindow.cpp</a>.
-We send back an IAccessible pointer which can be used by the client to
-get information about this root object. The assistive technology will
-use that root IAccessible to traverse the rest of the object tree, by
-navigating to children and then siblings, etc. Every navigation function
-such as accNavigate(), get_accChild() and get_accParent() returns an
-IAccessible pointer. <br>
-  </p>
-  <p>To create the root IAccessible for a window the first time it gets
-the <a
- href="http://lxr.mozilla.org/seamonkey/search?string=WM_GETOBJECT">WM_GETOBJECT</a>
-message in, nsWindow.cpp first generates an internal event called <a
- href="http://lxr.mozilla.org/seamonkey/search?string=NS_GETACCESSIBLE">NS_GETACCESSIBLE</a>,
-which is handled in <a
- href="http://lxr.mozilla.org/seamonkey/source/layout/html/base/src/nsPresShell.cpp#6345">PresShell::HandleEventInternal()</a>
-via the creation of an <a
- href="http://lxr.mozilla.org/seamonkey/find?string=msaa/nsDocAccessibleWrap">nsDocAccessibleWrap</a>
-for an inner window or <a
- href="http://lxr.mozilla.org/seamonkey/find?string=msaa/RootAccessibleWrap">RootAccessibleWrap</a>
-for a top level window. These classes implement both nsIAccessible, our
-cross platform API, as well as IAccessible, which is specific to
-Windows/MSAA/COM. The cross-platform nsDocAccessible and
-RootAccessible classes they inherit from are then told to start
-listening for DOM, page load and scroll events.&nbsp; These events cause
-MSAA-specific events, such as EVENT_OBJECT_FOCUS or
-EVENT_OBJECT_STATECHANGE, to fire on UI and document objects within the
-applicable window. We'll explain more about events later in this section.<br>
-  </p>
-  <p>Until the WM_GETOBJECT message is processed, the Gecko
-accessibility service is not used, and thus the accessibility.dll is not
-loaded, so there is almost zero overhead for accessibility API support
-in Mozilla or Gecko, in the general case. Once the accessibility
-service is created, however, Gecko loads code to create an object on
-demand for every UI or document object that should support IAccessible.
-The created objects are cached in a hash table, and shutdown when
-they're no longer needed. They may still exist in memory in a
-nonfunctional state until the assistive technology completely releases
-them. See the section on accessible roles to see what kinds of objects
-Gecko support IAccessible for.<br>
-  </p>
-</ul>
-<div style="margin-left: 40px;"> </div>
-<h3 style="margin-left: 40px;"><a
- name="The_Accessible_Tree_vs._the_DOM_Tree"></a>The Accessible Tree
-vs. the DOM Tree<br>
-</h3>
-<div style="margin-left: 40px;"> </div>
-<ul style="margin-left: 40px;">
-  <p>After the root or doc accessible for a window has been created and
-handed back to the MSAA client, it is used to traverse the rest of the
-IAccessible tree using accNavigation, get_accChild and get_accParent.
-Any IAccessible will support those methods. We also support
-IEnumVARIANT::Next() which allows for fast marshaling of all of an
-objects children to a client via COM. In other words, the assistive
-technology can say "give me all 20 children of this object into this
-array". That's much faster than 20 separate calls, one for each child.<br>
-  </p>
-  <p>In Mozilla, the client has another choice for tree navigation --
-it can utilize data stored in the DOM via Mozilla's custom <a
- href="http://lxr.mozilla.org/seamonkey/source/accessible/public/msaa/ISimpleDOMNode.idl">ISimpleDOMNode</a>
-COM interface. Any IAccessible can be used to QueryInterface to an
-ISimpleDOMNode, and vice versa for a round trip. However, one might QI
-ISimpleDOMNode to IAccessible only to find it is null, which means that
-particular node in question is not exposed in the IAccessible tree. See
-the following diagram for examples of nodes that do no support
-IAccessible.<br>
-  </p>
-</ul>
-<div style="margin-left: 40px;"> </div>
-<h3 style="margin-left: 40px;">MSAA tree vs. DOM tree - what's the
-relationship?</h3>
-<div style="margin-left: 40px;"> </div>
-<ul style="margin-left: 40px;">
-  <p> <img
- src="http://www.mozilla.org/projects/ui/accessibility/images/tree-relativity.gif"
- alt="Diagram showing MSAA tree is a subset of the DOM tree"
- title="Diagram showing MSAA tree is a subset of the DOM tree"> </p>
-The MSAA tree and the DOM tree are parallel structures, although the
-MSAA tree is a subset of the DOM tree. <code>QueryInterface()</code> can
-be used to switch between the interfaces used in the two trees
-(IAccessible and ISimpleDOMNode).     If there is no MSAA node for a DOM
-node,&nbsp; pAccessible-&gt;<code>QueryInterface(IID_IAccessible)</code>
-will return null.
-</ul>
-<h3 style="margin-left: 40px;"><a
- name="The_Implementations_Behind_IAccessible"></a>A Variety of
-Implementations for IAccessible</h3>
-<div style="margin-left: 40px;">
-<div style="margin-left: 40px;">
-<p>There are two main kinds of classes in Mozilla's accessibility class
-hierarchy, platform-specifc and cross-platform. All of the
-platform-specific classes have the word "Wrap" appended to them. The
-Wrap classes contain implementations and interfaces specific to MSAA or
-ATK. These platform-specific classes inherit from cross-platform
-classes, where the most of the implementation is done. For example,
-nsAccessibleWrap inherits from nsAccessible. Every accessible object in
-the MSAA tree has an implementation dertived from nsAccessible, which
-exposes accessibility information through nsIAccessible, in a generic
-cross-platform manner. <br>
-</p>
-<p>This default implementation for nsIAccessible knows how to use
-nsAccessibleTreeWalker to walk Mozilla's content DOM and frame tree,
-exposing only the objects that are needed for accessibility. The
-nsAccessibleTreeWalker class knows what it needs to expose by asking
-each DOM node's primary frame (a Gecko formatting object) for an
-nsIAccessible, using the nsIFrame::GetAccessible() method. If
-nsAccessibleTreeWalker gets an nsIAccessible back, then the DOM node
-considered to be an accessible object. The nsIAccessible that is
-returned is either a new one, or reused from the accessibility cache,
-and the correct type of accessibility object to correctly expose that
-DOM node through the cross-platform nsIAccessible and MSAA-specific
-IAccessible interfaces.<br>
-</p>
-<p>Every accessibility object created must be cached, and must inherit
-from nsAccessibleWrap so that it supports a base implementation of
-nsIAccessible and IAccessible. Apart from that, it is free to override
-IAccessible or nsIAccessible methods. In this way each class is tailored
-to the specific abilities and properties of the HTML or XUL/UI objects
-it applies to, and can support both MSAA, ATK and hopefully any future
-accessibility API's we need to support. For example
-nsHTMLButtonAccessible overrides nsIAccessible::GetAccRole to expose
-ROLE_BUTTON for IAccessible::get_accRole which uses that. <br>
-</p>
-</div>
-</div>
-<ul style="margin-left: 40px;">
-  <p>A more complicated set of nsIAccessible methods which can be
-overridden are GetAccFirstChild/GetAccLastChild/GetAccChildCount, which
-allows for objects to define their own decendant subtrees. The default
-behavior for nsIAccessible::getAccFirstChild is to instantiate a
-nsDOMTreeWalker, and ask it for the first child. However,
-nsImageAccessible overrides getAccFirstChild, returning the first area
-of an image map if there is one, otherwise nsnull. This is necessary
-because the image map areas can be in a completely different area of the
-DOM from the image they apply to.<br>
-  </p>
-</ul>
-<div style="margin-left: 40px;"> </div>
-<h3 style="margin-left: 40px;"><a name="Generating_MSAA_Events"></a>Generating
-MSAA Events</h3>
-<div style="margin-left: 40px;"> </div>
-<ul style="margin-left: 40px;">
-  <p>First, keep in mind that most MSAA events aren't utilized by
-accessibility aids. Therefore we implement only the handful that matter.
-See the <a
- href="file:///c%7C/moz/mozdocs/mozilla-org/html/projects/ui/accessibility/accessible-architecture.html#events">Events</a>
-cheat sheet above for the list of events we implement. By far the most
-important one is EVENT_OBJECT_FOCUS.<br>
-  </p>
-  <p>When a potential accessibility-related event occurs within
-Mozilla, it is typically listened for by nsDocAccessible or
-RootAccessible. The event listeners on these classes call
-FireToolkitEvent(), which is implemented for every accessible.
-Eventually, the event ends up at nsDocAccessibleWrap::FireToolkitEvent()
-which calls NotifyWinEvent from the Win32 API. NotifyWinEvent is passed
-arguments for the window the event occurred in, and the ID of the child
-within that window. Accessibility aids use the Win32 call
-SetWinEventHook() to register as a listener for these events. Creating
-a unique child ID for every object within a window can be difficult,
-see the problem and solution for <a
- href="file:///c%7C/moz/mozdocs/mozilla-org/html/projects/ui/accessibility/accessible-architecture.html#Hacky_caret_tracking_not_working">no
-unique child ID for object in window</a>.<br>
-  </p>
-  <p>The assistive technology chooses which events it is interested in
-learning more about by calling the Win32 method
-AccessibleObjectFromEvent, which returns the IAccessible to the node
-corresponding to the child number that had been indicated from
-NotifyWinEvent(). This ends up asking
-nsDocAccessibleWrap::get_accChild() for a child IAccessible which
-matches the child ID we indicated through NotifyWinEvent(). </p>
-  <p>In Mozilla, we use the DOM node pointer in the accessible object
-as a basis for its child ID, which is also used as a hash key into our
-cache. We also negate the 32 bit value so that it is always &lt;0,
-telling us that they're really looking for the IAccessible for an event,
-not child number x. During the callback, we look up the original
-accessible node in the nsDocAccessible's cache and return it. <br>
-  </p>
-</ul>
-<div style="margin-left: 40px;"> </div>
-<div style="margin-left: 40px;"> </div>
-<h2><a name="feedback"></a>5. Feedback</h2>
-<div style="margin-left: 40px;">How can this document be improved? Was
-it useful? Questions? Contact <a href="mailto:aaronl@netscape.com">aaronl@netscape.com</a><br>
-</div>
-</body>
-</html>
--- a/accessible/src/atk/ApplicationAccessibleWrap.h
+++ b/accessible/src/atk/ApplicationAccessibleWrap.h
@@ -33,36 +33,44 @@
  * 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 ***** */
 
-#ifndef MOZILLA_A11Y_APPLICATION_ACCESSIBLE_WRAP_H__
-#define MOZILLA_A11Y_APPLICATION_ACCESSIBLE_WRAP_H__
+#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__
+#define mozilla_a11y_ApplicationAccessibleWrap_h__
 
 #include "ApplicationAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+ 
 class ApplicationAccessibleWrap: public ApplicationAccessible
 {
 public:
-    static void Unload();
-    static void PreCreate();
+  static void Unload();
+  static void PreCreate();
 
 public:
-    ApplicationAccessibleWrap();
-    virtual ~ApplicationAccessibleWrap();
+  ApplicationAccessibleWrap();
+  virtual ~ApplicationAccessibleWrap();
 
-    // nsAccessNode
-    virtual bool Init();
+  // nsAccessNode
+  virtual bool Init();
 
-    // nsAccessible
-    virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
-    virtual bool AppendChild(nsAccessible* aChild);
-    virtual bool RemoveChild(nsAccessible* aChild);
+  // nsAccessible
+  virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
+  virtual bool AppendChild(nsAccessible* aChild);
+  virtual bool RemoveChild(nsAccessible* aChild);
 
-    // return the atk object for app root accessible
-    NS_IMETHOD GetNativeInterface(void **aOutAccessible);
+  /**
+   * Return the atk object for app root accessible.
+   */
+  NS_IMETHOD GetNativeInterface(void** aOutAccessible);
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif   /* __NS_APP_ROOT_ACCESSIBLE_H__ */
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -48,23 +48,23 @@
 #include "a11yGeneric.h"
 
 #include "nsIContent.h"
 #include "nsIDOMNode.h"
 #include "nsINameSpaceManager.h"
 #include "nsIStringBundle.h"
 #include "nsWeakReference.h"
 
-class ApplicationAccessible;
 class nsAccessNode;
 class nsDocAccessible;
 class nsIAccessibleDocument;
 
 namespace mozilla {
 namespace a11y {
+class ApplicationAccessible;
 class RootAccessible;
 }
 }
 
 class nsIPresShell;
 class nsPresContext;
 class nsIFrame;
 class nsIDocShellTreeItem;
@@ -79,17 +79,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsAccessNode)
 
   static void ShutdownXPAccessibility();
 
   /**
    * Return an application accessible.
    */
-  static ApplicationAccessible* GetApplicationAccessible();
+  static mozilla::a11y::ApplicationAccessible* GetApplicationAccessible();
 
   /**
    * Return the document accessible for this access node.
    */
   nsDocAccessible* Document() const { return mDoc; }
 
   /**
    * Return the root document accessible for this accessnode.
@@ -160,13 +160,13 @@ protected:
   nsCOMPtr<nsIContent> mContent;
   nsDocAccessible* mDoc;
 
 private:
   nsAccessNode() MOZ_DELETE;
   nsAccessNode(const nsAccessNode&) MOZ_DELETE;
   nsAccessNode& operator =(const nsAccessNode&) MOZ_DELETE;
   
-  static ApplicationAccessible* gApplicationAccessible;
+  static mozilla::a11y::ApplicationAccessible* gApplicationAccessible;
 };
 
 #endif
 
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1078,16 +1078,19 @@ nsDocAccessible::AttributeChangedImpl(ns
     FireDelayedAccessibleEvent(event);
     return;
   }
 
   // ARIA or XUL selection
   if ((aContent->IsXUL() && aAttribute == nsGkAtoms::selected) ||
       aAttribute == nsGkAtoms::aria_selected) {
     nsAccessible* item = GetAccessible(aContent);
+    if (!item)
+      return;
+
     nsAccessible* widget =
       nsAccUtils::GetSelectableContainer(item, item->State());
     if (widget) {
       AccSelChangeEvent::SelChangeType selChangeType =
         aContent->AttrValueIs(aNameSpaceID, aAttribute,
                               nsGkAtoms::_true, eCaseMatters) ?
           AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
 
--- a/accessible/src/generic/ApplicationAccessible.h
+++ b/accessible/src/generic/ApplicationAccessible.h
@@ -35,25 +35,28 @@
  * 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 ***** */
 
-#ifndef MOZILLA_A11Y_APPLICATION_ACCESSIBLE_H__
-#define MOZILLA_A11Y_APPLICATION_ACCESSIBLE_H__
+#ifndef mozilla_a11y_ApplicationAccessible_h__
+#define mozilla_a11y_ApplicationAccessible_h__
 
 #include "nsAccessibleWrap.h"
 #include "nsIAccessibleApplication.h"
 
 #include "nsIMutableArray.h"
 #include "nsIXULAppInfo.h"
 
+namespace mozilla {
+namespace a11y {
+ 
 /**
  * ApplicationAccessible is for the whole application of Mozilla.
  * Only one instance of ApplicationAccessible exists for one Mozilla instance.
  * And this one should be created when Mozilla Startup (if accessibility
  * feature has been enabled) and destroyed when Mozilla Shutdown.
  *
  * All the accessibility objects for toplevel windows are direct children of
  * the ApplicationAccessible instance.
@@ -125,10 +128,13 @@ protected:
   virtual void CacheChildren();
   virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
                                            nsresult *aError = nsnull) const;
 
 private:
   nsCOMPtr<nsIXULAppInfo> mAppInfo;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/mac/ApplicationAccessibleWrap.h
+++ b/accessible/src/mac/ApplicationAccessibleWrap.h
@@ -33,22 +33,28 @@
  * 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 ***** */
 
-#ifndef MOZILLA_A11Y_APPLICATION_ACCESSIBLE_WRAP_H__
-#define MOZILLA_A11Y_APPLICATION_ACCESSIBLE_WRAP_H__
+#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__
+#define mozilla_a11y_ApplicationAccessibleWrap_h__
 
 #include "ApplicationAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+ 
 class ApplicationAccessibleWrap: public ApplicationAccessible
 {
 public:
   static void PreCreate() {}
   static void Unload() {}
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/msaa/ApplicationAccessibleWrap.cpp
+++ b/accessible/src/msaa/ApplicationAccessibleWrap.cpp
@@ -41,16 +41,19 @@
 #include "ApplicationAccessibleWrap.h"
 
 #include "AccessibleApplication_i.c"
 
 #include "nsIGfxInfo.h"
 #include "nsIPersistentProperties2.h"
 #include "nsServiceManagerUtils.h"
 
+using namespace mozilla;
+using namespace mozilla::a11y;
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 NS_IMPL_ISUPPORTS_INHERITED0(ApplicationAccessibleWrap,
                              ApplicationAccessible)
 
 NS_IMETHODIMP
 ApplicationAccessibleWrap::GetAttributes(nsIPersistentProperties** aAttributes)
 {
--- a/accessible/src/msaa/ApplicationAccessibleWrap.h
+++ b/accessible/src/msaa/ApplicationAccessibleWrap.h
@@ -33,23 +33,26 @@
  * 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 ***** */
 
-#ifndef MOZILLA_A11Y_APPLICATION_ACCESSIBLE_WRAP_H__
-#define MOZILLA_A11Y_APPLICATION_ACCESSIBLE_WRAP_H__
+#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__
+#define mozilla_a11y_ApplicationAccessibleWrap_h__
 
 #include "ApplicationAccessible.h"
 
 #include "AccessibleApplication.h"
 
+namespace mozilla {
+namespace a11y {
+ 
 class ApplicationAccessibleWrap: public ApplicationAccessible,
                                  public IAccessibleApplication
 {
 public:
   // nsISupporst
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
@@ -71,10 +74,13 @@ public:
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_toolkitVersion(
           /* [retval][out] */ BSTR *version);
 
 public:
   static void PreCreate();
   static void Unload();
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/other/ApplicationAccessibleWrap.h
+++ b/accessible/src/other/ApplicationAccessibleWrap.h
@@ -33,22 +33,28 @@
  * 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 ***** */
 
-#ifndef MOZILLA_A11Y_APPLICATION_ACCESSIBLE_WRAP_H__
-#define MOZILLA_A11Y_APPLICATION_ACCESSIBLE_WRAP_H__
+#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__
+#define mozilla_a11y_ApplicationAccessibleWrap_h__
 
 #include "ApplicationAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+ 
 class ApplicationAccessibleWrap: public ApplicationAccessible
 {
 public:
   static void PreCreate() {}
   static void Unload() {}
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -644,13 +644,13 @@ SettingsListener.observe('language.curre
   ['ril.data.enabled', 'ril.data.roaming.enabled'].forEach(function(key) {
     SettingsListener.observe(key, false, function(value) {
       Services.prefs.setBoolPref(key, value);
     });
   });
 
   ['ril.data.apn', 'ril.data.user', 'ril.data.passwd'].forEach(function(key) {
     SettingsListener.observe(key, false, function(value) {
-      Services.prefs.setBoolPref(key, value);
+      Services.prefs.setCharPref(key, value);
     });
   });
 })();
 
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1335396801000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i86" id="{45147e67-4020-47e2-8f7a-55464fb535aa}">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
@@ -18,16 +18,34 @@
                     </versionRange>
                   </emItem>
       <emItem  blockID="i8" id="{B13721C7-F507-4982-B2E5-502A71474FED}">
                         <versionRange  minVersion=" " severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i43" id="supportaccessplugin@gmail.com">
                         </emItem>
+      <emItem  blockID="i47" id="youtube@youtube2.com">
+                        </emItem>
+      <emItem  blockID="i88" id="anttoolbar@ant.com">
+                        <versionRange  minVersion="2.4.6.4" maxVersion="2.4.6.4" severity="1">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i38" id="{B7082FAA-CB62-4872-9106-E42DD88EDE45}">
+                        <versionRange  minVersion="0.1" maxVersion="3.3.0.*">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.7a1" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                                <versionRange  minVersion="3.3.1" maxVersion="*">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="5.0a1" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i65" id="activity@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i66" id="youtubeer@youtuber.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
@@ -50,36 +68,65 @@
       <emItem  blockID="i61" id="youtube@youtube3.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                                 <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i10" id="{8CE11043-9A15-4207-A565-0C94C42D590D}">
                         </emItem>
+      <emItem  blockID="i1" id="mozilla_cc@internetdownloadmanager.com">
+                        <versionRange  minVersion="2.1" maxVersion="3.3">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.0a1" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                                <versionRange  minVersion=" " maxVersion="6.9.8">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.7a1pre" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i63" id="youtube@youtuber.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i46" id="{841468a1-d7f4-4bd3-84e6-bb0f13a06c64}">
+                        <versionRange  minVersion="0.1" maxVersion="*">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="9.0a1" maxVersion="9.0" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i18" id="msntoolbar@msn.com">
                         <versionRange  minVersion=" " maxVersion="6.*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i13" id="{E8E88AB0-7182-11DF-904E-6045E0D72085}">
                         </emItem>
       <emItem  blockID="i64" id="royal@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i72" os="WINNT" id="{4ED1F68A-5463-4931-9384-8FFF5ED91D92}">
                         <versionRange  minVersion="3.4.1" maxVersion="3.4.1.194" severity="1">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i77" id="{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}">
-                        <versionRange  minVersion="0" maxVersion="*">
+      <emItem  blockID="i4" id="{4B3803EA-5230-4DC3-A7FC-33638F3D3542}">
+                        <versionRange  minVersion="1.2" maxVersion="1.2">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.0a1" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i75" os="Darwin,Linux" id="firebug@software.joehewitt.com">
+                        <versionRange  minVersion="1.9.0" maxVersion="1.9.0" severity="1">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="9.0a1" maxVersion="9.*" />
+                          </targetApplication>
                     </versionRange>
                   </emItem>
       <emItem  blockID="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i53" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
                         <versionRange  minVersion="2.0.3" maxVersion="2.0.3">
@@ -92,52 +139,69 @@
       <emItem  blockID="i59" id="ghostviewer@youtube2.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i78" id="socialnetworktools@mozilla.doslash.org">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i70" id="psid-vhvxQHMZBOzUZA@jetpack">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+      <emItem  blockID="i51" id="admin@youtubeplayer.com">
+                        <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i68" id="flashupdate@adobe.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
-                        </emItem>
+      <emItem  blockID="i90" id="videoplugin@player.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i84" id="pink@rosaplugin.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i67" id="youtube2@youtube2.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i60" id="youtb3@youtb3.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i56" id="flash@adobe.com">
-                        <versionRange  minVersion="0" maxVersion="*">
+      <emItem  blockID="i23" id="firefox@bandoo.com">
+                        <versionRange  minVersion="5.0" maxVersion="5.0" severity="1">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.7a1pre" maxVersion="*" />
+                          </targetApplication>
                     </versionRange>
                   </emItem>
       <emItem  blockID="i55" id="youtube@youtube7.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i47" id="youtube@youtube2.com">
-                        </emItem>
+      <emItem  blockID="i87" os="WINNT" id="afurladvisor@anchorfree.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="13.0a1" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i22" id="ShopperReports@ShopperReports.com">
                         <versionRange  minVersion="3.1.22.0" maxVersion="3.1.22.0">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i2" id="fdm_ffext@freedownloadmanager.org">
+                        <versionRange  minVersion="1.0" maxVersion="1.3.1">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.0a1" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i44" id="sigma@labs.mozilla">
                         </emItem>
       <emItem  blockID="i5" id="support@daemon-tools.cc">
                         <versionRange  minVersion=" " maxVersion="1.0.0.5">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i69" id="{977f3b97-5461-4346-92c8-a14c749b77c9}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -152,30 +216,45 @@
                     </versionRange>
                   </emItem>
       <emItem  blockID="i48" id="admin@youtubespeedup.com">
                         </emItem>
       <emItem  blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
                         <versionRange  minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
+                        <versionRange  minVersion="2.2" maxVersion="2.2">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i76" id="crossriderapp3924@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i79" id="GifBlock@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
-                        <versionRange  minVersion="2.2" maxVersion="2.2">
+      <emItem  blockID="i70" id="psid-vhvxQHMZBOzUZA@jetpack">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i56" id="flash@adobe.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i45" id="{22119944-ED35-4ab1-910B-E619EA06A115}">
+                        <versionRange  minVersion="0.1" maxVersion="7.6.1">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="8.0a1" maxVersion="*" />
+                          </targetApplication>
                     </versionRange>
                   </emItem>
       <emItem  blockID="i82" id="{8f42fb8b-b6f6-45de-81c0-d6d39f54f971}">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
                         <versionRange  minVersion="1.1b1" maxVersion="1.1b1">
@@ -184,41 +263,95 @@
       <emItem  blockID="i3" id="langpack-vi-VN@firefox.mozilla.org">
                         <versionRange  minVersion="2.0" maxVersion="2.0">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i73" id="a1g0a9g219d@a1.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i51" id="admin@youtubeplayer.com">
+      <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
+                        </emItem>
+      <emItem  blockID="i77" id="{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i11" id="yslow@yahoo-inc.com">
+                        <versionRange  minVersion="2.0.5" maxVersion="2.0.5">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.5.7" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i52" id="ff-ext@youtube">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i24" id="{6E19037A-12E3-4295-8915-ED48BC341614}">
+                        <versionRange  minVersion="0.1" maxVersion="1.3.328.4" severity="1">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.7a1pre" maxVersion="*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i15" id="personas@christopher.beard">
+                        <versionRange  minVersion="1.6" maxVersion="1.6">
+                      <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="3.6" maxVersion="3.6.*" />
+                          </targetApplication>
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i21" id="support@update-firefox.com">
                         </emItem>
     </emItems>
 
   <pluginItems>
+      <pluginItem  blockID="p26">
+      <match name="name" exp="^Yahoo Application State Plugin$" />      <match name="description" exp="^Yahoo Application State Plugin$" />      <match name="filename" exp="npYState.dll" />                      <versionRange >
+                      <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+              <versionRange  minVersion="3.0a1" maxVersion="3.*" />
+            </targetApplication>
+                  </versionRange>
+                  </pluginItem>
+      <pluginItem  blockID="p27">
+      <match name="name" exp="QuickTime Plug-in 7[.]1[.]" />            <match name="filename" exp="npqtplugin.?[.]dll" />                      <versionRange >
+                      <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+              <versionRange  minVersion="3.0a1" maxVersion="3.*" />
+            </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p28">
                   <match name="filename" exp="NPFFAddOn.dll" />                        </pluginItem>
       <pluginItem  blockID="p31">
                   <match name="filename" exp="NPMySrch.dll" />                        </pluginItem>
+      <pluginItem  blockID="p32">
+                  <match name="filename" exp="npViewpoint.dll" />                      <versionRange >
+                      <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+              <versionRange  minVersion="3.0" maxVersion="*" />
+            </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p33">
       <match name="name" exp="[0-6]\.0\.[01]\d{2}\.\d+" />            <match name="filename" exp="npdeploytk.dll" />                      <versionRange  severity="1"></versionRange>
                   </pluginItem>
+      <pluginItem  blockID="p34">
+                  <match name="filename" exp="[Nn][Pp][Jj][Pp][Ii]1[56]0_[0-9]+\.[Dd][Ll][Ll]" />                      <versionRange >
+                      <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+              <versionRange  minVersion="3.6a1pre" maxVersion="*" />
+            </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p80">
-            <match name="description" exp="[^\d\._]((0(\.\d+(\.\d+([_\.]\d+)?)?)?)|(1\.(([0-5](\.\d+([_\.]\d+)?)?)|(6(\.0([_\.](0?\d|1\d|2\d|30))?)?)|(7(\.0([_\.][0-2])?)?))))([^\d\._]|$)" />      <match name="filename" exp="(npjp2\.dll)|(libnpjp2\.so)" />                      <versionRange  severity="1"></versionRange>
+      <match name="name" exp="\(TM\)" />      <match name="description" exp="[^\d\._]((0(\.\d+(\.\d+([_\.]\d+)?)?)?)|(1\.(([0-5](\.\d+([_\.]\d+)?)?)|(6(\.0([_\.](0?\d|1\d|2\d|30))?)?)|(7(\.0([_\.][0-2])?)?))))([^\d\._]|$)" />      <match name="filename" exp="(npjp2\.dll)|(libnpjp2\.so)" />                      <versionRange  severity="1"></versionRange>
                   </pluginItem>
       <pluginItem  blockID="p85">
-                  <match name="filename" exp="JavaPlugin2_NPAPI\.plugin" />                      <versionRange  minVersion="0" maxVersion="12.9.0" severity="1"></versionRange>
+                  <match name="filename" exp="JavaPlugin2_NPAPI\.plugin" />                      <versionRange  minVersion="0" maxVersion="13.6.0" severity="1"></versionRange>
+                  </pluginItem>
+      <pluginItem  os="Darwin" blockID="p89">
+                  <match name="filename" exp="AdobePDFViewerNPAPI\.plugin" />                      <versionRange  minVersion="0" maxVersion="10.1.3" severity="1"></versionRange>
                   </pluginItem>
     </pluginItems>
 
   <gfxItems>
     <gfxBlacklistEntry  blockID="g35">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
                       <device>0x0a6c</device>
                   </devices>
             <feature>DIRECT2D</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.17.12.5896</driverVersion>      <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
--- a/browser/base/content/aboutDialog.css
+++ b/browser/base/content/aboutDialog.css
@@ -60,13 +60,18 @@
 }
 
 .bottom-link,
 .bottom-link:focus {
   text-align: center;
   margin: 0 40px;
 }
 
+.text-link:-moz-focusring,
+.bottom-link:-moz-focusring {
+  outline: 1px dotted;
+}
+
 #currentChannel {
   margin: 0;
   padding: 0;
   font-weight: bold;
 }
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -720,17 +720,17 @@ HistoryMenu.prototype = {
     this.toggleTabsFromOtherComputers();
     this.toggleRestoreLastSession();
   },
 
   _onCommand: function HM__onCommand(aEvent) {
     let placesNode = aEvent.target._placesNode;
     if (placesNode) {
       PlacesUIUtils.markPageAsTyped(placesNode.uri);
-      openUILink(placesNode.uri, aEvent, false, true);
+      openUILink(placesNode.uri, aEvent, { ignoreAlt: true });
     }
   }
 };
 
 /**
  * Functions for handling events in the Bookmarks Toolbar and menu.
  */
 var BookmarksEventHandler = {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7306,20 +7306,23 @@ var gPluginHandler = {
       objLoadingContent.playPlugin();
       return;
     } else if (pluginsPermission == Ci.nsIPermissionManager.DENY_ACTION) {
       overlay.style.visibility = "hidden";
       return;
     }
 
     let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
-    overlay.addEventListener("click", function(aEvent) {
-      if (aEvent.button == 0 && aEvent.isTrusted)
-        gPluginHandler.activatePlugins(aEvent.target.ownerDocument.defaultView.top);
-    }, true);
+    // The overlay is null if the XBL binding is not attached (element is display:none).
+    if (overlay) {
+      overlay.addEventListener("click", function(aEvent) {
+        if (aEvent.button == 0 && aEvent.isTrusted)
+          gPluginHandler.activatePlugins(aEvent.target.ownerDocument.defaultView.top);
+      }, true);
+    }
 
     if (!browser._clickToPlayDoorhangerShown)
       gPluginHandler._showClickToPlayNotification(browser);
   },
 
   reshowClickToPlayNotification: function PH_reshowClickToPlayNotification() {
     if (!Services.prefs.getBoolPref("plugins.click_to_play"))
       return;
@@ -7868,17 +7871,17 @@ var FeedHandler = {
     if (/^https?/.test(feedURI.scheme))
       href = "feed:" + href;
     this.loadFeed(href, event);
   },
 
   loadFeed: function(href, event) {
     var feeds = gBrowser.selectedBrowser.feeds;
     try {
-      openUILink(href, event, false, true, false, null);
+      openUILink(href, event, { ignoreAlt: true });
     }
     finally {
       // We might default to a livebookmarks modal dialog,
       // so reset that if the user happens to click it again
       gBrowser.selectedBrowser.feeds = feeds;
     }
   },
 
--- a/browser/base/content/pageinfo/feeds.js
+++ b/browser/base/content/pageinfo/feeds.js
@@ -72,18 +72,18 @@ function initFeedTab()
 
   var feedListbox = document.getElementById("feedListbox");
   document.getElementById("feedTab").hidden = feedListbox.getRowCount() == 0;
 }
 
 function onSubscribeFeed()
 {
   var listbox = document.getElementById("feedListbox");
-  openUILink(listbox.selectedItem.getAttribute("feedURL"),
-             null, false, true, false, null);
+  openUILinkIn(listbox.selectedItem.getAttribute("feedURL"), "current",
+               { ignoreAlt: true });
 }
 
 function addRow(name, type, url)
 {
   var item = document.createElement("richlistitem");
   item.setAttribute("feed", "true");
   item.setAttribute("name", name);
   item.setAttribute("type", type);
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -1,11 +1,12 @@
 browser.jar:
 *   content/browser/inspector.html                (highlighter/inspector.html)
     content/browser/NetworkPanel.xhtml            (webconsole/NetworkPanel.xhtml)
+    content/browser/devtools/HUDService-content.js (webconsole/HUDService-content.js)
 *   content/browser/scratchpad.xul                (scratchpad/scratchpad.xul)
     content/browser/scratchpad.js                 (scratchpad/scratchpad.js)
     content/browser/splitview.css                 (shared/splitview.css)
 *   content/browser/styleeditor.xul               (styleeditor/styleeditor.xul)
     content/browser/styleeditor.css               (styleeditor/styleeditor.css)
     content/browser/devtools/csshtmltree.xul      (styleinspector/csshtmltree.xul)
     content/browser/devtools/cssruleview.xul      (styleinspector/cssruleview.xul)
     content/browser/devtools/styleinspector.css   (styleinspector/styleinspector.css)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/HUDService-content.js
@@ -0,0 +1,790 @@
+/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// This code is appended to the browser content script.
+(function _HUDServiceContent() {
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+let tempScope = {};
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", tempScope);
+Cu.import("resource://gre/modules/Services.jsm", tempScope);
+Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm", tempScope);
+Cu.import("resource:///modules/WebConsoleUtils.jsm", tempScope);
+
+let XPCOMUtils = tempScope.XPCOMUtils;
+let Services = tempScope.Services;
+let gConsoleStorage = tempScope.ConsoleAPIStorage;
+let WebConsoleUtils = tempScope.WebConsoleUtils;
+let l10n = WebConsoleUtils.l10n;
+tempScope = null;
+
+let _alive = true; // Track if this content script should still be alive.
+
+/**
+ * The Web Console content instance manager.
+ */
+let Manager = {
+  get window() content,
+  get console() this.window.console,
+  sandbox: null,
+  hudId: null,
+  _sequence: 0,
+  _messageListeners: ["WebConsole:Init", "WebConsole:EnableFeature",
+                      "WebConsole:DisableFeature", "WebConsole:Destroy"],
+  _messageHandlers: null,
+  _enabledFeatures: null,
+
+  /**
+   * Getter for a unique ID for the current Web Console content instance.
+   */
+  get sequenceId() "HUDContent-" + (++this._sequence),
+
+  /**
+   * Initialize the Web Console manager.
+   */
+  init: function Manager_init()
+  {
+    this._enabledFeatures = [];
+    this._messageHandlers = {};
+
+    this._messageListeners.forEach(function(aName) {
+      addMessageListener(aName, this);
+    }, this);
+
+    // Need to track the owner XUL window to listen to the unload and TabClose
+    // events, to avoid memory leaks.
+    let xulWindow = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIWebNavigation)
+                    .QueryInterface(Ci.nsIDocShell)
+                    .chromeEventHandler.ownerDocument.defaultView;
+
+    xulWindow.addEventListener("unload", this._onXULWindowClose, false);
+
+    let tabContainer = xulWindow.gBrowser.tabContainer;
+    tabContainer.addEventListener("TabClose", this._onTabClose, false);
+
+    // Need to track private browsing change and quit application notifications,
+    // again to avoid memory leaks. The Web Console main process cannot notify
+    // this content script when the XUL window close, tab close, private
+    // browsing change and quit application events happen, so we must call
+    // Manager.destroy() on our own.
+    Services.obs.addObserver(this, "private-browsing-change-granted", false);
+    Services.obs.addObserver(this, "quit-application-granted", false);
+  },
+
+  /**
+   * The message handler. This method forwards all the remote messages to the
+   * appropriate code.
+   */
+  receiveMessage: function Manager_receiveMessage(aMessage)
+  {
+    if (!_alive) {
+      return;
+    }
+
+    if (!aMessage.json || (aMessage.name != "WebConsole:Init" &&
+                           aMessage.json.hudId != this.hudId)) {
+      Cu.reportError("Web Console content script: received message " +
+                     aMessage.name + " from wrong hudId!");
+      return;
+    }
+
+    switch (aMessage.name) {
+      case "WebConsole:Init":
+        this._onInit(aMessage.json);
+        break;
+      case "WebConsole:EnableFeature":
+        this.enableFeature(aMessage.json.feature, aMessage.json);
+        break;
+      case "WebConsole:DisableFeature":
+        this.disableFeature(aMessage.json.feature);
+        break;
+      case "WebConsole:Destroy":
+        this.destroy();
+        break;
+      default: {
+        let handler = this._messageHandlers[aMessage.name];
+        handler && handler(aMessage.json);
+        break;
+      }
+    }
+  },
+
+  /**
+   * Observe notifications from the nsIObserverService.
+   *
+   * @param mixed aSubject
+   * @param string aTopic
+   * @param mixed aData
+   */
+  observe: function Manager_observe(aSubject, aTopic, aData)
+  {
+    if (_alive && (aTopic == "quit-application-granted" ||
+        (aTopic == "private-browsing-change-granted" &&
+         (aData == "enter" || aData == "exit")))) {
+      this.destroy();
+    }
+  },
+
+  /**
+   * The manager initialization code. This method is called when the Web Console
+   * remote process initializes the content process (this code!).
+   *
+   * @param object aMessage
+   *        The object received from the remote process. The WebConsole:Init
+   *        message properties:
+   *        - hudId - (required) the remote Web Console instance ID.
+   *        - features - (optional) array of features you want to enable from
+   *        the start. For each feature you enable you can pass feature-specific
+   *        options in a property on the JSON object you send with the same name
+   *        as the feature. See this.enableFeature() for the list of available
+   *        features.
+   *        - cachedMessages - (optional) an array of cached messages you want
+   *        to receive. See this._sendCachedMessages() for the list of available
+   *        message types.
+   *
+   *        Example message:
+   *        {
+   *          hudId: "foo1",
+   *          features: ["JSTerm", "ConsoleAPI"],
+   *          ConsoleAPI: { ... }, // ConsoleAPI-specific options
+   *          cachedMessages: ["ConsoleAPI"],
+   *        }
+   */
+  _onInit: function Manager_onInit(aMessage)
+  {
+    this.hudId = aMessage.hudId;
+    if (aMessage.features) {
+      aMessage.features.forEach(function(aFeature) {
+        this.enableFeature(aFeature, aMessage[aFeature]);
+      }, this);
+    }
+
+    if (aMessage.cachedMessages) {
+      this._sendCachedMessages(aMessage.cachedMessages);
+    }
+  },
+
+  /**
+   * Add a remote message handler. This is used by other components of the Web
+   * Console content script.
+   *
+   * @param string aName
+   *        Message name to listen for.
+   * @param function aCallback
+   *        Function to execute when the message is received. This function is
+   *        given the JSON object that came from the remote Web Console
+   *        instance.
+   *        Only one callback per message name is allowed!
+   */
+  addMessageHandler: function Manager_addMessageHandler(aName, aCallback)
+  {
+    if (aName in this._messageHandlers) {
+      Cu.reportError("Web Console content script: addMessageHandler() called for an existing message handler: " + aName);
+      return;
+    }
+
+    this._messageHandlers[aName] = aCallback;
+    addMessageListener(aName, this);
+  },
+
+  /**
+   * Remove the message handler for the given name.
+   *
+   * @param string aName
+   *        Message name for the handler you want removed.
+   */
+  removeMessageHandler: function Manager_removeMessageHandler(aName)
+  {
+    if (!(aName in this._messageHandlers)) {
+      return;
+    }
+
+    delete this._messageHandlers[aName];
+    removeMessageListener(aName, this);
+  },
+
+  /**
+   * Send a message to the remote Web Console instance.
+   *
+   * @param string aName
+   *        The name of the message you want to send.
+   * @param object aMessage
+   *        The message object you want to send.
+   */
+  sendMessage: function Manager_sendMessage(aName, aMessage)
+  {
+    aMessage.hudId = this.hudId;
+    if (!("id" in aMessage)) {
+      aMessage.id = this.sequenceId;
+    }
+
+    sendAsyncMessage(aName, aMessage);
+  },
+
+  /**
+   * Enable a feature in the Web Console content script. A feature is generally
+   * a set of observers/listeners that are added in the content process. This
+   * content script exposes the data via the message manager for the features
+   * you enable.
+   *
+   * Supported features:
+   *    - JSTerm - a JavaScript "terminal" which allows code execution.
+   *    - ConsoleAPI - support for routing the window.console API to the remote
+   *    process.
+   *    - PageError - route all the nsIScriptErrors from the nsIConsoleService
+   *    to the remote process.
+   *
+   * @param string aFeature
+   *        One of the supported features: JSTerm, ConsoleAPI.
+   * @param object [aMessage]
+   *        Optional JSON message object coming from the remote Web Console
+   *        instance. This can be used for feature-specific options.
+   */
+  enableFeature: function Manager_enableFeature(aFeature, aMessage)
+  {
+    if (this._enabledFeatures.indexOf(aFeature) != -1) {
+      return;
+    }
+
+    switch (aFeature) {
+      case "JSTerm":
+        JSTerm.init(aMessage);
+        break;
+      case "ConsoleAPI":
+        ConsoleAPIObserver.init(aMessage);
+        break;
+      case "PageError":
+        ConsoleListener.init(aMessage);
+        break;
+      default:
+        Cu.reportError("Web Console content: unknown feature " + aFeature);
+        break;
+    }
+
+    this._enabledFeatures.push(aFeature);
+  },
+
+  /**
+   * Disable a Web Console content script feature.
+   *
+   * @see this.enableFeature
+   * @param string aFeature
+   *        One of the supported features - see this.enableFeature() for the
+   *        list of supported features.
+   */
+  disableFeature: function Manager_disableFeature(aFeature)
+  {
+    let index = this._enabledFeatures.indexOf(aFeature);
+    if (index == -1) {
+      return;
+    }
+    this._enabledFeatures.splice(index, 1);
+
+    switch (aFeature) {
+      case "JSTerm":
+        JSTerm.destroy();
+        break;
+      case "ConsoleAPI":
+        ConsoleAPIObserver.destroy();
+        break;
+      case "PageError":
+        ConsoleListener.destroy();
+        break;
+      default:
+        Cu.reportError("Web Console content: unknown feature " + aFeature);
+        break;
+    }
+  },
+
+  /**
+   * Send the cached messages to the remote Web Console instance.
+   *
+   * @private
+   * @param array aMessageTypes
+   *        An array that lists which kinds of messages you want. Supported
+   *        message types: "ConsoleAPI" and "PageError".
+   */
+  _sendCachedMessages: function Manager__sendCachedMessages(aMessageTypes)
+  {
+    let messages = [];
+
+    while (aMessageTypes.length > 0) {
+      switch (aMessageTypes.shift()) {
+        case "ConsoleAPI":
+          messages.push.apply(messages, ConsoleAPIObserver.getCachedMessages());
+          break;
+        case "PageError":
+          messages.push.apply(messages, ConsoleListener.getCachedMessages());
+          break;
+      }
+    }
+
+    messages.sort(function(a, b) { return a.timeStamp - b.timeStamp; });
+
+    this.sendMessage("WebConsole:CachedMessages", {messages: messages});
+  },
+
+  /**
+   * The XUL window "unload" event handler which destroys this content script
+   * instance.
+   * @private
+   */
+  _onXULWindowClose: function Manager__onXULWindowClose()
+  {
+    if (_alive) {
+      Manager.destroy();
+    }
+  },
+
+  /**
+   * The "TabClose" event handler which destroys this content script
+   * instance, if needed.
+   * @private
+   */
+  _onTabClose: function Manager__onTabClose(aEvent)
+  {
+    let tab = aEvent.target;
+    if (_alive && tab.linkedBrowser.contentWindow === Manager.window) {
+      Manager.destroy();
+    }
+  },
+
+  /**
+   * Destroy the Web Console content script instance.
+   */
+  destroy: function Manager_destroy()
+  {
+    Services.obs.removeObserver(this, "private-browsing-change-granted");
+    Services.obs.removeObserver(this, "quit-application-granted");
+
+    _alive = false;
+    let xulWindow = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIWebNavigation)
+                    .QueryInterface(Ci.nsIDocShell)
+                    .chromeEventHandler.ownerDocument.defaultView;
+
+    xulWindow.removeEventListener("unload", this._onXULWindowClose, false);
+    let tabContainer = xulWindow.gBrowser.tabContainer;
+    tabContainer.removeEventListener("TabClose", this._onTabClose, false);
+
+    this._messageListeners.forEach(function(aName) {
+      removeMessageListener(aName, this);
+    }, this);
+
+    this._enabledFeatures.slice().forEach(this.disableFeature, this);
+
+    this.hudId = null;
+    this._messageHandlers = null;
+    Manager = ConsoleAPIObserver = JSTerm = ConsoleListener = null;
+    Cc = Ci = Cu = XPCOMUtils = Services = gConsoleStorage =
+      WebConsoleUtils = l10n = null;
+  },
+};
+
+/**
+ * The JavaScript terminal is meant to allow remote code execution for the Web
+ * Console.
+ */
+let JSTerm = {
+  /**
+   * Evaluation result objects are cached in this object. The chrome process can
+   * request any object based on its ID.
+   */
+  _objectCache: null,
+
+  /**
+   * Initialize the JavaScript terminal feature.
+   */
+  init: function JST_init()
+  {
+    this._objectCache = {};
+
+    Manager.addMessageHandler("JSTerm:GetEvalObject",
+                              this.handleGetEvalObject.bind(this));
+    Manager.addMessageHandler("JSTerm:ClearObjectCache",
+                              this.handleClearObjectCache.bind(this));
+  },
+
+  /**
+   * Handler for the remote "JSTerm:GetEvalObject" message. This allows the
+   * remote Web Console instance to retrieve an object from the content process.
+   *
+   * @param object aRequest
+   *        The message that requests the content object. Properties: cacheId,
+   *        objectId and resultCacheId.
+   *
+   *        Evaluated objects are stored in "buckets" (cache IDs). Each object
+   *        is assigned an ID (object ID). You can request a specific object
+   *        (objectId) from a specific cache (cacheId) and tell where the result
+   *        should be cached (resultCacheId). The requested object can have
+   *        further references to other objects - those references will be
+   *        cached in the "bucket" of your choice (based on resultCacheId). If
+   *        you do not provide any resultCacheId in the request message, then
+   *        cacheId will be used.
+   */
+  handleGetEvalObject: function JST_handleGetEvalObject(aRequest)
+  {
+    if (aRequest.cacheId in this._objectCache &&
+        aRequest.objectId in this._objectCache[aRequest.cacheId]) {
+      let object = this._objectCache[aRequest.cacheId][aRequest.objectId];
+      let resultCacheId = aRequest.resultCacheId || aRequest.cacheId;
+      let message = {
+        id: aRequest.id,
+        cacheId: aRequest.cacheId,
+        objectId: aRequest.objectId,
+        object: this.prepareObjectForRemote(object, resultCacheId),
+        childrenCacheId: resultCacheId,
+      };
+      Manager.sendMessage("JSTerm:EvalObject", message);
+    }
+    else {
+      Cu.reportError("JSTerm:GetEvalObject request " + aRequest.id +
+                     ": stale object.");
+    }
+  },
+
+  /**
+   * Handler for the remote "JSTerm:ClearObjectCache" message. This allows the
+   * remote Web Console instance to clear the cache of objects that it no longer
+   * uses.
+   *
+   * @param object aRequest
+   *        An object that holds one property: the cacheId you want cleared.
+   */
+  handleClearObjectCache: function JST_handleClearObjectCache(aRequest)
+  {
+    if (aRequest.cacheId in this._objectCache) {
+      delete this._objectCache[aRequest.cacheId];
+    }
+  },
+
+  /**
+   * Prepare an object to be sent to the remote Web Console instance.
+   *
+   * @param object aObject
+   *        The object you want to send to the remote Web Console instance.
+   * @param number aCacheId
+   *        Cache ID where you want object references to be stored into. The
+   *        given object may include references to other objects - those
+   *        references will be stored in the given cache ID so the remote
+   *        process can later retrieve them as well.
+   * @return array
+   *         An array that holds one element for each enumerable property and
+   *         method in aObject. Each element describes the property. For details
+   *         see WebConsoleUtils.namesAndValuesOf().
+   */
+  prepareObjectForRemote:
+  function JST_prepareObjectForRemote(aObject, aCacheId)
+  {
+    // Cache the properties that have inspectable values.
+    let propCache = this._objectCache[aCacheId] || {};
+    let result = WebConsoleUtils.namesAndValuesOf(aObject, propCache);
+    if (!(aCacheId in this._objectCache) && Object.keys(propCache).length > 0) {
+      this._objectCache[aCacheId] = propCache;
+    }
+
+    return result;
+  },
+
+  /**
+   * Destroy the JSTerm instance.
+   */
+  destroy: function JST_destroy()
+  {
+    Manager.removeMessageHandler("JSTerm:GetEvalObject");
+    Manager.removeMessageHandler("JSTerm:ClearObjectCache");
+
+    delete this._objectCache;
+  },
+};
+
+/**
+ * The window.console API observer. This allows the window.console API messages
+ * to be sent to the remote Web Console instance.
+ */
+let ConsoleAPIObserver = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  /**
+   * Initialize the window.console API observer.
+   */
+  init: function CAO_init()
+  {
+    // Note that the observer is process-wide. We will filter the messages as
+    // needed, see CAO_observe().
+    Services.obs.addObserver(this, "console-api-log-event", false);
+
+    Manager.addMessageHandler("ConsoleAPI:ClearCache",
+                              this.handleClearCache.bind(this));
+  },
+
+  /**
+   * The console API message observer. When messages are received from the
+   * observer service we forward them to the remote Web Console instance.
+   *
+   * @param object aMessage
+   *        The message object receives from the observer service.
+   * @param string aTopic
+   *        The message topic received from the observer service.
+   */
+  observe: function CAO_observe(aMessage, aTopic)
+  {
+    if (!_alive || !aMessage || aTopic != "console-api-log-event") {
+      return;
+    }
+
+    let apiMessage = aMessage.wrappedJSObject;
+
+    let msgWindow =
+      WebConsoleUtils.getWindowByOuterId(apiMessage.ID, Manager.window);
+    if (!msgWindow || msgWindow.top != Manager.window) {
+      // Not the same window!
+      return;
+    }
+
+    let messageToChrome = {};
+    this._prepareApiMessageForRemote(apiMessage, messageToChrome);
+    Manager.sendMessage("WebConsole:ConsoleAPI", messageToChrome);
+  },
+
+  /**
+   * Prepare a message from the console APi to be sent to the remote Web Console
+   * instance.
+   *
+   * @param object aOriginalMessage
+   *        The original message received from console-api-log-event.
+   * @param object aRemoteMessage
+   *        The object you want to send to the remote Web Console. This object
+   *        is updated to hold information from the original message. New
+   *        properties added:
+   *        - timeStamp
+   *        Message timestamp (same as the aOriginalMessage.timeStamp property).
+   *        - apiMessage
+   *        An object that copies almost all the properties from
+   *        aOriginalMessage. Arguments might be skipped if it holds references
+   *        to objects that cannot be sent as they are to the remote Web Console
+   *        instance.
+   *        - argumentsToString
+   *        Optional: the aOriginalMessage.arguments object stringified.
+   *
+   *        The apiMessage.arguments property is set to hold data appropriate
+   *        to the message level. A similar approach is used for
+   *        argumentsToString.
+   */
+  _prepareApiMessageForRemote:
+  function CAO__prepareApiMessageForRemote(aOriginalMessage, aRemoteMessage)
+  {
+    aRemoteMessage.apiMessage =
+      WebConsoleUtils.cloneObject(aOriginalMessage, true,
+        function(aKey, aValue, aObject) {
+          // We need to skip the arguments property from the original object.
+          if (aKey == "wrappedJSObject" || aObject === aOriginalMessage &&
+              aKey == "arguments") {
+            return false;
+          }
+          return true;
+        });
+
+    aRemoteMessage.timeStamp = aOriginalMessage.timeStamp;
+
+    switch (aOriginalMessage.level) {
+      case "trace":
+      case "time":
+      case "timeEnd":
+      case "group":
+      case "groupCollapsed":
+        aRemoteMessage.apiMessage.arguments =
+          WebConsoleUtils.cloneObject(aOriginalMessage.arguments, true);
+        break;
+
+      case "log":
+      case "info":
+      case "warn":
+      case "error":
+      case "debug":
+      case "groupEnd":
+        aRemoteMessage.argumentsToString =
+          Array.map(aOriginalMessage.arguments || [],
+                    this._formatObject.bind(this));
+        break;
+
+      case "dir": {
+        aRemoteMessage.objectsCacheId = Manager.sequenceId;
+        aRemoteMessage.argumentsToString = [];
+        let mapFunction = function(aItem) {
+          aRemoteMessage.argumentsToString.push(this._formatObject(aItem));
+          if (WebConsoleUtils.isObjectInspectable(aItem)) {
+            return JSTerm.prepareObjectForRemote(aItem,
+                                                 aRemoteMessage.objectsCacheId);
+          }
+          return aItem;
+        }.bind(this);
+
+        aRemoteMessage.apiMessage.arguments =
+          Array.map(aOriginalMessage.arguments || [], mapFunction);
+        break;
+      }
+      default:
+        Cu.reportError("Unknown Console API log level: " +
+                       aOriginalMessage.level);
+        break;
+    }
+  },
+
+  /**
+   * Format an object's value to be displayed in the Web Console.
+   *
+   * @private
+   * @param object aObject
+   *        The object you want to display.
+   * @return string
+   *         The string you can display for the given object.
+   */
+  _formatObject: function CAO__formatObject(aObject)
+  {
+    return typeof aObject == "string" ?
+           aObject : WebConsoleUtils.formatResult(aObject);
+  },
+
+  /**
+   * Get the cached messages for the current inner window.
+   *
+   * @see this._prepareApiMessageForRemote()
+   * @return array
+   *         The array of cached messages. Each element is a Console API
+   *         prepared to be sent to the remote Web Console instance.
+   */
+  getCachedMessages: function CAO_getCachedMessages()
+  {
+    let innerWindowId = WebConsoleUtils.getInnerWindowId(Manager.window);
+    let messages = gConsoleStorage.getEvents(innerWindowId);
+
+    let result = messages.map(function(aMessage) {
+      let remoteMessage = { _type: "ConsoleAPI" };
+      this._prepareApiMessageForRemote(aMessage.wrappedJSObject, remoteMessage);
+      return remoteMessage;
+    }, this);
+
+    return result;
+  },
+
+  /**
+   * Handler for the "ConsoleAPI:ClearCache" message.
+   */
+  handleClearCache: function CAO_handleClearCache()
+  {
+    let windowId = WebConsoleUtils.getInnerWindowId(Manager.window);
+    gConsoleStorage.clearEvents(windowId);
+  },
+
+  /**
+   * Destroy the ConsoleAPIObserver listeners.
+   */
+  destroy: function CAO_destroy()
+  {
+    Manager.removeMessageHandler("ConsoleAPI:ClearCache");
+    Services.obs.removeObserver(this, "console-api-log-event");
+  },
+};
+
+/**
+ * The nsIConsoleService listener. This is used to send all the page errors
+ * (JavaScript, CSS and more) to the remote Web Console instance.
+ */
+let ConsoleListener = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]),
+
+  /**
+   * Initialize the nsIConsoleService listener.
+   */
+  init: function CL_init()
+  {
+    Services.console.registerListener(this);
+  },
+
+  /**
+   * The nsIConsoleService observer. This method takes all the script error
+   * messages belonging to the current window and sends them to the remote Web
+   * Console instance.
+   *
+   * @param nsIScriptError aScriptError
+   *        The script error object coming from the nsIConsoleService.
+   */
+  observe: function CL_observe(aScriptError)
+  {
+    if (!_alive || !(aScriptError instanceof Ci.nsIScriptError) ||
+        !aScriptError.outerWindowID) {
+      return;
+    }
+
+    switch (aScriptError.category) {
+      // We ignore chrome-originating errors as we only care about content.
+      case "XPConnect JavaScript":
+      case "component javascript":
+      case "chrome javascript":
+      case "chrome registration":
+      case "XBL":
+      case "XBL Prototype Handler":
+      case "XBL Content Sink":
+      case "xbl javascript":
+        return;
+    }
+
+    let errorWindow =
+      WebConsoleUtils.getWindowByOuterId(aScriptError.outerWindowID,
+                                         Manager.window);
+    if (!errorWindow || errorWindow.top != Manager.window) {
+      return;
+    }
+
+    Manager.sendMessage("WebConsole:PageError", { pageError: aScriptError });
+  },
+
+  /**
+   * Get the cached page errors for the current inner window.
+   *
+   * @return array
+   *         The array of cached messages. Each element is an nsIScriptError
+   *         with an added _type property so the remote Web Console instance can
+   *         tell the difference between various types of cached messages.
+   */
+  getCachedMessages: function CL_getCachedMessages()
+  {
+    let innerWindowId = WebConsoleUtils.getInnerWindowId(Manager.window);
+    let result = [];
+    let errors = {};
+    Services.console.getMessageArray(errors, {});
+
+    (errors.value || []).forEach(function(aError) {
+      if (!(aError instanceof Ci.nsIScriptError) ||
+          aError.innerWindowID != innerWindowId) {
+        return;
+      }
+
+      let remoteMessage = WebConsoleUtils.cloneObject(aError);
+      remoteMessage._type = "PageError";
+      result.push(remoteMessage);
+    });
+
+    return result;
+  },
+
+  /**
+   * Remove the nsIConsoleService listener.
+   */
+  destroy: function CL_destroy()
+  {
+    Services.console.unregisterListener(this);
+  },
+};
+
+Manager.init();
+})();
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -49,17 +49,16 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const CONSOLEAPI_CLASS_ID = "{b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/NetworkHelper.jsm");
-Cu.import("resource:///modules/PropertyPanel.jsm");
 
 var EXPORTED_SYMBOLS = ["HUDService", "ConsoleUtils"];
 
 XPCOMUtils.defineLazyServiceGetter(this, "scriptError",
                                    "@mozilla.org/scripterror;1",
                                    "nsIScriptError");
 
 XPCOMUtils.defineLazyServiceGetter(this, "activityDistributor",
@@ -103,16 +102,28 @@ XPCOMUtils.defineLazyGetter(this, "Prope
   try {
     Cu.import("resource:///modules/PropertyPanel.jsm", obj);
   } catch (err) {
     Cu.reportError(err);
   }
   return obj.PropertyPanel;
 });
 
+XPCOMUtils.defineLazyGetter(this, "PropertyPanelAsync", function () {
+  let obj = {};
+  Cu.import("resource:///modules/PropertyPanelAsync.jsm", obj);
+  return obj.PropertyPanel;
+});
+
+XPCOMUtils.defineLazyGetter(this, "PropertyTreeViewAsync", function () {
+  let obj = {};
+  Cu.import("resource:///modules/PropertyPanelAsync.jsm", obj);
+  return obj.PropertyTreeView;
+});
+
 XPCOMUtils.defineLazyGetter(this, "AutocompletePopup", function () {
   var obj = {};
   try {
     Cu.import("resource:///modules/AutocompletePopup.jsm", obj);
   }
   catch (err) {
     Cu.reportError(err);
   }
@@ -131,20 +142,24 @@ XPCOMUtils.defineLazyGetter(this, "Scrat
 });
 
 XPCOMUtils.defineLazyGetter(this, "namesAndValuesOf", function () {
   var obj = {};
   Cu.import("resource:///modules/PropertyPanel.jsm", obj);
   return obj.namesAndValuesOf;
 });
 
-XPCOMUtils.defineLazyGetter(this, "gConsoleStorage", function () {
+XPCOMUtils.defineLazyGetter(this, "WebConsoleUtils", function () {
   let obj = {};
-  Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm", obj);
-  return obj.ConsoleAPIStorage;
+  Cu.import("resource:///modules/WebConsoleUtils.jsm", obj);
+  return obj.WebConsoleUtils;
+});
+
+XPCOMUtils.defineLazyGetter(this, "l10n", function() {
+  return WebConsoleUtils.l10n;
 });
 
 function LogFactory(aMessagePrefix)
 {
   function log(aMessage) {
     var _msg = aMessagePrefix + " " + aMessage + "\n";
     dump(_msg);
   }
@@ -276,16 +291,19 @@ const MINIMUM_CONSOLE_HEIGHT = 150;
 const MINIMUM_PAGE_HEIGHT = 50;
 
 // The default console height, as a ratio from the content window inner height.
 const DEFAULT_CONSOLE_HEIGHT = 0.33;
 
 // Constant used when checking the typeof objects.
 const TYPEOF_FUNCTION = "function";
 
+// This script is inserted into the content process.
+const CONTENT_SCRIPT_URL = "chrome://browser/content/devtools/HUDService-content.js";
+
 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.
@@ -607,31 +625,16 @@ function createElement(aDocument, aTag, 
  */
 function createAndAppendElement(aParent, aTag, aAttributes)
 {
   let node = createElement(aParent.ownerDocument, aTag, aAttributes);
   aParent.appendChild(node);
   return node;
 }
 
-/**
- * Convenience function to unwrap a wrapped object.
- *
- * @param aObject the object to unwrap
- */
-
-function unwrap(aObject)
-{
-  try {
-    return XPCNativeWrapper.unwrap(aObject);
-  } catch(e) {
-    return aObject;
-  }
-}
-
 ///////////////////////////////////////////////////////////////////////////
 //// NetworkPanel
 
 /**
  * Creates a new NetworkPanel.
  *
  * @param nsIDOMNode aParent
  *        Parent node to append the created panel to.
@@ -640,17 +643,17 @@ function unwrap(aObject)
  */
 function NetworkPanel(aParent, aHttpActivity)
 {
   let doc = aParent.ownerDocument;
   this.httpActivity = aHttpActivity;
 
   // Create the underlaying panel
   this.panel = createElement(doc, "panel", {
-    label: HUDService.getStr("NetworkPanel.label"),
+    label: l10n.getStr("NetworkPanel.label"),
     titlebar: "normal",
     noautofocus: "true",
     noautohide: "true",
     close: "true"
   });
 
   // Create the iframe that displays the NetworkPanel XHTML.
   this.iframe = createAndAppendElement(this.panel, "iframe", {
@@ -726,17 +729,17 @@ NetworkPanel.prototype =
    *        "NetworkPanel." before calling the HUDService.getFormatStr function.
    * @param array aArray
    *        Values used as placeholder for the i10n string.
    * @returns string
    *          The i10n formated string.
    */
   _format: function NP_format(aName, aArray)
   {
-    return HUDService.getFormatStr("NetworkPanel." + aName, aArray);
+    return l10n.getFormatStr("NetworkPanel." + aName, aArray);
   },
 
   /**
    * Returns the content type of the response body. This is based on the
    * response.header["Content-Type"] info. If this value is not available, then
    * the content type is tried to be estimated by the url file ending.
    *
    * @returns string or null
@@ -926,17 +929,17 @@ NetworkPanel.prototype =
   {
     let timing = this.httpActivity.timing;
     let request = this.httpActivity.request;
 
     this._appendTextNode("headUrl", this.httpActivity.url);
     this._appendTextNode("headMethod", this.httpActivity.method);
 
     this._appendTextNode("requestHeadersInfo",
-      ConsoleUtils.timestampString(timing.REQUEST_HEADER/1000));
+      l10n.timestampString(timing.REQUEST_HEADER/1000));
 
     this._appendList("requestHeadersContent", request.header, true);
 
     if ("Cookie" in request.header) {
       this._displayNode("requestCookie");
 
       let cookies = request.header.Cookie.split(";");
       let cookieList = {};
@@ -1271,29 +1274,35 @@ function pruneConsoleOutputIfNecessary(a
   let oldScrollHeight = scrollBox.scrollHeight;
   let scrolledToBottom = ConsoleUtils.isOutputScrolledToBottom(outputNode);
 
   // Prune the nodes.
   let messageNodes = outputNode.querySelectorAll(".webconsole-msg-" +
       CATEGORY_CLASS_FRAGMENTS[aCategory]);
   let removeNodes = messageNodes.length - logLimit;
   for (let i = 0; i < removeNodes; i++) {
-    if (messageNodes[i].classList.contains("webconsole-msg-cssparser")) {
+    let node = messageNodes[i];
+    if (node._evalCacheId && !node._panelOpen) {
+      hudRef.jsterm.clearObjectCache(node._evalCacheId);
+    }
+
+    if (node.classList.contains("webconsole-msg-cssparser")) {
       let desc = messageNodes[i].childNodes[2].textContent;
       let location = "";
-      if (messageNodes[i].childNodes[4]) {
-        location = messageNodes[i].childNodes[4].getAttribute("title");
+      if (node.childNodes[4]) {
+        location = node.childNodes[4].getAttribute("title");
       }
       delete hudRef.cssNodes[desc + location];
     }
-    else if (messageNodes[i].classList.contains("webconsole-msg-inspector")) {
-      hudRef.pruneConsoleDirNode(messageNodes[i]);
+    else if (node.classList.contains("webconsole-msg-inspector")) {
+      hudRef.pruneConsoleDirNode(node);
       continue;
     }
-    messageNodes[i].parentNode.removeChild(messageNodes[i]);
+
+    node.parentNode.removeChild(node);
   }
 
   if (!scrolledToBottom && removeNodes > 0 &&
       oldScrollHeight != scrollBox.scrollHeight) {
     scrollBox.scrollTop -= oldScrollHeight - scrollBox.scrollHeight;
   }
 
   return logLimit;
@@ -1357,107 +1366,31 @@ function HUD_SERVICE()
    * Response headers for requests that haven't finished yet.
    */
   this.openResponseHeaders = {};
 };
 
 HUD_SERVICE.prototype =
 {
   /**
-   * Last value entered
-   */
-  lastInputValue: "",
-
-  /**
-   * L10N shortcut function
-   *
-   * @param string aName
-   * @returns string
-   */
-  getStr: function HS_getStr(aName)
-  {
-    return stringBundle.GetStringFromName(aName);
-  },
-
-  /**
-   * L10N shortcut function
-   *
-   * @param string aName
-   * @returns (format) string
-   */
-  getFormatStr: function HS_getFormatStr(aName, aArray)
-  {
-    return stringBundle.formatStringFromName(aName, aArray, aArray.length);
-  },
-
-  /**
    * getter for UI commands to be used by the frontend
    *
    * @returns object
    */
   get consoleUI() {
     return HeadsUpDisplayUICommands;
   },
 
   /**
    * The sequencer is a generator (after initialization) that returns unique
    * integers
    */
   sequencer: null,
 
   /**
-   * Gets the ID of the outer window of this DOM window
-   *
-   * @param nsIDOMWindow aWindow
-   * @returns integer
-   */
-  getWindowId: function HS_getWindowId(aWindow)
-  {
-    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
-  },
-
-  /**
-   * Gets the ID of the inner window of this DOM window
-   *
-   * @param nsIDOMWindow aWindow
-   * @returns integer
-   */
-  getInnerWindowId: function HS_getInnerWindowId(aWindow)
-  {
-    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-           getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
-  },
-
-  /**
-   * Gets the top level content window that has an outer window with
-   * the given ID or returns null if no such content window exists
-   *
-   * @param integer aId
-   * @returns nsIDOMWindow
-   */
-  getWindowByWindowId: function HS_getWindowByWindowId(aId)
-  {
-    // In the future (post-Electrolysis), getOuterWindowWithId() could
-    // return null, because the originating window could have gone away
-    // while we were in the process of receiving and/or processing a
-    // message. For future-proofing purposes, we do a null check here.
-
-    let someWindow = Services.wm.getMostRecentWindow(null);
-    let content = null;
-
-    if (someWindow) {
-      let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                  .getInterface(Ci.nsIDOMWindowUtils);
-      content = windowUtils.getOuterWindowWithId(aId);
-    }
-
-    return content;
-  },
-
-  /**
    * Whether to save the bodies of network requests and responses. Disabled by
    * default to save memory.
    */
   saveRequestAndResponseBodies: false,
 
   /**
    * Tell the HUDService that a HeadsUpDisplay can be activated
    * for the window or context that has 'aContextDOMId' node id
@@ -1565,18 +1498,19 @@ HUD_SERVICE.prototype =
   deactivateHUDForContext: function HS_deactivateHUDForContext(aContext, aAnimated)
   {
     let browser = aContext.linkedBrowser;
     let window = browser.contentWindow;
     let chromeDocument = aContext.ownerDocument;
     let nBox = chromeDocument.defaultView.getNotificationBox(window);
     let hudId = "hud_" + nBox.id;
     let displayNode = chromeDocument.getElementById(hudId);
-
-    if (hudId in this.hudReferences && displayNode) {
+    let hudFound = (hudId in this.hudReferences) && displayNode;
+
+    if (hudFound) {
       if (!aAnimated) {
         this.storeHeight(hudId);
       }
 
       let hud = this.hudReferences[hudId];
       browser.webProgress.removeProgressListener(hud.progressListener);
       delete hud.progressListener;
 
@@ -1594,16 +1528,21 @@ HUD_SERVICE.prototype =
       procInstr.contexts = procInstr.contexts.filter(function(id) {
         return id !== hudId;
       });
       if (procInstr.contexts.length == 0 && procInstr.parentNode) {
         procInstr.parentNode.removeChild(procInstr);
         delete aContext.ownerDocument.gcliCssProcInstr;
       }
     }
+
+    if (hudFound) {
+      let id = WebConsoleUtils.supportsString(hudId);
+      Services.obs.notifyObservers(id, "web-console-destroyed", null);
+    }
   },
 
   /**
    * get a unique ID from the sequence generator
    *
    * @returns integer
    */
   sequenceId: function HS_sequencerId()
@@ -1798,19 +1737,16 @@ HUD_SERVICE.prototype =
 
   /**
    * Register a reference of each HeadsUpDisplay that is created
    */
   registerHUDReference:
   function HS_registerHUDReference(aHUD)
   {
     this.hudReferences[aHUD.hudId] = aHUD;
-
-    let id = ConsoleUtils.supString(aHUD.hudId);
-    Services.obs.notifyObservers(id, "web-console-created", null);
   },
 
   /**
    * Register a new Heads Up Display
    *
    * @returns void
    */
   registerDisplay: function HS_registerDisplay(aHUDId)
@@ -1889,19 +1825,16 @@ HUD_SERVICE.prototype =
     this.unregisterActiveContext(aHUDId);
 
     let popupset = hud.chromeDocument.getElementById("mainPopupSet");
     let panels = popupset.querySelectorAll("panel[hudId=" + aHUDId + "]");
     for (let i = 0; i < panels.length; i++) {
       panels[i].hidePopup();
     }
 
-    let id = ConsoleUtils.supString(aHUDId);
-    Services.obs.notifyObservers(id, "web-console-destroyed", null);
-
     if (Object.keys(this.hudReferences).length == 0) {
       let autocompletePopup = hud.chromeDocument.
                               getElementById("webConsole_autocompletePopup");
       if (autocompletePopup) {
         autocompletePopup.parentNode.removeChild(autocompletePopup);
       }
 
       this.suspend();
@@ -1919,18 +1852,16 @@ HUD_SERVICE.prototype =
     if (Object.keys(this.hudReferences).length > 0) {
       return;
     }
 
     // begin observing HTTP traffic
     this.startHTTPObservation();
 
     HUDWindowObserver.init();
-    HUDConsoleObserver.init();
-    ConsoleAPIObserver.init();
   },
 
   /**
    * Suspend Web Console activity. This is called when all Web Consoles are
    * closed.
    *
    * @returns void
    */
@@ -1945,18 +1876,16 @@ HUD_SERVICE.prototype =
     this.openRequests = {};
     this.openResponseHeaders = {};
 
     delete this.defaultFilterPrefs;
 
     delete this.lastFinishedRequestCallback;
 
     HUDWindowObserver.uninit();
-    HUDConsoleObserver.uninit();
-    ConsoleAPIObserver.shutdown();
   },
 
   /**
    * Shutdown all HeadsUpDisplays on xpcom-shutdown
    *
    * @returns void
    */
   shutdown: function HS_shutdown()
@@ -1982,17 +1911,17 @@ HUD_SERVICE.prototype =
    * Returns the hudId that is corresponding to the hud activated for the
    * passed aContentWindow. If there is no matching hudId null is returned.
    *
    * @param nsIDOMWindow aContentWindow
    * @returns string or null
    */
   getHudIdByWindow: function HS_getHudIdByWindow(aContentWindow)
   {
-    let windowId = this.getWindowId(aContentWindow);
+    let windowId = WebConsoleUtils.getOuterWindowId(aContentWindow);
     return this.getHudIdByWindowId(windowId);
   },
 
   /**
    * Returns the hudReference for a given id.
    *
    * @param string aId
    * @returns Object
@@ -2033,188 +1962,16 @@ HUD_SERVICE.prototype =
    */
   updateFilterText: function HS_updateFiltertext(aTextBoxNode)
   {
     var hudId = aTextBoxNode.getAttribute("hudId");
     this.adjustVisibilityOnSearchStringChange(hudId, aTextBoxNode.value);
   },
 
   /**
-   * Logs a message to the Web Console that originates from the window.console
-   * service ("console-api-log-event" notifications).
-   *
-   * @param string aHUDId
-   *        The ID of the Web Console to which to send the message.
-   * @param object aMessage
-   *        The message reported by the console service.
-   * @return void
-   */
-  logConsoleAPIMessage: function HS_logConsoleAPIMessage(aHUDId, aMessage)
-  {
-    // Pipe the message to createMessageNode().
-    let hud = HUDService.hudReferences[aHUDId];
-    function formatResult(x) {
-      if (typeof(x) == "string") {
-        return x;
-      }
-      if (hud.gcliterm) {
-        return hud.gcliterm.formatResult(x);
-      }
-      if (hud.jsterm) {
-        return hud.jsterm.formatResult(x);
-      }
-      return x;
-    }
-
-    let body = null;
-    let clipboardText = null;
-    let sourceURL = null;
-    let sourceLine = 0;
-    let level = aMessage.level;
-    let args = aMessage.arguments;
-
-    switch (level) {
-      case "log":
-      case "info":
-      case "warn":
-      case "error":
-      case "debug":
-        let mappedArguments = Array.map(args, formatResult);
-        body = Array.join(mappedArguments, " ");
-        sourceURL = aMessage.filename;
-        sourceLine = aMessage.lineNumber;
-        break;
-
-      case "trace":
-        let filename = ConsoleUtils.abbreviateSourceURL(args[0].filename);
-        let functionName = args[0].functionName ||
-                           this.getStr("stacktrace.anonymousFunction");
-        let lineNumber = args[0].lineNumber;
-
-        body = this.getFormatStr("stacktrace.outputMessage",
-                                 [filename, functionName, lineNumber]);
-
-        sourceURL = args[0].filename;
-        sourceLine = args[0].lineNumber;
-
-        clipboardText = "";
-
-        args.forEach(function(aFrame) {
-          clipboardText += aFrame.filename + " :: " +
-                           aFrame.functionName + " :: " +
-                           aFrame.lineNumber + "\n";
-        });
-
-        clipboardText = clipboardText.trimRight();
-        break;
-
-      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;
-
-      case "time":
-        if (!args) {
-          return;
-        }
-        if (args.error) {
-          Cu.reportError(this.getStr(args.error));
-          return;
-        }
-        body = this.getFormatStr("timerStarted", [args.name]);
-        clipboardText = body;
-        sourceURL = aMessage.filename;
-        sourceLine = aMessage.lineNumber;
-        break;
-
-      case "timeEnd":
-        if (!args) {
-          return;
-        }
-        body = this.getFormatStr("timeEnd", [args.name, args.duration]);
-        clipboardText = body;
-        sourceURL = aMessage.filename;
-        sourceLine = aMessage.lineNumber;
-        break;
-
-      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,
-                                              aMessage.timeStamp);
-
-    // Make the node bring up the property panel, to allow the user to inspect
-    // the stack trace.
-    if (level == "trace") {
-      node._stacktrace = args;
-
-      let linkNode = node.querySelector(".webconsole-msg-body");
-      linkNode.classList.add("hud-clickable");
-      linkNode.setAttribute("aria-haspopup", "true");
-
-      node.addEventListener("mousedown", function(aEvent) {
-        this._startX = aEvent.clientX;
-        this._startY = aEvent.clientY;
-      }, false);
-
-      node.addEventListener("click", function(aEvent) {
-        if (aEvent.detail != 1 || aEvent.button != 0 ||
-            (this._startX != aEvent.clientX &&
-             this._startY != aEvent.clientY)) {
-          return;
-        }
-
-        if (!this._panelOpen) {
-          let propPanel = hud.jsterm.openPropertyPanel(null,
-                                                       node._stacktrace,
-                                                       this);
-          propPanel.panel.setAttribute("hudId", aHUDId);
-          this._panelOpen = true;
-        }
-      }, false);
-    }
-
-    ConsoleUtils.outputMessageNode(node, aHUDId);
-
-    if (level == "dir") {
-      // Initialize the inspector message node, by setting the PropertyTreeView
-      // object on the tree view. This has to be done *after* the node is
-      // shown, because the tree binding must be attached first.
-      let tree = node.querySelector("tree");
-      tree.view = node.propertyTreeView;
-    }
-  },
-
-  /**
    * Inform user that the Web Console API has been replaced by a script
    * in a content page.
    *
    * @param string aHUDId
    *        The ID of the Web Console to which to send the message.
    * @return void
    */
   logWarningAboutReplacedAPI:
@@ -2225,83 +1982,16 @@ HUD_SERVICE.prototype =
     let message = stringBundle.GetStringFromName("ConsoleAPIDisabled");
     let node = ConsoleUtils.createMessageNode(chromeDocument, CATEGORY_JS,
                                               SEVERITY_WARNING, message,
                                               aHUDId);
     ConsoleUtils.outputMessageNode(node, aHUDId);
   },
 
   /**
-   * Reports an error in the page source, either JavaScript or CSS.
-   *
-   * @param nsIScriptError aScriptError
-   *        The error message to report.
-   * @return void
-   */
-  reportPageError: function HS_reportPageError(aScriptError)
-  {
-    if (!aScriptError.outerWindowID) {
-      return;
-    }
-
-    let category;
-
-    switch (aScriptError.category) {
-      // We ignore chrome-originating errors as we only care about content.
-      case "XPConnect JavaScript":
-      case "component javascript":
-      case "chrome javascript":
-      case "chrome registration":
-      case "XBL":
-      case "XBL Prototype Handler":
-      case "XBL Content Sink":
-      case "xbl javascript":
-        return;
-
-      case "CSS Parser":
-      case "CSS Loader":
-        category = CATEGORY_CSS;
-        break;
-
-      default:
-        category = CATEGORY_JS;
-        break;
-    }
-
-    // Warnings and legacy strict errors become warnings; other types become
-    // errors.
-    let severity = SEVERITY_ERROR;
-    if ((aScriptError.flags & aScriptError.warningFlag) ||
-        (aScriptError.flags & aScriptError.strictFlag)) {
-      severity = SEVERITY_WARNING;
-    }
-
-    let window = HUDService.getWindowByWindowId(aScriptError.outerWindowID);
-    if (window) {
-      let hudId = HUDService.getHudIdByWindow(window.top);
-      if (hudId) {
-        let outputNode = this.hudReferences[hudId].outputNode;
-        let chromeDocument = outputNode.ownerDocument;
-
-        let node = ConsoleUtils.createMessageNode(chromeDocument,
-                                                  category,
-                                                  severity,
-                                                  aScriptError.errorMessage,
-                                                  hudId,
-                                                  aScriptError.sourceName,
-                                                  aScriptError.lineNumber,
-                                                  null, null,
-                                                  aScriptError.timeStamp);
-
-        ConsoleUtils.outputMessageNode(node, hudId);
-      }
-    }
-  },
-
-  /**
    * Register a Gecko app's specialized ApplicationHooks object
    *
    * @returns void or throws "UNSUPPORTED APPLICATION" error
    */
   registerApplicationHooks:
   function HS_registerApplications(aAppName, aHooksObject)
   {
     switch(aAppName) {
@@ -2590,17 +2280,17 @@ HUD_SERVICE.prototype =
                                 timing.REQUEST_HEADER) / 1000);
 
                 // Add the request duration.
                 let linkNode = msgObject.linkNode;
                 let statusNode = linkNode.
                   querySelector(".webconsole-msg-status");
 
                 let statusText = httpActivity.response.status;
-                let timeText = self.getFormatStr("NetworkPanel.durationMS",
+                let timeText = l10n.getFormatStr("NetworkPanel.durationMS",
                                                  [ requestDuration ]);
                 let fullStatusText = "[" + statusText + " " + timeText + "]";
                 statusNode.setAttribute("value", fullStatusText);
 
                 let clipboardTextPieces =
                   [ httpActivity.method, httpActivity.url, fullStatusText ];
                 msgObject.messageNode.clipboardText =
                   clipboardTextPieces.join(" ");
@@ -2873,17 +2563,17 @@ HUD_SERVICE.prototype =
    */
   windowInitializer: function HS_WindowInitalizer(aContentWindow)
   {
     var xulWindow = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIWebNavigation)
                       .QueryInterface(Ci.nsIDocShell)
                       .chromeEventHandler.ownerDocument.defaultView;
 
-    let xulWindow = unwrap(xulWindow);
+    let xulWindow = WebConsoleUtils.unwrap(xulWindow);
 
     let docElem = xulWindow.document.documentElement;
     if (!docElem || docElem.getAttribute("windowtype") != "navigator:browser" ||
         !xulWindow.gBrowser) {
       // Do not do anything unless we have a browser window.
       // This may be a view-source window or other type of non-browser window.
       return;
     }
@@ -2938,36 +2628,34 @@ HUD_SERVICE.prototype =
       // get nBox object and call new HUD
       let config = { parentNode: nBox,
                      contentWindow: aContentWindow.top
                    };
 
       hud = new HeadsUpDisplay(config);
 
       HUDService.registerHUDReference(hud);
-      let windowId = this.getWindowId(aContentWindow.top);
+      let windowId = WebConsoleUtils.getOuterWindowId(aContentWindow.top);
       this.windowIds[windowId] = hudId;
 
       hud.progressListener = new ConsoleProgressListener(hudId);
 
       _browser.webProgress.addProgressListener(hud.progressListener,
         Ci.nsIWebProgress.NOTIFY_STATE_ALL);
-
-      hud.displayCachedConsoleMessages();
     }
     else {
       hud = this.hudReferences[hudId];
       if (aContentWindow == aContentWindow.top) {
         // TODO: name change?? doesn't actually re-attach the console
         hud.reattachConsole(aContentWindow);
       }
     }
 
     // Need to detect that the console component has been paved over.
-    let consoleObject = unwrap(aContentWindow).console;
+    let consoleObject = WebConsoleUtils.unwrap(aContentWindow).console;
     if (!("__mozillaConsole__" in consoleObject))
       this.logWarningAboutReplacedAPI(hudId);
 
     // register the controller to handle "select all" properly
     this.createController(xulWindow);
   },
 
   /**
@@ -3119,17 +2807,17 @@ HUD_SERVICE.prototype =
       // copied output.
       if (i > 0 && item.classList.contains("webconsole-new-group")) {
         newGroup = true;
       }
 
       // Ensure the selected item hasn't been filtered by type or string.
       if (!item.classList.contains("hud-filtered-by-type") &&
           !item.classList.contains("hud-filtered-by-string")) {
-        let timestampString = ConsoleUtils.timestampString(item.timestamp);
+        let timestampString = l10n.timestampString(item.timestamp);
         if (newGroup) {
           strings.push("--");
           newGroup = false;
         }
         strings.push("[" + timestampString + "] " + item.clipboardText);
       }
     }
     clipboardHelper.copyString(strings.join("\n"));
@@ -3229,16 +2917,22 @@ function HeadsUpDisplay(aConfig)
     this.parentNode = parentNode;
     this.notificationBox = parentNode;
   }
 
   // create textNode Factory:
   this.textFactory = NodeFactory("text", "xul", this.chromeDocument);
 
   this.chromeWindow = this.chromeDocument.defaultView;
+  this.browser = this.tab.linkedBrowser;
+  this.messageManager = this.browser.messageManager;
+
+  // Track callback functions registered for specific async requests sent to the
+  // content process.
+  this.asyncRequests = {};
 
   // create a panel dynamically and attach to the parentNode
   this.createHUD();
 
   this.HUDBox.lastTimestamp = 0;
   // create the JSTerm input element
   try {
     this.createConsoleInput(this.contentWindow, this.consoleWrap, this.outputNode);
@@ -3250,19 +2944,30 @@ function HeadsUpDisplay(aConfig)
     }
   }
   catch (ex) {
     Cu.reportError(ex);
   }
 
   // A cache for tracking repeated CSS Nodes.
   this.cssNodes = {};
+
+  this._setupMessageManager();
 }
 
 HeadsUpDisplay.prototype = {
+  /**
+   * Message names that the HUD listens for. These messages come from the remote
+   * Web Console content script.
+   *
+   * @private
+   * @type array
+   */
+  _messageListeners: ["JSTerm:EvalObject", "WebConsole:ConsoleAPI",
+                      "WebConsole:CachedMessages", "WebConsole:PageError"],
 
   consolePanel: null,
 
   /**
    * The nesting depth of the currently active console group.
    */
   groupDepth: 0,
 
@@ -3444,17 +3149,17 @@ HeadsUpDisplay.prototype = {
   /**
    * Retrieve the Web Console panel title.
    *
    * @return string
    *         The Web Console panel title.
    */
   getPanelTitle: function HUD_getPanelTitle()
   {
-    return this.getFormatStr("webConsoleWindowTitleAndURL", [this.uriSpec]);
+    return l10n.getFormatStr("webConsoleWindowTitleAndURL", [this.uriSpec]);
   },
 
   positions: {
     above: 0, // the childNode index
     below: 2,
     window: null
   },
 
@@ -3549,39 +3254,16 @@ HeadsUpDisplay.prototype = {
       this.jsterm.inputNode.focus();
     }
     if (this.gcliterm) {
       this.gcliterm.inputNode.focus();
     }
   },
 
   /**
-   * L10N shortcut function
-   *
-   * @param string aName
-   * @returns string
-   */
-  getStr: function HUD_getStr(aName)
-  {
-    return stringBundle.GetStringFromName(aName);
-  },
-
-  /**
-   * L10N shortcut function
-   *
-   * @param string aName
-   * @param array aArray
-   * @returns string
-   */
-  getFormatStr: function HUD_getFormatStr(aName, aArray)
-  {
-    return stringBundle.formatStringFromName(aName, aArray, aArray.length);
-  },
-
-  /**
    * The JSTerm object that contains the console's inputNode
    *
    */
   jsterm: null,
 
   /**
    * The GcliTerm object that contains the console's GCLI
    */
@@ -3619,49 +3301,40 @@ HeadsUpDisplay.prototype = {
       throw new Error("Unsupported Gecko Application");
     }
   },
 
   /**
    * Display cached messages that may have been collected before the UI is
    * displayed.
    *
-   * @returns void
-   */
-  displayCachedConsoleMessages: function HUD_displayCachedConsoleMessages()
-  {
-    let innerWindowId = HUDService.getInnerWindowId(this.contentWindow);
-
-    let messages = gConsoleStorage.getEvents(innerWindowId);
-
-    let errors = {};
-    Services.console.getMessageArray(errors, {});
-
-    // Filter the errors to find only those we should display.
-    let filteredErrors = (errors.value || []).filter(function(aError) {
-      return aError instanceof Ci.nsIScriptError &&
-             aError.innerWindowID == innerWindowId;
-    }, this);
-
-    messages.push.apply(messages, filteredErrors);
-    messages.sort(function(a, b) { return a.timeStamp - b.timeStamp; });
+   * @private
+   * @param array aRemoteMessages
+   *        Array of cached messages coming from the remote Web Console
+   *        content instance.
+   */
+  _displayCachedConsoleMessages:
+  function HUD__displayCachedConsoleMessages(aRemoteMessages)
+  {
+    if (!aRemoteMessages.length) {
+      return;
+    }
 
     // Turn off scrolling for the moment.
     ConsoleUtils.scroll = false;
     this.outputNode.hidden = true;
 
-    // Display all messages.
-    messages.forEach(function(aMessage) {
-      if (aMessage instanceof Ci.nsIScriptError) {
-        HUDService.reportPageError(aMessage);
-      }
-      else {
-        // In this case the cached message is a console message generated
-        // by the ConsoleAPI, not an nsIScriptError
-        HUDService.logConsoleAPIMessage(this.hudId, aMessage);
+    aRemoteMessages.forEach(function(aMessage) {
+      switch (aMessage._type) {
+        case "PageError":
+          this.reportPageError(aMessage);
+          break;
+        case "ConsoleAPI":
+          this.logConsoleAPIMessage(aMessage);
+          break;
       }
     }, this);
 
     this.outputNode.hidden = false;
     ConsoleUtils.scroll = true;
 
     // Scroll to bottom.
     let numChildren = this.outputNode.childNodes.length;
@@ -3743,17 +3416,17 @@ HeadsUpDisplay.prototype = {
     consoleWrap.setAttribute("flex", "1");
 
     this.filterSpacer = this.makeXULNode("spacer");
     this.filterSpacer.setAttribute("flex", "1");
 
     this.filterBox = this.makeXULNode("textbox");
     this.filterBox.setAttribute("class", "compact hud-filter-box");
     this.filterBox.setAttribute("hudId", this.hudId);
-    this.filterBox.setAttribute("placeholder", this.getStr("stringFilter"));
+    this.filterBox.setAttribute("placeholder", l10n.getStr("stringFilter"));
     this.filterBox.setAttribute("type", "search");
 
     this.setFilterTextBoxEvents();
 
     this.createConsoleMenu(this.consoleWrap);
 
     this.filterPrefs = HUDService.getDefaultFilterPrefs(this.hudId);
 
@@ -3921,38 +3594,38 @@ HeadsUpDisplay.prototype = {
       this.positionConsole("below");
     }).bind(this);
     this._positionConsoleWindow = (function HUD_positionWindow() {
       this.positionConsole("window");
     }).bind(this);
 
     let button = this.makeXULNode("toolbarbutton");
     button.setAttribute("type", "menu");
-    button.setAttribute("label", this.getStr("webConsolePosition"));
-    button.setAttribute("tooltip", this.getStr("webConsolePositionTooltip"));
+    button.setAttribute("label", l10n.getStr("webConsolePosition"));
+    button.setAttribute("tooltip", l10n.getStr("webConsolePositionTooltip"));
 
     let menuPopup = this.makeXULNode("menupopup");
     button.appendChild(menuPopup);
 
     let itemAbove = this.makeXULNode("menuitem");
-    itemAbove.setAttribute("label", this.getStr("webConsolePositionAbove"));
+    itemAbove.setAttribute("label", l10n.getStr("webConsolePositionAbove"));
     itemAbove.setAttribute("type", "checkbox");
     itemAbove.setAttribute("autocheck", "false");
     itemAbove.addEventListener("command", this._positionConsoleAbove, false);
     menuPopup.appendChild(itemAbove);
 
     let itemBelow = this.makeXULNode("menuitem");
-    itemBelow.setAttribute("label", this.getStr("webConsolePositionBelow"));
+    itemBelow.setAttribute("label", l10n.getStr("webConsolePositionBelow"));
     itemBelow.setAttribute("type", "checkbox");
     itemBelow.setAttribute("autocheck", "false");
     itemBelow.addEventListener("command", this._positionConsoleBelow, false);
     menuPopup.appendChild(itemBelow);
 
     let itemWindow = this.makeXULNode("menuitem");
-    itemWindow.setAttribute("label", this.getStr("webConsolePositionWindow"));
+    itemWindow.setAttribute("label", l10n.getStr("webConsolePositionWindow"));
     itemWindow.setAttribute("type", "checkbox");
     itemWindow.setAttribute("autocheck", "false");
     itemWindow.addEventListener("command", this._positionConsoleWindow, false);
     menuPopup.appendChild(itemWindow);
 
     this.positionMenuitems = {
       last: null,
       above: itemAbove,
@@ -3975,38 +3648,38 @@ HeadsUpDisplay.prototype = {
     let id = this.hudId + "-output-contextmenu";
     menuPopup.setAttribute("id", id);
     menuPopup.addEventListener("popupshowing", function() {
       saveBodiesItem.setAttribute("checked",
         HUDService.saveRequestAndResponseBodies);
     }, true);
 
     let saveBodiesItem = this.makeXULNode("menuitem");
-    saveBodiesItem.setAttribute("label", this.getStr("saveBodies.label"));
+    saveBodiesItem.setAttribute("label", l10n.getStr("saveBodies.label"));
     saveBodiesItem.setAttribute("accesskey",
-                                 this.getStr("saveBodies.accesskey"));
+                                 l10n.getStr("saveBodies.accesskey"));
     saveBodiesItem.setAttribute("type", "checkbox");
     saveBodiesItem.setAttribute("buttonType", "saveBodies");
     saveBodiesItem.setAttribute("oncommand", "HUDConsoleUI.command(this);");
     menuPopup.appendChild(saveBodiesItem);
 
     menuPopup.appendChild(this.makeXULNode("menuseparator"));
 
     let copyItem = this.makeXULNode("menuitem");
-    copyItem.setAttribute("label", this.getStr("copyCmd.label"));
-    copyItem.setAttribute("accesskey", this.getStr("copyCmd.accesskey"));
+    copyItem.setAttribute("label", l10n.getStr("copyCmd.label"));
+    copyItem.setAttribute("accesskey", l10n.getStr("copyCmd.accesskey"));
     copyItem.setAttribute("key", "key_copy");
     copyItem.setAttribute("command", "cmd_copy");
     copyItem.setAttribute("buttonType", "copy");
     menuPopup.appendChild(copyItem);
 
     let selectAllItem = this.makeXULNode("menuitem");
-    selectAllItem.setAttribute("label", this.getStr("selectAllCmd.label"));
+    selectAllItem.setAttribute("label", l10n.getStr("selectAllCmd.label"));
     selectAllItem.setAttribute("accesskey",
-                               this.getStr("selectAllCmd.accesskey"));
+                               l10n.getStr("selectAllCmd.accesskey"));
     selectAllItem.setAttribute("hudId", this.hudId);
     selectAllItem.setAttribute("buttonType", "selectAll");
     selectAllItem.setAttribute("oncommand", "HUDConsoleUI.command(this);");
     menuPopup.appendChild(selectAllItem);
 
     aConsoleWrapper.appendChild(menuPopup);
     aConsoleWrapper.setAttribute("context", id);
   },
@@ -4027,30 +3700,30 @@ HeadsUpDisplay.prototype = {
     let toolbarButton = this.makeXULNode("toolbarbutton");
     aParent.appendChild(toolbarButton);
 
     let toggleFilter = HeadsUpDisplayUICommands.toggleFilter;
     toolbarButton.addEventListener("click", toggleFilter, false);
 
     let name = aDescriptor.name;
     toolbarButton.setAttribute("type", "menu-button");
-    toolbarButton.setAttribute("label", this.getStr("btn" + name));
-    toolbarButton.setAttribute("tooltip", this.getStr("tip" + name));
+    toolbarButton.setAttribute("label", l10n.getStr("btn" + name));
+    toolbarButton.setAttribute("tooltip", l10n.getStr("tip" + name));
     toolbarButton.setAttribute("category", aDescriptor.category);
     toolbarButton.setAttribute("hudId", this.hudId);
     toolbarButton.classList.add("webconsole-filter-button");
 
     let menuPopup = this.makeXULNode("menupopup");
     toolbarButton.appendChild(menuPopup);
 
     let someChecked = false;
     for (let i = 0; i < aDescriptor.severities.length; i++) {
       let severity = aDescriptor.severities[i];
       let menuItem = this.makeXULNode("menuitem");
-      menuItem.setAttribute("label", this.getStr("btn" + severity.name));
+      menuItem.setAttribute("label", l10n.getStr("btn" + severity.name));
       menuItem.setAttribute("type", "checkbox");
       menuItem.setAttribute("autocheck", "false");
       menuItem.setAttribute("hudId", this.hudId);
 
       let prefKey = severity.prefKey;
       menuItem.setAttribute("prefKey", prefKey);
 
       let checked = this.filterPrefs[prefKey];
@@ -4107,17 +3780,17 @@ HeadsUpDisplay.prototype = {
         hud.jsterm.clearOutput(true);
       }
       if (hud.gcliterm) {
         hud.gcliterm.clearOutput();
       }
     }
 
     let clearButton = this.makeXULNode("toolbarbutton");
-    clearButton.setAttribute("label", this.getStr("btnClear"));
+    clearButton.setAttribute("label", l10n.getStr("btnClear"));
     clearButton.classList.add("webconsole-clear-console-button");
     clearButton.addEventListener("command", HUD_clearButton_onCommand, false);
 
     aToolbar.appendChild(clearButton);
   },
 
   /**
    * Destroy the property inspector message node. This performs the necessary
@@ -4128,18 +3801,20 @@ HeadsUpDisplay.prototype = {
    *        console.dir call.
    */
   pruneConsoleDirNode: function HUD_pruneConsoleDirNode(aMessageNode)
   {
     aMessageNode.parentNode.removeChild(aMessageNode);
     let tree = aMessageNode.querySelector("tree");
     tree.parentNode.removeChild(tree);
     aMessageNode.propertyTreeView = null;
+    if (tree.view) {
+      tree.view.data = null;
+    }
     tree.view = null;
-    tree = null;
   },
 
   /**
    * Create the Web Console UI.
    *
    * @return nsIDOMNode
    *         The Web Console container element (HUDBox).
    */
@@ -4152,100 +3827,442 @@ HeadsUpDisplay.prototype = {
     }
     return this.HUDBox;
   },
 
   uiInOwnWindow: false,
 
   get console() { return this.contentWindow.wrappedJSObject.console; },
 
-  getLogCount: function HUD_getLogCount()
-  {
-    return this.outputNode.childNodes.length;
-  },
-
-  getLogNodes: function HUD_getLogNodes()
-  {
-    return this.outputNode.childNodes;
+  /**
+   * Logs a message to the Web Console that originates from the remote Web
+   * Console instance.
+   *
+   * @param object aMessage
+   *        The message received from the remote Web Console instance.
+   *        console service. This object needs to hold:
+   *          - hudId - the Web Console ID.
+   *          - apiMessage - a representation of the object sent by the console
+   *          storage service. This object holds the console message level, the
+   *          arguments that were passed to the console method and other
+   *          information.
+   *          - argumentsToString - the array of arguments passed to the console
+   *          method, each converted to a string.
+   */
+  logConsoleAPIMessage: function HUD_logConsoleAPIMessage(aMessage)
+  {
+    let body = null;
+    let clipboardText = null;
+    let sourceURL = null;
+    let sourceLine = 0;
+    let level = aMessage.apiMessage.level;
+    let args = aMessage.apiMessage.arguments;
+    let argsToString = aMessage.argumentsToString;
+
+    switch (level) {
+      case "log":
+      case "info":
+      case "warn":
+      case "error":
+      case "debug":
+        body = argsToString.join(" ");
+        sourceURL = aMessage.apiMessage.filename;
+        sourceLine = aMessage.apiMessage.lineNumber;
+        break;
+
+      case "trace":
+        let filename = WebConsoleUtils.abbreviateSourceURL(args[0].filename);
+        let functionName = args[0].functionName ||
+                           l10n.getStr("stacktrace.anonymousFunction");
+        let lineNumber = args[0].lineNumber;
+
+        body = l10n.getFormatStr("stacktrace.outputMessage",
+                                 [filename, functionName, lineNumber]);
+
+        sourceURL = args[0].filename;
+        sourceLine = args[0].lineNumber;
+
+        clipboardText = "";
+
+        args.forEach(function(aFrame) {
+          clipboardText += aFrame.filename + " :: " +
+                           aFrame.functionName + " :: " +
+                           aFrame.lineNumber + "\n";
+        });
+
+        clipboardText = clipboardText.trimRight();
+        break;
+
+      case "dir":
+        body = {
+          cacheId: aMessage.objectsCacheId,
+          resultString: argsToString[0],
+          remoteObject: args[0],
+          remoteObjectProvider:
+            this.jsterm.remoteObjectProvider.bind(this.jsterm),
+        };
+        clipboardText = body.resultString;
+        sourceURL = aMessage.apiMessage.filename;
+        sourceLine = aMessage.apiMessage.lineNumber;
+        break;
+
+      case "group":
+      case "groupCollapsed":
+        clipboardText = body = args;
+        sourceURL = aMessage.apiMessage.filename;
+        sourceLine = aMessage.apiMessage.lineNumber;
+        this.groupDepth++;
+        break;
+
+      case "groupEnd":
+        if (this.groupDepth > 0) {
+          this.groupDepth--;
+        }
+        return;
+
+      case "time":
+        if (!args) {
+          return;
+        }
+        if (args.error) {
+          Cu.reportError(l10n.getStr(args.error));
+          return;
+        }
+        body = l10n.getFormatStr("timerStarted", [args.name]);
+        clipboardText = body;
+        sourceURL = aMessage.apiMessage.filename;
+        sourceLine = aMessage.apiMessage.lineNumber;
+        break;
+
+      case "timeEnd":
+        if (!args) {
+          return;
+        }
+        body = l10n.getFormatStr("timeEnd", [args.name, args.duration]);
+        clipboardText = body;
+        sourceURL = aMessage.apiMessage.filename;
+        sourceLine = aMessage.apiMessage.lineNumber;
+        break;
+
+      default:
+        Cu.reportError("Unknown Console API log level: " + level);
+        return;
+    }
+
+    let node = ConsoleUtils.createMessageNode(this.chromeDocument,
+                                              CATEGORY_WEBDEV,
+                                              LEVELS[level],
+                                              body,
+                                              this.hudId,
+                                              sourceURL,
+                                              sourceLine,
+                                              clipboardText,
+                                              level,
+                                              aMessage.timeStamp);
+
+    // Make the node bring up the property panel, to allow the user to inspect
+    // the stack trace.
+    if (level == "trace") {
+      node._stacktrace = args;
+
+      this.makeOutputMessageLink(node, function _traceNodeClickCallback() {
+        if (node._panelOpen) {
+          return;
+        }
+
+        let options = {
+          anchor: node,
+          data: { object: node._stacktrace },
+        };
+
+        let propPanel = this.jsterm.openPropertyPanelAsync(options);
+        propPanel.panel.setAttribute("hudId", this.hudId);
+      }.bind(this));
+    }
+
+    ConsoleUtils.outputMessageNode(node, this.hudId);
+
+    if (level == "dir") {
+      // Initialize the inspector message node, by setting the PropertyTreeView
+      // object on the tree view. This has to be done *after* the node is
+      // shown, because the tree binding must be attached first.
+      let tree = node.querySelector("tree");
+      tree.view = node.propertyTreeView;
+
+      // Make sure the cached evaluated object will be purged when the node is
+      // removed.
+      node._evalCacheId = aMessage.objectsCacheId;
+    }
+  },
+
+  /**
+   * Reports an error in the page source, either JavaScript or CSS.
+   *
+   * @param nsIScriptError aScriptError
+   *        The error message to report.
+   */
+  reportPageError: function HUD_reportPageError(aScriptError)
+  {
+    if (!aScriptError.outerWindowID) {
+      return;
+    }
+
+    let category;
+
+    switch (aScriptError.category) {
+      // We ignore chrome-originating errors as we only care about content.
+      case "XPConnect JavaScript":
+      case "component javascript":
+      case "chrome javascript":
+      case "chrome registration":
+      case "XBL":
+      case "XBL Prototype Handler":
+      case "XBL Content Sink":
+      case "xbl javascript":
+        return;
+
+      case "CSS Parser":
+      case "CSS Loader":
+        category = CATEGORY_CSS;
+        break;
+
+      default:
+        category = CATEGORY_JS;
+        break;
+    }
+
+    // Warnings and legacy strict errors become warnings; other types become
+    // errors.
+    let severity = SEVERITY_ERROR;
+    if ((aScriptError.flags & aScriptError.warningFlag) ||
+        (aScriptError.flags & aScriptError.strictFlag)) {
+      severity = SEVERITY_WARNING;
+    }
+
+    let node = ConsoleUtils.createMessageNode(this.chromeDocument,
+                                              category,
+                                              severity,
+                                              aScriptError.errorMessage,
+                                              this.hudId,
+                                              aScriptError.sourceName,
+                                              aScriptError.lineNumber,
+                                              null,
+                                              null,
+                                              aScriptError.timeStamp);
+
+    ConsoleUtils.outputMessageNode(node, this.hudId);
   },
 
   ERRORS: {
     HUD_BOX_DOES_NOT_EXIST: "Heads Up Display does not exist",
     TAB_ID_REQUIRED: "Tab DOM ID is required",
     PARENTNODE_NOT_FOUND: "parentNode element not found"
   },
 
   /**
+   * Setup the message manager used to communicate with the Web Console content
+   * script. This method loads the content script, adds the message listeners
+   * and initializes the connection to the content script.
+   *
+   * @private
+   */
+  _setupMessageManager: function HUD__setupMessageManager()
+  {
+    this.messageManager.loadFrameScript(CONTENT_SCRIPT_URL, true);
+
+    this._messageListeners.forEach(function(aName) {
+      this.messageManager.addMessageListener(aName, this);
+    }, this);
+
+    let message = {
+      hudId: this.hudId,
+      features: ["ConsoleAPI", "JSTerm", "PageError"],
+      cachedMessages: ["ConsoleAPI", "PageError"],
+    };
+    this.sendMessageToContent("WebConsole:Init", message);
+  },
+
+  /**
+   * Handler for all of the messages coming from the Web Console content script.
+   *
+   * @private
+   * @param object aMessage
+   *        A MessageManager object that holds the remote message.
+   */
+  receiveMessage: function HUD_receiveMessage(aMessage)
+  {
+    if (!aMessage.json || aMessage.json.hudId != this.hudId) {
+      Cu.reportError("JSTerm: received message " + aMessage.name +
+                     " from wrong hudId.");
+      return;
+    }
+
+    switch (aMessage.name) {
+      case "JSTerm:EvalObject":
+        this._receiveMessageWithCallback(aMessage.json);
+        break;
+      case "WebConsole:ConsoleAPI":
+        this.logConsoleAPIMessage(aMessage.json);
+        break;
+      case "WebConsole:PageError":
+        this.reportPageError(aMessage.json.pageError);
+        break;
+      case "WebConsole:CachedMessages":
+        this._displayCachedConsoleMessages(aMessage.json.messages);
+        this._onInitComplete();
+        break;
+    }
+  },
+
+  /**
+   * Callback method for when the Web Console initialization is complete. For
+   * now this method sends the web-console-created notification using the
+   * nsIObserverService.
+   *
+   * @private
+   */
+  _onInitComplete: function HUD__onInitComplete()
+  {
+    let id = WebConsoleUtils.supportsString(this.hudId);
+    Services.obs.notifyObservers(id, "web-console-created", null);
+  },
+
+  /**
+   * Handler for messages that have an associated callback function. The
+   * this.sendMessageToContent() allows one to provide a function to be invoked
+   * when the content script replies to the message previously sent. This is the
+   * method that invokes the callback.
+   *
+   * @see this.sendMessageToContent
+   * @private
+   * @param object aResponse
+   *        Message object received from the content script.
+   */
+  _receiveMessageWithCallback:
+  function HUD__receiveMessageWithCallback(aResponse)
+  {
+    if (aResponse.id in this.asyncRequests) {
+      let request = this.asyncRequests[aResponse.id];
+      request.callback(aResponse, request.message);
+      delete this.asyncRequests[aResponse.id];
+    }
+    else {
+      Cu.reportError("receiveMessageWithCallback response for stale request " +
+                     "ID " + aResponse.id);
+    }
+  },
+
+  /**
+   * Send a message to the content script.
+   *
+   * @param string aName
+   *        The name of the message you want to send.
+   *
+   * @param object aMessage
+   *        The message object you want to send. This object needs to have no
+   *        cyclic references and it needs to be JSON-stringifiable.
+   *
+   * @param function [aCallback]
+   *        Optional function you want to have called when the content script
+   *        replies to your message. Your callback receives two arguments:
+   *        (1) the response object from the content script and (2) the message
+   *        you sent to the content script (which is aMessage here).
+   */
+  sendMessageToContent:
+  function HUD_sendMessageToContent(aName, aMessage, aCallback)
+  {
+    aMessage.hudId = this.hudId;
+    if (!("id" in aMessage)) {
+      aMessage.id = "HUDChrome-" + HUDService.sequenceId();
+    }
+
+    if (aCallback) {
+      this.asyncRequests[aMessage.id] = {
+        name: aName,
+        message: aMessage,
+        callback: aCallback,
+      };
+    }
+
+    this.messageManager.sendAsyncMessage(aName, aMessage);
+  },
+
+  /**
+   * Make a link given an output element.
+   *
+   * @param nsIDOMNode aNode
+   *        The message element you want to make a link for.
+   * @param function aCallback
+   *        The function you want invoked when the user clicks on the message
+   *        element.
+   */
+  makeOutputMessageLink: function HUD_makeOutputMessageLink(aNode, aCallback)
+  {
+    let linkNode;
+    if (aNode.category === CATEGORY_NETWORK) {
+      linkNode = aNode.querySelector(".webconsole-msg-link");
+    }
+    else {
+      linkNode = aNode.querySelector(".webconsole-msg-body");
+      linkNode.classList.add("hud-clickable");
+    }
+
+    linkNode.setAttribute("aria-haspopup", "true");
+
+    aNode.addEventListener("mousedown", function(aEvent) {
+      this._startX = aEvent.clientX;
+      this._startY = aEvent.clientY;
+    }, false);
+
+    aNode.addEventListener("click", function(aEvent) {
+      if (aEvent.detail != 1 || aEvent.button != 0 ||
+          (this._startX != aEvent.clientX &&
+           this._startY != aEvent.clientY)) {
+        return;
+      }
+
+      aCallback(this, aEvent);
+    }, false);
+  },
+
+  /**
    * Destroy the HUD object. Call this method to avoid memory leaks when the Web
    * Console is closed.
    */
   destroy: function HUD_destroy()
   {
+    this.sendMessageToContent("WebConsole:Destroy", {hudId: this.hudId});
+
+    this._messageListeners.forEach(function(aName) {
+      this.messageManager.removeMessageListener(aName, this);
+    }, this);
+
     if (this.jsterm) {
       this.jsterm.destroy();
     }
     if (this.gcliterm) {
       this.gcliterm.destroy();
     }
 
+    delete this.asyncRequests;
+    delete this.messageManager;
+    delete this.browser;
+
     this.positionMenuitems.above.removeEventListener("command",
       this._positionConsoleAbove, false);
     this.positionMenuitems.below.removeEventListener("command",
       this._positionConsoleBelow, false);
     this.positionMenuitems.window.removeEventListener("command",
       this._positionConsoleWindow, false);
 
     this.closeButton.removeEventListener("command",
       this.closeButtonOnCommand, false);
   },
 };
 
-
-//////////////////////////////////////////////////////////////////////////////
-// ConsoleAPIObserver
-//////////////////////////////////////////////////////////////////////////////
-
-let ConsoleAPIObserver = {
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
-
-  init: function CAO_init()
-  {
-    Services.obs.addObserver(this, "quit-application-granted", false);
-    Services.obs.addObserver(this, "console-api-log-event", false);
-  },
-
-  observe: function CAO_observe(aMessage, aTopic, aData)
-  {
-    if (aTopic == "console-api-log-event") {
-      aMessage = aMessage.wrappedJSObject;
-      let windowId = parseInt(aData);
-      let win = HUDService.getWindowByWindowId(windowId);
-      if (!win)
-        return;
-
-      // Find the HUD ID for the topmost window
-      let hudId = HUDService.getHudIdByWindow(win.top);
-      if (!hudId)
-        return;
-
-      HUDService.logConsoleAPIMessage(hudId, aMessage);
-    }
-    else if (aTopic == "quit-application-granted") {
-      HUDService.shutdown();
-    }
-  },
-
-  shutdown: function CAO_shutdown()
-  {
-    Services.obs.removeObserver(this, "quit-application-granted");
-    Services.obs.removeObserver(this, "console-api-log-event");
-  }
-};
-
 /**
  * Creates a DOM Node factory for XUL nodes - as well as textNodes
  * @param aFactoryType "xul" or "text"
  * @param ignored This parameter is currently ignored, and will be removed
  * See bug 594304
  * @param aDocument The document, the factory is to generate nodes from
  * @return DOM Node Factory function
  */
@@ -4407,17 +4424,17 @@ function findCompletionBeginning(aStr)
  *            {
  *              matches: [ string, string, string ],
  *              matchProp: Last part of the inputValue that was used to find
  *                         the matches-strings.
  *            }
  */
 function JSPropertyProvider(aScope, aInputValue)
 {
-  let obj = unwrap(aScope);
+  let obj = WebConsoleUtils.unwrap(aScope);
 
   // Analyse the aInputValue and find the beginning of the last part that
   // should be completed.
   let beginning = findCompletionBeginning(aInputValue);
 
   // There was an error analysing the string.
   if (beginning.err) {
     return null;
@@ -4446,17 +4463,17 @@ function JSPropertyProvider(aScope, aInp
       // If obj is undefined or null, then there is no chance to run completion
       // on it. Exit here.
       if (typeof obj === "undefined" || obj === null) {
         return null;
       }
 
       // Check if prop is a getter function on obj. Functions can change other
       // stuff so we can't execute them to get the next object. Stop here.
-      if (isNonNativeGetter(obj, prop)) {
+      if (WebConsoleUtils.isNonNativeGetter(obj, prop)) {
         return null;
       }
       try {
         obj = obj[prop];
       }
       catch (ex) {
         return null;
       }
@@ -4468,61 +4485,33 @@ function JSPropertyProvider(aScope, aInp
 
   // If obj is undefined or null, then there is no chance to run
   // completion on it. Exit here.
   if (typeof obj === "undefined" || obj === null) {
     return null;
   }
 
   // Skip Iterators and Generators.
-  if (isIteratorOrGenerator(obj)) {
+  if (WebConsoleUtils.isIteratorOrGenerator(obj)) {
     return null;
   }
 
   let matches = [];
   for (let prop in obj) {
     if (prop.indexOf(matchProp) == 0) {
       matches.push(prop);
     }
   }
 
   return {
     matchProp: matchProp,
     matches: matches.sort(),
   };
 }
 
-function isIteratorOrGenerator(aObject)
-{
-  if (aObject === null) {
-    return false;
-  }
-
-  if (typeof aObject == "object") {
-    if (typeof aObject.__iterator__ == "function" ||
-        aObject.constructor && aObject.constructor.name == "Iterator") {
-      return true;
-    }
-
-    try {
-      let str = aObject.toString();
-      if (typeof aObject.next == "function" &&
-          str.indexOf("[object Generator") == 0) {
-        return true;
-      }
-    }
-    catch (ex) {
-      // window.history.next throws in the typeof check above.
-      return false;
-    }
-  }
-
-  return false;
-}
-
 //////////////////////////////////////////////////////////////////////////
 // JSTerm
 //////////////////////////////////////////////////////////////////////////
 
 /**
  * JSTermHelper
  *
  * Defines a set of functions ("helper functions") that are available from the
@@ -4630,34 +4619,34 @@ function JSTermHelper(aJSTerm)
    *
    * @param object aObject
    *        Object to return the property names from.
    * @returns array of string
    */
   aJSTerm.sandbox.keys = function JSTH_keys(aObject)
   {
     try {
-      return Object.keys(unwrap(aObject));
+      return Object.keys(WebConsoleUtils.unwrap(aObject));
     }
     catch (ex) {
       aJSTerm.console.error(ex.message);
     }
   };
 
   /**
    * Returns the values of all properties on aObject.
    *
    * @param object aObject
    *        Object to display the values from.
    * @returns array of string
    */
   aJSTerm.sandbox.values = function JSTH_values(aObject)
   {
     let arrValues = [];
-    let obj = unwrap(aObject);
+    let obj = WebConsoleUtils.unwrap(aObject);
 
     try {
       for (let prop in obj) {
         arrValues.push(obj[prop]);
       }
     }
     catch (ex) {
       aJSTerm.console.error(ex.message);
@@ -4681,17 +4670,18 @@ function JSTermHelper(aJSTerm)
    *
    * @param object aObject
    *        Object to inspect.
    * @returns void
    */
   aJSTerm.sandbox.inspect = function JSTH_inspect(aObject)
   {
     aJSTerm.helperEvaluated = true;
-    let propPanel = aJSTerm.openPropertyPanel(null, unwrap(aObject));
+    let propPanel = aJSTerm.openPropertyPanel(null,
+                                              WebConsoleUtils.unwrap(aObject));
     propPanel.panel.setAttribute("hudId", aJSTerm.hudId);
   };
 
   aJSTerm.sandbox.inspectrules = function JSTH_inspectrules(aNode)
   {
     aJSTerm.helperEvaluated = true;
     let doc = aJSTerm.inputNode.ownerDocument;
     let win = doc.defaultView;
@@ -4742,26 +4732,26 @@ function JSTermHelper(aJSTerm)
    * @param object aObject
    *        Object to print to the output.
    * @returns void
    */
   aJSTerm.sandbox.pprint = function JSTH_pprint(aObject)
   {
     aJSTerm.helperEvaluated = true;
     if (aObject === null || aObject === undefined || aObject === true || aObject === false) {
-      aJSTerm.console.error(HUDService.getStr("helperFuncUnsupportedTypeError"));
+      aJSTerm.console.error(l10n.getStr("helperFuncUnsupportedTypeError"));
       return;
     }
     else if (typeof aObject === TYPEOF_FUNCTION) {
       aJSTerm.writeOutput(aObject + "\n", CATEGORY_OUTPUT, SEVERITY_LOG);
       return;
     }
 
     let output = [];
-    let pairs = namesAndValuesOf(unwrap(aObject));
+    let pairs = namesAndValuesOf(WebConsoleUtils.unwrap(aObject));
 
     pairs.forEach(function(pair) {
       output.push("  " + pair.display);
     });
 
     aJSTerm.writeOutput(output.join("\n"), CATEGORY_OUTPUT, SEVERITY_LOG);
   };
 
@@ -4812,27 +4802,28 @@ function JSTerm(aContext, aParentNode, a
   this.setTimeout = aParentNode.ownerDocument.defaultView.setTimeout;
 
   let node = aParentNode;
   while (!node.hasAttribute("id")) {
     node = node.parentNode;
   }
   this.hudId = node.getAttribute("id");
 
+  this.history = [];
   this.historyIndex = 0;
   this.historyPlaceHolder = 0;  // this.history.length;
   this.log = LogFactory("*** JSTerm:");
   this.autocompletePopup = new AutocompletePopup(aParentNode.ownerDocument);
   this.autocompletePopup.onSelect = this.onAutocompleteSelect.bind(this);
   this.autocompletePopup.onClick = this.acceptProposedCompletion.bind(this);
   this.init();
 }
 
 JSTerm.prototype = {
-
+  lastInputValue: "",
   propertyProvider: JSPropertyProvider,
 
   COMPLETE_FORWARD: 0,
   COMPLETE_BACKWARD: 1,
   COMPLETE_HINT_ONLY: 2,
 
   init: function JST_init()
   {
@@ -4892,17 +4883,17 @@ JSTerm.prototype = {
    */
   evalInSandbox: function JST_evalInSandbox(aString)
   {
     // The help function needs to be easy to guess, so we make the () optional
     if (aString.trim() === "help" || aString.trim() === "?") {
       aString = "help()";
     }
 
-    let window = unwrap(this.sandbox.window);
+    let window = WebConsoleUtils.unwrap(this.sandbox.window);
     let $ = null, $$ = null;
 
     // We prefer to execute the page-provided implementations for the $() and
     // $$() functions.
     if (typeof window.$ == "function") {
       $ = this.sandbox.$;
       delete this.sandbox.$;
     }
@@ -4937,18 +4928,18 @@ JSTerm.prototype = {
 
     try {
       this.helperEvaluated = false;
       let result = this.evalInSandbox(aExecuteString);
 
       // Hide undefined results coming from helpers.
       let shouldShow = !(result === undefined && this.helperEvaluated);
       if (shouldShow) {
-        let inspectable = this.isResultInspectable(result);
-        let resultString = this.formatResult(result);
+        let inspectable = WebConsoleUtils.isObjectInspectable(result);
+        let resultString = WebConsoleUtils.formatResult(result);
 
         if (inspectable) {
           this.writeOutputJS(aExecuteString, result, resultString);
         }
         else {
           this.writeOutput(resultString, CATEGORY_OUTPUT, SEVERITY_LOG);
         }
       }
@@ -4988,18 +4979,18 @@ JSTerm.prototype = {
     //    result will be inspected by this panel.
     let buttons = [];
 
     // If there is a evalString passed to this function, then add a `Update`
     // button to the panel so that the evalString can be reexecuted to update
     // the content of the panel.
     if (aEvalString !== null) {
       buttons.push({
-        label: HUDService.getStr("update.button"),
-        accesskey: HUDService.getStr("update.accesskey"),
+        label: l10n.getStr("update.button"),
+        accesskey: l10n.getStr("update.accesskey"),
         oncommand: function () {
           try {
             var result = self.evalInSandbox(aEvalString);
 
             if (result !== undefined) {
               // TODO: This updates the value of the tree.
               // However, the states of opened nodes is not saved.
               // See bug 586246.
@@ -5011,29 +5002,91 @@ JSTerm.prototype = {
           }
         }
       });
     }
 
     let doc = self.document;
     let parent = doc.getElementById("mainPopupSet");
     let title = (aEvalString
-        ? HUDService.getFormatStr("jsPropertyInspectTitle", [aEvalString])
-        : HUDService.getStr("jsPropertyTitle"));
+        ? l10n.getFormatStr("jsPropertyInspectTitle", [aEvalString])
+        : l10n.getStr("jsPropertyTitle"));
 
     propPanel = new PropertyPanel(parent, doc, title, aOutputObject, buttons);
     propPanel.linkNode = aAnchor;
 
     let panel = propPanel.panel;
     panel.openPopup(aAnchor, "after_pointer", 0, 0, false, false);
     panel.sizeTo(350, 450);
     return propPanel;
   },
 
   /**
+   * Opens a new property panel that allows the inspection of the given object.
+   * The object information can be retrieved both async and sync, depending on
+   * the given options.
+   *
+   * @param object aOptions
+   *        Property panel options:
+   *        - title:
+   *        Panel title.
+   *        - anchor:
+   *        The DOM element you want the panel to be anchored to.
+   *        - updateButtonCallback:
+   *        An optional function you want invoked when the user clicks the
+   *        Update button. If this function is not provided the Update button is
+   *        not shown.
+   *        - data:
+   *        An object that represents the object you want to inspect. Please see
+   *        the PropertyPanelAsync documentation - this object is passed to the
+   *        PropertyPanelAsync constructor
+   * @param object aResponse
+   *        The response object to display/inspect inside of the tree.
+   * @param nsIDOMNode aAnchor
+   *        A node to popup the panel next to (using "after_pointer").
+   * @return object
+   *         The new instance of PropertyPanelAsync.
+   */
+  openPropertyPanelAsync: function JST_openPropertyPanelAsync(aOptions)
+  {
+    // The property panel has one button:
+    //    `Update`: reexecutes the string executed on the command line. The
+    //    result will be inspected by this panel.
+    let buttons = [];
+
+    if (aOptions.updateButtonCallback) {
+      buttons.push({
+        label: l10n.getStr("update.button"),
+        accesskey: l10n.getStr("update.accesskey"),
+        oncommand: aOptions.updateButtonCallback,
+      });
+    }
+
+    let parent = this.document.getElementById("mainPopupSet");
+    let title = aOptions.title ?
+                l10n.getFormatStr("jsPropertyInspectTitle", [aOptions.title]) :
+                l10n.getStr("jsPropertyTitle");
+
+    let propPanel = new PropertyPanelAsync(parent, title, aOptions.data, buttons);
+
+    propPanel.panel.openPopup(aOptions.anchor, "after_pointer", 0, 0, false, false);
+    propPanel.panel.sizeTo(350, 450);
+
+    if (aOptions.anchor) {
+      propPanel.panel.addEventListener("popuphiding", function onPopupHide() {
+        propPanel.panel.removeEventListener("popuphiding", onPopupHide, false);
+        aOptions.anchor._panelOpen = false;
+      }, false);
+      aOptions.anchor._panelOpen = true;
+    }
+
+    return propPanel;
+  },
+
+  /**
    * Writes a JS object to the JSTerm outputNode. If the user clicks on the
    * written object, openPropertyPanel is called to open up a panel to inspect
    * the object.
    *
    * @param string aEvalString
    *        String that was evaluated to get the aOutputObject.
    * @param object aResultObject
    *        The evaluation result object.
@@ -5094,161 +5147,48 @@ JSTerm.prototype = {
     let node = ConsoleUtils.createMessageNode(this.document,
                                               aCategory, aSeverity,
                                               aOutputMessage, this.hudId);
 
     ConsoleUtils.outputMessageNode(node, this.hudId);
   },
 
   /**
-   * Format the jsterm execution result based on its type.
-   *
-   * @param mixed aResult
-   *        The evaluation result object you want displayed.
-   * @returns string
-   *          The string that can be displayed.
-   */
-  formatResult: function JST_formatResult(aResult)
-  {
-    let output = "";
-    let type = this.getResultType(aResult);
-
-    switch (type) {
-      case "string":
-        output = this.formatString(aResult);
-        break;
-      case "boolean":
-      case "date":
-      case "error":
-      case "number":
-      case "regexp":
-        output = aResult.toString();
-        break;
-      case "null":
-      case "undefined":
-        output = type;
-        break;
-      default:
-        if (aResult.toSource) {
-          try {
-            output = aResult.toSource();
-          } catch (ex) { }
-        }
-        if (!output || output == "({})") {
-          output = aResult.toString();
-        }
-        break;
-    }
-
-    return output;
-  },
-
-  /**
-   * Format a string for output.
-   *
-   * @param string aString
-   *        The string you want to display.
-   * @returns string
-   *          The string that can be displayed.
-   */
-  formatString: function JST_formatString(aString)
-  {
-    function isControlCode(c) {
-      // See http://en.wikipedia.org/wiki/C0_and_C1_control_codes
-      // C0 is 0x00-0x1F, C1 is 0x80-0x9F (inclusive).
-      // We also include DEL (U+007F) and NBSP (U+00A0), which are not strictly
-      // in C1 but border it.
-      return (c <= 0x1F) || (0x7F <= c && c <= 0xA0);
-    }
-
-    function replaceFn(aMatch, aType, aHex) {
-      // Leave control codes escaped, but unescape the rest of the characters.
-      let c = parseInt(aHex, 16);
-      return isControlCode(c) ? aMatch : String.fromCharCode(c);
-    }
-
-    let output = uneval(aString).replace(/\\(x)([0-9a-fA-F]{2})/g, replaceFn)
-                 .replace(/\\(u)([0-9a-fA-F]{4})/g, replaceFn);
-
-    return output;
-  },
-
-  /**
-   * Determine if the jsterm execution result is inspectable or not.
-   *
-   * @param mixed aResult
-   *        The evaluation result object you want to check if it is inspectable.
-   * @returns boolean
-   *          True if the object is inspectable or false otherwise.
-   */
-  isResultInspectable: function JST_isResultInspectable(aResult)
-  {
-    let isEnumerable = false;
-
-    // Skip Iterators and Generators.
-    if (isIteratorOrGenerator(aResult)) {
-      return false;
-    }
-
-    for (let p in aResult) {
-      isEnumerable = true;
-      break;
-    }
-
-    return isEnumerable && typeof(aResult) != "string";
-  },
-
-  /**
-   * Determine the type of the jsterm execution result.
-   *
-   * @param mixed aResult
-   *        The evaluation result object you want to check.
-   * @returns string
-   *          Constructor name or type: string, number, boolean, regexp, date,
-   *          function, object, null, undefined...
-   */
-  getResultType: function JST_getResultType(aResult)
-  {
-    let type = aResult === null ? "null" : typeof aResult;
-    if (type == "object" && aResult.constructor && aResult.constructor.name) {
-      type = aResult.constructor.name;
-    }
-
-    return type.toLowerCase();
-  },
-
-  /**
    * Clear the Web Console output.
    *
    * @param boolean aClearStorage
    *        True if you want to clear the console messages storage associated to
    *        this Web Console.
    */
   clearOutput: function JST_clearOutput(aClearStorage)
   {
     let hud = HUDService.getHudReferenceById(this.hudId);
     hud.cssNodes = {};
 
-    let node = hud.outputNode;
-    while (node.firstChild) {
-      if (node.firstChild.classList &&
-          node.firstChild.classList.contains("webconsole-msg-inspector")) {
-        hud.pruneConsoleDirNode(node.firstChild);
+    let outputNode = hud.outputNode;
+    let node;
+    while ((node = outputNode.firstChild)) {
+      if (node._evalCacheId && !node._panelOpen) {
+        this.clearObjectCache(node._evalCacheId);
+      }
+
+      if (node.classList &&
+          node.classList.contains("webconsole-msg-inspector")) {
+        hud.pruneConsoleDirNode(node);
       }
       else {
-        hud.outputNode.removeChild(node.firstChild);
+        outputNode.removeChild(node);
       }
     }
 
     hud.HUDBox.lastTimestamp = 0;
     hud.groupDepth = 0;
 
     if (aClearStorage) {
-      let windowId = HUDService.getInnerWindowId(hud.contentWindow);
-      gConsoleStorage.clearEvents(windowId);
+      hud.sendMessageToContent("ConsoleAPI:ClearCache", {});
     }
   },
 
   /**
    * Updates the size of the input field (command line) to fit its contents.
    *
    * @returns void
    */
@@ -5377,17 +5317,17 @@ JSTerm.prototype = {
       case Ci.nsIDOMKeyEvent.DOM_VK_TAB:
         // Generate a completion and accept the first proposed value.
         if (this.complete(this.COMPLETE_HINT_ONLY) &&
             this.lastCompletion &&
             this.acceptProposedCompletion()) {
           aEvent.preventDefault();
         }
         else {
-          this.updateCompleteNode(HUDService.getStr("Autocomplete.blank"));
+          this.updateCompleteNode(l10n.getStr("Autocomplete.blank"));
           aEvent.preventDefault();
         }
         break;
 
       default:
         break;
     }
   },
@@ -5484,17 +5424,17 @@ JSTerm.prototype = {
       return false;
     }
 
     let multiline = /[\r\n]/.test(node.value);
     return node.selectionStart == node.value.length ? true :
            node.selectionStart == 0 && !multiline;
   },
 
-  history: [],
+  history: null,
 
   // Stores the data for the last completion.
   lastCompletion: null,
 
   /**
    * Completes the current typed text in the inputNode. Completion is performed
    * only if the selection/cursor is at the end of the string. If no completion
    * is found, the current inputNode value and cursor/selection stay.
@@ -5643,16 +5583,57 @@ JSTerm.prototype = {
   updateCompleteNode: function JSTF_updateCompleteNode(aSuffix)
   {
     // completion prefix = input, with non-control chars replaced by spaces
     let prefix = aSuffix ? this.inputNode.value.replace(/[\S]/g, " ") : "";
     this.completeNode.value = prefix + aSuffix;
   },
 
   /**
+   * Clear the object cache from the Web Console content instance.
+   *
+   * @param string aCacheId
+   *        The cache ID you want to clear. Multiple objects are cached into one
+   *        group which is given an ID.
+   */
+  clearObjectCache: function JST_clearObjectCache(aCacheId)
+  {
+    let hud = HUDService.getHudReferenceById(this.hudId);
+    hud.sendMessageToContent("JSTerm:ClearObjectCache", {cacheId: aCacheId});
+  },
+
+  /**
+   * The remote object provider allows you to retrieve a given object from
+   * a specific cache and have your callback invoked when the desired object is
+   * received from the Web Console content instance.
+   *
+   * @param string aCacheId
+   *        Retrieve the desired object from this cache ID.
+   * @param string aObjectId
+   *        The ID of the object you want.
+   * @param string aResultCacheId
+   *        The ID of the cache where you want any object references to be
+   *        stored into.
+   * @param function aCallback
+   *        The function you want invoked when the desired object is retrieved.
+   */
+  remoteObjectProvider:
+  function JST_remoteObjectProvider(aCacheId, aObjectId, aResultCacheId,
+                                    aCallback) {
+    let message = {
+      cacheId: aCacheId,
+      objectId: aObjectId,
+      resultCacheId: aResultCacheId,
+    };
+
+    let hud = HUDService.getHudReferenceById(this.hudId);
+    hud.sendMessageToContent("JSTerm:GetEvalObject", message, aCallback);
+  },
+
+  /**
    * Destroy the JSTerm object. Call this method to avoid memory leaks.
    */
   destroy: function JST_destroy()
   {
     this.inputNode.removeEventListener("keypress", this._keyPress, false);
     this.inputNode.removeEventListener("input", this._inputEventHandler, false);
     this.inputNode.removeEventListener("keyup", this._inputEventHandler, false);
   },
@@ -5775,50 +5756,16 @@ FirefoxApplicationHooks.prototype = {
  */
 
 ConsoleUtils = {
   /**
    * Flag to turn on and off scrolling.
    */
   scroll: true,
 
-  supString: function ConsoleUtils_supString(aString)
-  {
-    let str = Cc["@mozilla.org/supports-string;1"].
-      createInstance(Ci.nsISupportsString);
-    str.data = aString;
-    return str;
-  },
-
-  /**
-   * Generates a millisecond resolution timestamp.
-   *
-   * @returns integer
-   */
-  timestamp: function ConsoleUtils_timestamp()
-  {
-    return Date.now();
-  },
-
-  /**
-   * Generates a formatted timestamp string for displaying in console messages.
-   *
-   * @param integer [ms] Optional, allows you to specify the timestamp in
-   * milliseconds since the UNIX epoch.
-   * @returns string The timestamp formatted for display.
-   */
-  timestampString: function ConsoleUtils_timestampString(ms)
-  {
-    var d = new Date(ms ? ms : null);
-    let hours = d.getHours(), minutes = d.getMinutes();
-    let seconds = d.getSeconds(), milliseconds = d.getMilliseconds();
-    let parameters = [ hours, minutes, seconds, milliseconds ];
-    return HUDService.getFormatStr("timestampFormat", parameters);
-  },
-
   /**
    * Scrolls a node so that it's visible in its containing XUL "scrollbox"
    * element.
    *
    * @param nsIDOMNode aNode
    *        The node to make visible.
    * @returns void
    */
@@ -5908,18 +5855,20 @@ ConsoleUtils = {
     // Store the body text, since it is needed later for the property tree
     // case.
     let body = aBody;
     // If a string was supplied for the body, turn it into a DOM node and an
     // associated clipboard string now.
     aClipboardText = aClipboardText ||
                      (aBody + (aSourceURL ? " @ " + aSourceURL : "") +
                               (aSourceLine ? ":" + aSourceLine : ""));
-    aBody = aBody instanceof Ci.nsIDOMNode && !(aLevel == "dir") ?
-            aBody : aDocument.createTextNode(aBody);
+    if (!(aBody instanceof Ci.nsIDOMNode)) {
+      aBody = aDocument.createTextNode(aLevel == "dir" ?
+                                       aBody.resultString : aBody);
+    }
 
     if (!aBody.nodeType) {
       aBody = aDocument.createTextNode(aBody.toString());
     }
     if (typeof aBody == "string") {
       aBody = aDocument.createTextNode(aBody);
     }
 
@@ -5930,18 +5879,18 @@ ConsoleUtils = {
     let repeatNode = aDocument.createElementNS(XUL_NS, "label");
     repeatNode.setAttribute("value", "1");
     repeatNode.classList.add("webconsole-msg-repeat");
     repeatContainer.appendChild(repeatNode);
 
     // Create the timestamp.
     let timestampNode = aDocument.createElementNS(XUL_NS, "label");
     timestampNode.classList.add("webconsole-timestamp");
-    let timestamp = aTimeStamp || ConsoleUtils.timestamp();
-    let timestampString = ConsoleUtils.timestampString(timestamp);
+    let timestamp = aTimeStamp || Date.now();
+    let timestampString = l10n.timestampString(timestamp);
     timestampNode.setAttribute("value", timestampString);
 
     // Create the source location (e.g. www.example.com:6) that sits on the
     // right side of the message, if applicable.
     let locationNode;
     if (aSourceURL) {
       locationNode = this.createLocationNode(aDocument, aSourceURL,
                                              aSourceLine);
@@ -5981,18 +5930,25 @@ ConsoleUtils = {
       tree.appendChild(treecols);
 
       tree.appendChild(aDocument.createElement("treechildren"));
 
       bodyContainer.appendChild(tree);
       node.appendChild(bodyContainer);
       node.classList.add("webconsole-msg-inspector");
       // Create the treeView object.
-      let treeView = node.propertyTreeView = new PropertyTreeView();
-      treeView.data = body;
+      let treeView = node.propertyTreeView = new PropertyTreeViewAsync();
+
+      treeView.data = {
+        rootCacheId: body.cacheId,
+        panelCacheId: body.cacheId,
+        remoteObject: body.remoteObject,
+        remoteObjectProvider: body.remoteObjectProvider,
+      };
+
       tree.setAttribute("rows", treeView.rowCount);
     }
     else {
       node.appendChild(bodyNode);
     }
     node.appendChild(repeatContainer);
     if (locationNode) {
       node.appendChild(locationNode);
@@ -6057,17 +6013,17 @@ ConsoleUtils = {
    */
   createLocationNode:
   function ConsoleUtils_createLocationNode(aDocument, aSourceURL,
                                            aSourceLine) {
     let locationNode = aDocument.createElementNS(XUL_NS, "label");
 
     // Create the text, which consists of an abbreviated version of the URL
     // plus an optional line number.
-    let text = ConsoleUtils.abbreviateSourceURL(aSourceURL);
+    let text = WebConsoleUtils.abbreviateSourceURL(aSourceURL);
     if (aSourceLine) {
       text += ":" + aSourceLine;
     }
     locationNode.setAttribute("value", text);
 
     // Style appropriately.
     locationNode.setAttribute("crop", "center");
     locationNode.setAttribute("title", aSourceURL);
@@ -6254,17 +6210,17 @@ ConsoleUtils = {
 
     // Scroll to the new node if it is not filtered, and if the output node is
     // scrolled at the bottom or if the new node is a jsterm input/output
     // message.
     if (!isFiltered && !isRepeated && (scrolledToBottom || isInputOutput)) {
       ConsoleUtils.scrollToVisible(aNode);
     }
 
-    let id = ConsoleUtils.supString(aHUDId);
+    let id = WebConsoleUtils.supportsString(aHUDId);
     let nodeID = aNode.getAttribute("id");
     Services.obs.notifyObservers(id, "web-console-message-created", nodeID);
   },
 
   /**
    * Check if the given output node is scrolled to the bottom.
    *
    * @param nsIDOMNode aOutputNode
@@ -6277,46 +6233,16 @@ ConsoleUtils = {
   {
     let lastNodeHeight = aOutputNode.lastChild ?
                          aOutputNode.lastChild.clientHeight : 0;
     let scrollBox = aOutputNode.scrollBoxObject.element;
 
     return scrollBox.scrollTop + scrollBox.clientHeight >=
            scrollBox.scrollHeight - lastNodeHeight / 2;
   },
-
-  /**
-   * Abbreviates the given source URL so that it can be displayed flush-right
-   * without being too distracting.
-   *
-   * @param string aSourceURL
-   *        The source URL to shorten.
-   * @return string
-   *         The abbreviated form of the source URL.
-   */
-  abbreviateSourceURL: function ConsoleUtils_abbreviateSourceURL(aSourceURL) {
-    // Remove any query parameters.
-    let hookIndex = aSourceURL.indexOf("?");
-    if (hookIndex > -1) {
-      aSourceURL = aSourceURL.substring(0, hookIndex);
-    }
-
-    // Remove a trailing "/".
-    if (aSourceURL[aSourceURL.length - 1] == "/") {
-      aSourceURL = aSourceURL.substring(0, aSourceURL.length - 1);
-    }
-
-    // Remove all but the last path component.
-    let slashIndex = aSourceURL.lastIndexOf("/");
-    if (slashIndex > -1) {
-      aSourceURL = aSourceURL.substring(slashIndex + 1);
-    }
-
-    return aSourceURL;
-  }
 };
 
 //////////////////////////////////////////////////////////////////////////
 // HeadsUpDisplayUICommands
 //////////////////////////////////////////////////////////////////////////
 
 HeadsUpDisplayUICommands = {
   refreshCommand: function UIC_refreshCommand() {
@@ -6509,41 +6435,38 @@ ConsoleEntry.prototype = {
   }
 };
 
 //////////////////////////////////////////////////////////////////////////
 // HUDWindowObserver
 //////////////////////////////////////////////////////////////////////////
 
 HUDWindowObserver = {
-  QueryInterface: XPCOMUtils.generateQI(
-    [Ci.nsIObserver,]
-  ),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   init: function HWO_init()
   {
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.obs.addObserver(this, "content-document-global-created", false);
+    Services.obs.addObserver(this, "quit-application-granted", false);
   },
 
   observe: function HWO_observe(aSubject, aTopic, aData)
   {
     if (aTopic == "content-document-global-created") {
       HUDService.windowInitializer(aSubject);
     }
-    else if (aTopic == "xpcom-shutdown") {
-      this.uninit();
+    else if (aTopic == "quit-application-granted") {
+      HUDService.shutdown();
     }
   },
 
   uninit: function HWO_uninit()
   {
     Services.obs.removeObserver(this, "content-document-global-created");
-    Services.obs.removeObserver(this, "xpcom-shutdown");
-    this.initialConsoleCreated = false;
+    Services.obs.removeObserver(this, "quit-application-granted");
   },
 
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 // CommandController
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -6628,54 +6551,16 @@ CommandController.prototype = {
         break;
       case "cmd_selectAll":
         this.selectAll(outputNode);
         break;
     }
   }
 };
 
-///////////////////////////////////////////////////////////////////////////////
-// HUDConsoleObserver
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * HUDConsoleObserver: Observes nsIConsoleService for global consoleMessages,
- * if a message originates inside a contentWindow we are tracking,
- * then route that message to the HUDService for logging.
- */
-
-HUDConsoleObserver = {
-  QueryInterface: XPCOMUtils.generateQI(
-    [Ci.nsIObserver]
-  ),
-
-  init: function HCO_init()
-  {
-    Services.console.registerListener(this);
-    Services.obs.addObserver(this, "quit-application-granted", false);
-  },
-
-  uninit: function HCO_uninit()
-  {
-    Services.console.unregisterListener(this);
-    Services.obs.removeObserver(this, "quit-application-granted");
-  },
-
-  observe: function HCO_observe(aSubject, aTopic, aData)
-  {
-    if (aTopic == "quit-application-granted") {
-      this.uninit();
-    }
-    else if (aSubject instanceof Ci.nsIScriptError) {
-      HUDService.reportPageError(aSubject);
-    }
-  }
-};
-
 /**
  * A WebProgressListener that listens for location changes, to update HUDService
  * state information on page navigation.
  *
  * @constructor
  * @param string aHudId
  *        The HeadsUpDisplay ID.
  */
@@ -7040,22 +6925,22 @@ GcliTerm.prototype = {
       '    <image class="webconsole-msg-icon"/>' +
       '    <spacer flex="1"/>' +
       '  </vbox>' +
       '  <hbox flex="1" class="gcliterm-msg-body">${output}</hbox>' +
       '  <hbox align="start"><label value="1" class="webconsole-msg-repeat"/></hbox>' +
       '</richlistitem>').firstChild;
 
     let hud = HUDService.getHudReferenceById(this.hudId);
-    let timestamp = ConsoleUtils.timestamp();
+    let timestamp = Date.now();
     template(element, {
       iconContainerStyle: "margin-left=" + (hud.groupDepth * GROUP_INDENT) + "px",
       output: output,
       timestamp: timestamp,
-      timestampString: ConsoleUtils.timestampString(timestamp),
+      timestampString: l10n.timestampString(timestamp),
       clipboardText: output.innerText,
       id: "console-msg-" + HUDService.sequenceId()
     });
 
     ConsoleUtils.setMessageType(element, CATEGORY_OUTPUT, SEVERITY_LOG);
     ConsoleUtils.outputMessageNode(element, this.hudId);
   },
 
@@ -7078,17 +6963,17 @@ GcliTerm.prototype = {
    * Evaluates a string in the sandbox.
    *
    * @param string aString
    *        String to evaluate in the sandbox
    * @return The result of the evaluation
    */
   evalInSandbox: function Gcli_evalInSandbox(aString)
   {
-    let window = unwrap(this.sandbox.window);
+    let window = WebConsoleUtils.unwrap(this.sandbox.window);
     let temp$ = null;
     let temp$$ = null;
 
     // We prefer to execute the page-provided implementations for the $() and
     // $$() functions.
     if (typeof window.$ == "function") {
       temp$ = this.sandbox.$;
       delete this.sandbox.$;
@@ -7136,19 +7021,19 @@ GcliTerm.prototype = {
                     aOptions.clipboardText || undefined);
 
     ConsoleUtils.outputMessageNode(node, this.hudId);
   },
 
   clearOutput: JSTerm.prototype.clearOutput,
   openPropertyPanel: JSTerm.prototype.openPropertyPanel,
 
-  formatResult: JSTerm.prototype.formatResult,
-  getResultType: JSTerm.prototype.getResultType,
-  formatString: JSTerm.prototype.formatString,
+  formatResult: WebConsoleUtils.formatResult,
+  getResultType: WebConsoleUtils.getResultType,
+  formatString: WebConsoleUtils.formatResultString,
 };
 
 /**
  * A fancy version of toString()
  */
 function nameObject(aObj) {
   if (aObj.constructor && aObj.constructor.name) {
     return aObj.constructor.name;
--- a/browser/devtools/webconsole/Makefile.in
+++ b/browser/devtools/webconsole/Makefile.in
@@ -41,18 +41,20 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
 		PropertyPanel.jsm \
+		PropertyPanelAsync.jsm \
 		NetworkHelper.jsm \
 		AutocompletePopup.jsm \
+		WebConsoleUtils.jsm \
 		gcli.jsm \
 		GcliCommands.jsm \
 		GcliTiltCommands.jsm \
 		$(NULL)
 
 EXTRA_PP_JS_MODULES = \
 		HUDService.jsm \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/PropertyPanelAsync.jsm
@@ -0,0 +1,466 @@
+/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "WebConsoleUtils", function () {
+  let obj = {};
+  Cu.import("resource:///modules/WebConsoleUtils.jsm", obj);
+  return obj.WebConsoleUtils;
+});
+
+var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView"];
+
+
+///////////////////////////////////////////////////////////////////////////
+//// PropertyTreeView.
+
+/**
+ * This is an implementation of the nsITreeView interface. For comments on the
+ * interface properties, see the documentation:
+ * https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsITreeView
+ */
+var PropertyTreeView = function() {
+  this._rows = [];
+  this._objectCache = {};
+};
+
+PropertyTreeView.prototype = {
+  /**
+   * Stores the visible rows of the tree.
+   * @private
+   */
+  _rows: null,
+
+  /**
+   * Stores the nsITreeBoxObject for this tree.
+   * @private
+   */
+  _treeBox: null,
+
+  /**
+   * Stores cached information about local objects being inspected.
+   * @private
+   */
+  _objectCache: null,
+
+  /**
+   * Use this setter to update the content of the tree.
+   *
+   * @param object aObject
+   *        An object that holds information about the object you want to
+   *        display in the property panel. Object properties:
+   *        - object:
+   *        This is the raw object you want to display. You can only provide
+   *        this object if you want the property panel to work in sync mode.
+   *        - remoteObject:
+   *        An array that holds information on the remote object being
+   *        inspected. Each element in this array describes each property in the
+   *        remote object. See WebConsoleUtils.namesAndValuesOf() for details.
+   *        - rootCacheId:
+   *        The cache ID where the objects referenced in remoteObject are found.
+   *        - panelCacheId:
+   *        The cache ID where any object retrieved by this property panel
+   *        instance should be stored into.
+   *        - remoteObjectProvider:
+   *        A function that is invoked when a new object is needed. This is
+   *        called when the user tries to expand an inspectable property. The
+   *        callback must take four arguments:
+   *          - fromCacheId:
+   *          Tells from where to retrieve the object the user picked (from
+   *          which cache ID).
+   *          - objectId:
+   *          The object ID the user wants.
+   *          - panelCacheId:
+   *          Tells in which cache ID to store the objects referenced by
+   *          objectId so they can be retrieved later.
+   *          - callback:
+   *          The callback function to be invoked when the remote object is
+   *          received. This function takes one argument: the raw message
+   *          received from the Web Console content script.
+   */
+  set data(aData) {
+    let oldLen = this._rows.length;
+
+    this._cleanup();
+
+    if (!aData) {
+      return;
+    }
+
+    if (aData.remoteObject) {
+      this._rootCacheId = aData.rootCacheId;
+      this._panelCacheId = aData.panelCacheId;
+      this._remoteObjectProvider = aData.remoteObjectProvider;
+      this._rows = [].concat(aData.remoteObject);
+      this._updateRemoteObject(this._rows, 0);
+    }
+    else if (aData.object) {
+      this._rows = this._inspectObject(aData.object);
+    }
+    else {
+      throw new Error("First argument must have a .remoteObject or " +
+                      "an .object property!");
+    }
+
+    if (this._treeBox) {
+      this._treeBox.beginUpdateBatch();
+      if (oldLen) {
+        this._treeBox.rowCountChanged(0, -oldLen);
+      }
+      this._treeBox.rowCountChanged(0, this._rows.length);
+      this._treeBox.endUpdateBatch();
+    }
+  },
+
+  /**
+   * Update a remote object so it can be used with the tree view. This method
+   * adds properties to each array element.
+   *
+   * @private
+   * @param array aObject
+   *        The remote object you want prepared for use with the tree view.
+   * @param number aLevel
+   *        The level you want to give to each property in the remote object.
+   */
+  _updateRemoteObject: function PTV__updateRemoteObject(aObject, aLevel)
+  {
+    aObject.forEach(function(aElement) {
+      aElement.level = aLevel;
+      aElement.isOpened = false;
+      aElement.children = null;
+    });
+  },
+
+  /**
+   * Inspect a local object.
+   *
+   * @private
+   * @param object aObject
+   *        The object you want to inspect.
+   */
+  _inspectObject: function PTV__inspectObject(aObject)
+  {
+    this._objectCache = {};
+    this._remoteObjectProvider = this._localObjectProvider.bind(this);
+    let children = WebConsoleUtils.namesAndValuesOf(aObject, this._objectCache);
+    this._updateRemoteObject(children, 0);
+    return children;
+  },
+
+  /**
+   * An object provider for when the user inspects local objects (not remote
+   * ones).
+   *
+   * @private
+   * @param string aFromCacheId
+   *        The cache ID from where to retrieve the desired object.
+   * @param string aObjectId
+   *        The ID of the object you want.
+   * @param string aDestCacheId
+   *        The ID of the cache where to store any objects referenced by the
+   *        desired object.
+   * @param function aCallback
+   *        The function you want to receive the object.
+   */
+  _localObjectProvider:
+  function PTV__localObjectProvider(aFromCacheId, aObjectId, aDestCacheId,
+                                    aCallback)
+  {
+    let object = WebConsoleUtils.namesAndValuesOf(this._objectCache[aObjectId],
+                                                  this._objectCache);
+    aCallback({cacheId: aFromCacheId,
+               objectId: aObjectId,
+               object: object,
+               childrenCacheId: aDestCacheId || aFromCacheId,
+    });
+  },
+
+  /** nsITreeView interface implementation **/
+
+  selection: null,
+
+  get rowCount()                     { return this._rows.length; },
+  setTree: function(treeBox)         { this._treeBox = treeBox;  },
+  getCellText: function(idx, column) {
+    let row = this._rows[idx];
+    return row.name + ": " + row.value;
+  },
+  getLevel: function(idx) {
+    return this._rows[idx].level;
+  },
+  isContainer: function(idx) {
+    return !!this._rows[idx].inspectable;
+  },
+  isContainerOpen: function(idx) {
+    return this._rows[idx].isOpened;
+  },
+  isContainerEmpty: function(idx)    { return false; },
+  isSeparator: function(idx)         { return false; },
+  isSorted: function()               { return false; },
+  isEditable: function(idx, column)  { return false; },
+  isSelectable: function(row, col)   { return true; },
+
+  getParentIndex: function(idx)
+  {
+    if (this.getLevel(idx) == 0) {
+      return -1;
+    }
+    for (var t = idx - 1; t >= 0; t--) {
+      if (this.isContainer(t)) {
+        return t;
+      }
+    }
+    return -1;
+  },
+
+  hasNextSibling: function(idx, after)
+  {
+    var thisLevel = this.getLevel(idx);
+    return this._rows.slice(after + 1).some(function (r) r.level == thisLevel);
+  },
+
+  toggleOpenState: function(idx)
+  {
+    let item = this._rows[idx];
+    if (!item.inspectable) {
+      return;
+    }
+
+    if (item.isOpened) {
+      this._treeBox.beginUpdateBatch();
+      item.isOpened = false;
+
+      var thisLevel = item.level;
+      var t = idx + 1, deleteCount = 0;
+      while (t < this._rows.length && this.getLevel(t++) > thisLevel) {
+        deleteCount++;
+      }
+
+      if (deleteCount) {
+        this._rows.splice(idx + 1, deleteCount);
+        this._treeBox.rowCountChanged(idx + 1, -deleteCount);
+      }
+      this._treeBox.invalidateRow(idx);
+      this._treeBox.endUpdateBatch();
+    }
+    else {
+      let levelUpdate = true;
+      let callback = function _onRemoteResponse(aResponse) {
+        this._treeBox.beginUpdateBatch();
+        item.isOpened = true;
+
+        if (levelUpdate) {
+          this._updateRemoteObject(aResponse.object, item.level + 1);
+          item.children = aResponse.object;
+        }
+
+        this._rows.splice.apply(this._rows, [idx + 1, 0].concat(item.children));
+
+        this._treeBox.rowCountChanged(idx + 1, item.children.length);
+        this._treeBox.invalidateRow(idx);
+        this._treeBox.endUpdateBatch();
+      }.bind(this);
+
+      if (!item.children) {
+        let fromCacheId = item.level > 0 ? this._panelCacheId :
+                                           this._rootCacheId;
+        this._remoteObjectProvider(fromCacheId, item.objectId,
+                                   this._panelCacheId, callback);
+      }
+      else {
+        levelUpdate = false;
+        callback({object: item.children});
+      }
+    }
+  },
+
+  getImageSrc: function(idx, column) { },
+  getProgressMode : function(idx,column) { },
+  getCellValue: function(idx, column) { },
+  cycleHeader: function(col, elem) { },
+  selectionChanged: function() { },
+  cycleCell: function(idx, column) { },
+  performAction: function(action) { },
+  performActionOnCell: function(action, index, column) { },
+  performActionOnRow: function(action, row) { },
+  getRowProperties: function(idx, column, prop) { },
+  getCellProperties: function(idx, column, prop) { },
+  getColumnProperties: function(column, element, prop) { },
+
+  setCellValue: function(row, col, value)               { },
+  setCellText: function(row, col, value)                { },
+  drop: function(index, orientation, dataTransfer)      { },
+  canDrop: function(index, orientation, dataTransfer)   { return false; },
+
+  _cleanup: function PTV__cleanup()
+  {
+    if (this._rows.length) {
+      // Reset the existing _rows children to the initial state.
+      this._updateRemoteObject(this._rows, 0);
+      this._rows = [];
+    }
+
+    delete this._objectCache;
+    delete this._rootCacheId;
+    delete this._panelCacheId;
+    delete this._remoteObjectProvider;
+  },
+};
+
+///////////////////////////////////////////////////////////////////////////
+//// Helper for creating the panel.
+
+/**
+ * Creates a DOMNode and sets all the attributes of aAttributes on the created
+ * element.
+ *
+ * @param nsIDOMDocument aDocument
+ *        Document to create the new DOMNode.
+ * @param string aTag
+ *        Name of the tag for the DOMNode.
+ * @param object aAttributes
+ *        Attributes set on the created DOMNode.
+ * @returns nsIDOMNode
+ */
+function createElement(aDocument, aTag, aAttributes)
+{
+  let node = aDocument.createElement(aTag);
+  for (var attr in aAttributes) {
+    node.setAttribute(attr, aAttributes[attr]);
+  }
+  return node;
+}
+
+/**
+ * Creates a new DOMNode and appends it to aParent.
+ *
+ * @param nsIDOMDocument aDocument
+ *        Document to create the new DOMNode.
+ * @param nsIDOMNode aParent
+ *        A parent node to append the created element.
+ * @param string aTag
+ *        Name of the tag for the DOMNode.
+ * @param object aAttributes
+ *        Attributes set on the created DOMNode.
+ * @returns nsIDOMNode
+ */
+function appendChild(aDocument, aParent, aTag, aAttributes)
+{
+  let node = createElement(aDocument, aTag, aAttributes);
+  aParent.appendChild(node);
+  return node;
+}
+
+///////////////////////////////////////////////////////////////////////////
+//// PropertyPanel
+
+/**
+ * Creates a new PropertyPanel.
+ *
+ * @see PropertyTreeView
+ * @param nsIDOMNode aParent
+ *        Parent node to append the created panel to.
+ * @param string aTitle
+ *        Title for the panel.
+ * @param string aObject
+ *        Object to display in the tree. For details about this object please
+ *        see the PropertyTreeView.data property in this file.
+ * @param array of objects aButtons
+ *        Array with buttons to display at the bottom of the panel.
+ */
+function PropertyPanel(aParent, aTitle, aObject, aButtons)
+{
+  let document = aParent.ownerDocument;
+
+  // Create the underlying panel
+  this.panel = createElement(document, "panel", {
+    label: aTitle,
+    titlebar: "normal",
+    noautofocus: "true",
+    noautohide: "true",
+    close: "true",
+  });
+
+  // Create the tree.
+  let tree = this.tree = createElement(document, "tree", {
+    flex: 1,
+    hidecolumnpicker: "true"
+  });
+
+  let treecols = document.createElement("treecols");
+  appendChild(document, treecols, "treecol", {
+    primary: "true",
+    flex: 1,
+    hideheader: "true",
+    ignoreincolumnpicker: "true"
+  });
+  tree.appendChild(treecols);
+
+  tree.appendChild(document.createElement("treechildren"));
+  this.panel.appendChild(tree);
+
+  // Create the footer.
+  let footer = createElement(document, "hbox", { align: "end" });
+  appendChild(document, footer, "spacer", { flex: 1 });
+
+  // The footer can have butttons.
+  let self = this;
+  if (aButtons) {
+    aButtons.forEach(function(button) {
+      let buttonNode = appendChild(document, footer, "button", {
+        label: button.label,
+        accesskey: button.accesskey || "",
+        class: button.class || "",
+      });
+      buttonNode.addEventListener("command", button.oncommand, false);
+    });
+  }
+
+  appendChild(document, footer, "resizer", { dir: "bottomend" });
+  this.panel.appendChild(footer);
+
+  aParent.appendChild(this.panel);
+
+  // Create the treeView object.
+  this.treeView = new PropertyTreeView();
+  this.treeView.data = aObject;
+
+  // Set the treeView object on the tree view. This has to be done *after* the
+  // panel is shown. This is because the tree binding must be attached first.
+  this.panel.addEventListener("popupshown", function onPopupShow()
+  {
+    self.panel.removeEventListener("popupshown", onPopupShow, false);
+    self.tree.view = self.treeView;
+  }, false);
+
+  this.panel.addEventListener("popuphidden", function onPopupHide()
+  {
+    self.panel.removeEventListener("popuphidden", onPopupHide, false);
+    self.destroy();
+  }, false);
+}
+
+/**
+ * Destroy the PropertyPanel. This closes the panel and removes it from the
+ * browser DOM.
+ */
+PropertyPanel.prototype.destroy = function PP_destroy()
+{
+  this.treeView.data = null;
+  this.panel.parentNode.removeChild(this.panel);
+  this.treeView = null;
+  this.panel = null;
+  this.tree = null;
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/WebConsoleUtils.jsm
@@ -0,0 +1,706 @@
+/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+var EXPORTED_SYMBOLS = ["WebConsoleUtils"];
+
+const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
+
+const TYPES = { OBJECT: 0,
+                FUNCTION: 1,
+                ARRAY: 2,
+                OTHER: 3,
+                ITERATOR: 4,
+                GETTER: 5,
+                GENERATOR: 6,
+                STRING: 7
+              };
+
+var gObjectId = 0;
+
+var WebConsoleUtils = {
+  TYPES: TYPES,
+
+  /**
+   * Convenience function to unwrap a wrapped object.
+   *
+   * @param aObject the object to unwrap.
+   * @return aObject unwrapped.
+   */
+  unwrap: function WCU_unwrap(aObject)
+  {
+    try {
+      return XPCNativeWrapper.unwrap(aObject);
+    }
+    catch (ex) {
+      return aObject;
+    }
+  },
+
+  /**
+   * Wrap a string in an nsISupportsString object.
+   *
+   * @param string aString
+   * @return nsISupportsString
+   */
+  supportsString: function WCU_supportsString(aString)
+  {
+    let str = Cc["@mozilla.org/supports-string;1"].
+              createInstance(Ci.nsISupportsString);
+    str.data = aString;
+    return str;
+  },
+
+  /**
+   * Clone an object.
+   *
+   * @param object aObject
+   *        The object you want cloned.
+   * @param boolean aRecursive
+   *        Tells if you want to dig deeper into the object, to clone
+   *        recursively.
+   * @param function [aFilter]
+   *        Optional, filter function, called for every property. Three
+   *        arguments are passed: key, value and object. Return true if the
+   *        property should be added to the cloned object. Return false to skip
+   *        the property.
+   * @return object
+   *         The cloned object.
+   */
+  cloneObject: function WCU_cloneObject(aObject, aRecursive, aFilter)
+  {
+    if (typeof aObject != "object") {
+      return aObject;
+    }
+
+    let temp;
+
+    if (Array.isArray(aObject)) {
+      temp = [];
+      Array.forEach(aObject, function(aValue, aIndex) {
+        if (!aFilter || aFilter(aIndex, aValue, aObject)) {
+          temp.push(aRecursive ? WCU_cloneObject(aValue) : aValue);
+        }
+      });
+    }
+    else {
+      temp = {};
+      for (let key in aObject) {
+        let value = aObject[key];
+        if (aObject.hasOwnProperty(key) &&
+            (!aFilter || aFilter(key, value, aObject))) {
+          temp[key] = aRecursive ? WCU_cloneObject(value) : value;
+        }
+      }
+    }
+
+    return temp;
+  },
+
+  /**
+   * Gets the ID of the inner window of this DOM window.
+   *
+   * @param nsIDOMWindow aWindow
+   * @return integer
+   *         Inner ID for the given aWindow.
+   */
+  getInnerWindowId: function WCU_getInnerWindowId(aWindow)
+  {
+    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+           getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+  },
+
+  /**
+   * Gets the ID of the outer window of this DOM window.
+   *
+   * @param nsIDOMWindow aWindow
+   * @return integer
+   *         Outer ID for the given aWindow.
+   */
+  getOuterWindowId: function WCU_getOuterWindowId(aWindow)
+  {
+    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+           getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+  },
+
+  /**
+   * Gets the window that has the given outer ID.
+   *
+   * @param integer aOuterId
+   * @param nsIDOMWindow [aHintWindow]
+   *        Optional, the window object used to QueryInterface to
+   *        nsIDOMWindowUtils. If this is not given,
+   *        Services.wm.getMostRecentWindow() is used.
+   * @return nsIDOMWindow|null
+   *         The window object with the given outer ID.
+   */
+  getWindowByOuterId: function WCU_getWindowByOuterId(aOuterId, aHintWindow)
+  {
+    let someWindow = aHintWindow || Services.wm.getMostRecentWindow(null);
+    let content = null;
+
+    if (someWindow) {
+      let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+                                   getInterface(Ci.nsIDOMWindowUtils);
+      content = windowUtils.getOuterWindowWithId(aOuterId);
+    }
+
+    return content;
+  },
+
+  /**
+   * Gets the window that has the given inner ID.
+   *
+   * @param integer aInnerId
+   * @param nsIDOMWindow [aHintWindow]
+   *        Optional, the window object used to QueryInterface to
+   *        nsIDOMWindowUtils. If this is not given,
+   *        Services.wm.getMostRecentWindow() is used.
+   * @return nsIDOMWindow|null
+   *         The window object with the given inner ID.
+   */
+  getWindowByInnerId: function WCU_getWindowByInnerId(aInnerId, aHintWindow)
+  {
+    let someWindow = aHintWindow || Services.wm.getMostRecentWindow(null);
+    let content = null;
+
+    if (someWindow) {
+      let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+                                   getInterface(Ci.nsIDOMWindowUtils);
+      content = windowUtils.getInnerWindowWithId(aInnerId);
+    }
+
+    return content;
+  },
+
+  /**
+   * Abbreviates the given source URL so that it can be displayed flush-right
+   * without being too distracting.
+   *
+   * @param string aSourceURL
+   *        The source URL to shorten.
+   * @return string
+   *         The abbreviated form of the source URL.
+   */
+  abbreviateSourceURL: function WCU_abbreviateSourceURL(aSourceURL)
+  {
+    // Remove any query parameters.
+    let hookIndex = aSourceURL.indexOf("?");
+    if (hookIndex > -1) {
+      aSourceURL = aSourceURL.substring(0, hookIndex);
+    }
+
+    // Remove a trailing "/".
+    if (aSourceURL[aSourceURL.length - 1] == "/") {
+      aSourceURL = aSourceURL.substring(0, aSourceURL.length - 1);
+    }
+
+    // Remove all but the last path component.
+    let slashIndex = aSourceURL.lastIndexOf("/");
+    if (slashIndex > -1) {
+      aSourceURL = aSourceURL.substring(slashIndex + 1);
+    }
+
+    return aSourceURL;
+  },
+
+  /**
+   * Format the jsterm execution result based on its type.
+   *
+   * @param mixed aResult
+   *        The evaluation result object you want displayed.
+   * @return string
+   *         The string that can be displayed.
+   */
+  formatResult: function WCU_formatResult(aResult)
+  {
+    let output = "";
+    let type = this.getResultType(aResult);
+
+    switch (type) {
+      case "string":
+        output = this.formatResultString(aResult);
+        break;
+      case "boolean":
+      case "date":
+      case "error":
+      case "number":
+      case "regexp":
+        output = aResult.toString();
+        break;
+      case "null":
+      case "undefined":
+        output = type;
+        break;
+      default:
+        if (aResult.toSource) {
+          try {
+            output = aResult.toSource();
+          } catch (ex) { }
+        }
+        if (!output || output == "({})") {
+          output = aResult.toString();
+        }
+        break;
+    }
+
+    return output;
+  },
+
+  /**
+   * Format a string for output.
+   *
+   * @param string aString
+   *        The string you want to display.
+   * @return string
+   *         The string that can be displayed.
+   */
+  formatResultString: function WCU_formatResultString(aString)
+  {
+    function isControlCode(c) {
+      // See http://en.wikipedia.org/wiki/C0_and_C1_control_codes
+      // C0 is 0x00-0x1F, C1 is 0x80-0x9F (inclusive).
+      // We also include DEL (U+007F) and NBSP (U+00A0), which are not strictly
+      // in C1 but border it.
+      return (c <= 0x1F) || (0x7F <= c && c <= 0xA0);
+    }
+
+    function replaceFn(aMatch, aType, aHex) {
+      // Leave control codes escaped, but unescape the rest of the characters.
+      let c = parseInt(aHex, 16);
+      return isControlCode(c) ? aMatch : String.fromCharCode(c);
+    }
+
+    let output = uneval(aString).replace(/\\(x)([0-9a-fA-F]{2})/g, replaceFn)
+                 .replace(/\\(u)([0-9a-fA-F]{4})/g, replaceFn);
+
+    return output;
+  },
+
+  /**
+   * Determine if an object can be inspected or not.
+   *
+   * @param mixed aObject
+   *        The object you want to check if it can be inspected.
+   * @return boolean
+   *         True if the object is inspectable or false otherwise.
+   */
+  isObjectInspectable: function WCU_isObjectInspectable(aObject)
+  {
+    let isEnumerable = false;
+
+    // Skip Iterators and Generators.
+    if (this.isIteratorOrGenerator(aObject)) {
+      return false;
+    }
+
+    try {
+      for (let p in aObject) {
+        isEnumerable = true;
+        break;
+      }
+    }
+    catch (ex) {
+      // Proxy objects can lack an enumerable method.
+    }
+
+    return isEnumerable && typeof(aObject) != "string";
+  },
+
+  /**
+   * Determine the type of the jsterm execution result.
+   *
+   * @param mixed aResult
+   *        The evaluation result object you want to check.
+   * @return string
+   *         Constructor name or type: string, number, boolean, regexp, date,
+   *         function, object, null, undefined...
+   */
+  getResultType: function WCU_getResultType(aResult)
+  {
+    let type = aResult === null ? "null" : typeof aResult;
+    if (type == "object" && aResult.constructor && aResult.constructor.name) {
+      type = aResult.constructor.name;
+    }
+
+    return type.toLowerCase();
+  },
+
+  /**
+   * Figures out the type of aObject and the string to display as the object
+   * value.
+   *
+   * @see TYPES
+   * @param object aObject
+   *        The object to operate on.
+   * @return object
+   *         An object of the form:
+   *         {
+   *           type: TYPES.OBJECT || TYPES.FUNCTION || ...
+   *           display: string for displaying the object
+   *         }
+   */
+  presentableValueFor: function WCU_presentableValueFor(aObject)
+  {
+    let type = this.getResultType(aObject);
+    let presentable;
+
+    switch (type) {
+      case "undefined":
+      case "null":
+        return {
+          type: TYPES.OTHER,
+          display: type
+        };
+
+      case "array":
+        return {
+          type: TYPES.ARRAY,
+          display: "Array"
+        };
+
+      case "string":
+        return {
+          type: TYPES.STRING,
+          display: "\"" + aObject + "\""
+        };
+
+      case "date":
+      case "regexp":
+      case "number":
+      case "boolean":
+        return {
+          type: TYPES.OTHER,
+          display: aObject.toString()
+        };
+
+      case "iterator":
+        return {
+          type: TYPES.ITERATOR,
+          display: "Iterator"
+        };
+
+      case "function":
+        presentable = aObject.toString();
+        return {
+          type: TYPES.FUNCTION,
+          display: presentable.substring(0, presentable.indexOf(')') + 1)
+        };
+
+      default:
+        presentable = String(aObject);
+        let m = /^\[object (\S+)\]/.exec(presentable);
+
+        try {
+          if (type == "object" && typeof aObject.next == "function" &&
+              m && m[1] == "Generator") {
+            return {
+              type: TYPES.GENERATOR,
+              display: m[1]
+            };
+          }
+        }
+        catch (ex) {
+          // window.history.next throws in the typeof check above.
+          return {
+            type: TYPES.OBJECT,
+            display: m ? m[1] : "Object"
+          };
+        }
+
+        if (type == "object" && typeof aObject.__iterator__ == "function") {
+          return {
+            type: TYPES.ITERATOR,
+            display: "Iterator"
+          };
+        }
+
+        return {
+          type: TYPES.OBJECT,
+          display: m ? m[1] : "Object"
+        };
+    }
+  },
+
+  /**
+   * Tells if the given function is native or not.
+   *
+   * @param function aFunction
+   *        The function you want to check if it is native or not.
+   * @return boolean
+   *         True if the given function is native, false otherwise.
+   */
+  isNativeFunction: function WCU_isNativeFunction(aFunction)
+  {
+    return typeof aFunction == "function" && !("prototype" in aFunction);
+  },
+
+  /**
+   * Tells if the given property of the provided object is a non-native getter or
+   * not.
+   *
+   * @param object aObject
+   *        The object that contains the property.
+   * @param string aProp
+   *        The property you want to check if it is a getter or not.
+   * @return boolean
+   *         True if the given property is a getter, false otherwise.
+   */
+  isNonNativeGetter: function WCU_isNonNativeGetter(aObject, aProp)
+  {
+    if (typeof aObject != "object") {
+      return false;
+    }
+    let desc;
+    while (aObject) {
+      try {
+        if (desc = Object.getOwnPropertyDescriptor(aObject, aProp)) {
+          break;
+        }
+      }
+      catch (ex) {
+        // Native getters throw here. See bug 520882.
+        if (ex.name == "NS_ERROR_XPC_BAD_CONVERT_JS" ||
+            ex.name == "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO") {
+          return false;
+        }
+        throw ex;
+      }
+      aObject = Object.getPrototypeOf(aObject);
+    }
+    if (desc && desc.get && !this.isNativeFunction(desc.get)) {
+      return true;
+    }
+    return false;
+  },
+
+  /**
+   * Get an array that describes the properties of the given object.
+   *
+   * @param object aObject
+   *        The object to get the properties from.
+   * @param object aObjectCache
+   *        Optional object cache where to store references to properties of
+   *        aObject that are inspectable. See this.isObjectInspectable().
+   * @return array
+   *         An array that describes each property from the given object. Each
+   *         array element is an object (a property descriptor). Each property
+   *         descriptor has the following properties:
+   *         - name - property name
+   *         - value - a presentable property value representation (see
+   *                   this.presentableValueFor())
+   *         - type - value type (see this.presentableValueFor())
+   *         - inspectable - tells if the property value holds further
+   *                         properties (see this.isObjectInspectable()).
+   *         - objectId - optional, available only if aObjectCache is given and
+   *         if |inspectable| is true. You can do
+   *         aObjectCache[propertyDescriptor.objectId] to get the actual object
+   *         referenced by the property of aObject.
+   */
+  namesAndValuesOf: function WCU_namesAndValuesOf(aObject, aObjectCache)
+  {
+    let pairs = [];
+    let value, presentable;
+
+    let isDOMDocument = aObject instanceof Ci.nsIDOMDocument;
+
+    for (let propName in aObject) {
+      // See bug 632275: skip deprecated width and height properties.
+      if (isDOMDocument && (propName == "width" || propName == "height")) {
+        continue;
+      }
+
+      // Also skip non-native getters.
+      if (this.isNonNativeGetter(aObject, propName)) {
+        value = "";
+        presentable = {type: TYPES.GETTER, display: "Getter"};
+      }
+      else {
+        value = aObject[propName];
+        presentable = this.presentableValueFor(value);
+      }
+
+      let pair = {};
+      pair.name = propName;
+      pair.value = presentable.display;
+      pair.inspectable = false;
+      pair.type = presentable.type;
+
+      switch (presentable.type) {
+        case TYPES.GETTER:
+        case TYPES.ITERATOR:
+        case TYPES.GENERATOR:
+        case TYPES.STRING:
+          break;
+        default:
+          try {
+            for (let p in value) {
+              pair.inspectable = true;
+              break;
+            }
+          }
+          catch (ex) { }
+          break;
+      }
+
+      // Store the inspectable object.
+      if (pair.inspectable && aObjectCache) {
+        pair.objectId = ++gObjectId;
+        aObjectCache[pair.objectId] = value;
+      }
+
+      pairs.push(pair);
+    }
+
+    pairs.sort(function(a, b)
+    {
+      // Convert the pair.name to a number for later sorting.
+      let aNumber = parseFloat(a.name);
+      let bNumber = parseFloat(b.name);
+
+      // Sort numbers.
+      if (!isNaN(aNumber) && isNaN(bNumber)) {
+        return -1;
+      }
+      else if (isNaN(aNumber) && !isNaN(bNumber)) {
+        return 1;
+      }
+      else if (!isNaN(aNumber) && !isNaN(bNumber)) {
+        return aNumber - bNumber;
+      }
+      // Sort string.
+      else if (a.name < b.name) {
+        return -1;
+      }
+      else if (a.name > b.name) {
+        return 1;
+      }
+      else {
+        return 0;
+      }
+    });
+
+    return pairs;
+  },
+
+  /**
+   * Check if the given object is an iterator or a generator.
+   *
+   * @param object aObject
+   *        The object you want to check.
+   * @return boolean
+   *         True if the given object is an iterator or a generator, otherwise
+   *         false is returned.
+   */
+  isIteratorOrGenerator: function WCU_isIteratorOrGenerator(aObject)
+  {
+    if (aObject === null) {
+      return false;
+    }
+
+    if (typeof aObject == "object") {
+      if (typeof aObject.__iterator__ == "function" ||
+          aObject.constructor && aObject.constructor.name == "Iterator") {
+        return true;
+      }
+
+      try {
+        let str = aObject.toString();
+        if (typeof aObject.next == "function" &&
+            str.indexOf("[object Generator") == 0) {
+          return true;
+        }
+      }
+      catch (ex) {
+        // window.history.next throws in the typeof check above.
+        return false;
+      }
+    }
+
+    return false;
+  },
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Localization
+//////////////////////////////////////////////////////////////////////////
+
+WebConsoleUtils.l10n = {
+  /**
+   * Generates a formatted timestamp string for displaying in console messages.
+   *
+   * @param integer [aMilliseconds]
+   *        Optional, allows you to specify the timestamp in milliseconds since
+   *        the UNIX epoch.
+   * @return string
+   *         The timestamp formatted for display.
+   */
+  timestampString: function WCU_l10n_timestampString(aMilliseconds)
+  {
+    let d = new Date(aMilliseconds ? aMilliseconds : null);
+    let hours = d.getHours(), minutes = d.getMinutes();
+    let seconds = d.getSeconds(), milliseconds = d.getMilliseconds();
+    let parameters = [hours, minutes, seconds, milliseconds];
+    return this.getFormatStr("timestampFormat", parameters);
+  },
+
+  /**
+   * Retrieve a localized string.
+   *
+   * @param string aName
+   *        The string name you want from the Web Console string bundle.
+   * @return string
+   *         The localized string.
+   */
+  getStr: function WCU_l10n_getStr(aName)
+  {
+    let result;
+    try {
+      result = this.stringBundle.GetStringFromName(aName);
+    }
+    catch (ex) {
+      Cu.reportError("Failed to get string: " + aName);
+      throw ex;
+    }
+    return result;
+  },
+
+  /**
+   * Retrieve a localized string formatted with values coming from the given
+   * array.
+   *
+   * @param string aName
+   *        The string name you want from the Web Console string bundle.
+   * @param array aArray
+   *        The array of values you want in the formatted string.
+   * @return string
+   *         The formatted local string.
+   */
+  getFormatStr: function WCU_l10n_getFormatStr(aName, aArray)
+  {
+    let result;
+    try {
+      result = this.stringBundle.formatStringFromName(aName, aArray, aArray.length);
+    }
+    catch (ex) {
+      Cu.reportError("Failed to format string: " + aName);
+      throw ex;
+    }
+    return result;
+  },
+};
+
+XPCOMUtils.defineLazyGetter(WebConsoleUtils.l10n, "stringBundle", function() {
+  return Services.strings.createBundle(STRINGS_URI);
+});
--- a/browser/devtools/webconsole/test/browser_cached_messages.js
+++ b/browser/devtools/webconsole/test/browser_cached_messages.js
@@ -39,45 +39,43 @@
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-webconsole-error-observer.html";
 
 function test()
 {
   waitForExplicitFinish();
 
   expectUncaughtException();
 
-  gBrowser.selectedTab = gBrowser.addTab(TEST_URI);
-
+  addTab(TEST_URI);
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     testOpenUI(true);
   }, true);
 }
 
 function testOpenUI(aTestReopen)
 {
   // test to see if the messages are
   // displayed when the console UI is opened
 
-  HUDService.activateHUDForContext(gBrowser.selectedTab);
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.getHudReferenceById(hudId);
+  openConsole(null, function(hud) {
+    testLogEntry(hud.outputNode, "log Bazzle",
+                 "Find a console log entry from before console UI is opened",
+                 false, null);
 
-  testLogEntry(hud.outputNode, "log Bazzle",
-               "Find a console log entry from before console UI is opened",
-               false, null);
+    testLogEntry(hud.outputNode, "error Bazzle",
+                 "Find a console error entry from before console UI is opened",
+                 false, null);
 
-  testLogEntry(hud.outputNode, "error Bazzle",
-               "Find a console error entry from before console UI is opened",
-               false, null);
+    testLogEntry(hud.outputNode, "bazBug611032", "Found the JavaScript error");
+    testLogEntry(hud.outputNode, "cssColorBug611032", "Found the CSS error");
 
-  testLogEntry(hud.outputNode, "bazBug611032", "Found the JavaScript error");
-  testLogEntry(hud.outputNode, "cssColorBug611032", "Found the CSS error");
-
-  HUDService.deactivateHUDForContext(gBrowser.selectedTab);
+    HUDService.deactivateHUDForContext(gBrowser.selectedTab);
 
-  if (aTestReopen) {
-    HUDService.deactivateHUDForContext(gBrowser.selectedTab);
-    executeSoon(testOpenUI);
-  } else {
-    executeSoon(finish);
-  }
+    if (aTestReopen) {
+      HUDService.deactivateHUDForContext(gBrowser.selectedTab);
+      executeSoon(testOpenUI);
+    }
+    else {
+      executeSoon(finish);
+    }
+  });
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_abbreviate_source_url.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_abbreviate_source_url.js
@@ -10,12 +10,12 @@ function test() {
   testAbbreviation("http://example.com/foo/bar/", "bar");
   testAbbreviation("http://example.com/foo.js?bar=1&baz=2", "foo.js");
   testAbbreviation("http://example.com/foo/?bar=1&baz=2", "foo");
 
   finishTest();
 }
 
 function testAbbreviation(aFullURL, aAbbreviatedURL) {
-  is(ConsoleUtils.abbreviateSourceURL(aFullURL), aAbbreviatedURL, aFullURL +
+  is(WebConsoleUtils.abbreviateSourceURL(aFullURL), aAbbreviatedURL, aFullURL +
      " is abbreviated to " + aAbbreviatedURL);
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js
@@ -46,43 +46,36 @@ function test() {
   expectUncaughtException();
   addTab(TEST_URI);
   browser.addEventListener("load", onLoad, true);
 }
 
 // see bug 580030: the error handler fails silently after page reload.
 // https://bugzilla.mozilla.org/show_bug.cgi?id=580030
 function onLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+  browser.removeEventListener(aEvent.type, onLoad, true);
 
-  openConsole();
-
-  browser.addEventListener("load", testErrorsAfterPageReload, true);
-  executeSoon(function() {
+  openConsole(null, function(hud) {
+    hud.jsterm.clearOutput();
+    browser.addEventListener("load", testErrorsAfterPageReload, true);
     content.location.reload();
   });
 }
 
 function testErrorsAfterPageReload(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+  browser.removeEventListener(aEvent.type, testErrorsAfterPageReload, true);
 
   // dispatch a click event to the button in the test page and listen for
   // errors.
 
   Services.console.registerListener(consoleObserver);
 
-  var button = content.document.querySelector("button").wrappedJSObject;
-  var clickEvent = content.document.createEvent("MouseEvents");
-  clickEvent.initMouseEvent("click", true, true,
-    content, 0, 0, 0, 0, 0, false, false,
-    false, false, 0, null);
-
-  executeSoon(function() {
-    button.dispatchEvent(clickEvent);
-  });
+  let button = content.document.querySelector("button").wrappedJSObject;
+  ok(button, "button found");
+  EventUtils.sendMouseEvent({type: "click"}, button, content);
 }
 
 var consoleObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   observe: function test_observe(aMessage)
   {
     // Ignore errors we don't care about.
@@ -90,16 +83,20 @@ var consoleObserver = {
       aMessage.category != "content javascript") {
       return;
     }
 
     Services.console.unregisterListener(this);
 
     let outputNode = HUDService.getHudByWindow(content).outputNode;
 
-    executeSoon(function() {
-      let msg = "Found the error message after page reload";
-      testLogEntry(outputNode, "fooBazBaz", msg);
-      finishTest();
+    waitForSuccess({
+      name: "error message after page reload",
+      validatorFn: function()
+      {
+        return outputNode.textContent.indexOf("fooBazBaz") > -1;
+      },
+      successFn: finishTest,
+      failureFn: finishTest,
     });
   }
 };
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js
@@ -16,17 +16,17 @@ function test() {
   addTab(TEST_URI);
   browser.addEventListener("DOMContentLoaded", testTimestamp, false);
 
   function testTimestamp()
   {
     browser.removeEventListener("DOMContentLoaded", testTimestamp, false);
     const TEST_TIMESTAMP = 12345678;
     let date = new Date(TEST_TIMESTAMP);
-    let localizedString = ConsoleUtils.timestampString(TEST_TIMESTAMP);
+    let localizedString = WebConsoleUtils.l10n.timestampString(TEST_TIMESTAMP);
     isnot(localizedString.indexOf(date.getHours()), -1, "the localized " +
           "timestamp contains the hours");
     isnot(localizedString.indexOf(date.getMinutes()), -1, "the localized " +
           "timestamp contains the minutes");
     isnot(localizedString.indexOf(date.getSeconds()), -1, "the localized " +
           "timestamp contains the seconds");
     isnot(localizedString.indexOf(date.getMilliseconds()), -1, "the localized " +
           "timestamp contains the milliseconds");
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js
@@ -47,24 +47,24 @@ function test() {
   expectUncaughtException();
   addTab(TEST_DUPLICATE_ERROR_URI);
   browser.addEventListener("DOMContentLoaded", testDuplicateErrors, false);
 }
 
 function testDuplicateErrors() {
   browser.removeEventListener("DOMContentLoaded", testDuplicateErrors,
                               false);
-  openConsole();
-
-  HUDService.getHudByWindow(content).jsterm.clearOutput();
+  openConsole(null, function(hud) {
+    hud.jsterm.clearOutput();
 
-  Services.console.registerListener(consoleObserver);
+    Services.console.registerListener(consoleObserver);
 
-  expectUncaughtException();
-  content.location.reload();
+    expectUncaughtException();
+    content.location.reload();
+  });
 }
 
 var consoleObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   observe: function (aMessage)
   {
     // we ignore errors we don't care about
@@ -72,23 +72,32 @@ var consoleObserver = {
       aMessage.category != "content javascript") {
       return;
     }
 
     Services.console.unregisterListener(this);
 
     outputNode = HUDService.getHudByWindow(content).outputNode;
 
-    executeSoon(function () {
-      var text = outputNode.textContent;
-      var error1pos = text.indexOf("fooDuplicateError1");
-      ok(error1pos > -1, "found fooDuplicateError1");
-      if (error1pos > -1) {
-        ok(text.indexOf("fooDuplicateError1", error1pos + 1) == -1,
-          "no duplicate for fooDuplicateError1");
-      }
+    waitForSuccess({
+      name: "fooDuplicateError1 error displayed",
+      validatorFn: function()
+      {
+        return outputNode.textContent.indexOf("fooDuplicateError1") > -1;
+      },
+      successFn: function()
+      {
+        let text = outputNode.textContent;
+        let error1pos = text.indexOf("fooDuplicateError1");
+        ok(error1pos > -1, "found fooDuplicateError1");
+        if (error1pos > -1) {
+          ok(text.indexOf("fooDuplicateError1", error1pos + 1) == -1,
+            "no duplicate for fooDuplicateError1");
+        }
 
-      findLogEntry("test-duplicate-error.html");
+        findLogEntry("test-duplicate-error.html");
 
-      finishTest();
+        finishTest();
+      },
+      failureFn: finishTest,
     });
   }
 };
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585237_line_limit.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585237_line_limit.js
@@ -7,60 +7,118 @@
  *  Patrick Walton <pcwalton@mozilla.com>
  *  Mihai Șucan <mihai.sucan@gmail.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 // Tests that the Web Console limits the number of lines displayed according to
 // the user's preferences.
 
-const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
+const TEST_URI = "data:text/html;charset=utf8,test for bug 585237";
+let hud, testDriver;
 
 function test() {
   addTab(TEST_URI);
-  browser.addEventListener("DOMContentLoaded", testLineLimit,
-                                            false);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, function(aHud) {
+      hud = aHud;
+      testDriver = testGen();
+      testNext();
+    });
+  }, true);
 }
 
-function testLineLimit() {
-  browser.removeEventListener("DOMContentLoaded",testLineLimit, false);
+function testNext() {
+  testDriver.next();
+}
 
-  openConsole();
+function testGen() {
+  let console = content.console;
+  outputNode = hud.outputNode;
 
-  let console = browser.contentWindow.wrappedJSObject.console;
-  outputNode = HUDService.getHudByWindow(content).outputNode;
+  hud.jsterm.clearOutput();
 
   let prefBranch = Services.prefs.getBranch("devtools.hud.loglimit.");
   prefBranch.setIntPref("console", 20);
 
-  for (let i = 0; i < 20; i++) {
+  for (let i = 0; i < 30; i++) {
     console.log("foo #" + i); // must change message to prevent repeats
   }
 
+  waitForSuccess({
+    name: "20 console.log messages displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("foo #29") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
   is(countMessageNodes(), 20, "there are 20 message nodes in the output " +
      "when the log limit is set to 20");
 
-  console.log("bar");
+  console.log("bar bug585237");
+
+  waitForSuccess({
+    name: "another console.log message displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bar bug585237") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
   is(countMessageNodes(), 20, "there are still 20 message nodes in the " +
      "output when adding one more");
 
   prefBranch.setIntPref("console", 30);
   for (let i = 0; i < 20; i++) {
     console.log("boo #" + i); // must change message to prevent repeats
   }
 
+  waitForSuccess({
+    name: "another 20 console.log message displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("boo #19") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
   is(countMessageNodes(), 30, "there are 30 message nodes in the output " +
      "when the log limit is set to 30");
 
   prefBranch.setIntPref("console", 0);
   console.log("baz");
-  is(countMessageNodes(), 0, "there are no message nodes in the output when " +
-     "the log limit is set to zero");
+
+  waitForSuccess({
+    name: "clear output",
+    validatorFn: function()
+    {
+      return countMessageNodes() == 0;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
 
   prefBranch.clearUserPref("console");
-  prefBranch = console = outputNode = null;
+  hud = testDriver = prefBranch = console = outputNode = null;
   finishTest();
+
+  yield;
 }
 
 function countMessageNodes() {
   return outputNode.querySelectorAll(".hud-msg-node").length;
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585956_console_trace.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585956_console_trace.js
@@ -41,20 +41,20 @@ const TEST_URI = "http://example.com/bro
 function test() {
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
 function tabLoaded() {
   browser.removeEventListener("load", tabLoaded, true);
 
-  openConsole();
-
-  browser.addEventListener("load", tabReloaded, true);
-  content.location.reload();
+  openConsole(null, function() {
+    browser.addEventListener("load", tabReloaded, true);
+    content.location.reload();
+  });
 }
 
 function tabReloaded() {
   browser.removeEventListener("load", tabReloaded, true);
 
   // The expected stack trace object.
   let stacktrace = [
     { filename: TEST_URI, lineNumber: 9, functionName: null, language: 2 },
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_587617_output_copy.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_587617_output_copy.js
@@ -7,55 +7,59 @@
  *  Patrick Walton <pcwalton@mozilla.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
 
 function test() {
   addTab(TEST_URI);
-  browser.addEventListener("load", tabLoaded, true);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
 }
 
-function tabLoaded() {
-  browser.removeEventListener("load", tabLoaded, true);
-  openConsole();
-
+function consoleOpened(HUD) {
   // See bugs 574036, 586386 and 587617.
-
-  let HUD = HUDService.getHudByWindow(content);
   outputNode = HUD.outputNode;
   let selection = getSelection();
   let jstermInput = HUD.jsterm.inputNode;
   let console = content.wrappedJSObject.console;
   let contentSelection = content.wrappedJSObject.getSelection();
 
-  let make_selection = function () {
-    let controller =
-      top.document.commandDispatcher.
-      getControllerForCommand("cmd_copy");
-    is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled");
+  HUD.jsterm.clearOutput();
 
-    console.log("Hello world!");
+  let controller = top.document.commandDispatcher.
+                   getControllerForCommand("cmd_copy");
+  is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled");
 
-    outputNode.selectedIndex = 0;
-    outputNode.focus();
+  console.log("Hello world! bug587617");
 
-    goUpdateCommand("cmd_copy");
-
-    controller = top.document.commandDispatcher.
-      getControllerForCommand("cmd_copy");
-    is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+  waitForSuccess({
+    name: "console log 'Hello world!' message",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bug587617") > -1;
+    },
+    successFn: function()
+    {
+      outputNode.selectedIndex = 0;
+      outputNode.focus();
 
-    let selectedNode = outputNode.getItemAtIndex(0);
-    waitForClipboard(getExpectedClipboardText(selectedNode), clipboardSetup,
-                     testContextMenuCopy, testContextMenuCopy);
-  };
-
-  make_selection();
+      goUpdateCommand("cmd_copy");
+      controller = top.document.commandDispatcher.
+        getControllerForCommand("cmd_copy");
+      is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+      let selectedNode = outputNode.getItemAtIndex(0);
+      waitForClipboard(getExpectedClipboardText(selectedNode), clipboardSetup,
+                       testContextMenuCopy, testContextMenuCopy);
+    },
+    failureFn: finishTest,
+  });
 }
 
 // Test that the context menu "Copy" (which has a different code path) works
 // properly as well.
 function testContextMenuCopy() {
   let contextMenuId = outputNode.getAttribute("context");
   let contextMenu = document.getElementById(contextMenuId);
   ok(contextMenu, "the output node has a context menu");
@@ -69,16 +73,16 @@ function testContextMenuCopy() {
   copyItem.dispatchEvent(commandEvent);
 
   let selectedNode = outputNode.getItemAtIndex(0);
   waitForClipboard(getExpectedClipboardText(selectedNode), clipboardSetup,
     finishTest, finishTest);
 }
 
 function getExpectedClipboardText(aItem) {
-  return "[" + ConsoleUtils.timestampString(aItem.timestamp) + "] " +
+  return "[" + WebConsoleUtils.l10n.timestampString(aItem.timestamp) + "] " +
          aItem.clipboardText;
 }
 
 function clipboardSetup() {
   goDoCommand("cmd_copy");
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js
@@ -39,51 +39,46 @@
  * ***** END LICENSE BLOCK ***** */
 
 // Tests that adding text to one of the output labels doesn't cause errors.
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
 
 function test() {
   addTab(TEST_URI);
-  browser.addEventListener("DOMContentLoaded", testTextNodeInsertion,
-                           false);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, testTextNodeInsertion);
+  }, true);
 }
 
 // Test for bug 588730: Adding a text node to an existing label element causes
 // warnings
-function testTextNodeInsertion() {
-  browser.removeEventListener("DOMContentLoaded", testTextNodeInsertion,
-                              false);
-  openConsole();
-
-  let outputNode = HUDService.getHudByWindow(content).outputNode;
+function testTextNodeInsertion(hud) {
+  let outputNode = hud.outputNode;
 
   let label = document.createElementNS(
     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "label");
   outputNode.appendChild(label);
 
   let error = false;
   let listener = {
     observe: function(aMessage) {
       let messageText = aMessage.message;
       if (messageText.indexOf("JavaScript Warning") !== -1) {
         error = true;
       }
     }
   };
 
-  let nsIConsoleServiceClass = Cc["@mozilla.org/consoleservice;1"];
-  let nsIConsoleService =
-    nsIConsoleServiceClass.getService(Ci.nsIConsoleService);
-  nsIConsoleService.registerListener(listener);
+  Services.console.registerListener(listener);
 
   // This shouldn't fail.
   label.appendChild(document.createTextNode("foo"));
 
   executeSoon(function() {
-    nsIConsoleService.unregisterListener(listener);
+    Services.console.unregisterListener(listener);
     ok(!error, "no error when adding text nodes as children of labels");
 
     finishTest();
   });
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js
@@ -45,68 +45,51 @@ const TEST_DUMMY_URI = "http://example.c
 let tab1, tab2;
 
 function test() {
   addTab(TEST_URI);
   tab1 = tab;
   browser.addEventListener("load", tab1Loaded, true);
 }
 
-/**
- * Check if a log entry exists in the HUD output node.
- *
- * @param {Element} aOutputNode the HUD output node.
- * @param {string} aMatchString the string you want to check if it exists in the
- * output node.
- * @param {boolean} [aOnlyVisible=false] find only messages that are visible,
- * not hidden by the filter.
- * @param {boolean} [aFailIfFound=false] fail the test if the string is found in
- * the output node.
- */
-
 function tab1Loaded(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
-  browser.contentWindow.wrappedJSObject.console.log("FOO");
-  try {
-    openConsole();
-  }
-  catch (ex) {
-    log(ex);
-    log(ex.stack);
-  }
-
-  tab2 = gBrowser.addTab(TEST_DUMMY_URI);
-  gBrowser.selectedTab = tab2;
-  gBrowser.selectedBrowser.addEventListener("load", tab2Loaded, true);
+  browser.removeEventListener(aEvent.type, tab1Loaded, true);
+  content.console.log("FOO");
+  openConsole(null, function() {
+    tab2 = gBrowser.addTab(TEST_DUMMY_URI);
+    gBrowser.selectedTab = tab2;
+    gBrowser.selectedBrowser.addEventListener("load", tab2Loaded, true);
+  });
 }
 
 function tab2Loaded(aEvent) {
-  tab2.linkedBrowser.removeEventListener(aEvent.type, arguments.callee, true);
+  tab2.linkedBrowser.removeEventListener(aEvent.type, tab2Loaded, true);
 
-  HUDService.activateHUDForContext(gBrowser.selectedTab);
-
-  tab1.linkedBrowser.addEventListener("load", tab1Reloaded, true);
-  tab1.linkedBrowser.contentWindow.location.reload();
+  openConsole(gBrowser.selectedTab, function() {
+    tab1.linkedBrowser.addEventListener("load", tab1Reloaded, true);
+    tab1.linkedBrowser.contentWindow.location.reload();
+  });
 }
 
 function tab1Reloaded(aEvent) {
-  tab1.linkedBrowser.removeEventListener(aEvent.type, arguments.callee, true);
+  tab1.linkedBrowser.removeEventListener(aEvent.type, tab1Reloaded, true);
 
   let hud1 = HUDService.getHudByWindow(tab1.linkedBrowser.contentWindow);
   let outputNode1 = hud1.outputNode;
 
   let msg = "Found the iframe network request in tab1";
   testLogEntry(outputNode1, TEST_IFRAME_URI, msg, true);
 
   let hud2 = HUDService.getHudByWindow(tab2.linkedBrowser.contentWindow);
   let outputNode2 = hud2.outputNode;
 
   isnot(outputNode1, outputNode2,
         "the two HUD outputNodes must be different");
 
   msg = "Didn't find the iframe network request in tab2";
   testLogEntry(outputNode2, TEST_IFRAME_URI, msg, true, true);
 
-  HUDService.deactivateHUDForContext(tab2);
-  gBrowser.removeTab(tab2);
-
-  finishTest();
+  closeConsole(tab2, function() {
+    gBrowser.removeTab(tab2);
+    tab1 = tab2 = null;
+    executeSoon(finishTest);
+  });
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js
@@ -23,64 +23,87 @@ function test() {
   addTabs(win1);
 
   // Open a new window.
   win2 = OpenBrowserWindow();
   win2.addEventListener("load", onWindowLoad, true);
 }
 
 function onWindowLoad(aEvent) {
-  win2.removeEventListener(aEvent.type, arguments.callee, true);
+  win2.removeEventListener(aEvent.type, onWindowLoad, true);
 
   // Add two tabs in the new window.
   addTabs(win2);
 }
 
 function addTabs(aWindow) {
   for (let i = 0; i < 2; i++) {
     let tab = aWindow.gBrowser.addTab(TEST_URI);
     openTabs.push(tab);
 
-    tab.linkedBrowser.addEventListener("load", function(aEvent) {
-      tab.linkedBrowser.removeEventListener(aEvent.type, arguments.callee,
-        true);
+    tab.linkedBrowser.addEventListener("load", function onLoad(aEvent) {
+      tab.linkedBrowser.removeEventListener(aEvent.type, onLoad, true);
 
       loadedTabCount++;
       if (loadedTabCount >= 4) {
-        executeSoon(performTest);
+        executeSoon(openConsoles);
       }
     }, true);
   }
 }
 
-function performTest() {
+function openConsoles() {
   // open the Web Console for each of the four tabs and log a message.
+  let consolesOpen = 0;
   for (let i = 0; i < openTabs.length; i++) {
     let tab = openTabs[i];
-    HUDService.activateHUDForContext(tab);
-    let hudId = HUDService.getHudIdByWindow(tab.linkedBrowser.contentWindow);
-    ok(hudId, "HUD is open for tab " + i);
-    let HUD = HUDService.hudReferences[hudId];
-    HUD.console.log("message for tab " + i);
+    openConsole(tab, function(index, hud) {
+      ok(hud, "HUD is open for tab " + index);
+      hud.console.log("message for tab " + index);
+      consolesOpen++;
+    }.bind(null, i));
   }
 
-  let displays = Object.keys(HUDService.hudReferences);
-  is(displays.length, 4, "four displays found");
+  waitForSuccess({
+    name: "4 web consoles opened",
+    validatorFn: function()
+    {
+      return consolesOpen == 4;
+    },
+    successFn: closeConsoles,
+    failureFn: closeConsoles,
+  });
+}
+
+function closeConsoles() {
+  let consolesClosed = 0;
+
+  function onWebConsoleClose(aSubject, aTopic) {
+    if (aTopic == "web-console-destroyed") {
+      consolesClosed++;
+    }
+  }
+
+  Services.obs.addObserver(onWebConsoleClose, "web-console-destroyed", false);
 
   win2.close();
 
-  executeSoon(function() {
-    win1.gBrowser.removeTab(openTabs[0]);
-    win1.gBrowser.removeTab(openTabs[1]);
+  win1.gBrowser.removeTab(openTabs[0]);
+  win1.gBrowser.removeTab(openTabs[1]);
+
+  openTabs = win1 = win2 = null;
 
-    executeSoon(function() {
-      displays = Object.keys(HUDService.hudReferences);
-      is(displays.length, 0, "no displays found");
-      ok(!HUDService.storage, "no storage found");
-      ok(!HUDService.httpObserver, "no httpObserver found");
+  function onTimeout() {
+    Services.obs.removeObserver(onWebConsoleClose, "web-console-destroyed");
+    executeSoon(finishTest);
+  }
 
-      displays = openTabs = win1 = win2 = null;
-
-      finishTest();
-    });
+  waitForSuccess({
+    name: "4 web consoles closed",
+    validatorFn: function()
+    {
+      return consolesClosed == 4;
+    },
+    successFn: onTimeout,
+    failureFn: onTimeout,
   });
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
@@ -84,116 +84,119 @@ const TESTS = [
     category: "CSS Parser",
     matchString: "foobarCanvasCssParser",
   },
   { // #14
     file: "test-bug-595934-image.html",
     category: "Image",
     matchString: "corrupt",
   },
-  /* Disabled until bug 675221 lands.
-  { // #7
+  { // #15
     file: "test-bug-595934-workers.html",
     category: "Web Worker",
     matchString: "fooBarWorker",
-  },*/
+    expectError: true,
+  },
 ];
 
 let pos = -1;
 
 let foundCategory = false;
 let foundText = false;
 let output = null;
 let jsterm = null;
+let testEnded = false;
 
 let TestObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   observe: function test_observe(aSubject)
   {
-    if (!(aSubject instanceof Ci.nsIScriptError)) {
+    if (testEnded || !(aSubject instanceof Ci.nsIScriptError)) {
       return;
     }
 
     is(aSubject.category, TESTS[pos].category,
       "test #" + pos + ": error category '" + TESTS[pos].category + "'");
 
     if (aSubject.category == TESTS[pos].category) {
       foundCategory = true;
       if (foundText) {
         executeSoon(testNext);
       }
     }
     else {
       ok(false, aSubject.sourceName + ':' + aSubject.lineNumber + '; ' +
                 aSubject.errorMessage);
-      executeSoon(finish);
+      testEnded = true;
+      executeSoon(finishTest);
     }
   }
 };
 
-function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
-
-  openConsole();
-
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
+function consoleOpened(hud) {
   output = hud.outputNode;
   output.addEventListener("DOMNodeInserted", onDOMNodeInserted, false);
   jsterm = hud.jsterm;
 
   Services.console.registerListener(TestObserver);
 
+  registerCleanupFunction(testEnd);
+
   executeSoon(testNext);
 }
 
 function testNext() {
   jsterm.clearOutput();
   foundCategory = false;
   foundText = false;
 
   pos++;
   if (pos < TESTS.length) {
     let test = TESTS[pos];
     let testLocation = TESTS_PATH + test.file;
     if (test.onload) {
-      browser.addEventListener("load", function(aEvent) {
+      browser.addEventListener("load", function onLoad(aEvent) {
         if (content.location.href == testLocation) {
-          browser.removeEventListener(aEvent.type, arguments.callee, true);
+          browser.removeEventListener(aEvent.type, onLoad, true);
           test.onload(aEvent);
         }
       }, true);
     }
 
+    if (test.expectError) {
+      expectUncaughtException();
+    }
+
     content.location = testLocation;
   }
   else {
-    executeSoon(finish);
+    testEnded = true;
+    executeSoon(finishTest);
   }
 }
 
 function testEnd() {
   Services.console.unregisterListener(TestObserver);
   output.removeEventListener("DOMNodeInserted", onDOMNodeInserted, false);
-  output = jsterm = null;
-  finishTest();
+  TestObserver = output = jsterm = null;
 }
 
 function onDOMNodeInserted(aEvent) {
   let textContent = output.textContent;
   foundText = textContent.indexOf(TESTS[pos].matchString) > -1;
   if (foundText) {
     ok(foundText, "test #" + pos + ": message found '" + TESTS[pos].matchString + "'");
   }
 
   if (foundCategory) {
     executeSoon(testNext);
   }
 }
 
 function test() {
-  registerCleanupFunction(testEnd);
-
   addTab("data:text/html;charset=utf-8,Web Console test for bug 595934 - message categories coverage.");
-  browser.addEventListener("load", tabLoad, true);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js
@@ -16,30 +16,24 @@ function test() {
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
 function tabLoaded(aEvent) {
   browser.removeEventListener("load", tabLoaded, true);
   openConsole();
 
-  browser.addEventListener("load", contentLoaded, true);
-  content.location.reload();
-}
+  let button = content.document.querySelector("button");
+  let outputNode = HUDService.getHudByWindow(content).outputNode;
 
-function contentLoaded(aEvent) {
-  browser.removeEventListener("load", contentLoaded, true);
-
-  let button = content.document.querySelector("button");
   expectUncaughtException();
   EventUtils.sendMouseEvent({ type: "click" }, button, content);
-  executeSoon(buttonClicked);
+
+  waitForSuccess({
+    name: "external script error message",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bogus is not defined") > -1;
+    },
+    successFn: finishTest,
+    failureFn: finishTest,
+  });
 }
-
-function buttonClicked() {
-  let outputNode = HUDService.getHudByWindow(content).outputNode;
-
-  let msg = "the error from the external script was logged";
-  testLogEntry(outputNode, "bogus", msg);
-
-  finishTest();
-}
-
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js
@@ -8,55 +8,53 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-597756-reopen-closed-tab.html";
 
 let newTabIsOpen = false;
 
 function tabLoaded(aEvent) {
-  gBrowser.selectedBrowser.removeEventListener(aEvent.type, arguments.callee, true);
-
-  HUDService.activateHUDForContext(gBrowser.selectedTab);
+  gBrowser.selectedBrowser.removeEventListener(aEvent.type, tabLoaded, true);
 
-  gBrowser.selectedBrowser.addEventListener("load", tabReloaded, true);
-  expectUncaughtException();
-  content.location.reload();
+  openConsole(gBrowser.selectedTab, function() {
+    gBrowser.selectedBrowser.addEventListener("load", tabReloaded, true);
+    expectUncaughtException();
+    content.location.reload();
+  });
 }
 
 function tabReloaded(aEvent) {
-  gBrowser.selectedBrowser.removeEventListener(aEvent.type, arguments.callee, true);
+  gBrowser.selectedBrowser.removeEventListener(aEvent.type, tabReloaded, true);
 
   let hudId = HUDService.getHudIdByWindow(content);
   let HUD = HUDService.hudReferences[hudId];
   ok(HUD, "Web Console is open");
 
   isnot(HUD.outputNode.textContent.indexOf("fooBug597756_error"), -1,
     "error message must be in console output");
 
   executeSoon(function() {
     if (newTabIsOpen) {
-      testEnd();
+      executeSoon(finishTest);
       return;
     }
 
-    let newTab = gBrowser.addTab();
-    gBrowser.removeCurrentTab();
-    gBrowser.selectedTab = newTab;
+    closeConsole(gBrowser.selectedTab, function() {
+      gBrowser.removeCurrentTab();
+
+      let newTab = gBrowser.addTab();
+      gBrowser.selectedTab = newTab;
 
-    newTabIsOpen = true;
-    gBrowser.selectedBrowser.addEventListener("load", tabLoaded, true);
-    expectUncaughtException();
-    content.location = TEST_URI;
+      newTabIsOpen = true;
+      gBrowser.selectedBrowser.addEventListener("load", tabLoaded, true);
+      expectUncaughtException();
+      content.location = TEST_URI;
+    });
   });
 }
 
-function testEnd() {
-  gBrowser.removeCurrentTab();
-  executeSoon(finishTest);
-}
-
 function test() {
   expectUncaughtException();
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_598357_jsterm_output.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_598357_jsterm_output.js
@@ -87,96 +87,122 @@ let inputValues = [
   [true, "({a:'b', c:'d', e:1, f:'2'})", '({a:"b", c:"d", e:1, f:"2"})',
     "[object Object",
     '({a:"b", c:"d", e:1, f:"2"})'],
 ];
 
 let eventHandlers = [];
 let popupShown = [];
 let HUD;
+let testDriver;
 
 function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+  browser.removeEventListener(aEvent.type, tabLoad, true);
 
   waitForFocus(function () {
-    openConsole();
-
-    let hudId = HUDService.getHudIdByWindow(content);
-    HUD = HUDService.hudReferences[hudId];
-
-    executeSoon(testNext);
+    openConsole(null, function(aHud) {
+      HUD = aHud;
+      testNext();
+    });
   }, content);
 }
 
+function subtestNext() {
+  testDriver.next();
+}
+
 function testNext() {
-  let cpos = ++pos;
-  if (cpos == inputValues.length) {
-    if (popupShown.length == inputValues.length) {
-      executeSoon(testEnd);
-    }
+  pos++;
+  if (pos == inputValues.length) {
+    testEnd();
     return;
   }
 
+  testDriver = testGen();
+  testDriver.next();
+}
+
+function testGen() {
+  let cpos = pos;
+
   let showsPropertyPanel = inputValues[cpos][0];
   let inputValue = inputValues[cpos][1];
   let expectedOutput = inputValues[cpos][2];
 
   let printOutput = inputValues[cpos].length >= 4 ?
     inputValues[cpos][3] : expectedOutput;
 
   let consoleOutput = inputValues[cpos].length >= 5 ?
     inputValues[cpos][4] : printOutput;
 
   let consoleTest = inputValues[cpos][5] || inputValue;
 
   HUD.jsterm.clearOutput();
 
+  // Test the console.log() output.
+
   // Ugly but it does the job.
   with (content) {
     eval("HUD.console.log(" + consoleTest + ")");
   }
 
-  let outputItem = HUD.outputNode.
-    querySelector(".hud-log:last-child");
+  waitForSuccess({
+    name: "console.log message for test #" + cpos,
+    validatorFn: function()
+    {
+      return HUD.outputNode.querySelector(".hud-log");
+    },
+    successFn: subtestNext,
+    failureFn: testNext,
+  });
+
+  yield;
+
+  let outputItem = HUD.outputNode.querySelector(".hud-log:last-child");
   ok(outputItem,
     "found the window.console output line for inputValues[" + cpos + "]");
   ok(outputItem.textContent.indexOf(consoleOutput) > -1,
     "console API output is correct for inputValues[" + cpos + "]");
 
   HUD.jsterm.clearOutput();
 
+  // Test jsterm print() output.
+
   HUD.jsterm.setInputValue("print(" + inputValue + ")");
   HUD.jsterm.execute();
 
   outputItem = HUD.outputNode.querySelector(".webconsole-msg-output:" +
                                             "last-child");
   ok(outputItem,
     "found the jsterm print() output line for inputValues[" + cpos + "]");
   ok(outputItem.textContent.indexOf(printOutput) > -1,
     "jsterm print() output is correct for inputValues[" + cpos + "]");
 
+  // Test jsterm execution output.
+
   let eventHandlerID = eventHandlers.length + 1;
 
   let propertyPanelShown = function(aEvent) {
     let label = aEvent.target.getAttribute("label");
     if (!label || label.indexOf(inputValue) == -1) {
       return;
     }
 
-    document.removeEventListener(aEvent.type, arguments.callee, false);
+    document.removeEventListener(aEvent.type, propertyPanelShown, false);
     eventHandlers[eventHandlerID] = null;
 
     ok(showsPropertyPanel,
       "the property panel shown for inputValues[" + cpos + "]");
 
     aEvent.target.hidePopup();
 
     popupShown[cpos] = true;
-    if (popupShown.length == inputValues.length) {
-      executeSoon(testEnd);
+
+    if (showsPropertyPanel) {
+      subtestNext();
     }
   };
 
   document.addEventListener("popupshown", propertyPanelShown, false);
 
   eventHandlers.push(propertyPanelShown);
 
   HUD.jsterm.clearOutput();
@@ -187,25 +213,28 @@ function testNext() {
                                             "last-child");
   ok(outputItem, "found the jsterm output line for inputValues[" + cpos + "]");
   ok(outputItem.textContent.indexOf(expectedOutput) > -1,
     "jsterm output is correct for inputValues[" + cpos + "]");
 
   let messageBody = outputItem.querySelector(".webconsole-msg-body");
   ok(messageBody, "we have the message body for inputValues[" + cpos + "]");
 
-  messageBody.addEventListener("click", function(aEvent) {
-    this.removeEventListener(aEvent.type, arguments.callee, false);
-    executeSoon(testNext);
-  }, false);
-
   // Send the mousedown, mouseup and click events to check if the property
   // panel opens.
   EventUtils.sendMouseEvent({ type: "mousedown" }, messageBody, window);
   EventUtils.sendMouseEvent({ type: "click" }, messageBody, window);
+
+  if (showsPropertyPanel) {
+    yield; // wait for the panel to open if we need to.
+  }
+
+  testNext();
+
+  yield;
 }
 
 function testEnd() {
   if (testEnded) {
     return;
   }
 
   testEnded = true;
@@ -217,16 +246,17 @@ function testEnd() {
   }
 
   for (let i = 0; i < inputValues.length; i++) {
     if (inputValues[i][0] && !popupShown[i]) {
       ok(false, "the property panel failed to show for inputValues[" + i + "]");
     }
   }
 
+  testDriver = null;
   executeSoon(finishTest);
 }
 
 function test() {
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoad, true);
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_601352_scroll.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_601352_scroll.js
@@ -3,23 +3,18 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Contributor(s):
  *  Mihai Șucan <mihai.sucan@gmail.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
-function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
-
-  openConsole();
-
-  let hudId = HUDService.getHudIdByWindow(content);
-  let HUD = HUDService.hudReferences[hudId];
+function consoleOpened(HUD) {
+  HUD.jsterm.clearOutput();
 
   let longMessage = "";
   for (let i = 0; i < 50; i++) {
     longMessage += "LongNonwrappingMessage";
   }
 
   for (let i = 0; i < 50; i++) {
     HUD.console.log("test message " + i);
@@ -28,17 +23,17 @@ function tabLoad(aEvent) {
   HUD.console.log(longMessage);
 
   for (let i = 0; i < 50; i++) {
     HUD.console.log("test message " + i);
   }
 
   HUD.jsterm.execute("1+1");
 
-  executeSoon(function() {
+  function performTest() {
     let scrollBox = HUD.outputNode.scrollBoxObject.element;
     isnot(scrollBox.scrollTop, 0, "scroll location is not at the top");
 
     let node = HUD.outputNode.getItemAtIndex(HUD.outputNode.itemCount - 1);
     let rectNode = node.getBoundingClientRect();
     let rectOutput = HUD.outputNode.getBoundingClientRect();
 
     // Visible scroll viewport.
@@ -49,16 +44,29 @@ function tabLoad(aEvent) {
 
     // Bottom position of the last message node, relative to the outputNode.
     let bottom = rectNode.bottom - rectOutput.top;
 
     ok(top >= 0 && Math.floor(bottom) <= height + 1,
        "last message is visible");
 
     finishTest();
+  };
+
+  waitForSuccess({
+    name: "console output displayed",
+    validatorFn: function()
+    {
+      return HUD.outputNode.itemCount == 103;
+    },
+    successFn: performTest,
+    failureFn: finishTest,
   });
 }
 
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 601352");
-  browser.addEventListener("load", tabLoad, true);
+  browser.addEventListener("load", function tabLoad(aEvent) {
+    browser.removeEventListener(aEvent.type, tabLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_602572_log_bodies_checkbox.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_602572_log_bodies_checkbox.js
@@ -11,31 +11,30 @@
 let menuitems = [], menupopups = [], huds = [], tabs = [];
 
 function test()
 {
   // open tab 1
   addTab("data:text/html;charset=utf-8,Web Console test for bug 602572: log bodies checkbox. tab 1");
   tabs.push(tab);
 
-  browser.addEventListener("load", function(aEvent) {
-    browser.removeEventListener(aEvent.type, arguments.callee, true);
-
-    openConsole();
+  browser.addEventListener("load", function onLoad1(aEvent) {
+    browser.removeEventListener(aEvent.type, onLoad1, true);
 
-    // open tab 2
-    addTab("data:text/html;charset=utf-8,Web Console test for bug 602572: log bodies checkbox. tab 2");
-    tabs.push(tab);
+    openConsole(null, function() {
+      // open tab 2
+      addTab("data:text/html;charset=utf-8,Web Console test for bug 602572: log bodies checkbox. tab 2");
+      tabs.push(tab);
 
-    browser.addEventListener("load", function(aEvent) {
-      browser.removeEventListener(aEvent.type, arguments.callee, true);
+      browser.addEventListener("load", function onLoad2(aEvent) {
+        browser.removeEventListener(aEvent.type, onLoad2, true);
 
-      openConsole();
-      executeSoon(startTest);
-    }, true);
+        openConsole(null, startTest);
+      }, true);
+    });
   }, true);
 }
 
 function startTest()
 {
   // Find the relevant elements in the Web Console of tab 2.
   let win2 = tabs[1].linkedBrowser.contentWindow;
   let hudId2 = HUDService.getHudIdByWindow(win2);
@@ -47,44 +46,44 @@ function startTest()
 
   // Open the context menu from tab 2.
   menupopups[1].addEventListener("popupshown", onpopupshown2, false);
   menupopups[1].openPopup(huds[1].outputNode, "overlap", 10, 10, true, false);
 }
 
 function onpopupshown2(aEvent)
 {
-  menupopups[1].removeEventListener(aEvent.type, arguments.callee, false);
+  menupopups[1].removeEventListener(aEvent.type, onpopupshown2, false);
 
   // By default bodies are not logged.
   isnot(menuitems[1].getAttribute("checked"), "true",
         "menuitems[1] is not checked");
 
   ok(!HUDService.saveRequestAndResponseBodies, "bodies are not logged");
 
   // Enable body logging.
   HUDService.saveRequestAndResponseBodies = true;
 
-  menupopups[1].addEventListener("popuphidden", function(aEvent) {
-    menupopups[1].removeEventListener(aEvent.type, arguments.callee, false);
+  menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
+    menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
 
     // Reopen the context menu.
     menupopups[1].addEventListener("popupshown", onpopupshown2b, false);
     menupopups[1].openPopup(huds[1].outputNode, "overlap", 11, 11, true, false);
   }, false);
   menupopups[1].hidePopup();
 }
 
 function onpopupshown2b(aEvent)
 {
-  menupopups[1].removeEventListener(aEvent.type, arguments.callee, false);
+  menupopups[1].removeEventListener(aEvent.type, onpopupshown2b, false);
   is(menuitems[1].getAttribute("checked"), "true", "menuitems[1] is checked");
 
-  menupopups[1].addEventListener("popuphidden", function(aEvent) {
-    menupopups[1].removeEventListener(aEvent.type, arguments.callee, false);
+  menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
+    menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
 
     // Switch to tab 1 and open the Web Console context menu from there.
     gBrowser.selectedTab = tabs[0];
     waitForFocus(function() {
       // Find the relevant elements in the Web Console of tab 1.
       let win1 = tabs[0].linkedBrowser.contentWindow;
       let hudId1 = HUDService.getHudIdByWindow(win1);
       huds[0] = HUDService.hudReferences[hudId1];
@@ -97,48 +96,49 @@ function onpopupshown2b(aEvent)
       menupopups[0].openPopup(huds[0].outputNode, "overlap", 12, 12, true, false);
     }, tabs[0].linkedBrowser.contentWindow);
   }, false);
   menupopups[1].hidePopup();
 }
 
 function onpopupshown1(aEvent)
 {
-  menupopups[0].removeEventListener(aEvent.type, arguments.callee, false);
+  menupopups[0].removeEventListener(aEvent.type, onpopupshown1, false);
 
   // The menuitem checkbox must be in sync with the other tabs.
   is(menuitems[0].getAttribute("checked"), "true", "menuitems[0] is checked");
 
   // Disable body logging.
   HUDService.saveRequestAndResponseBodies = false;
 
   // Close the menu, and switch back to tab 2.
-  menupopups[0].addEventListener("popuphidden", function(aEvent) {
-    menupopups[0].removeEventListener(aEvent.type, arguments.callee, false);
+  menupopups[0].addEventListener("popuphidden", function _onhidden(aEvent) {
+    menupopups[0].removeEventListener(aEvent.type, _onhidden, false);
 
     gBrowser.selectedTab = tabs[1];
     waitForFocus(function() {
       // Reopen the context menu from tab 2.
       menupopups[1].addEventListener("popupshown", onpopupshown2c, false);
       menupopups[1].openPopup(huds[1].outputNode, "overlap", 13, 13, true, false);
     }, tabs[1].linkedBrowser.contentWindow);
   }, false);
   menupopups[0].hidePopup();
 }
 
 function onpopupshown2c(aEvent)
 {
-  menupopups[1].removeEventListener(aEvent.type, arguments.callee, false);
+  menupopups[1].removeEventListener(aEvent.type, onpopupshown2c, false);
 
   isnot(menuitems[1].getAttribute("checked"), "true",
         "menuitems[1] is not checked");
 
-  menupopups[1].addEventListener("popuphidden", function(aEvent) {
-    menupopups[1].removeEventListener(aEvent.type, arguments.callee, false);
+  menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
+    menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
 
     // Done!
     huds = menuitems = menupopups = tabs = null;
-    HUDService.deactivateHUDForContext(gBrowser.selectedTab);
-    gBrowser.removeCurrentTab();
-    executeSoon(finishTest);
+    closeConsole(gBrowser.selectedTab, function() {
+      gBrowser.removeCurrentTab();
+      executeSoon(finishTest);
+    });
   }, false);
   menupopups[1].hidePopup();
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_603750_websocket.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_603750_websocket.js
@@ -34,38 +34,43 @@ let TestObserver = {
     }
     else {
       lastWindowId = aSubject.outerWindowID;
     }
   }
 };
 
 function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+  browser.removeEventListener(aEvent.type, tabLoad, true);
 
   openConsole();
 
   let hudId = HUDService.getHudIdByWindow(content);
   hud = HUDService.hudReferences[hudId];
 
   Services.console.registerListener(TestObserver);
 
   content.location = TEST_URI;
 }
 
 function performTest() {
-  let textContent = hud.outputNode.textContent;
-  isnot(textContent.indexOf("ws://0.0.0.0:81"), -1,
-        "first error message found");
-  isnot(textContent.indexOf("ws://0.0.0.0:82"), -1,
-        "second error message found");
-
   Services.console.unregisterListener(TestObserver);
   Services.prefs.setBoolPref(pref_ws, oldPref_ws);
-  finishTest();
+
+  waitForSuccess({
+    name: "websocket error messages displayed",
+    validatorFn: function()
+    {
+      let textContent = hud.outputNode.textContent;
+      return textContent.indexOf("ws://0.0.0.0:81") > -1 &&
+             textContent.indexOf("ws://0.0.0.0:82") > -1;
+    },
+    successFn: finishTest,
+    failureFn: finishTest,
+  });
 }
 
 function test() {
   oldPref_ws = Services.prefs.getBoolPref(pref_ws);
 
   Services.prefs.setBoolPref(pref_ws, true);
 
   addTab("data:text/html;charset=utf-8,Web Console test for bug 603750: Web Socket errors");
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_611795.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_611795.js
@@ -2,51 +2,76 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = 'data:text/html;charset=utf-8,<div style="-moz-opacity:0;">test repeated' +
                  ' css warnings</div><p style="-moz-opacity:0">hi</p>';
 
 function onContentLoaded()
 {
-  browser.removeEventListener("load", arguments.callee, true);
+  browser.removeEventListener("load", onContentLoaded, true);
 
   let HUD = HUDService.getHudByWindow(content);
   let jsterm = HUD.jsterm;
   let outputNode = HUD.outputNode;
 
-  let msg = "The unknown CSS property warning is displayed only once";
-  let node = outputNode.firstChild;
+  let cssWarning = "Unknown property '-moz-opacity'.  Declaration dropped.";
+
+  waitForSuccess({
+    name: "2 repeated CSS warnings",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf(cssWarning) > -1;
+    },
+    successFn: function()
+    {
+      let msg = "The unknown CSS property warning is displayed only once";
+      let node = outputNode.firstChild;
 
-  is(node.childNodes[2].textContent, "Unknown property '-moz-opacity'.  Declaration dropped.", "correct node")
-  is(node.childNodes[3].firstChild.getAttribute("value"), 2, msg);
+      is(node.childNodes[2].textContent, cssWarning, "correct node");
+      is(node.childNodes[3].firstChild.getAttribute("value"), 2, msg);
+
+      testConsoleLogRepeats();
+    },
+    failureFn: finishTest,
+  });
+}
+
+function testConsoleLogRepeats()
+{
+  let HUD = HUDService.getHudByWindow(content);
+  let jsterm = HUD.jsterm;
+  let outputNode = HUD.outputNode;
 
   jsterm.clearOutput();
 
   jsterm.setInputValue("for (let i = 0; i < 10; ++i) console.log('this is a line of reasonably long text that I will use to verify that the repeated text node is of an appropriate size.');");
   jsterm.execute();
 
-  let msg = "The console output is repeated 10 times";
-  let node = outputNode.querySelector(".webconsole-msg-console");
-  is(node.childNodes[3].firstChild.getAttribute("value"), 10, msg);
-
-  jsterm.clearOutput();
-  finishTest();
+  waitForSuccess({
+    name: "10 repeated console.log messages",
+    validatorFn: function()
+    {
+      let node = outputNode.querySelector(".webconsole-msg-console");
+      return node && node.childNodes[3].firstChild.getAttribute("value") == 10;
+    },
+    successFn: finishTest,
+    failureFn: finishTest,
+  });
 }
 
 /**
  * Unit test for bug 611795:
  * Repeated CSS messages get collapsed into one.
  */
 function test()
 {
   addTab(TEST_URI);
-  browser.addEventListener("load", function() {
-    browser.removeEventListener("load", arguments.callee, true);
-
-    openConsole();
-    // Clear cached messages that are shown once the Web Console opens.
-    HUDService.getHudByWindow(content).jsterm.clearOutput(true);
-
-    browser.addEventListener("load", onContentLoaded, true);
-    content.location.reload();
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, function(aHud) {
+      // Clear cached messages that are shown once the Web Console opens.
+      aHud.jsterm.clearOutput(true);
+      browser.addEventListener("load", onContentLoaded, true);
+      content.location.reload();
+    });
   }, true);
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
@@ -64,11 +64,11 @@ function tabLoaded() {
                getControllerForCommand("cmd_copy");
   is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
 
   waitForClipboard(getExpectedClipboardText(HUD.outputNode.selectedItem),
     clipboard_setup, clipboard_copy_done, clipboard_copy_done);
 }
 
 function getExpectedClipboardText(aItem) {
-  return "[" + ConsoleUtils.timestampString(aItem.timestamp) + "] " +
+  return "[" + WebConsoleUtils.l10n.timestampString(aItem.timestamp) + "] " +
          aItem.clipboardText;
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js
@@ -2,60 +2,103 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Contributor(s):
  *   Mihai Șucan <mihai.sucan@gmail.com>
  */
 
-function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+let hud, testDriver;
 
-  openConsole();
+function testNext() {
+  testDriver.next();
+}
 
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
+function testGen() {
+  hud.jsterm.clearOutput();
   let outputNode = hud.outputNode;
   let scrollBox = outputNode.scrollBoxObject.element;
 
   for (let i = 0; i < 150; i++) {
     hud.console.log("test message " + i);
   }
 
+  waitForSuccess({
+    name: "150 console.log messages displayed",
+    validatorFn: function()
+    {
+      return outputNode.querySelectorAll(".hud-log").length == 150;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
   let oldScrollTop = scrollBox.scrollTop;
   ok(oldScrollTop > 0, "scroll location is not at the top");
 
   // scroll to the first node
   outputNode.focus();
 
   EventUtils.synthesizeKey("VK_HOME", {});
 
   let topPosition = scrollBox.scrollTop;
   isnot(topPosition, oldScrollTop, "scroll location updated (moved to top)");
 
-  executeSoon(function() {
-    // add a message and make sure scroll doesn't change
-    hud.console.log("test message 150");
+  // add a message and make sure scroll doesn't change
+  hud.console.log("test message 150");
 
-    is(scrollBox.scrollTop, topPosition, "scroll location is still at the top");
+  waitForSuccess({
+    name: "console.log message no. 151 displayed",
+    validatorFn: function()
+    {
+      return outputNode.querySelectorAll(".hud-log").length == 151;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
 
-    // scroll back to the bottom
-    outputNode.lastChild.focus();
-    EventUtils.synthesizeKey("VK_END", {});
+  yield;
+
+  is(scrollBox.scrollTop, topPosition, "scroll location is still at the top");
+
+  // scroll back to the bottom
+  outputNode.lastChild.focus();
+  EventUtils.synthesizeKey("VK_END", {});
 
-    executeSoon(function() {
-      oldScrollTop = outputNode.scrollTop;
+  oldScrollTop = outputNode.scrollTop;
 
-      hud.console.log("test message 151");
+  hud.console.log("test message 151");
 
-      isnot(scrollBox.scrollTop, oldScrollTop,
-            "scroll location updated (moved to bottom)");
+  waitForSuccess({
+    name: "console.log message no. 152 displayed",
+    validatorFn: function()
+    {
+      return outputNode.querySelectorAll(".hud-log").length == 152;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
 
-      finishTest();
-    });
-  });
+  yield;
+
+  isnot(scrollBox.scrollTop, oldScrollTop,
+        "scroll location updated (moved to bottom)");
+
+  hud = testDriver = null;
+  finishTest();
+  
+  yield;
 }
 
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 613642: remember scroll location");
-  browser.addEventListener("load", tabLoad, true);
+  browser.addEventListener("load", function tabLoad(aEvent) {
+    browser.removeEventListener(aEvent.type, tabLoad, true);
+    openConsole(null, function(aHud) {
+      hud = aHud;
+      testDriver = testGen();
+      testDriver.next();
+    });
+  }, true);
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js
@@ -2,34 +2,48 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Contributor(s):
  *   Mihai Șucan <mihai.sucan@gmail.com>
  */
 
-function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+let hud, testDriver;
 
-  openConsole();
+function testNext() {
+  testDriver.next();
+}
 
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
+function testGen() {
+  hud.jsterm.clearOutput();
+
   let outputNode = hud.outputNode;
   let oldPref = Services.prefs.getIntPref("devtools.hud.loglimit.console");
 
   Services.prefs.setIntPref("devtools.hud.loglimit.console", 140);
   let scrollBoxElement = outputNode.scrollBoxObject.element;
   let boxObject = outputNode.scrollBoxObject;
 
   for (let i = 0; i < 150; i++) {
     hud.console.log("test message " + i);
   }
 
+  waitForSuccess({
+    name: "150 console.log messages displayed",
+    validatorFn: function()
+    {
+      return outputNode.querySelectorAll(".hud-log").length == 140;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
   let oldScrollTop = scrollBoxElement.scrollTop;
   ok(oldScrollTop > 0, "scroll location is not at the top");
 
   let firstNode = outputNode.firstChild;
   ok(firstNode, "found the first message");
 
   let msgNode = outputNode.querySelectorAll("richlistitem")[80];
   ok(msgNode, "found the 80th message");
@@ -40,24 +54,48 @@ function tabLoad(aEvent) {
   isnot(scrollBoxElement.scrollTop, oldScrollTop,
         "scroll location updated (scrolled to message)");
 
   oldScrollTop = scrollBoxElement.scrollTop;
 
   // add a message
   hud.console.log("hello world");
 
+  waitForSuccess({
+    name: "console.log message #151 displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("hello world") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
   // Scroll location needs to change, because one message is also removed, and
   // we need to scroll a bit towards the top, to keep the current view in sync.
   isnot(scrollBoxElement.scrollTop, oldScrollTop,
         "scroll location updated (added a message)");
 
   isnot(outputNode.firstChild, firstNode,
         "first message removed");
 
   Services.prefs.setIntPref("devtools.hud.loglimit.console", oldPref);
+
+  hud = testDriver = null;
   finishTest();
+
+  yield;
 }
 
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 613642: maintain scroll with pruning of old messages");
-  browser.addEventListener("load", tabLoad, true);
+  browser.addEventListener("load", function tabLoad(aEvent) {
+    browser.removeEventListener(aEvent.type, tabLoad, true);
+
+    openConsole(null, function(aHud) {
+      hud = aHud;
+      testDriver = testGen();
+      testDriver.next();
+    });
+  }, true);
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js
@@ -2,42 +2,52 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Contributor(s):
  *   Mihai Șucan <mihai.sucan@gmail.com>
  */
 
-function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+function consoleOpened(hud) {
+  hud.jsterm.clearOutput();
 
-  openConsole();
-
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
   let outputNode = hud.outputNode;
   let boxObject = outputNode.scrollBoxObject.element;
 
   for (let i = 0; i < 150; i++) {
     hud.console.log("test message " + i);
   }
 
-  let oldScrollTop = boxObject.scrollTop;
-  ok(oldScrollTop > 0, "scroll location is not at the top");
-
-  hud.jsterm.execute("'hello world'");
-
-  isnot(boxObject.scrollTop, oldScrollTop, "scroll location updated");
+  waitForSuccess({
+    name: "console.log messages displayed",
+    validatorFn: function()
+    {
+      return outputNode.itemCount == 150;
+    },
+    successFn: function()
+    {
+      let oldScrollTop = boxObject.scrollTop;
+      ok(oldScrollTop > 0, "scroll location is not at the top");
 
-  oldScrollTop = boxObject.scrollTop;
-  outputNode.scrollBoxObject.ensureElementIsVisible(outputNode.lastChild);
+      hud.jsterm.execute("'hello world'");
+
+      isnot(boxObject.scrollTop, oldScrollTop, "scroll location updated");
+
+      oldScrollTop = boxObject.scrollTop;
+      outputNode.scrollBoxObject.ensureElementIsVisible(outputNode.lastChild);
 
-  is(boxObject.scrollTop, oldScrollTop, "scroll location is the same");
+      is(boxObject.scrollTop, oldScrollTop, "scroll location is the same");
 
-  finishTest();
+      finishTest();
+    },
+    failureFn: finishTest,
+  });
 }
 
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 614793: jsterm result scroll");
-  browser.addEventListener("load", tabLoad, true);
+  browser.addEventListener("load", function onLoad(aEvent) {
+    browser.removeEventListener(aEvent.type, onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js
@@ -48,63 +48,56 @@ let TestObserver = {
   observe: function test_observe(aSubject)
   {
     if (testEnded || !(aSubject instanceof Ci.nsIScriptError)) {
       return;
     }
 
     is(aSubject.category, "content javascript", "error category");
 
+    testEnded = true;
     if (aSubject.category == "content javascript") {
       executeSoon(checkOutput);
     }
     else {
-      testEnd();
+      executeSoon(finishTest);
     }
   }
 };
 
 function checkOutput()
 {
-  if (testEnded) {
-    return;
-  }
-
-  let textContent = hud.outputNode.textContent;
-  isnot(textContent.indexOf("bug618078exception"), -1,
-        "exception message");
-
-  testEnd();
+  waitForSuccess({
+    name: "exception message",
+    validatorFn: function()
+    {
+      return hud.outputNode.textContent.indexOf("bug618078exception") > -1;
+    },
+    successFn: finishTest,
+    failureFn: finishTest,
+  });
 }
 
 function testEnd()
 {
-  if (testEnded) {
-    return;
-  }
-
-  testEnded = true;
   Services.console.unregisterListener(TestObserver);
-  finishTest();
 }
 
 function test()
 {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 618078");
 
-  browser.addEventListener("load", function() {
-    browser.removeEventListener("load", arguments.callee, true);
-
-    openConsole();
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
 
-    let hudId = HUDService.getHudIdByWindow(content);
-    hud = HUDService.hudReferences[hudId];
+    openConsole(null, function(aHud) {
+      hud = aHud;
+      Services.console.registerListener(TestObserver);
+      registerCleanupFunction(testEnd);
 
-    Services.console.registerListener(TestObserver);
-    registerCleanupFunction(testEnd);
-
-    executeSoon(function() {
-      expectUncaughtException();
-      content.location = TEST_URI;
+      executeSoon(function() {
+        expectUncaughtException();
+        content.location = TEST_URI;
+      });
     });
   }, true);
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_618311_private_browsing.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_618311_private_browsing.js
@@ -39,32 +39,33 @@
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
 
 let pb = Cc["@mozilla.org/privatebrowsing;1"].
          getService(Ci.nsIPrivateBrowsingService);
 
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 618311 (private browsing)");
 
-  browser.addEventListener("load", function() {
-    browser.removeEventListener("load", arguments.callee, true);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
 
     registerCleanupFunction(function() {
       pb.privateBrowsingEnabled = false;
       pb = null;
     });
 
     ok(!pb.privateBrowsingEnabled, "private browsing is not enabled");
 
     togglePBAndThen(function() {
       ok(pb.privateBrowsingEnabled, "private browsing is enabled");
 
-      HUDService.activateHUDForContext(gBrowser.selectedTab);
-      content.location = TEST_URI;
-      gBrowser.selectedBrowser.addEventListener("load", tabLoaded, true);
+      openConsole(gBrowser.selectedTab, function() {
+        content.location = TEST_URI;
+        gBrowser.selectedBrowser.addEventListener("load", tabLoaded, true);
+      });
     });
   }, true);
 }
 
 function tabLoaded() {
   gBrowser.selectedBrowser.removeEventListener("load", tabLoaded, true);
 
   let hudId = HUDService.getHudIdByWindow(content);
@@ -123,16 +124,17 @@ function tabLoaded() {
       document.removeEventListener("popuphidden", onpopuphidden, false);
 
       executeSoon(function() {
         let popups = popupset.querySelectorAll("panel[hudId=" + hudId + "]");
         is(popups.length, 0, "no popups found");
 
         ok(!pb.privateBrowsingEnabled, "private browsing is not enabled");
 
+        gBrowser.removeCurrentTab();
         executeSoon(finishTest);
       });
     }
   };
 
   document.addEventListener("popupshown", onpopupshown, false);
 
   registerCleanupFunction(function() {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js
@@ -5,24 +5,19 @@
  *
  * Contributor(s):
  *   Mihai Sucan <mihai.sucan@gmail.com>
  */
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-621644-jsterm-dollar.html";
 
 function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+  browser.removeEventListener(aEvent.type, tabLoad, true);
 
-  waitForFocus(function () {
-    openConsole();
-
-    let hudId = HUDService.getHudIdByWindow(content);
-    let HUD = HUDService.hudReferences[hudId];
-
+  openConsole(null, function(HUD) {
     HUD.jsterm.clearOutput();
 
     HUD.jsterm.setInputValue("$(document.body)");
     HUD.jsterm.execute();
 
     let outputItem = HUD.outputNode.
                      querySelector(".webconsole-msg-output:last-child");
     ok(outputItem.textContent.indexOf("<p>") > -1,
@@ -34,15 +29,15 @@ function tabLoad(aEvent) {
     HUD.jsterm.execute();
 
     outputItem = HUD.outputNode.
                      querySelector(".webconsole-msg-output:last-child");
     ok(outputItem.textContent.indexOf("621644") > -1,
        "jsterm output is correct for $$()");
 
     executeSoon(finishTest);
-  }, content);
+  });
 }
 
 function test() {
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoad, true);
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_626484_output_copy_order.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_626484_output_copy_order.js
@@ -1,42 +1,50 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
-let itemsSet, HUD;
+let itemsSet, HUD, outputNode;
 
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 626484");
-  browser.addEventListener("load", tabLoaded, true);
+  browser.addEventListener("load", function tabLoaded(aEvent) {
+    browser.removeEventListener(aEvent.type, tabLoaded, true);
+    openConsole(null, consoleOpened);
+  }, true);
 }
 
-function tabLoaded(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
-  openConsole();
 
-  let console = browser.contentWindow.wrappedJSObject.console;
+function consoleOpened(aHud) {
+  HUD = aHud;
+  outputNode = HUD.outputNode;
+  HUD.jsterm.clearOutput();
+
+  let console = content.wrappedJSObject.console;
   console.log("The first line.");
   console.log("The second line.");
   console.log("The last line.");
-
-  let hudId = HUDService.getHudIdByWindow(content);
-  HUD = HUDService.hudReferences[hudId];
-  outputNode = HUD.outputNode;
-
   itemsSet = [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1],
     [2, 1, 0]];
 
-  nextTest();
+  waitForSuccess({
+    name: "console.log messages displayed",
+    validatorFn: function()
+    {
+      return outputNode.querySelectorAll(".hud-log").length == 3;
+    },
+    successFn: nextTest,
+    failureFn: finishTest,
+  });
 }
 
 function nextTest() {
   if (itemsSet.length === 0) {
     outputNode.clearSelection();
     HUD.jsterm.clearOutput();
-    HUD = null;
-    finish();
+    HUD = outputNode = null;
+    executeSoon(finishTest);
   }
   else {
     outputNode.clearSelection();
     let items = itemsSet.shift();
     items.forEach(function (index) {
       outputNode.addItemToSelection(outputNode.getItemAtIndex(index));
     });
     outputNode.focus();
@@ -45,17 +53,17 @@ function nextTest() {
   }
 }
 
 function getExpectedClipboardText(aItemCount) {
   let expectedClipboardText = [];
   for (let i = 0; i < aItemCount; i++) {
     let item = outputNode.getItemAtIndex(i);
     expectedClipboardText.push("[" +
-      ConsoleUtils.timestampString(item.timestamp) + "] " +
+      WebConsoleUtils.l10n.timestampString(item.timestamp) + "] " +
       item.clipboardText);
   }
   return expectedClipboardText.join("\n");
 }
 
 function clipboardSetup() {
   goDoCommand("cmd_copy");
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
@@ -40,65 +40,70 @@ const TEST_URI = "http://example.com/bro
 
 function test() {
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
 function tabLoaded() {
   browser.removeEventListener("load", tabLoaded, true);
+
+  let tmp = {};
+  Cu.import("resource:///modules/WebConsoleUtils.jsm", tmp);
+  let WCU = tmp.WebConsoleUtils;
+
   openConsole();
 
   let hudId = HUDService.getHudIdByWindow(content);
   let HUD = HUDService.hudReferences[hudId];
   let jsterm = HUD.jsterm;
 
   let win = content.wrappedJSObject;
 
   // Make sure autocomplete does not walk through iterators and generators.
   let result = win.gen1.next();
   let completion = jsterm.propertyProvider(win, "gen1.");
   is(completion, null, "no matchees for gen1");
-  ok(!jsterm.isResultInspectable(win.gen1),
+  ok(!WCU.isObjectInspectable(win.gen1),
      "gen1 is not inspectable");
 
   is(result+1, win.gen1.next(), "gen1.next() did not execute");
 
   result = win.gen2.next();
 
   completion = jsterm.propertyProvider(win, "gen2.");
   is(completion, null, "no matchees for gen2");
-  ok(!jsterm.isResultInspectable(win.gen2),
+  ok(!WCU.isObjectInspectable(win.gen2),
      "gen2 is not inspectable");
 
   is((result/2+1)*2, win.gen2.next(),
      "gen2.next() did not execute");
 
   result = win.iter1.next();
   is(result[0], "foo", "iter1.next() [0] is correct");
   is(result[1], "bar", "iter1.next() [1] is correct");
 
   completion = jsterm.propertyProvider(win, "iter1.");
   is(completion, null, "no matchees for iter1");
-  ok(!jsterm.isResultInspectable(win.iter1),
+  ok(!WCU.isObjectInspectable(win.iter1),
      "iter1 is not inspectable");
 
   result = win.iter1.next();
   is(result[0], "baz", "iter1.next() [0] is correct");
   is(result[1], "baaz", "iter1.next() [1] is correct");
 
   completion = jsterm.propertyProvider(content, "iter2.");
   is(completion, null, "no matchees for iter2");
-  ok(!jsterm.isResultInspectable(win.iter2),
+  ok(!WCU.isObjectInspectable(win.iter2),
      "iter2 is not inspectable");
 
   completion = jsterm.propertyProvider(win, "window.");
   ok(completion, "matches available for window");
   ok(completion.matches.length, "matches available for window (length)");
-  ok(jsterm.isResultInspectable(win),
+  ok(WCU.isObjectInspectable(win),
      "window is inspectable");
 
   let panel = jsterm.openPropertyPanel("Test", win);
   ok(panel, "opened the Property Panel");
   let rows = panel.treeView._rows;
   ok(rows.length, "Property Panel rows are available");
 
   let find = function(display, children) {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_644419_log_limits.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_644419_log_limits.js
@@ -5,108 +5,154 @@
  */
 
 // Tests that the Web Console limits the number of lines displayed according to
 // the limit set for each category.
 
 const TEST_URI = "http://example.com/browser/browser/devtools/" +
                  "webconsole/test/test-bug-644419-log-limits.html";
 
-var gOldPref, gHudId;
+var gOldPref;
 
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 644419: Console should " +
          "have user-settable log limits for each message category");
   browser.addEventListener("load", onLoad, true);
 }
 
 function onLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
-
-  openConsole();
+  browser.removeEventListener(aEvent.type, onLoad, true);
 
-  gHudId = HUDService.getHudIdByWindow(content);
-  browser.addEventListener("load", testWebDevLimits, true);
-  expectUncaughtException();
-  content.location = TEST_URI;
+  openConsole(null, function(aHud) {
+    aHud.jsterm.clearOutput();
+    hud = aHud;
+    outputNode = aHud.outputNode;
+
+    browser.addEventListener("load", testWebDevLimits, true);
+    expectUncaughtException();
+    content.location = TEST_URI;
+  });
 }
 
 function testWebDevLimits(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
+  browser.removeEventListener(aEvent.type, testWebDevLimits, true);
   gOldPref = Services.prefs.getIntPref("devtools.hud.loglimit.console");
   Services.prefs.setIntPref("devtools.hud.loglimit.console", 10);
 
-  let hud = HUDService.hudReferences[gHudId];
-  outputNode = hud.outputNode;
-
-  executeSoon(function() {
-    // Find the sentinel entry.
-    findLogEntry("bar is not defined");
-
-    // Fill the log with Web Developer errors.
-    for (let i = 0; i < 11; i++) {
-      hud.console.log("test message " + i);
-    }
-    testLogEntry(outputNode, "test message 0", "first message is pruned", false, true);
-    findLogEntry("test message 1");
-    // Check if the sentinel entry is still there.
-    findLogEntry("bar is not defined");
-
-    Services.prefs.setIntPref("devtools.hud.loglimit.console", gOldPref);
-    testJsLimits();
+  // Find the sentinel entry.
+  waitForSuccess({
+    name: "bar is not defined",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bar is not defined") > -1;
+    },
+    successFn: testWebDevLimits2,
+    failureFn: testWebDevLimits2,
   });
 }
 
-function testJsLimits(aEvent) {
+function testWebDevLimits2() {
+  // Fill the log with Web Developer errors.
+  for (let i = 0; i < 11; i++) {
+    hud.console.log("test message " + i);
+  }
+
+  waitForSuccess({
+    name: "11 console.log messages displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("test message 10") > -1;
+    },
+    successFn: function()
+    {
+      testLogEntry(outputNode, "test message 0", "first message is pruned", false, true);
+      findLogEntry("test message 1");
+      // Check if the sentinel entry is still there.
+      findLogEntry("bar is not defined");
+
+      Services.prefs.setIntPref("devtools.hud.loglimit.console", gOldPref);
+      testJsLimits();
+    },
+    failureFn: testJsLimits,
+  });
+}
+
+function testJsLimits() {
   gOldPref = Services.prefs.getIntPref("devtools.hud.loglimit.exception");
   Services.prefs.setIntPref("devtools.hud.loglimit.exception", 10);
 
-  let hud = HUDService.hudReferences[gHudId];
   hud.jsterm.clearOutput();
-  outputNode = hud.outputNode;
   hud.console.log("testing JS limits");
 
   // Find the sentinel entry.
-  findLogEntry("testing JS limits");
+  waitForSuccess({
+    name: "console.log 'testing JS limits'",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("testing JS limits") > -1;
+    },
+    successFn: testJsLimits2,
+    failureFn: testNetLimits,
+  });
+}
+
+function testJsLimits2() {
   // Fill the log with JS errors.
   let head = content.document.getElementsByTagName("head")[0];
   for (let i = 0; i < 11; i++) {
     var script = content.document.createElement("script");
     script.text = "fubar" + i + ".bogus(6);";
     expectUncaughtException();
     head.insertBefore(script, head.firstChild);
   }
 
-  executeSoon(function() {
-    testLogEntry(outputNode, "fubar0 is not defined", "first message is pruned", false, true);
-    findLogEntry("fubar1 is not defined");
-    // Check if the sentinel entry is still there.
-    findLogEntry("testing JS limits");
+  waitForSuccess({
+    name: "10 JS errors shown",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("fubar10 is not defined") > -1;
+    },
+    successFn: function()
+    {
+      testLogEntry(outputNode, "fubar0 is not defined", "first message is pruned", false, true);
+      findLogEntry("fubar1 is not defined");
+      // Check if the sentinel entry is still there.
+      findLogEntry("testing JS limits");
 
-    Services.prefs.setIntPref("devtools.hud.loglimit.exception", gOldPref);
-    testNetLimits();
+      Services.prefs.setIntPref("devtools.hud.loglimit.exception", gOldPref);
+      testNetLimits();
+    },
+    failureFn: testNetLimits,
   });
 }
 
 var gCounter, gImage;
 
-function testNetLimits(aEvent) {
+function testNetLimits() {
   gOldPref = Services.prefs.getIntPref("devtools.hud.loglimit.network");
   Services.prefs.setIntPref("devtools.hud.loglimit.network", 10);
 
-  let hud = HUDService.hudReferences[gHudId];
   hud.jsterm.clearOutput();
-  outputNode = hud.outputNode;
   hud.console.log("testing Net limits");
 
   // Find the sentinel entry.
-  findLogEntry("testing Net limits");
-  // Fill the log with network messages.
-  gCounter = 0;
-  loadImage();
+  waitForSuccess({
+    name: "console.log 'testing Net limits'",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("testing Net limits") > -1;
+    },
+    successFn: function()
+    {
+      // Fill the log with network messages.
+      gCounter = 0;
+      loadImage();
+    },
+    failureFn: testCssLimits,
+  });
 }
 
 function loadImage() {
   if (gCounter < 11) {
     let body = content.document.getElementsByTagName("body")[0];
     gImage && gImage.removeEventListener("load", loadImage, true);
     gImage = content.document.createElement("img");
     gImage.src = "test-image.png?_fubar=" + gCounter;
@@ -120,37 +166,56 @@ function loadImage() {
   findLogEntry("test-image.png?_fubar=1");
   // Check if the sentinel entry is still there.
   findLogEntry("testing Net limits");
 
   Services.prefs.setIntPref("devtools.hud.loglimit.network", gOldPref);
   testCssLimits();
 }
 
-function testCssLimits(aEvent) {
+function testCssLimits() {
   gOldPref = Services.prefs.getIntPref("devtools.hud.loglimit.cssparser");
   Services.prefs.setIntPref("devtools.hud.loglimit.cssparser", 10);
 
-  let hud = HUDService.hudReferences[gHudId];
   hud.jsterm.clearOutput();
-  outputNode = hud.outputNode;
   hud.console.log("testing CSS limits");
 
   // Find the sentinel entry.
-  findLogEntry("testing CSS limits");
+  waitForSuccess({
+    name: "console.log 'testing CSS limits'",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("testing CSS limits") > -1;
+    },
+    successFn: testCssLimits2,
+    failureFn: finishTest,
+  });
+}
 
+function testCssLimits2() {
   // Fill the log with CSS errors.
   let body = content.document.getElementsByTagName("body")[0];
   for (let i = 0; i < 11; i++) {
     var div = content.document.createElement("div");
     div.setAttribute("style", "-moz-foobar" + i + ": 42;");
     body.insertBefore(div, body.firstChild);
   }
-  executeSoon(function() {
-    testLogEntry(outputNode, "Unknown property '-moz-foobar0'", "first message is pruned", false, true);
-    findLogEntry("Unknown property '-moz-foobar1'");
-    // Check if the sentinel entry is still there.
-    findLogEntry("testing CSS limits");
 
-    Services.prefs.setIntPref("devtools.hud.loglimit.cssparser", gOldPref);
-    finishTest();
+  waitForSuccess({
+    name: "10 CSS errors shown",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("-moz-foobar10") > -1;
+    },
+    successFn: function()
+    {
+      testLogEntry(outputNode, "Unknown property '-moz-foobar0'",
+                   "first message is pruned", false, true);
+      findLogEntry("Unknown property '-moz-foobar1'");
+      // Check if the sentinel entry is still there.
+      findLogEntry("testing CSS limits");
+
+      Services.prefs.setIntPref("devtools.hud.loglimit.cssparser", gOldPref);
+      finishTest();
+    },
+    failureFn: finishTest,
   });
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
@@ -92,27 +92,29 @@ function runSelectionTests()
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   executeSoon(function() {
     InspectorUI.highlighter.addListener("nodeselected", performTestComparisons);
     EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
   });
 }
 
-function performTestComparisons(evt)
+function performTestComparisons()
 {
   InspectorUI.highlighter.removeListener("nodeselected", performTestComparisons);
 
   InspectorUI.stopInspecting();
   is(InspectorUI.highlighter.node, h1, "node selected");
   is(InspectorUI.selection, h1, "selection matches node");
 
-  HUDService.activateHUDForContext(gBrowser.selectedTab);
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
+  openConsole(gBrowser.selectedTab, performWebConsoleTests);
+}
+
+function performWebConsoleTests(hud)
+{
   let jsterm = hud.jsterm;
   outputNode = hud.outputNode;
 
   jsterm.clearOutput();
   jsterm.execute("$0");
   findLogEntry("[object HTMLHeadingElement");
 
   jsterm.clearOutput();
@@ -122,18 +124,17 @@ function performTestComparisons(evt)
   is(InspectorUI.selection.textContent, msg, "node successfully updated");
 
   doc = h1 = null;
   executeSoon(finishUp);
 }
 
 function finishUp() {
   InspectorUI.closeInspectorUI();
-  gBrowser.removeCurrentTab();
-  finish();
+  finishTest();
 }
 
 function test()
 {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_658368_time_methods.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_658368_time_methods.js
@@ -4,84 +4,85 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests that the Console API implements the time() and timeEnd() methods.
 
 function test() {
   addTab("http://example.com/browser/browser/devtools/webconsole/" +
          "test/test-bug-658368-time-methods.html");
-  openConsole();
-  browser.addEventListener("load", onLoad, true);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
 }
 
-function onLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, onLoad, true);
-
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
+function consoleOpened(hud) {
   outputNode = hud.outputNode;
 
   executeSoon(function() {
     findLogEntry("aTimer: timer started");
     findLogEntry("ms");
 
     // The next test makes sure that timers with the same name but in separate
     // tabs, do not contain the same value.
     addTab("data:text/html;charset=utf-8,<script type='text/javascript'>" +
            "console.timeEnd('bTimer');</script>");
-    openConsole();
-    browser.addEventListener("load", testTimerIndependenceInTabs, true);
+    browser.addEventListener("load", function onLoad() {
+      browser.removeEventListener("load", onLoad, true);
+      openConsole(null, testTimerIndependenceInTabs);
+    }, true);
   });
 }
 
-function testTimerIndependenceInTabs(aEvent) {
-  browser.removeEventListener(aEvent.type, testTimerIndependenceInTabs, true);
-
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
+function testTimerIndependenceInTabs(hud) {
   outputNode = hud.outputNode;
 
   executeSoon(function() {
     testLogEntry(outputNode, "bTimer: timer started", "bTimer was not started",
                  false, true);
 
     // The next test makes sure that timers with the same name but in separate
     // pages, do not contain the same value.
-    browser.addEventListener("load", testTimerIndependenceInSameTab, true);
+    browser.addEventListener("load", function onLoad() {
+      browser.removeEventListener("load", onLoad, true);
+      executeSoon(testTimerIndependenceInSameTab);
+    }, true);
     content.location = "data:text/html;charset=utf-8,<script type='text/javascript'>" +
            "console.time('bTimer');</script>";
   });
 }
 
-function testTimerIndependenceInSameTab(aEvent) {
-  browser.removeEventListener(aEvent.type, testTimerIndependenceInSameTab, true);
-
+function testTimerIndependenceInSameTab() {
   let hudId = HUDService.getHudIdByWindow(content);
   let hud = HUDService.hudReferences[hudId];
   outputNode = hud.outputNode;
 
   executeSoon(function() {
     findLogEntry("bTimer: timer started");
     hud.jsterm.clearOutput();
 
     // Now the following console.timeEnd() call shouldn't display anything,
     // if the timers in different pages are not related.
-    browser.addEventListener("load", testTimerIndependenceInSameTabAgain, true);
+    browser.addEventListener("load", function onLoad() {
+      browser.removeEventListener("load", onLoad, true);
+      executeSoon(testTimerIndependenceInSameTabAgain);
+    }, true);
     content.location = "data:text/html;charset=utf-8,<script type='text/javascript'>" +
            "console.timeEnd('bTimer');</script>";
   });
 }
 
-function testTimerIndependenceInSameTabAgain(aEvent) {
-  browser.removeEventListener(aEvent.type, testTimerIndependenceInSameTabAgain, true);
-
+function testTimerIndependenceInSameTabAgain(hud) {
   let hudId = HUDService.getHudIdByWindow(content);
   let hud = HUDService.hudReferences[hudId];
   outputNode = hud.outputNode;
 
   executeSoon(function() {
     testLogEntry(outputNode, "bTimer: timer started", "bTimer was not started",
                  false, true);
 
-    finishTest();
+    closeConsole(gBrowser.selectedTab, function() {
+      gBrowser.removeCurrentTab();
+      executeSoon(finishTest);
+    });
   });
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_659907_console_dir.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_659907_console_dir.js
@@ -4,42 +4,53 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests that console.dir works as intended.
 
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 659907: Expand console " +
          "object with a dir method");
-  browser.addEventListener("load", onLoad, true);
+  browser.addEventListener("load", function onLoad(aEvent) {
+    browser.removeEventListener(aEvent.type, onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
 }
 
-function onLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
-
-  openConsole();
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
+function consoleOpened(hud) {
   outputNode = hud.outputNode;
   content.console.dir(content.document);
-  findLogEntry("[object HTMLDocument");
+  waitForSuccess({
+    name: "console.dir displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("[object HTMLDocument") > -1;
+    },
+    successFn: testConsoleDir.bind(null, outputNode),
+    failureFn: finishTest,
+  });
+}
+
+function testConsoleDir(outputNode) {
   let msg = outputNode.querySelectorAll(".webconsole-msg-inspector");
   is(msg.length, 1, "one message node displayed");
-  let rows = msg[0].propertyTreeView._rows;
+  let view = msg[0].propertyTreeView;
   let foundQSA = false;
   let foundLocation = false;
   let foundWrite = false;
-  for (let i = 0; i < rows.length; i++) {
-    if (rows[i].display == "querySelectorAll: function querySelectorAll()") {
+  for (let i = 0; i < view.rowCount; i++) {
+    let text = view.getCellText(i);
+    if (text == "querySelectorAll: function querySelectorAll()") {
       foundQSA = true;
     }
-    else if (rows[i].display  == "location: Object") {
+    else if (text  == "location: Object") {
       foundLocation = true;
     }
-    else if (rows[i].display  == "write: function write()") {
+    else if (text  == "write: function write()") {
       foundWrite = true;
     }
   }
   ok(foundQSA, "found document.querySelectorAll");
   ok(foundLocation, "found document.location");
   ok(foundWrite, "found document.write");
-  finishTest();
+  msg = view = outputNode = null;
+  executeSoon(finishTest);
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_664131_console_group.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_664131_console_group.js
@@ -2,55 +2,132 @@
 /*
  * 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;
 
+let testDriver, hud;
+
 function test() {
   addTab("data:text/html;charset=utf-8,Web Console test for bug 664131: Expand console " +
          "object with group methods");
-  browser.addEventListener("load", onLoad, true);
+  browser.addEventListener("load", function onLoad(aEvent) {
+    browser.removeEventListener(aEvent.type, onLoad, true);
+    openConsole(null, function(aHud) {
+      hud = aHud;
+      testDriver = testGen();
+      testNext();
+    });
+  }, true);
+}
+
+function testNext() {
+  testDriver.next();
 }
 
-function onLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, arguments.callee, true);
-
-  openConsole();
-  let hudId = HUDService.getHudIdByWindow(content);
-  let hud = HUDService.hudReferences[hudId];
+function testGen() {
   outputNode = hud.outputNode;
 
-  content.console.group("a");
-  findLogEntry("a");
+  hud.jsterm.clearOutput();
+
+  content.console.group("bug664131a");
+
+  waitForSuccess({
+    name: "console.group displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bug664131a") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
   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");
+
+  content.console.log("bug664131a-inside");
+
+  waitForSuccess({
+    name: "console.log message displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bug664131a-inside") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
+  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");
+
+  content.console.groupEnd("bug664131a");
+  content.console.log("bug664131-outside");
+
+  waitForSuccess({
+    name: "console.log message displayed after groupEnd()",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bug664131-outside") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
+  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");
+
+  content.console.groupCollapsed("bug664131b");
+
+  waitForSuccess({
+    name: "console.groupCollapsed displayed",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bug664131b") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
+  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");
 
+
   // Test that clearing the console removes the indentation.
   hud.jsterm.clearOutput();
-  content.console.log("cleared");
-  findLogEntry("cleared");
-  let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container");
+  content.console.log("bug664131-cleared");
+
+  waitForSuccess({
+    name: "console.log displayed after clearOutput",
+    validatorFn: function()
+    {
+      return outputNode.textContent.indexOf("bug664131-cleared") > -1;
+    },
+    successFn: testNext,
+    failureFn: finishTest,
+  });
+
+  yield;
+
+  msg = outputNode.querySelectorAll(".webconsole-msg-icon-container");
   is(msg.length, 1, "one message node displayed");
   is(msg[0].style.marginLeft, "0px", "correct group indent found");
 
+  testDriver = hud = null;
   finishTest();
+
+  yield;
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_chrome.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_chrome.js
@@ -37,36 +37,36 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 // Tests that code completion works properly.
 
 function test() {
   addTab(getBrowserURL());
-  browser.addEventListener("DOMContentLoaded", testChrome, false);
+  browser.addEventListener("DOMContentLoaded", function onLoad() {
+    browser.removeEventListener("DOMContentLoaded", onLoad, true);
+    openConsole();
+    testChrome(HUDService.getHudByWindow(content));
+  }, true);
 }
 
-function testChrome() {
-  browser.removeEventListener("DOMContentLoaded", testChrome, false);
-
-  openConsole();
-
-  let hud = HUDService.getHudByWindow(content);
+function testChrome(hud) {
   ok(hud, "we have a console");
   
   ok(hud.HUDBox, "we have the console display");
 
   let jsterm = hud.jsterm;
   ok(jsterm, "we have a jsterm");
 
   let input = jsterm.inputNode;
   ok(hud.outputNode, "we have an output node");
 
   // Test typing 'docu'.
   input.value = "docu";
   input.setSelectionRange(4, 4);
   jsterm.complete(jsterm.COMPLETE_HINT_ONLY);
   is(jsterm.completeNode.value, "    ment", "'docu' completion");
 
-  finishTest();
+  gBrowser.removeCurrentTab();
+  executeSoon(finishTest);
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_console_extras.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_console_extras.js
@@ -36,30 +36,36 @@
  * ***** END LICENSE BLOCK ***** */
 
 // Tests that the basic console.log()-style APIs and filtering work.
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-extras.html";
 
 function test() {
   addTab(TEST_URI);
-  browser.addEventListener("DOMContentLoaded", onLoad, false);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
 }
 
-function onLoad() {
-  browser.removeEventListener("DOMContentLoaded", onLoad, false);
-  let doc = content.document;
-  openConsole();
-  let button = doc.querySelector("button");
+function consoleOpened(hud) {
+  waitForSuccess({
+    name: "two nodes displayed",
+    validatorFn: function()
+    {
+      return hud.outputNode.querySelectorAll(".hud-msg-node").length == 2;
+    },
+    successFn: function()
+    {
+      let nodes = hud.outputNode.querySelectorAll(".hud-msg-node");
+      ok(/start/.test(nodes[0].textContent), "start found");
+      ok(/end/.test(nodes[1].textContent), "end found - complete!");
+
+      finishTest();
+    },
+    failureFn: finishTest,
+  });
+
+  let button = content.document.querySelector("button");
   ok(button, "we have the button");
   EventUtils.sendMouseEvent({ type: "click" }, button, content);
-  executeSoon(testButtonClicked);
 }
-
-function testButtonClicked()