Merge MC -> JM
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 09 Aug 2011 13:29:11 -0700
changeset 76104 a6c87fd27ba9ddaf996a25f9752358e7cfb587bd
parent 76103 bde71d2d88fbf0eebd289c847395415fb5b4bb84 (current diff)
parent 74078 a0e3c589c8fad05ab6e67efe7cd4911469561dbf (diff)
child 76105 7f3e8f6ba47a5019647c97e135316b6134862375
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
milestone8.0a1
Merge MC -> JM
browser/base/content/browser.js
browser/base/content/scratchpad.js
browser/base/content/scratchpad.xul
browser/base/content/test/browser_scratchpad_bug_646070_chrome_context_pref.js
browser/base/content/test/browser_scratchpad_bug_660560_tab.js
browser/base/content/test/browser_scratchpad_contexts.js
browser/base/content/test/browser_scratchpad_execute_print.js
browser/base/content/test/browser_scratchpad_files.js
browser/base/content/test/browser_scratchpad_initialization.js
browser/base/content/test/browser_scratchpad_inspect.js
browser/base/content/test/browser_scratchpad_tab_switch.js
browser/base/content/test/browser_scratchpad_ui.js
browser/branding/aurora/splash.bmp
browser/branding/nightly/splash.bmp
browser/branding/official/splash.bmp
browser/branding/unofficial/splash.bmp
content/html/content/test/test_bug418756.html
db/README.html
db/mdb/Makefile.in
db/mdb/public/Makefile.in
db/mdb/public/mdb.h
db/mork/Makefile.in
db/mork/build/Makefile.in
db/mork/build/nsIMdbFactoryFactory.h
db/mork/build/nsMorkCID.h
db/mork/build/nsMorkFactory.cpp
db/mork/src/Makefile.in
db/mork/src/mork.h
db/mork/src/morkArray.cpp
db/mork/src/morkArray.h
db/mork/src/morkAtom.cpp
db/mork/src/morkAtom.h
db/mork/src/morkAtomMap.cpp
db/mork/src/morkAtomMap.h
db/mork/src/morkAtomSpace.cpp
db/mork/src/morkAtomSpace.h
db/mork/src/morkBead.cpp
db/mork/src/morkBead.h
db/mork/src/morkBlob.cpp
db/mork/src/morkBlob.h
db/mork/src/morkBuilder.cpp
db/mork/src/morkBuilder.h
db/mork/src/morkCell.cpp
db/mork/src/morkCell.h
db/mork/src/morkCellObject.cpp
db/mork/src/morkCellObject.h
db/mork/src/morkCh.cpp
db/mork/src/morkCh.h
db/mork/src/morkConfig.cpp
db/mork/src/morkConfig.h
db/mork/src/morkCursor.cpp
db/mork/src/morkCursor.h
db/mork/src/morkDeque.cpp
db/mork/src/morkDeque.h
db/mork/src/morkEnv.cpp
db/mork/src/morkEnv.h
db/mork/src/morkFactory.cpp
db/mork/src/morkFactory.h
db/mork/src/morkFile.cpp
db/mork/src/morkFile.h
db/mork/src/morkHandle.cpp
db/mork/src/morkHandle.h
db/mork/src/morkIntMap.cpp
db/mork/src/morkIntMap.h
db/mork/src/morkMap.cpp
db/mork/src/morkMap.h
db/mork/src/morkNode.cpp
db/mork/src/morkNode.h
db/mork/src/morkNodeMap.cpp
db/mork/src/morkNodeMap.h
db/mork/src/morkObject.cpp
db/mork/src/morkObject.h
db/mork/src/morkParser.cpp
db/mork/src/morkParser.h
db/mork/src/morkPool.cpp
db/mork/src/morkPool.h
db/mork/src/morkPortTableCursor.cpp
db/mork/src/morkPortTableCursor.h
db/mork/src/morkProbeMap.cpp
db/mork/src/morkProbeMap.h
db/mork/src/morkQuickSort.cpp
db/mork/src/morkQuickSort.h
db/mork/src/morkRow.cpp
db/mork/src/morkRow.h
db/mork/src/morkRowCellCursor.cpp
db/mork/src/morkRowCellCursor.h
db/mork/src/morkRowMap.cpp
db/mork/src/morkRowMap.h
db/mork/src/morkRowObject.cpp
db/mork/src/morkRowObject.h
db/mork/src/morkRowSpace.cpp
db/mork/src/morkRowSpace.h
db/mork/src/morkSearchRowCursor.cpp
db/mork/src/morkSearchRowCursor.h
db/mork/src/morkSink.cpp
db/mork/src/morkSink.h
db/mork/src/morkSpace.cpp
db/mork/src/morkSpace.h
db/mork/src/morkStore.cpp
db/mork/src/morkStore.h
db/mork/src/morkStream.cpp
db/mork/src/morkStream.h
db/mork/src/morkTable.cpp
db/mork/src/morkTable.h
db/mork/src/morkTableRowCursor.cpp
db/mork/src/morkTableRowCursor.h
db/mork/src/morkThumb.cpp
db/mork/src/morkThumb.h
db/mork/src/morkUniqRowCursor.h
db/mork/src/morkWriter.cpp
db/mork/src/morkWriter.h
db/mork/src/morkYarn.cpp
db/mork/src/morkYarn.h
db/mork/src/morkZone.cpp
db/mork/src/morkZone.h
db/mork/src/orkinHeap.cpp
db/mork/src/orkinHeap.h
db/morkreader/Makefile.in
db/morkreader/external/Makefile.in
db/morkreader/nsMorkReader.cpp
db/morkreader/nsMorkReader.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/public/coreEvents/Makefile.in
dom/public/coreEvents/nsIDOMCompositionListener.h
dom/public/coreEvents/nsIDOMContextMenuListener.h
dom/public/coreEvents/nsIDOMFocusListener.h
dom/public/coreEvents/nsIDOMFormListener.h
dom/public/coreEvents/nsIDOMKeyListener.h
dom/public/coreEvents/nsIDOMLoadListener.h
dom/public/coreEvents/nsIDOMMouseListener.h
dom/public/coreEvents/nsIDOMMouseMotionListener.h
dom/public/coreEvents/nsIDOMTextListener.h
dom/public/coreEvents/nsIDOMUIListener.h
extensions/spellcheck/hunspell/tests/suggestiontest/Makefile.orig
js/src/Makefile.in
js/src/assembler/assembler/ARMAssembler.cpp
js/src/assembler/assembler/ARMAssembler.h
js/src/assembler/assembler/MacroAssemblerARM.h
js/src/configure.in
js/src/jit-test/tests/basic/bug640079.js
js/src/jit-test/tests/basic/bug643670.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsclone.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgcinlines.h
js/src/jshashtable.h
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/jsopcode.cpp
js/src/jsparse.cpp
js/src/jsregexp.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstypedarray.cpp
js/src/jstypedarray.h
js/src/jstypedarrayinlines.h
js/src/jsval.h
js/src/jsvalue.h
js/src/jsxdrapi.h
js/src/jsxml.cpp
js/src/make_unicode.py
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/NunboxAssembler.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/TypedArrayIC.h
js/src/shell/Makefile.in
js/src/shell/js.cpp
js/src/tests/js1_8_5/extensions/findReferences-01.js
js/src/tests/js1_8_5/extensions/findReferences-02.js
js/src/tests/js1_8_5/extensions/jstests.list
js/src/tests/js1_8_5/regress/jstests.list
js/src/tracejit/Writer.cpp
js/src/tracejit/Writer.h
js/src/vm/Stack.cpp
js/src/xpconnect/loader/ISO8601DateUtils.jsm
js/src/xpconnect/shell/xpcshell.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcinlines.h
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcpublic.h
js/src/xpconnect/src/xpcwrappednativescope.cpp
js/src/xpconnect/wrappers/WrapperFactory.cpp
layout/generic/test/test_bug670058.html
layout/generic/test/test_bug670508.html
layout/generic/test/test_bug671319.html
layout/generic/test/test_bug673315-1.html
layout/generic/test/test_selection_scrolling.html
layout/generic/test/window_selection_scrolling.html
layout/reftests/backgrounds/background-size-no-intrinsic-height-image-ref.html
layout/reftests/backgrounds/background-size-no-intrinsic-height-image.html
layout/reftests/backgrounds/background-size-no-intrinsic-width-image-ref.html
layout/reftests/backgrounds/background-size-no-intrinsic-width-image.html
layout/reftests/backgrounds/no-intrinsic-size.svg
mobile/app/splash.bmp
mobile/components/build/nsIPhoneSupport.idl
mobile/components/build/nsPhoneSupport.cpp
mobile/components/build/nsPhoneSupport.h
modules/libpref/src/init/all.js
toolkit/components/places/nsMorkHistoryImporter.cpp
toolkit/components/places/nsNoMorkStubImporter.cpp
toolkit/components/places/tests/unit/history.dat
toolkit/components/places/tests/unit/migrateFrecency.dat
toolkit/components/satchel/test/unit/formhistory.dat
toolkit/components/satchel/test/unit/test_bug_329741.js
toolkit/components/webapps/Makefile.in
toolkit/components/webapps/nsIWebappsSupport.idl
toolkit/components/webapps/nsWebappsSupport.cpp
toolkit/components/webapps/nsWebappsSupport.h
toolkit/content/tests/widgets/test_arrowpanel.xul
toolkit/xre/nsSplashScreen.h
toolkit/xre/nsSplashScreenDummy.cpp
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -403,22 +403,19 @@ nsAccessibleWrap::CreateMaiInterfaces(vo
 {
     PRUint16 interfacesBits = 0;
     
     // Add Interfaces for each nsIAccessible.ext interfaces
 
     // the Component interface are supported by all nsIAccessible
     interfacesBits |= 1 << MAI_INTERFACE_COMPONENT;
 
-    // Add Action interface if the action count is more than zero.
-    PRUint8 actionCount = 0;
-    nsresult rv = GetNumActions(&actionCount);
-    if (NS_SUCCEEDED(rv) && actionCount > 0) {
-       interfacesBits |= 1 << MAI_INTERFACE_ACTION; 
-    }
+  // Add Action interface if the action count is more than zero.
+  if (ActionCount() > 0)
+    interfacesBits |= 1 << MAI_INTERFACE_ACTION;
 
     //nsIAccessibleText
     nsCOMPtr<nsIAccessibleText> accessInterfaceText;
     QueryInterface(NS_GET_IID(nsIAccessibleText),
                    getter_AddRefs(accessInterfaceText));
     if (accessInterfaceText) {
         interfacesBits |= 1 << MAI_INTERFACE_TEXT;
     }
--- a/accessible/src/atk/nsMaiInterfaceAction.cpp
+++ b/accessible/src/atk/nsMaiInterfaceAction.cpp
@@ -68,23 +68,18 @@ doActionCB(AtkAction *aAction, gint aAct
  
     nsresult rv = accWrap->DoAction(aActionIndex);
     return (NS_FAILED(rv)) ? FALSE : TRUE;
 }
 
 gint
 getActionCountCB(AtkAction *aAction)
 {
-    nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
-    if (!accWrap)
-        return 0;
-
-    PRUint8 num = 0;
-    nsresult rv = accWrap->GetNumActions(&num);
-    return (NS_FAILED(rv)) ? 0 : static_cast<gint>(num);
+  nsAccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
+  return accWrap ? accWrap->ActionCount() : 0;
 }
 
 const gchar *
 getActionDescriptionCB(AtkAction *aAction, gint aActionIndex)
 {
     nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
     if (!accWrap)
         return nsnull;
--- a/accessible/src/base/AccGroupInfo.cpp
+++ b/accessible/src/base/AccGroupInfo.cpp
@@ -127,37 +127,27 @@ AccGroupInfo::AccGroupInfo(nsAccessible*
     }
 
     mSetSize++;
   }
 
   if (mParent)
     return;
 
-  // Compute parent.
   PRUint32 parentRole = parent->Role();
-
-  // In the case of ARIA row in treegrid, return treegrid since ARIA
-  // groups aren't used to organize levels in ARIA treegrids.
-  if (aRole == nsIAccessibleRole::ROLE_ROW &&
-      parentRole == nsIAccessibleRole::ROLE_TREE_TABLE) {
+  if (IsConceptualParent(aRole, parentRole))
     mParent = parent;
-    return;
-  }
 
-  // In the case of ARIA tree, a tree can be arranged by using ARIA groups
-  // to organize levels. In this case the parent of the tree item will be
-  // a group and the previous treeitem of that should be the tree item
-  // parent. Or, if the parent is something other than a tree we will
-  // return that.
-
-  if (parentRole != nsIAccessibleRole::ROLE_GROUPING) {
-    mParent = parent;
+  // In the case of ARIA tree (not ARIA treegrid) a tree can be arranged by
+  // using ARIA groups to organize levels. In this case the parent of the tree
+  // item will be a group and the previous treeitem of that should be the tree
+  // item parent.
+  if (parentRole != nsIAccessibleRole::ROLE_GROUPING ||
+      aRole != nsIAccessibleRole::ROLE_OUTLINEITEM)
     return;
-  }
 
   nsAccessible* parentPrevSibling = parent->PrevSibling();
   if (!parentPrevSibling)
     return;
 
   PRUint32 parentPrevSiblingRole = parentPrevSibling->Role();
   if (parentPrevSiblingRole == nsIAccessibleRole::ROLE_TEXT_LEAF) {
     // XXX Sometimes an empty text accessible is in the hierarchy here,
@@ -169,8 +159,42 @@ AccGroupInfo::AccGroupInfo(nsAccessible*
       parentPrevSiblingRole = parentPrevSibling->Role();
   }
 
   // Previous sibling of parent group is a tree item, this is the
   // conceptual tree item parent.
   if (parentPrevSiblingRole == nsIAccessibleRole::ROLE_OUTLINEITEM)
     mParent = parentPrevSibling;
 }
+
+bool
+AccGroupInfo::IsConceptualParent(PRUint32 aRole, PRUint32 aParentRole)
+{
+  if (aParentRole == nsIAccessibleRole::ROLE_OUTLINE &&
+      aRole == nsIAccessibleRole::ROLE_OUTLINEITEM)
+    return true;
+  if ((aParentRole == nsIAccessibleRole::ROLE_TABLE ||
+       aParentRole == nsIAccessibleRole::ROLE_TREE_TABLE) &&
+      aRole == nsIAccessibleRole::ROLE_ROW)
+    return true;
+  if (aParentRole == nsIAccessibleRole::ROLE_ROW &&
+      (aRole == nsIAccessibleRole::ROLE_CELL ||
+       aRole == nsIAccessibleRole::ROLE_GRID_CELL))
+    return true;
+  if (aParentRole == nsIAccessibleRole::ROLE_LIST &&
+      aRole == nsIAccessibleRole::ROLE_LISTITEM)
+    return true;
+  if (aParentRole == nsIAccessibleRole::ROLE_COMBOBOX_LIST &&
+      aRole == nsIAccessibleRole::ROLE_COMBOBOX_OPTION)
+    return true;
+  if (aParentRole == nsIAccessibleRole::ROLE_LISTBOX &&
+      aRole == nsIAccessibleRole::ROLE_OPTION)
+    return true;
+  if (aParentRole == nsIAccessibleRole::ROLE_PAGETABLIST &&
+      aRole == nsIAccessibleRole::ROLE_PAGETAB)
+    return true;
+  if ((aParentRole == nsIAccessibleRole::ROLE_POPUP_MENU ||
+       aParentRole == nsIAccessibleRole::ROLE_MENUPOPUP) &&
+      aRole == nsIAccessibleRole::ROLE_MENUITEM)
+    return true;
+
+  return false;
+}
--- a/accessible/src/base/AccGroupInfo.h
+++ b/accessible/src/base/AccGroupInfo.h
@@ -47,17 +47,17 @@
 class AccGroupInfo
 {
 public:
   AccGroupInfo(nsAccessible* aItem, PRUint32 aRole);
   ~AccGroupInfo() { MOZ_COUNT_DTOR(AccGroupInfo); }
 
   PRInt32 PosInSet() const { return mPosInSet; }
   PRUint32 SetSize() const { return mSetSize; }
-  nsAccessible* GetConceptualParent() const { return mParent; }
+  nsAccessible* ConceptualParent() const { return mParent; }
 
   /**
    * Create group info.
    */
   static AccGroupInfo* CreateGroupInfo(nsAccessible* aAccessible)
   {
     PRUint32 role = aAccessible->Role();
     if (role != nsIAccessibleRole::ROLE_ROW &&
@@ -83,14 +83,20 @@ private:
   static PRUint32 BaseRole(PRUint32 aRole)
   {
     if (aRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
         aRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
       return nsIAccessibleRole::ROLE_MENUITEM;
     return aRole;
   }
 
+  /**
+   * Return true if the given parent role is conceptual parent of the given
+   * role.
+   */
+  static bool IsConceptualParent(PRUint32 aRole, PRUint32 aParentRole);
+
   PRUint32 mPosInSet;
   PRUint32 mSetSize;
   nsAccessible* mParent;
 };
 
 #endif
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -50,17 +50,17 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector
 ////////////////////////////////////////////////////////////////////////////////
 
 NotificationController::NotificationController(nsDocAccessible* aDocument,
                                                nsIPresShell* aPresShell) :
   mObservingState(eNotObservingRefresh), mDocument(aDocument),
-  mPresShell(aPresShell), mTreeConstructedState(eTreeConstructionPending)
+  mPresShell(aPresShell)
 {
   mTextHash.Init();
 
   // Schedule initial accessible tree construction.
   ScheduleProcessing();
 }
 
 NotificationController::~NotificationController()
@@ -149,20 +149,16 @@ NotificationController::ScheduleChildDoc
   ScheduleProcessing();
 }
 
 void
 NotificationController::ScheduleContentInsertion(nsAccessible* aContainer,
                                                  nsIContent* aStartChildNode,
                                                  nsIContent* aEndChildNode)
 {
-  // Ignore content insertions until we constructed accessible tree.
-  if (mTreeConstructedState == eTreeConstructionPending)
-    return;
-
   nsRefPtr<ContentInsertion> insertion = new ContentInsertion(mDocument,
                                                               aContainer);
   if (insertion && insertion->InitChildList(aStartChildNode, aEndChildNode) &&
       mContentInsertions.AppendElement(insertion)) {
     ScheduleProcessing();
   }
 }
 
@@ -202,29 +198,28 @@ NotificationController::WillRefresh(mozi
   if (!mDocument)
     return;
 
   // Any generic notifications should be queued if we're processing content
   // insertions or generic notifications.
   mObservingState = eRefreshProcessingForUpdate;
 
   // Initial accessible tree construction.
-  if (mTreeConstructedState == eTreeConstructionPending) {
+  if (!mDocument->HasLoadState(nsDocAccessible::eTreeConstructed)) {
     // If document is not bound to parent at this point then the document is not
     // ready yet (process notifications later).
     if (!mDocument->IsBoundToParent())
       return;
 
 #ifdef DEBUG_NOTIFICATIONS
     printf("\ninitial tree created, document: %p, document node: %p\n",
            mDocument.get(), mDocument->GetDocumentNode());
 #endif
 
-    mTreeConstructedState = eTreeConstructed;
-    mDocument->NotifyOfInitialUpdate();
+    mDocument->DoInitialUpdate();
 
     NS_ASSERTION(mContentInsertions.Length() == 0,
                  "Pending content insertions while initial accessible tree isn't created!");
   }
 
   // Process content inserted notifications to update the tree. Process other
   // notifications like DOM events and then flush event queue. If any new
   // notifications are queued during this processing then they will be processed
@@ -245,18 +240,18 @@ NotificationController::WillRefresh(mozi
       return;
   }
 
   // Process rendered text change notifications.
   mTextHash.EnumerateEntries(TextEnumerator, mDocument);
   mTextHash.Clear();
 
   // Bind hanging child documents.
-  PRUint32 childDocCount = mHangingChildDocuments.Length();
-  for (PRUint32 idx = 0; idx < childDocCount; idx++) {
+  PRUint32 hangingDocCnt = mHangingChildDocuments.Length();
+  for (PRUint32 idx = 0; idx < hangingDocCnt; idx++) {
     nsDocAccessible* childDoc = mHangingChildDocuments[idx];
 
     nsIContent* ownerContent = mDocument->GetDocumentNode()->
       FindContentForSubDocument(childDoc->GetDocumentNode());
     if (ownerContent) {
       nsAccessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
       if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
         if (mDocument->AppendChildDocument(childDoc))
@@ -266,16 +261,35 @@ NotificationController::WillRefresh(mozi
       }
 
       // Failed to bind the child document, destroy it.
       childDoc->Shutdown();
     }
   }
   mHangingChildDocuments.Clear();
 
+  // If the document is ready and all its subdocuments are completely loaded
+  // then process the document load.
+  if (mDocument->HasLoadState(nsDocAccessible::eReady) &&
+      !mDocument->HasLoadState(nsDocAccessible::eCompletelyLoaded) &&
+      hangingDocCnt == 0) {
+    PRUint32 childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
+    for (; childDocIdx < childDocCnt; childDocIdx++) {
+      nsDocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx);
+      if (!childDoc->HasLoadState(nsDocAccessible::eCompletelyLoaded))
+        break;
+    }
+
+    if (childDocIdx == childDocCnt) {
+      mDocument->ProcessLoad();
+      if (!mDocument)
+        return;
+    }
+  }
+
   // Process only currently queued generic notifications.
   nsTArray < nsRefPtr<Notification> > notifications;
   notifications.SwapElements(mNotifications);
 
   PRUint32 notificationCount = notifications.Length();
   for (PRUint32 idx = 0; idx < notificationCount; idx++) {
     notifications[idx]->Process();
     if (!mDocument)
@@ -305,20 +319,22 @@ NotificationController::WillRefresh(mozi
         if (showOrHideEvent->mTextChangeEvent)
           mDocument->ProcessPendingEvent(showOrHideEvent->mTextChangeEvent);
       }
     }
     if (!mDocument)
       return;
   }
 
-  // Stop further processing if there are no newly queued insertions,
-  // notifications or events.
+  // Stop further processing if there are no new notifications of any kind or
+  // events and document load is processed.
   if (mContentInsertions.Length() == 0 && mNotifications.Length() == 0 &&
-      mEvents.Length() == 0 &&
+      mEvents.Length() == 0 && mTextHash.Count() == 0 &&
+      mHangingChildDocuments.Length() == 0 &&
+      mDocument->HasLoadState(nsDocAccessible::eCompletelyLoaded) &&
       mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
     mObservingState = eNotObservingRefresh;
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationController: event queue
 
--- a/accessible/src/base/NotificationController.h
+++ b/accessible/src/base/NotificationController.h
@@ -123,24 +123,16 @@ public:
   virtual ~NotificationController();
 
   NS_IMETHOD_(nsrefcnt) AddRef(void);
   NS_IMETHOD_(nsrefcnt) Release(void);
 
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
 
   /**
-   * Return true when tree is constructed.
-   */
-  inline bool IsTreeConstructed()
-  {
-    return mTreeConstructedState == eTreeConstructed;
-  }
-
-  /**
    * Shutdown the notification controller.
    */
   void Shutdown();
 
   /**
    * Put an accessible event into the queue to process it later.
    */
   void QueueEvent(AccEvent* aEvent);
@@ -150,21 +142,18 @@ public:
    */
   void ScheduleChildDocBinding(nsDocAccessible* aDocument);
 
   /**
    * Schedule the accessible tree update because of rendered text changes.
    */
   inline void ScheduleTextUpdate(nsIContent* aTextNode)
   {
-    // Ignore the notification if initial tree construction hasn't been done yet.
-    if (mTreeConstructedState != eTreeConstructionPending &&
-        mTextHash.PutEntry(aTextNode)) {
+    if (mTextHash.PutEntry(aTextNode))
       ScheduleProcessing();
-    }
   }
 
   /**
    * Pend accessible tree update for content insertion.
    */
   void ScheduleContentInsertion(nsAccessible* aContainer,
                                 nsIContent* aStartChildNode,
                                 nsIContent* aEndChildNode);
@@ -295,27 +284,16 @@ private:
   nsRefPtr<nsDocAccessible> mDocument;
 
   /**
    * The presshell of the document accessible.
    */
   nsIPresShell* mPresShell;
 
   /**
-   * Indicate whether initial construction of the document's accessible tree
-   * performed or pending. When the document accessible is created then
-   * we construct its initial accessible tree.
-   */
-  enum eTreeConstructedState {
-    eTreeConstructed,
-    eTreeConstructionPending
-  };
-  eTreeConstructedState mTreeConstructedState;
-
-  /**
    * Child documents that needs to be bound to the tree.
    */
   nsTArray<nsRefPtr<nsDocAccessible> > mHangingChildDocuments;
 
   /**
    * Storage for content inserted notification information.
    */
   class ContentInsertion
--- a/accessible/src/base/TextUpdater.cpp
+++ b/accessible/src/base/TextUpdater.cpp
@@ -82,66 +82,68 @@ TextUpdater::DoUpdate(const nsAString& a
   // Get the text leaf accessible offset and invalidate cached offsets after it.
   mTextOffset = mHyperText->GetChildOffset(mTextLeaf, PR_TRUE);
   NS_ASSERTION(mTextOffset != -1,
                "Text leaf hasn't offset within hyper text!");
 
   PRUint32 oldLen = aOldText.Length(), newLen = aNewText.Length();
   PRUint32 minLen = NS_MIN(oldLen, newLen);
 
-  // Text was appended or removed to/from the end.
-  if (aSkipStart == minLen) {
-    // If text has been appended to the end, fire text inserted event.
-    if (oldLen < newLen) {
-      UpdateTextNFireEvent(aNewText, Substring(aNewText, oldLen),
-                           oldLen, PR_TRUE);
-      return;
-    }
-
-    // Text has been removed from the end, fire text removed event.
-    UpdateTextNFireEvent(aNewText, Substring(aOldText, newLen),
-                         newLen, PR_FALSE);
-    return;
-  }
-
   // Trim coinciding substrings from the end.
   PRUint32 skipEnd = 0;
   while (minLen - skipEnd > aSkipStart &&
          aNewText[newLen - skipEnd - 1] == aOldText[oldLen - skipEnd - 1]) {
     skipEnd++;
   }
 
-  // Text was appended or removed to/from the start.
-  if (skipEnd == minLen) {
-    // If text has been appended to the start, fire text inserted event.
-    if (oldLen < newLen) {
-      UpdateTextNFireEvent(aNewText, Substring(aNewText, 0, newLen - skipEnd),
-                           0, PR_TRUE);
-      return;
-    }
-
-    // Text has been removed from the start, fire text removed event.
-    UpdateTextNFireEvent(aNewText, Substring(aOldText, 0, oldLen - skipEnd),
-                         0, PR_FALSE);
-    return;
-  }
-
-  // Find the difference between strings and fire events.
-  // Note: we can skip initial and final coinciding characters since they don't
-  // affect the Levenshtein distance.
-
   PRInt32 strLen1 = oldLen - aSkipStart - skipEnd;
   PRInt32 strLen2 = newLen - aSkipStart - skipEnd;
 
   const nsAString& str1 = Substring(aOldText, aSkipStart, strLen1);
   const nsAString& str2 = Substring(aNewText, aSkipStart, strLen2);
 
   // Increase offset of the text leaf on skipped characters amount.
   mTextOffset += aSkipStart;
 
+  // It could be single insertion or removal or the case of long strings. Do not
+  // calculate the difference between long strings and prefer to fire pair of
+  // insert/remove events as the old string was replaced on the new one.
+  if (strLen1 == 0 || strLen2 == 0 ||
+      strLen1 > kMaxStrLen || strLen2 > kMaxStrLen) {
+    if (strLen1 > 0) {
+      // Fire text change event for removal.
+      nsRefPtr<AccEvent> textRemoveEvent =
+        new AccTextChangeEvent(mHyperText, mTextOffset, str1, PR_FALSE);
+      mDocument->FireDelayedAccessibleEvent(textRemoveEvent);
+    }
+
+    if (strLen2 > 0) {
+      // Fire text change event for insertion.
+      nsRefPtr<AccEvent> textInsertEvent =
+        new AccTextChangeEvent(mHyperText, mTextOffset, str2, PR_TRUE);
+      mDocument->FireDelayedAccessibleEvent(textInsertEvent);
+    }
+
+    // Fire value change event.
+    if (mHyperText->Role() == nsIAccessibleRole::ROLE_ENTRY) {
+      nsRefPtr<AccEvent> valueChangeEvent =
+        new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, mHyperText,
+                     eAutoDetect, AccEvent::eRemoveDupes);
+      mDocument->FireDelayedAccessibleEvent(valueChangeEvent);
+    }
+
+    // Update the text.
+    mTextLeaf->SetText(aNewText);
+    return;
+  }
+
+  // Otherwise find the difference between strings and fire events.
+  // Note: we can skip initial and final coinciding characters since they don't
+  // affect the Levenshtein distance.
+
   // Compute the flat structured matrix need to compute the difference.
   PRUint32 len1 = strLen1 + 1, len2 = strLen2 + 1;
   PRUint32* entries = new PRUint32[len1 * len2];
 
   for (PRUint32 colIdx = 0; colIdx < len1; colIdx++)
     entries[colIdx] = colIdx;
 
   PRUint32* row = entries;
@@ -233,32 +235,8 @@ TextUpdater::ComputeTextChangeEvents(con
     return;
   }
 
   if (rowEnd)
     FireInsertEvent(Substring(aStr2, 0, rowEnd), 0, aEvents);
   if (colEnd)
     FireDeleteEvent(Substring(aStr1, 0, colEnd), 0, aEvents);
 }
-
-void
-TextUpdater::UpdateTextNFireEvent(const nsAString& aNewText,
-                                  const nsAString& aChangeText,
-                                  PRUint32 aAddlOffset,
-                                  PRBool aIsInserted)
-{
-  // Fire text change event.
-  nsRefPtr<AccEvent> textChangeEvent =
-    new AccTextChangeEvent(mHyperText, mTextOffset + aAddlOffset, aChangeText,
-                           aIsInserted);
-  mDocument->FireDelayedAccessibleEvent(textChangeEvent);
-
-  // Fire value change event.
-  if (mHyperText->Role() == nsIAccessibleRole::ROLE_ENTRY) {
-    nsRefPtr<AccEvent> valueChangeEvent =
-      new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, mHyperText,
-                   eAutoDetect, AccEvent::eRemoveDupes);
-    mDocument->FireDelayedAccessibleEvent(valueChangeEvent);
-  }
-
-  // Update the text.
-  mTextLeaf->SetText(aNewText);
-}
--- a/accessible/src/base/TextUpdater.h
+++ b/accessible/src/base/TextUpdater.h
@@ -103,21 +103,20 @@ private:
   {
     nsRefPtr<AccEvent> event =
       new AccTextChangeEvent(mHyperText, mTextOffset + aAddlOffset,
                              aText, PR_FALSE);
     aEvents.AppendElement(event);
   }
 
   /**
-   * Update the text and fire text change/value change events.
+   * The constant used to skip string difference calculation in case of long
+   * strings.
    */
-  void UpdateTextNFireEvent(const nsAString& aNewText,
-                            const nsAString& aChangeText, PRUint32 aAddlOffset,
-                            PRBool aIsInserted);
+  const static PRUint32 kMaxStrLen = 1 << 6;
 
 private:
   nsDocAccessible* mDocument;
   nsTextAccessible* mTextLeaf;
   nsHyperTextAccessible* mHyperText;
   PRInt32 mTextOffset;
 };
 
--- a/accessible/src/base/nsARIAGridAccessible.cpp
+++ b/accessible/src/base/nsARIAGridAccessible.cpp
@@ -101,17 +101,19 @@ nsARIAGridAccessible::GetColumnCount(PRI
 {
   NS_ENSURE_ARG_POINTER(acolumnCount);
   *acolumnCount = 0;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   AccIterator rowIter(this, filters::GetRow);
-  nsAccessible *row = rowIter.GetNext();
+  nsAccessible* row = rowIter.GetNext();
+  if (!row)
+    return NS_OK;
 
   AccIterator cellIter(row, filters::GetCell);
   nsAccessible *cell = nsnull;
 
   while ((cell = cellIter.GetNext()))
     (*acolumnCount)++;
 
   return NS_OK;
--- a/accessible/src/base/nsAccDocManager.cpp
+++ b/accessible/src/base/nsAccDocManager.cpp
@@ -185,53 +185,35 @@ nsAccDocManager::OnStateChange(nsIWebPro
     HandleDOMDocumentLoad(document, eventType);
     return NS_OK;
   }
 
   // Document loading was started.
   NS_LOG_ACCDOCLOAD("start document loading", aWebProgress, aRequest,
                     aStateFlags)
 
-  if (!IsEventTargetDocument(document))
-    return NS_OK;
-
   nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(document);
   if (!docAcc)
     return NS_OK;
 
   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));
   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
   NS_ENSURE_STATE(docShell);
 
-  // Fire reload and state busy events on existing document accessible while
-  // event from user input flag can be calculated properly and accessible
-  // is alive. When new document gets loaded then this one is destroyed.
+  bool isReloading = false;
   PRUint32 loadType;
   docShell->GetLoadType(&loadType);
   if (loadType == LOAD_RELOAD_NORMAL ||
       loadType == LOAD_RELOAD_BYPASS_CACHE ||
       loadType == LOAD_RELOAD_BYPASS_PROXY ||
       loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE) {
-
-    // Fire reload event.
-    nsRefPtr<AccEvent> reloadEvent =
-      new AccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, docAcc);
-    nsEventShell::FireEvent(reloadEvent);
+    isReloading = true;
   }
 
-  // Mark the document accessible as loading, if it stays alive then we'll mark
-  // it as loaded when we receive proper notification.
-  docAcc->MarkAsLoading();
-
-  // Fire state busy change event. Use delayed event since we don't care
-  // actually if event isn't delivered when the document goes away like a shot.
-  nsRefPtr<AccEvent> stateEvent =
-    new AccStateChangeEvent(document, states::BUSY, PR_TRUE);
-  docAcc->FireDelayedAccessibleEvent(stateEvent);
-
+  docAcc->NotifyOfLoading(isReloading);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccDocManager::OnProgressChange(nsIWebProgress *aWebProgress,
                                   nsIRequest *aRequest,
                                   PRInt32 aCurSelfProgress,
                                   PRInt32 aMaxSelfProgress,
@@ -333,64 +315,17 @@ nsAccDocManager::HandleDOMDocumentLoad(n
   // was loaded completely. However if it's not created yet then create it.
   nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
   if (!docAcc) {
     docAcc = CreateDocOrRootAccessible(aDocument);
     if (!docAcc)
       return;
   }
 
-  // Mark the document as loaded to drop off the busy state flag on it.
-  docAcc->MarkAsLoaded();
-
-  // Do not fire document complete/stop events for root chrome document
-  // accessibles and for frame/iframe documents because
-  // a) screen readers start working on focus event in the case of root chrome
-  // documents
-  // b) document load event on sub documents causes screen readers to act is if
-  // entire page is reloaded.
-  if (!IsEventTargetDocument(aDocument))
-    return;
-
-  // Fire complete/load stopped if the load event type is given.
-  if (aLoadEventType) {
-    nsRefPtr<AccEvent> loadEvent = new AccEvent(aLoadEventType, aDocument);
-    docAcc->FireDelayedAccessibleEvent(loadEvent);
-  }
-
-  // Fire busy state change event.
-  nsRefPtr<AccEvent> stateEvent =
-    new AccStateChangeEvent(aDocument, states::BUSY, PR_FALSE);
-  docAcc->FireDelayedAccessibleEvent(stateEvent);
-}
-
-PRBool
-nsAccDocManager::IsEventTargetDocument(nsIDocument *aDocument) const
-{
-  nsCOMPtr<nsISupports> container = aDocument->GetContainer();
-  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
-    do_QueryInterface(container);
-  NS_ASSERTION(docShellTreeItem, "No document shell for document!");
-
-  nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
-  docShellTreeItem->GetParent(getter_AddRefs(parentTreeItem));
-
-  // It's not a root document.
-  if (parentTreeItem) {
-    nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
-    docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
-
-    // It's not a sub document, i.e. a frame or iframe.
-    return (sameTypeRoot == docShellTreeItem);
-  }
-
-  // It's not chrome root document.
-  PRInt32 contentType;
-  docShellTreeItem->GetItemType(&contentType);
-  return (contentType == nsIDocShellTreeItem::typeContent);
+  docAcc->NotifyOfLoad(aLoadEventType);
 }
 
 void
 nsAccDocManager::AddListeners(nsIDocument *aDocument,
                               PRBool aAddDOMContentLoadedListener)
 {
   nsPIDOMWindow *window = aDocument->GetWindow();
   nsIDOMEventTarget *target = window->GetChromeEventHandler();
--- a/accessible/src/base/nsAccDocManager.h
+++ b/accessible/src/base/nsAccDocManager.h
@@ -116,34 +116,16 @@ private:
    * @param  aDocument       [in] loaded DOM document
    * @param  aLoadEventType  [in] specifies the event type to fire load event,
    *                           if 0 then no event is fired
    */
   void HandleDOMDocumentLoad(nsIDocument *aDocument,
                              PRUint32 aLoadEventType);
 
   /**
-   * Return true if accessibility events accompanying document accessible
-   * loading should be fired.
-   *
-   * The rules are: do not fire events for root chrome document accessibles and
-   * for sub document accessibles (like HTML frame of iframe) of the loading
-   * document accessible.
-   *
-   * XXX: in general AT expect events for document accessible loading into
-   * tabbrowser, events from other document accessibles may break AT. We need to
-   * figure out what AT wants to know about loading page (for example, some of
-   * them have separate processing of iframe documents on the page and therefore
-   * they need a way to distinguish sub documents from page document). Ideally
-   * we should make events firing for any loaded document and provide additional
-   * info AT are needing.
-   */
-  PRBool IsEventTargetDocument(nsIDocument *aDocument) const;
-
-  /**
    * Add 'pagehide' and 'DOMContentLoaded' event listeners.
    */
   void AddListeners(nsIDocument *aDocument, PRBool aAddPageShowListener);
 
   /**
    * Create document or root accessible.
    */
   nsDocAccessible *CreateDocOrRootAccessible(nsIDocument *aDocument);
--- a/accessible/src/base/nsAccessibilityAtomList.h
+++ b/accessible/src/base/nsAccessibilityAtomList.h
@@ -38,17 +38,17 @@
 
 /******
 
   This file contains the list of all accessibility nsIAtoms and their values
 
   It is designed to be used as inline input to nsAccessibilityAtoms.cpp *only*
   through the magic of C preprocessing.
 
-  All entires must be enclosed in the macro ACCESSIBILITY_ATOM which will have cruel
+  All entries must be enclosed in the macro ACCESSIBILITY_ATOM which will have cruel
   and unusual things done to it
 
   It is recommended (but not strictly necessary) to keep all entries
   in alphabetical order
 
   The first argument to ACCESSIBILITY_ATOM is the C++ identifier of the atom
   The second argument is the string value of the atom
 
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -658,16 +658,21 @@ nsAccessible::IsVisible(PRBool* aIsOffsc
   }
   return isVisible;
 }
 
 PRUint64
 nsAccessible::NativeState()
 {
   PRUint64 state = 0;
+
+  nsDocAccessible* document = GetDocAccessible();
+  if (!document || !document->IsInDocument(this))
+    state |= states::STALE;
+
   PRBool disabled = PR_FALSE;
   if (mContent->IsElement()) {
     nsEventStates elementState = mContent->AsElement()->State();
 
     if (elementState.HasState(NS_EVENT_STATE_INVALID))
       state |= states::INVALID;
 
     if (elementState.HasState(NS_EVENT_STATE_REQUIRED))
@@ -1844,32 +1849,33 @@ PRUint32
 nsAccessible::NativeRole()
 {
   return nsCoreUtils::IsXLink(mContent) ?
     nsIAccessibleRole::ROLE_LINK : nsIAccessibleRole::ROLE_NOTHING;
 }
 
 // readonly attribute PRUint8 numActions
 NS_IMETHODIMP
-nsAccessible::GetNumActions(PRUint8 *aNumActions)
+nsAccessible::GetNumActions(PRUint8* aActionCount)
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-  *aNumActions = 0;
-
+  NS_ENSURE_ARG_POINTER(aActionCount);
+  *aActionCount = 0;
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  PRUint32 actionRule = GetActionRule(State());
-  if (actionRule == eNoAction)
-    return NS_OK;
-
-  *aNumActions = 1;
+  *aActionCount = ActionCount();
   return NS_OK;
 }
 
+PRUint8
+nsAccessible::ActionCount()
+{
+  return GetActionRule(State()) == eNoAction ? 0 : 1;
+}
+
 /* DOMString getAccActionName (in PRUint8 index); */
 NS_IMETHODIMP
 nsAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   if (aIndex != 0)
     return NS_ERROR_INVALID_ARG;
@@ -2135,17 +2141,17 @@ nsAccessible::GetRelationByType(PRUint32
           (mRoleMapEntry->role == nsIAccessibleRole::ROLE_OUTLINEITEM ||
            mRoleMapEntry->role == nsIAccessibleRole::ROLE_ROW)) {
 
         AccGroupInfo* groupInfo = GetGroupInfo();
         if (!groupInfo)
           return NS_OK_NO_RELATION_TARGET;
 
         return nsRelUtils::AddTarget(aRelationType, aRelation,
-                                     groupInfo->GetConceptualParent());
+                                     groupInfo->ConceptualParent());
       }
 
       // If accessible is in its own Window, or is the root of a document,
       // then we should provide NODE_CHILD_OF relation so that MSAA clients
       // can easily get to true parent instead of getting to oleacc's
       // ROLE_WINDOW accessible which will prevent us from going up further
       // (because it is system generated and has no idea about the hierarchy
       // above it).
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -414,16 +414,21 @@ public:
 
   inline bool IsTextLeaf() const { return mFlags & eTextLeafAccessible; }
   nsTextAccessible* AsTextLeaf();
 
   //////////////////////////////////////////////////////////////////////////////
   // ActionAccessible
 
   /**
+   * Return the number of actions that can be performed on this accessible.
+   */
+  virtual PRUint8 ActionCount();
+
+  /**
    * Return access key, such as Alt+D.
    */
   virtual KeyBinding AccessKey() const;
 
   /**
    * Return global keyboard shortcut for default action, such as Ctrl+O for
    * Open file menuitem.
    */
@@ -671,17 +676,17 @@ protected:
    * @param aValue - value of the attribute
    *
    * @return - NS_OK_NO_ARIA_VALUE if there is no setted ARIA attribute
    */
   nsresult GetAttrValue(nsIAtom *aAriaProperty, double *aValue);
 
   /**
    * Return the action rule based on ARIA enum constants EActionRule
-   * (see nsARIAMap.h). Used by GetNumActions() and GetActionName().
+   * (see nsARIAMap.h). Used by ActionCount() and GetActionName().
    *
    * @param aStates  [in] states of the accessible
    */
   PRUint32 GetActionRule(PRUint64 aStates);
 
   /**
    * Return group info.
    */
--- a/accessible/src/base/nsApplicationAccessible.cpp
+++ b/accessible/src/base/nsApplicationAccessible.cpp
@@ -232,22 +232,20 @@ nsApplicationAccessible::TakeSelection()
 }
 
 NS_IMETHODIMP
 nsApplicationAccessible::TakeFocus()
 {
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsApplicationAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsApplicationAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-  *aNumActions = 0;
-  return NS_OK;
+  return 0;
 }
 
 NS_IMETHODIMP
 nsApplicationAccessible::GetActionName(PRUint8 aIndex, nsAString &aName)
 {
   aName.Truncate();
   return NS_ERROR_INVALID_ARG;
 }
--- a/accessible/src/base/nsApplicationAccessible.h
+++ b/accessible/src/base/nsApplicationAccessible.h
@@ -98,17 +98,16 @@ public:
   NS_IMETHOD GetRelationsCount(PRUint32 *aRelationsCount);
   NS_IMETHOD GetRelation(PRUint32 aIndex, nsIAccessibleRelation **aRelation);
   NS_IMETHOD GetRelations(nsIArray **aRelations);
   NS_IMETHOD GetBounds(PRInt32 *aX, PRInt32 *aY,
                        PRInt32 *aWidth, PRInt32 *aHeight);
   NS_IMETHOD SetSelected(PRBool aIsSelected);
   NS_IMETHOD TakeSelection();
   NS_IMETHOD TakeFocus();
-  NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString &aName);
   NS_IMETHOD GetActionDescription(PRUint8 aIndex, nsAString &aDescription);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsIAccessibleApplication
   NS_DECL_NSIACCESSIBLEAPPLICATION
 
   // nsAccessNode
@@ -124,16 +123,17 @@ public:
   virtual PRUint64 State();
   virtual PRUint64 NativeState();
   virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                      EWhichChildAtPoint aWhichChild);
 
   virtual void InvalidateChildren();
 
   // ActionAccessible
+  virtual PRUint8 ActionCount();
   virtual KeyBinding AccessKey() const;
 
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
   virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
                                            nsresult *aError = nsnull) const;
--- a/accessible/src/base/nsBaseWidgetAccessible.cpp
+++ b/accessible/src/base/nsBaseWidgetAccessible.cpp
@@ -133,23 +133,20 @@ nsLinkableAccessible::GetValue(nsAString
   nsAccessible::GetValue(aValue);
   if (!aValue.IsEmpty())
     return NS_OK;
 
   return mIsLink ? mActionAcc->GetValue(aValue) : NS_ERROR_NOT_IMPLEMENTED;
 }
 
 
-NS_IMETHODIMP
-nsLinkableAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsLinkableAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-
-  *aNumActions = (mIsOnclick || mIsLink) ? 1 : 0;
-  return NS_OK;
+  return (mIsOnclick || mIsLink) ? 1 : 0;
 }
 
 NS_IMETHODIMP
 nsLinkableAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   // Action 0 (default action): Jump to link
--- a/accessible/src/base/nsBaseWidgetAccessible.h
+++ b/accessible/src/base/nsBaseWidgetAccessible.h
@@ -82,29 +82,29 @@ class nsLinkableAccessible : public nsAc
 public:
   enum { eAction_Jump = 0 };
 
   nsLinkableAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD GetValue(nsAString& _retval);
   NS_IMETHOD TakeFocus();
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
   virtual PRUint64 NativeState();
 
   // ActionAccessible
+  virtual PRUint8 ActionCount();
   virtual KeyBinding AccessKey() const;
 
   // HyperLinkAccessible
   virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);
 
 protected:
   // nsAccessible
   virtual void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -100,17 +100,18 @@ static const PRUint32 kRelationAttrsLen 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/desctructor
 
 nsDocAccessible::
   nsDocAccessible(nsIDocument *aDocument, nsIContent *aRootContent,
                   nsIWeakReference *aShell) :
   nsHyperTextAccessibleWrap(aRootContent, aShell),
-  mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE)
+  mDocument(aDocument), mScrollPositionChangedTicks(0),
+  mLoadState(eTreeConstructionPending), mLoadEventType(0)
 {
   mFlags |= eDocAccessible;
 
   mDependentIDsHash.Init();
   // XXX aaronl should we use an algorithm for the initial cache size?
   mAccessibleCache.Init(kDefaultCacheSize);
   mNodeToAccessibleMap.Init(kDefaultCacheSize);
 
@@ -304,21 +305,26 @@ nsDocAccessible::NativeState()
     // XXX Need to invent better check to see if doc is focusable,
     // which it should be if it is scrollable. A XUL document could be focusable.
     // See bug 376803.
     state |= states::FOCUSABLE;
     if (gLastFocusedNode == mDocument)
       state |= states::FOCUSED;
   }
 
-  // Expose state busy until the document is loaded or tree is constructed.
-  if (!mIsLoaded || !mNotificationController->IsTreeConstructed()) {
-    state |= states::BUSY | states::STALE;
-  }
- 
+  // Expose stale state until the document is ready (DOM is loaded and tree is
+  // constructed).
+  if (!HasLoadState(eReady))
+    state |= states::STALE;
+
+  // Expose state busy until the document and all its subdocuments is completely
+  // loaded.
+  if (!HasLoadState(eCompletelyLoaded))
+    state |= states::BUSY;
+
   nsIFrame* frame = GetFrame();
   if (!frame || !nsCoreUtils::CheckVisibilityInParentChain(frame)) {
     state |= states::INVISIBLE | states::OFFSCREEN;
   }
 
   nsCOMPtr<nsIEditor> editor;
   GetAssociatedEditor(getter_AddRefs(editor));
   state |= editor ? states::EDITABLE : states::READONLY;
@@ -600,17 +606,17 @@ nsDocAccessible::Init()
   mNotificationController = new NotificationController(this, shell);
   if (!mNotificationController)
     return PR_FALSE;
 
   // Mark the document accessible as loaded if its DOM document was loaded at
   // this point (this can happen because a11y is started late or DOM document
   // having no container was loaded.
   if (mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
-    mIsLoaded = PR_TRUE;
+    mLoadState |= eDOMLoaded;
 
   AddEventListeners();
   return PR_TRUE;
 }
 
 void
 nsDocAccessible::Shutdown()
 {
@@ -1371,18 +1377,19 @@ nsDocAccessible::UnbindFromDocument(nsAc
   mAccessibleCache.Remove(uniqueID);
 }
 
 void
 nsDocAccessible::ContentInserted(nsIContent* aContainerNode,
                                  nsIContent* aStartChildNode,
                                  nsIContent* aEndChildNode)
 {
-  /// Pend tree update on content insertion until layout.
-  if (mNotificationController) {
+  // Ignore content insertions until we constructed accessible tree. Otherwise
+  // schedule tree update on content insertion after layout.
+  if (mNotificationController && HasLoadState(eTreeConstructed)) {
     // Update the whole tree of this document accessible when the container is
     // null (document element is inserted or removed).
     nsAccessible* container = aContainerNode ?
       GetAccessibleOrContainer(aContainerNode) : this;
 
     mNotificationController->ScheduleContentInsertion(container,
                                                       aStartChildNode,
                                                       aEndChildNode);
@@ -1462,18 +1469,46 @@ nsDocAccessible::CacheChildren()
   nsAccessible* child = nsnull;
   while ((child = walker.NextChild()) && AppendChild(child));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
 void
-nsDocAccessible::NotifyOfInitialUpdate()
+nsDocAccessible::NotifyOfLoading(bool aIsReloading)
 {
+  // Mark the document accessible as loading, if it stays alive then we'll mark
+  // it as loaded when we receive proper notification.
+  mLoadState &= ~eDOMLoaded;
+
+  if (!IsLoadEventTarget())
+    return;
+
+  if (aIsReloading) {
+    // Fire reload and state busy events on existing document accessible while
+    // event from user input flag can be calculated properly and accessible
+    // is alive. When new document gets loaded then this one is destroyed.
+    nsRefPtr<AccEvent> reloadEvent =
+      new AccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, this);
+    nsEventShell::FireEvent(reloadEvent);
+  }
+
+  // Fire state busy change event. Use delayed event since we don't care
+  // actually if event isn't delivered when the document goes away like a shot.
+  nsRefPtr<AccEvent> stateEvent =
+    new AccStateChangeEvent(mDocument, states::BUSY, PR_TRUE);
+  FireDelayedAccessibleEvent(stateEvent);
+}
+
+void
+nsDocAccessible::DoInitialUpdate()
+{
+  mLoadState |= eTreeConstructed;
+
   // The content element may be changed before the initial update and then we
   // miss the notification (since content tree change notifications are ignored
   // prior to initial update). Make sure the content element is valid.
   nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocument);
   if (contentElm && mContent != contentElm)
     mContent = contentElm;
 
   // Build initial tree.
@@ -1487,16 +1522,44 @@ nsDocAccessible::NotifyOfInitialUpdate()
     nsRefPtr<AccEvent> reorderEvent =
       new AccEvent(nsIAccessibleEvent::EVENT_REORDER, Parent(), eAutoDetect,
                    AccEvent::eCoalesceFromSameSubtree);
     ParentDocument()->FireDelayedAccessibleEvent(reorderEvent);
   }
 }
 
 void
+nsDocAccessible::ProcessLoad()
+{
+  mLoadState |= eCompletelyLoaded;
+
+  // Do not fire document complete/stop events for root chrome document
+  // accessibles and for frame/iframe documents because
+  // a) screen readers start working on focus event in the case of root chrome
+  // documents
+  // b) document load event on sub documents causes screen readers to act is if
+  // entire page is reloaded.
+  if (!IsLoadEventTarget())
+    return;
+
+  // Fire complete/load stopped if the load event type is given.
+  if (mLoadEventType) {
+    nsRefPtr<AccEvent> loadEvent = new AccEvent(mLoadEventType, this);
+    nsEventShell::FireEvent(loadEvent);
+
+    mLoadEventType = 0;
+  }
+
+  // Fire busy state change event.
+  nsRefPtr<AccEvent> stateEvent =
+    new AccStateChangeEvent(this, states::BUSY, PR_FALSE);
+  nsEventShell::FireEvent(stateEvent);
+}
+
+void
 nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider,
                                     nsIAtom* aRelAttr)
 {
   for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
     nsIAtom* relAttr = *kRelationAttrs[idx];
     if (aRelAttr && aRelAttr != relAttr)
       continue;
 
@@ -1959,8 +2022,35 @@ nsDocAccessible::ShutdownChildrenInSubtr
       jdx++;
     }
 
     ShutdownChildrenInSubtree(child);
   }
 
   UnbindFromDocument(aAccessible);
 }
+
+bool
+nsDocAccessible::IsLoadEventTarget() const
+{
+  nsCOMPtr<nsISupports> container = mDocument->GetContainer();
+  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
+    do_QueryInterface(container);
+  NS_ASSERTION(docShellTreeItem, "No document shell for document!");
+
+  nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
+  docShellTreeItem->GetParent(getter_AddRefs(parentTreeItem));
+
+  // It's not a root document.
+  if (parentTreeItem) {
+    nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
+    docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
+
+    // It's not a sub document, i.e. a frame or iframe.
+    return (sameTypeRoot == docShellTreeItem);
+  }
+
+  // It's not chrome root document.
+  PRInt32 contentType;
+  docShellTreeItem->GetItemType(&contentType);
+  return (contentType == nsIDocShellTreeItem::typeContent);
+}
+
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -129,29 +129,44 @@ public:
 
   // nsDocAccessible
 
   /**
    * Return true if associated DOM document was loaded and isn't unloading.
    */
   PRBool IsContentLoaded() const
   {
+    // eDOMLoaded flag check is used for error pages as workaround to make this
+    // method return correct result since error pages do not receive 'pageshow'
+    // event and as consequence nsIDocument::IsShowing() returns false.
     return mDocument && mDocument->IsVisible() &&
-      (mDocument->IsShowing() || mIsLoaded);
+      (mDocument->IsShowing() || HasLoadState(eDOMLoaded));
   }
 
   /**
-   * Marks this document as loaded or loading, used to expose busy state.
-   * The loaded flag has special meaning for error pages and used as workaround
-   * to make IsContentLoaded() return correct result since these pages do not
-   * receive pageshow event and as consequence nsIDocument::IsShowing() returns
-   * false.
+   * Document load states.
    */
-  void MarkAsLoaded() { mIsLoaded = PR_TRUE; }
-  void MarkAsLoading() { mIsLoaded = PR_FALSE; }
+  enum LoadState {
+    // initial tree construction is pending
+    eTreeConstructionPending = 0,
+    // initial tree construction done
+    eTreeConstructed = 1,
+    // DOM document is loaded.
+    eDOMLoaded = 1 << 1,
+    // document is ready
+    eReady = eTreeConstructed | eDOMLoaded,
+    // document and all its subdocuments are ready
+    eCompletelyLoaded = eReady | 1 << 2
+  };
+
+  /**
+   * Return true if the document has given document state.
+   */
+  bool HasLoadState(LoadState aState) const
+    { return (mLoadState & aState) == aState; }
 
   /**
    * Return a native window handler or pointer depending on platform.
    */
   virtual void* GetNativeWindow() const;
 
   /**
    * Return the parent document.
@@ -230,19 +245,29 @@ public:
    *
    * @return the accessible object
    */
   nsAccessible* GetAccessible(nsINode* aNode) const;
 
   /**
    * Return whether the given DOM node has an accessible or not.
    */
-  inline bool HasAccessible(nsINode* aNode)
+  inline bool HasAccessible(nsINode* aNode) const
+    { return GetAccessible(aNode); }
+
+  /**
+   * Return true if the given accessible is in document.
+   */
+  inline bool IsInDocument(nsAccessible* aAccessible) const
   {
-    return GetAccessible(aNode);
+    nsAccessible* acc = aAccessible;
+    while (acc && !acc->IsPrimaryForNode())
+      acc = acc->Parent();
+
+    return acc ? mNodeToAccessibleMap.Get(acc->GetNode()) : false;
   }
 
   /**
    * Return the cached accessible by the given unique ID within this document.
    *
    * @note   the unique ID matches with the uniqueID() of nsAccessNode
    *
    * @param  aUniqueID  [in] the unique ID used to cache the node.
@@ -311,17 +336,18 @@ public:
 
   /**
    * Updates accessible tree when rendered text is changed.
    */
   inline void UpdateText(nsIContent* aTextNode)
   {
     NS_ASSERTION(mNotificationController, "The document was shut down!");
 
-    if (mNotificationController)
+    // Ignore the notification if initial tree construction hasn't been done yet.
+    if (mNotificationController && HasLoadState(eTreeConstructed))
       mNotificationController->ScheduleTextUpdate(aTextNode);
   }
 
   /**
    * Recreate an accessible, results in hide/show events pair.
    */
   void RecreateAccessible(nsIContent* aContent);
 
@@ -331,20 +357,39 @@ protected:
   virtual void CacheChildren();
 
   // nsDocAccessible
     virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
     virtual nsresult AddEventListeners();
     virtual nsresult RemoveEventListeners();
 
   /**
-   * Notify this document that was bound to the accessible document tree.
+   * Marks this document as loaded or loading.
+   */
+  inline void NotifyOfLoad(PRUint32 aLoadEventType)
+  {
+    mLoadState |= eDOMLoaded;
+    mLoadEventType = aLoadEventType;
+  }
+
+  void NotifyOfLoading(bool aIsReloading);
+
+  friend class nsAccDocManager;
+
+  /**
+   * Perform initial update (create accessible tree).
    * Can be overridden by wrappers to prepare initialization work.
    */
-  virtual void NotifyOfInitialUpdate();
+  virtual void DoInitialUpdate();
+
+  /**
+   * Process document load notification, fire document load and state busy
+   * events if applicable.
+   */
+  void ProcessLoad();
 
     void AddScrollListener();
     void RemoveScrollListener();
 
   /**
    * Append the given document accessible to this document's child document
    * accessibles.
    */
@@ -469,40 +514,63 @@ protected:
    * Shutdown any cached accessible in the subtree.
    *
    * @param aAccessible  [in] the root of the subrtee to invalidate accessible
    *                      child/parent refs in
    */
   void ShutdownChildrenInSubtree(nsAccessible *aAccessible);
 
   /**
+   * Return true if accessibility events accompanying document accessible
+   * loading should be fired.
+   *
+   * The rules are: do not fire events for root chrome document accessibles and
+   * for sub document accessibles (like HTML frame of iframe) of the loading
+   * document accessible.
+   *
+   * XXX: in general AT expect events for document accessible loading into
+   * tabbrowser, events from other document accessibles may break AT. We need to
+   * figure out what AT wants to know about loading page (for example, some of
+   * them have separate processing of iframe documents on the page and therefore
+   * they need a way to distinguish sub documents from page document). Ideally
+   * we should make events firing for any loaded document and provide additional
+   * info AT are needing.
+   */
+  bool IsLoadEventTarget() const;
+
+  /**
    * Used to fire scrolling end event after page scroll.
    *
    * @param aTimer    [in] the timer object
    * @param aClosure  [in] the document accessible where scrolling happens
    */
   static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure);
 
+protected:
+
   /**
    * Cache of accessibles within this document accessible.
    */
   nsAccessibleHashtable mAccessibleCache;
   nsDataHashtable<nsPtrHashKey<const nsINode>, nsAccessible*>
     mNodeToAccessibleMap;
 
     nsCOMPtr<nsIDocument> mDocument;
     nsCOMPtr<nsITimer> mScrollWatchTimer;
     PRUint16 mScrollPositionChangedTicks; // Used for tracking scroll events
 
-protected:
+  /**
+   * Bit mask of document load states (@see LoadState).
+   */
+  PRUint32 mLoadState;
 
   /**
-   * Specifies if the document was loaded, used for error pages only.
+   * Type of document load event fired after the document is loaded completely.
    */
-  PRPackedBool mIsLoaded;
+  PRUint32 mLoadEventType;
 
   static PRUint64 gLastFocusedAccessiblesState;
 
   nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments;
 
   /**
    * A storage class for pairing content with one of its relation attributes.
    */
--- a/accessible/src/base/nsFormControlAccessible.cpp
+++ b/accessible/src/base/nsFormControlAccessible.cpp
@@ -186,23 +186,20 @@ ProgressMeterAccessible<Max>::SetCurrent
 ////////////////////////////////////////////////////////////////////////////////
 
 nsRadioButtonAccessible::
   nsRadioButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsFormControlAccessible(aContent, aShell)
 {
 }
 
-NS_IMETHODIMP
-nsRadioButtonAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsRadioButtonAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-  *aNumActions = 1;
-
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP nsRadioButtonAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {
     aName.AssignLiteral("select"); 
     return NS_OK;
   }
--- a/accessible/src/base/nsFormControlAccessible.h
+++ b/accessible/src/base/nsFormControlAccessible.h
@@ -70,21 +70,23 @@ public:
   */
 class nsRadioButtonAccessible : public nsFormControlAccessible
 {
 
 public:
   nsRadioButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
   enum { eAction_Click = 0 };
 };
 
 
 #endif
 
--- a/accessible/src/base/nsOuterDocAccessible.cpp
+++ b/accessible/src/base/nsOuterDocAccessible.cpp
@@ -107,24 +107,21 @@ nsOuterDocAccessible::GetAttributesInter
     return NS_OK;
   }
   return nsAccessible::GetAttributesInternal(aAttributes);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessible
 
-NS_IMETHODIMP
-nsOuterDocAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsOuterDocAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-  *aNumActions = 0;
-
   // Internal frame, which is the doc's parent, should not have a click action.
-  return NS_OK;
+  return 0;
 }
 
 NS_IMETHODIMP
 nsOuterDocAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   return NS_ERROR_INVALID_ARG;
--- a/accessible/src/base/nsOuterDocAccessible.h
+++ b/accessible/src/base/nsOuterDocAccessible.h
@@ -53,17 +53,16 @@
 class nsOuterDocAccessible : public nsAccessibleWrap
 {
 public:
   nsOuterDocAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD GetActionDescription(PRUint8 aIndex, nsAString& aDescription);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
@@ -72,14 +71,17 @@ public:
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
   virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                      EWhichChildAtPoint aWhichChild);
 
   virtual void InvalidateChildren();
   virtual PRBool AppendChild(nsAccessible *aAccessible);
   virtual PRBool RemoveChild(nsAccessible *aAccessible);
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 protected:
   // nsAccessible
   virtual void CacheChildren();
 };
 
 #endif  
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -44,18 +44,17 @@
 #include "nsIAccessibleDocument.h"
 #ifdef MOZ_XUL
 #include "nsXULTreeAccessible.h"
 #endif
 
 #include "nsHashtable.h"
 #include "nsCaretAccessible.h"
 #include "nsIDocument.h"
-#include "nsIDOMFocusListener.h"
-#include "nsIDOMFormListener.h"
+#include "nsIDOMEventListener.h"
 
 #define NS_ROOTACCESSIBLE_IMPL_CID                      \
 {  /* eaba2cf0-21b1-4e2b-b711-d3a89dcd5e1a */           \
   0xeaba2cf0,                                           \
   0x21b1,                                               \
   0x4e2b,                                               \
   { 0xb7, 0x11, 0xd3, 0xa8, 0x9d, 0xcd, 0x5e, 0x1a }    \
 }
--- a/accessible/src/html/nsHTMLFormControlAccessible.cpp
+++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp
@@ -73,20 +73,20 @@ nsHTMLCheckboxAccessible::
 }
 
 PRUint32
 nsHTMLCheckboxAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_CHECKBUTTON;
 }
 
-NS_IMETHODIMP nsHTMLCheckboxAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsHTMLCheckboxAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP nsHTMLCheckboxAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {    // 0 is the magic value for default action
     // cycle, check or uncheck
     PRUint64 state = NativeState();
 
@@ -232,20 +232,20 @@ nsHTMLRadioButtonAccessible::GetPosition
 ////////////////////////////////////////////////////////////////////////////////
 
 nsHTMLButtonAccessible::
   nsHTMLButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsHyperTextAccessibleWrap(aContent, aShell)
 {
 }
 
-NS_IMETHODIMP nsHTMLButtonAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsHTMLButtonAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP nsHTMLButtonAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {
     aName.AssignLiteral("press"); 
     return NS_OK;
   }
@@ -320,20 +320,20 @@ nsHTMLButtonAccessible::GetNameInternal(
 ////////////////////////////////////////////////////////////////////////////////
 
 nsHTML4ButtonAccessible::
   nsHTML4ButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsHyperTextAccessibleWrap(aContent, aShell)
 {
 }
 
-NS_IMETHODIMP nsHTML4ButtonAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsHTML4ButtonAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;;
+  return 1;
 }
 
 NS_IMETHODIMP nsHTML4ButtonAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {
     aName.AssignLiteral("press"); 
     return NS_OK;
   }
@@ -509,20 +509,20 @@ nsHTMLTextFieldAccessible::NativeState()
       if (!formContent || !autocomplete.LowerCaseEqualsLiteral("off"))
         state |= states::SUPPORTS_AUTOCOMPLETION;
     }
   }
 
   return state;
 }
 
-NS_IMETHODIMP nsHTMLTextFieldAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsHTMLTextFieldAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;;
+  return 1;
 }
 
 NS_IMETHODIMP nsHTMLTextFieldAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {
     aName.AssignLiteral("activate");
     return NS_OK;
   }
--- a/accessible/src/html/nsHTMLFormControlAccessible.h
+++ b/accessible/src/html/nsHTMLFormControlAccessible.h
@@ -54,23 +54,25 @@ class nsHTMLCheckboxAccessible : public 
 {
 
 public:
   enum { eAction_Click = 0 };
 
   nsHTMLCheckboxAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 
 /**
  * Accessible for HTML input@type="radio" element.
  */
 class nsHTMLRadioButtonAccessible : public nsRadioButtonAccessible
 {
@@ -93,46 +95,50 @@ class nsHTMLButtonAccessible : public ns
 {
 
 public:
   enum { eAction_Click = 0 };
 
   nsHTMLButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 
 /**
  * Accessible for HTML button element.
  */
 class nsHTML4ButtonAccessible : public nsHyperTextAccessibleWrap
 {
 
 public:
   enum { eAction_Click = 0 };
 
   nsHTML4ButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 
 /**
  * Accessible for HTML input@type="text" element.
  */
 class nsHTMLTextFieldAccessible : public nsHyperTextAccessibleWrap
 {
@@ -141,27 +147,29 @@ public:
   enum { eAction_Click = 0 };
 
   nsHTMLTextFieldAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& _retval); 
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsIAccessibleEditableText
   NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 
 /**
  * Accessible for HTML fieldset element.
  */
 class nsHTMLGroupboxAccessible : public nsHyperTextAccessibleWrap
 {
--- a/accessible/src/html/nsHTMLImageAccessible.cpp
+++ b/accessible/src/html/nsHTMLImageAccessible.cpp
@@ -126,32 +126,21 @@ PRUint32
 nsHTMLImageAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_GRAPHIC;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessible
 
-NS_IMETHODIMP
-nsHTMLImageAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsHTMLImageAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-  *aNumActions = 0;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
-  nsresult rv= nsLinkableAccessible::GetNumActions(aNumActions);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (HasLongDesc())
-    (*aNumActions)++;
-
-  return NS_OK;
+  PRUint8 actionCount = nsLinkableAccessible::ActionCount();
+  return HasLongDesc() ? actionCount + 1 : actionCount;
 }
 
 NS_IMETHODIMP
 nsHTMLImageAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   if (IsDefunct())
@@ -243,14 +232,10 @@ nsHTMLImageAccessible::HasLongDesc()
 }
 
 PRBool
 nsHTMLImageAccessible::IsValidLongDescIndex(PRUint8 aIndex)
 {
   if (!HasLongDesc())
     return PR_FALSE;
 
-  PRUint8 numActions = 0;
-  nsresult rv = nsLinkableAccessible::GetNumActions(&numActions);  
-  NS_ENSURE_SUCCESS(rv, PR_FALSE);
-
-  return (aIndex == numActions);
+  return aIndex == nsLinkableAccessible::ActionCount();
 }
--- a/accessible/src/html/nsHTMLImageAccessible.h
+++ b/accessible/src/html/nsHTMLImageAccessible.h
@@ -52,29 +52,31 @@ class nsHTMLImageAccessible : public nsL
 {
 public:
   nsHTMLImageAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsIAccessibleImage
   NS_DECL_NSIACCESSIBLEIMAGE
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 private:
   /**
    * Determine if this image accessible has a longdesc attribute.
    *
    * @returns  true if the longdesc attribute is present.
    */
   PRBool HasLongDesc();
   
--- a/accessible/src/html/nsHTMLLinkAccessible.cpp
+++ b/accessible/src/html/nsHTMLLinkAccessible.cpp
@@ -115,26 +115,20 @@ nsHTMLLinkAccessible::GetValue(nsAString
   if (!aValue.IsEmpty())
     return NS_OK;
   
   nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
   nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
   return presShell->GetLinkLocation(DOMNode, aValue);
 }
 
-NS_IMETHODIMP
-nsHTMLLinkAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsHTMLLinkAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-
-  if (!IsLinked())
-    return nsHyperTextAccessible::GetNumActions(aNumActions);
-
-  *aNumActions = 1;
-  return NS_OK;
+  return IsLinked() ? 1 : nsHyperTextAccessible::ActionCount();
 }
 
 NS_IMETHODIMP
 nsHTMLLinkAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   if (!IsLinked())
--- a/accessible/src/html/nsHTMLLinkAccessible.h
+++ b/accessible/src/html/nsHTMLLinkAccessible.h
@@ -47,24 +47,26 @@ class nsHTMLLinkAccessible : public nsHy
 public:
   nsHTMLLinkAccessible(nsIContent *aContent, nsIWeakReference *aShell);
  
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
 
-  NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
   // HyperLinkAccessible
   virtual bool IsLink();
   virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);
 
 protected:
   enum { eAction_Jump = 0 };
 
   /**
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -366,20 +366,20 @@ NS_IMETHODIMP nsHTMLSelectOptionAccessib
 {
   if (aIndex == eAction_Select) {
     aName.AssignLiteral("select"); 
     return NS_OK;
   }
   return NS_ERROR_INVALID_ARG;
 }
 
-NS_IMETHODIMP nsHTMLSelectOptionAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsHTMLSelectOptionAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP nsHTMLSelectOptionAccessible::DoAction(PRUint8 index)
 {
   if (index == eAction_Select) {   // default action
     nsCOMPtr<nsIDOMHTMLOptionElement> newHTMLOption(do_QueryInterface(mContent));
     if (!newHTMLOption) 
       return NS_ERROR_FAILURE;
@@ -611,19 +611,20 @@ NS_IMETHODIMP nsHTMLSelectOptGroupAccess
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsHTMLSelectOptGroupAccessible::ActionCount()
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return 0;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectOptGroupAccessible: nsAccessible protected
 
 void
 nsHTMLSelectOptGroupAccessible::CacheChildren()
 {
@@ -768,21 +769,20 @@ nsHTMLComboboxAccessible::GetFocusedOpti
   */
 NS_IMETHODIMP nsHTMLComboboxAccessible::GetValue(nsAString& aValue)
 {
   // Use accessible name of currently focused option.
   nsAccessible *option = GetFocusedOptionAccessible();
   return option ? option->GetName(aValue) : NS_OK;
 }
 
-/** Just one action ( click ). */
-NS_IMETHODIMP nsHTMLComboboxAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsHTMLComboboxAccessible::ActionCount()
 {
-  *aNumActions = 1;
-  return NS_OK;
+  return 1;
 }
 
 /**
   * Programmaticaly toggle the combo box
   */
 NS_IMETHODIMP nsHTMLComboboxAccessible::DoAction(PRUint8 aIndex)
 {
   if (aIndex != nsHTMLComboboxAccessible::eAction_Click) {
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -103,28 +103,30 @@ public:
   enum { eAction_Select = 0 };  
   
   nsHTMLSelectOptionAccessible(nsIContent *aContent, nsIWeakReference *aShell);
   virtual ~nsHTMLSelectOptionAccessible() {}
 
   // nsIAccessible
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD SetSelected(PRBool aSelect);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
   virtual PRInt32 GetLevelInternal();
   virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                           PRInt32 *aSetSize);
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
   /**
    * Return focused option if any.
    */
   static already_AddRefed<nsIContent> GetFocusedOption(nsIContent *aListNode);
 
   static void SelectionChangedIfOption(nsIContent *aPossibleOption);
 
 protected:
@@ -149,22 +151,24 @@ class nsHTMLSelectOptGroupAccessible : p
 public:
 
   nsHTMLSelectOptGroupAccessible(nsIContent *aContent, nsIWeakReference *aShell);
   virtual ~nsHTMLSelectOptGroupAccessible() {}
 
   // nsIAccessible
   NS_IMETHOD DoAction(PRUint8 index);  
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 protected:
   // nsAccessible
   virtual void CacheChildren();
 };
 
 /** ------------------------------------------------------ */
 /**  Finally, the Combobox widgets                         */
 /** ------------------------------------------------------ */
@@ -180,28 +184,30 @@ public:
   enum { eAction_Click = 0 };
 
   nsHTMLComboboxAccessible(nsIContent *aContent, nsIWeakReference *aShell);
   virtual ~nsHTMLComboboxAccessible() {}
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& _retval);
   NS_IMETHOD DoAction(PRUint8 index);
-  NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
   virtual void Description(nsString& aDescription);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual void InvalidateChildren();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 protected:
   // nsAccessible
   virtual void CacheChildren();
 
   // nsHTMLComboboxAccessible
 
   /**
    * Return focused option accessible.
--- a/accessible/src/msaa/CAccessibleAction.cpp
+++ b/accessible/src/msaa/CAccessibleAction.cpp
@@ -58,31 +58,29 @@ CAccessibleAction::QueryInterface(REFIID
   }
 
   return E_NOINTERFACE;
 }
 
 // IAccessibleAction
 
 STDMETHODIMP
-CAccessibleAction::nActions(long *aNumActions)
+CAccessibleAction::nActions(long* aActionCount)
 {
 __try {
-  *aNumActions = 0;
+  if (!aActionCount)
+    return E_INVALIDARG;
 
-  nsCOMPtr<nsIAccessible> acc(do_QueryObject(this));
-  if (!acc)
+  *aActionCount = 0;
+
+  nsRefPtr<nsAccessible> acc(do_QueryObject(this));
+  if (!acc || acc->IsDefunct())
     return E_FAIL;
 
-  PRUint8 count = 0;
-  nsresult rv = acc->GetNumActions(&count);
-  if (NS_FAILED(rv))
-    return GetHRESULT(rv);
-
-  *aNumActions = count;
+  *aActionCount = acc->ActionCount();
   return S_OK;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
 CAccessibleAction::doAction(long aActionIndex)
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -229,19 +229,26 @@ STDMETHODIMP nsAccessibleWrap::get_accPa
 
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return S_OK;
 }
 
 STDMETHODIMP nsAccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
 {
 __try {
+  if (!pcountChildren)
+    return E_INVALIDARG;
+
   *pcountChildren = 0;
+
+  if (IsDefunct())
+    return E_FAIL;
+
   if (nsAccUtils::MustPrune(this))
-    return NS_OK;
+    return S_OK;
 
   *pcountChildren = GetChildCount();
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
 
   return S_OK;
 }
 
 STDMETHODIMP nsAccessibleWrap::get_accChild(
--- a/accessible/src/msaa/nsDocAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsDocAccessibleWrap.cpp
@@ -263,19 +263,19 @@ nsDocAccessibleWrap::GetNativeWindow() c
 {
   return mHWND ? mHWND : nsDocAccessible::GetNativeWindow();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsDocAccessible protected
 
 void
-nsDocAccessibleWrap::NotifyOfInitialUpdate()
+nsDocAccessibleWrap::DoInitialUpdate()
 {
-  nsDocAccessible::NotifyOfInitialUpdate();
+  nsDocAccessible::DoInitialUpdate();
 
   if (nsWinUtils::IsWindowEmulationStarted()) {
     // Create window for tab document.
     if (nsCoreUtils::IsTabDocument(mDocument)) {
       mozilla::dom::TabChild* tabChild =
         mozilla::dom::GetTabChildFrom(mDocument->GetShell());
 
       nsRootAccessible* rootDocument = RootAccessible();
--- a/accessible/src/msaa/nsDocAccessibleWrap.h
+++ b/accessible/src/msaa/nsDocAccessibleWrap.h
@@ -92,15 +92,15 @@ public:
   // nsAccessNode
   virtual void Shutdown();
 
   // nsDocAccessible
   virtual void* GetNativeWindow() const;
 
 protected:
   // nsDocAccessible
-  virtual void NotifyOfInitialUpdate();
+  virtual void DoInitialUpdate();
 
 protected:
   void* mHWND;
 };
 
 #endif
--- a/accessible/src/xforms/nsXFormsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsAccessible.cpp
@@ -540,23 +540,20 @@ nsXFormsSelectableItemAccessible::
 
 NS_IMETHODIMP
 nsXFormsSelectableItemAccessible::GetValue(nsAString& aValue)
 {
   nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
   return sXFormsService->GetValue(DOMNode, aValue);
 }
 
-NS_IMETHODIMP
-nsXFormsSelectableItemAccessible::GetNumActions(PRUint8 *aCount)
+PRUint8
+nsXFormsSelectableItemAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aCount);
-
-  *aCount = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXFormsSelectableItemAccessible::DoAction(PRUint8 aIndex)
 {
   if (aIndex != eAction_Click)
     return NS_ERROR_INVALID_ARG;
 
--- a/accessible/src/xforms/nsXFormsAccessible.h
+++ b/accessible/src/xforms/nsXFormsAccessible.h
@@ -185,17 +185,19 @@ protected:
  */
 class nsXFormsSelectableItemAccessible : public nsXFormsAccessible
 {
 public:
   nsXFormsSelectableItemAccessible(nsIContent *aContent,
                                    nsIWeakReference *aShell);
 
   NS_IMETHOD GetValue(nsAString& aValue);
-  NS_IMETHOD GetNumActions(PRUint8 *aCount);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 protected:
   bool IsSelected();
 };
 
 #endif
 
--- a/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp
@@ -110,23 +110,20 @@ nsXFormsTriggerAccessible::NativeRole()
 
 NS_IMETHODIMP
 nsXFormsTriggerAccessible::GetValue(nsAString& aValue)
 {
   aValue.Truncate();
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsXFormsTriggerAccessible::GetNumActions(PRUint8 *aCount)
+PRUint8
+nsXFormsTriggerAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aCount);
-
-  *aCount = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXFormsTriggerAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {
     aName.AssignLiteral("press");
     return NS_OK;
@@ -158,23 +155,20 @@ nsXFormsInputAccessible::
 NS_IMPL_ISUPPORTS_INHERITED3(nsXFormsInputAccessible, nsAccessible, nsHyperTextAccessible, nsIAccessibleText, nsIAccessibleEditableText)
 
 PRUint32
 nsXFormsInputAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_ENTRY;
 }
 
-NS_IMETHODIMP
-nsXFormsInputAccessible::GetNumActions(PRUint8* aCount)
+PRUint8
+nsXFormsInputAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aCount);
-
-  *aCount = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXFormsInputAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex != eAction_Click)
     return NS_ERROR_INVALID_ARG;
 
@@ -220,23 +214,20 @@ nsXFormsInputBooleanAccessible::NativeSt
   NS_ENSURE_SUCCESS(rv, state);
 
   if (value.EqualsLiteral("true"))
     state |= states::CHECKED;
 
   return state;
 }
 
-NS_IMETHODIMP
-nsXFormsInputBooleanAccessible::GetNumActions(PRUint8 *aCount)
+PRUint8
+nsXFormsInputBooleanAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aCount);
-
-  *aCount = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXFormsInputBooleanAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex != eAction_Click)
     return NS_ERROR_INVALID_ARG;
 
--- a/accessible/src/xforms/nsXFormsFormControlsAccessible.h
+++ b/accessible/src/xforms/nsXFormsFormControlsAccessible.h
@@ -76,61 +76,67 @@ public:
 class nsXFormsTriggerAccessible : public nsXFormsAccessible
 {
 public:
   nsXFormsTriggerAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
 
-  NS_IMETHOD GetNumActions(PRUint8 *aCount);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 /**
  * Accessible object for xforms:input and xforms:textarea.
  */
 
 class nsXFormsInputAccessible : public nsXFormsEditableAccessible
 {
 public:
   nsXFormsInputAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *aCount);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 /**
  * Accessible object for xforms:input[type="xsd:boolean"].
  */
 
 class nsXFormsInputBooleanAccessible : public nsXFormsAccessible
 {
 public:
   nsXFormsInputBooleanAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *aCount);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 /**
  * Accessible object for xforms:input[type="xsd:date"].
  */
 
 class nsXFormsInputDateAccessible : public nsXFormsContainerAccessible
 {
--- a/accessible/src/xforms/nsXFormsWidgetsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsWidgetsAccessible.cpp
@@ -65,23 +65,20 @@ nsXFormsDropmarkerWidgetAccessible::Nati
   PRBool isOpen = PR_FALSE;
   nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
   nsresult rv = sXFormsService->IsDropmarkerOpen(DOMNode, &isOpen);
   NS_ENSURE_SUCCESS(rv, 0);
 
   return isOpen ? states::PRESSED: 0;
 }
 
-NS_IMETHODIMP
-nsXFormsDropmarkerWidgetAccessible::GetNumActions(PRUint8 *aCount)
+PRUint8
+nsXFormsDropmarkerWidgetAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aCount);
-
-  *aCount = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXFormsDropmarkerWidgetAccessible::GetActionName(PRUint8 aIndex,
                                                   nsAString& aName)
 {
   if (aIndex != eAction_Click)
     return NS_ERROR_INVALID_ARG;
--- a/accessible/src/xforms/nsXFormsWidgetsAccessible.h
+++ b/accessible/src/xforms/nsXFormsWidgetsAccessible.h
@@ -50,23 +50,25 @@
 class nsXFormsDropmarkerWidgetAccessible : public nsLeafAccessible,
                                            public nsXFormsAccessibleBase
 {
 public:
   nsXFormsDropmarkerWidgetAccessible(nsIContent *aContent,
                                      nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *aCount);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 
 /**
  * Accessible object for calendar widget. It is used by xforms:input[xsd:date].
  */
 class nsXFormsCalendarWidgetAccessible : public nsAccessibleWrap
 {
--- a/accessible/src/xul/nsXULComboboxAccessible.cpp
+++ b/accessible/src/xul/nsXULComboboxAccessible.cpp
@@ -146,25 +146,21 @@ nsXULComboboxAccessible::GetAllowsAnonCh
     // so that the entry field is a child
     return PR_TRUE;
   }
 
   // Argument of PR_FALSE indicates we don't walk anonymous children for
   // menuitems
   return PR_FALSE;
 }
-
-NS_IMETHODIMP
-nsXULComboboxAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsXULComboboxAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-
   // Just one action (click).
-  *aNumActions = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXULComboboxAccessible::DoAction(PRUint8 aIndex)
 {
   if (aIndex != nsXULComboboxAccessible::eAction_Click) {
     return NS_ERROR_INVALID_ARG;
   }
--- a/accessible/src/xul/nsXULComboboxAccessible.h
+++ b/accessible/src/xul/nsXULComboboxAccessible.h
@@ -51,19 +51,21 @@ class nsXULComboboxAccessible : public n
 public:
   enum { eAction_Click = 0 };
 
   nsXULComboboxAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
   NS_IMETHOD DoAction(PRUint8 aIndex);
-  NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
 
   // nsAccessible
   virtual void Description(nsString& aDescription);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual PRBool GetAllowsAnonChildAccessibles();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 #endif
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -77,23 +77,20 @@ nsXULButtonAccessible::
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULButtonAccessible: nsISupports
 
 NS_IMPL_ISUPPORTS_INHERITED0(nsXULButtonAccessible, nsAccessible)
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULButtonAccessible: nsIAccessible
 
-NS_IMETHODIMP
-nsXULButtonAccessible::GetNumActions(PRUint8 *aCount)
+PRUint8
+nsXULButtonAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aCount);
-
-  *aCount = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXULButtonAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {
     aName.AssignLiteral("press"); 
     return NS_OK;
@@ -244,20 +241,20 @@ nsXULButtonAccessible::ContainsMenu()
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULDropmarkerAccessible::
   nsXULDropmarkerAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsFormControlAccessible(aContent, aShell)
 {
 }
 
-NS_IMETHODIMP nsXULDropmarkerAccessible::GetNumActions(PRUint8 *aResult)
+PRUint8
+nsXULDropmarkerAccessible::ActionCount()
 {
-  *aResult = 1;
-  return NS_OK;
+  return 1;
 }
 
 PRBool nsXULDropmarkerAccessible::DropmarkerOpen(PRBool aToggleOpen)
 {
   PRBool isOpen = PR_FALSE;
 
   nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
     do_QueryInterface(mContent->GetParent());
@@ -315,37 +312,36 @@ nsXULDropmarkerAccessible::NativeRole()
 }
 
 PRUint64
 nsXULDropmarkerAccessible::NativeState()
 {
   return DropmarkerOpen(PR_FALSE) ? states::PRESSED : 0;
 }
 
-                      
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULCheckboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULCheckboxAccessible::
   nsXULCheckboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsFormControlAccessible(aContent, aShell)
 {
 }
 
 PRUint32
 nsXULCheckboxAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_CHECKBUTTON;
 }
 
-NS_IMETHODIMP nsXULCheckboxAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsXULCheckboxAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;
+  return 1;
 }
 
 /**
   * Return the name of our only action
   */
 NS_IMETHODIMP nsXULCheckboxAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {
@@ -681,16 +677,19 @@ nsXULTextFieldAccessible::
 
 NS_IMPL_ISUPPORTS_INHERITED3(nsXULTextFieldAccessible, nsAccessible, nsHyperTextAccessible, nsIAccessibleText, nsIAccessibleEditableText)
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTextFieldAccessible: nsIAccessible
 
 NS_IMETHODIMP nsXULTextFieldAccessible::GetValue(nsAString& aValue)
 {
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
   PRUint64 state = NativeState();
 
   if (state & states::PROTECTED)    // Don't return password text!
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIDOMXULTextBoxElement> textBox(do_QueryInterface(mContent));
   if (textBox) {
     return textBox->GetValue(aValue);
@@ -748,24 +747,23 @@ PRUint32
 nsXULTextFieldAccessible::NativeRole()
 {
   if (mContent->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
                             nsAccessibilityAtoms::password, eIgnoreCase))
     return nsIAccessibleRole::ROLE_PASSWORD_TEXT;
   return nsIAccessibleRole::ROLE_ENTRY;
 }
 
-
 /**
   * Only one actions available
   */
-NS_IMETHODIMP nsXULTextFieldAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsXULTextFieldAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;
+  return 1;
 }
 
 /**
   * Return the name of our only action
   */
 NS_IMETHODIMP nsXULTextFieldAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click) {
--- a/accessible/src/xul/nsXULFormControlAccessible.h
+++ b/accessible/src/xul/nsXULFormControlAccessible.h
@@ -62,24 +62,26 @@ class nsXULButtonAccessible : public nsA
 public:
   enum { eAction_Click = 0 };
   nsXULButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
 
   // nsXULButtonAccessible
   PRBool ContainsMenu();
 };
@@ -90,43 +92,47 @@ protected:
  */
 class nsXULCheckboxAccessible : public nsFormControlAccessible
 {
 public:
   enum { eAction_Click = 0 };
   nsXULCheckboxAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 /**
  * Used for XUL dropmarker element.
  */
 class nsXULDropmarkerAccessible : public nsFormControlAccessible
 {
 public:
   enum { eAction_Click = 0 };
   nsXULDropmarkerAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 private:
   PRBool DropmarkerOpen(PRBool aToggleOpen);
 };
 
 /**
  * Used for XUL groupbox element.
  */
 class nsXULGroupboxAccessible : public nsAccessibleWrap
@@ -235,29 +241,31 @@ public:
   enum { eAction_Click = 0 };
 
   nsXULTextFieldAccessible(nsIContent* aContent, nsIWeakReference *aShell);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsIAccessibleEditableText
   NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
 
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual PRBool GetAllowsAnonChildAccessibles();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 protected:
   // nsAccessible
   virtual void CacheChildren();
 
   // nsXULTextFieldAccessible
   already_AddRefed<nsIContent> GetInputField() const;
 };
 
--- a/accessible/src/xul/nsXULListboxAccessible.cpp
+++ b/accessible/src/xul/nsXULListboxAccessible.cpp
@@ -90,23 +90,20 @@ nsXULColumnItemAccessible::NativeRole()
 }
 
 PRUint64
 nsXULColumnItemAccessible::NativeState()
 {
   return states::READONLY;
 }
 
-NS_IMETHODIMP
-nsXULColumnItemAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsXULColumnItemAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-
-  *aNumActions = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXULColumnItemAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex != eAction_Click)
     return NS_ERROR_INVALID_ARG;
 
--- a/accessible/src/xul/nsXULListboxAccessible.h
+++ b/accessible/src/xul/nsXULListboxAccessible.h
@@ -67,24 +67,26 @@ public:
  * (xul:listcol and xul:treecol).
  */
 class nsXULColumnItemAccessible : public nsLeafAccessible
 {
 public:
   nsXULColumnItemAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
   enum { eAction_Click = 0 };
 };
 
 /*
  * A class the represents the XUL Listbox widget.
  */
 class nsXULListboxAccessible : public nsXULSelectableAccessible,
                                public nsIAccessibleTable
--- a/accessible/src/xul/nsXULMenuAccessible.cpp
+++ b/accessible/src/xul/nsXULMenuAccessible.cpp
@@ -560,20 +560,20 @@ NS_IMETHODIMP nsXULMenuitemAccessible::G
 {
   if (aIndex == eAction_Click) {
     aName.AssignLiteral("click"); 
     return NS_OK;
   }
   return NS_ERROR_INVALID_ARG;
 }
 
-NS_IMETHODIMP nsXULMenuitemAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsXULMenuitemAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;
+  return 1;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULMenuSeparatorAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenuSeparatorAccessible::
@@ -607,22 +607,22 @@ NS_IMETHODIMP nsXULMenuSeparatorAccessib
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsXULMenuSeparatorAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsXULMenuSeparatorAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsXULMenuSeparatorAccessible::ActionCount()
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return 0;
 }
 
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULMenupopupAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenupopupAccessible::
   nsXULMenupopupAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsXULSelectableAccessible(aContent, aShell)
 { 
--- a/accessible/src/xul/nsXULMenuAccessible.h
+++ b/accessible/src/xul/nsXULMenuAccessible.h
@@ -79,51 +79,53 @@ class nsXULMenuitemAccessible : public n
 public:
   enum { eAction_Click = 0 };
 
   nsXULMenuitemAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
 
   // nsAccessible
   virtual void Description(nsString& aDescription);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual PRInt32 GetLevelInternal();
   virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                           PRInt32 *aSetSize);
 
   virtual PRBool GetAllowsAnonChildAccessibles();
 
   // ActionAccessible
+  virtual PRUint8 ActionCount();
   virtual KeyBinding AccessKey() const;
   virtual KeyBinding KeyboardShortcut() const;
 };
 
 /**
  * Used for XUL menuseparator element.
  */
 class nsXULMenuSeparatorAccessible : public nsXULMenuitemAccessible
 {
 public:
   nsXULMenuSeparatorAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 
 /**
  * Used for XUL menupopup and panel.
  */
 class nsXULMenupopupAccessible : public nsXULSelectableAccessible
 {
--- a/accessible/src/xul/nsXULSliderAccessible.cpp
+++ b/accessible/src/xul/nsXULSliderAccessible.cpp
@@ -92,23 +92,20 @@ nsXULSliderAccessible::NativeState()
 // nsIAccessible
 
 NS_IMETHODIMP
 nsXULSliderAccessible::GetValue(nsAString& aValue)
 {
   return GetSliderAttr(nsAccessibilityAtoms::curpos, aValue);
 }
 
-NS_IMETHODIMP
-nsXULSliderAccessible::GetNumActions(PRUint8 *aCount)
+PRUint8
+nsXULSliderAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aCount);
-
-  *aCount = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXULSliderAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   NS_ENSURE_ARG(aIndex == 0);
--- a/accessible/src/xul/nsXULSliderAccessible.h
+++ b/accessible/src/xul/nsXULSliderAccessible.h
@@ -51,28 +51,30 @@ class nsXULSliderAccessible : public nsA
 public:
   nsXULSliderAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
-  NS_IMETHOD GetNumActions(PRUint8 *aCount);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsIAccessibleValue
   NS_DECL_NSIACCESSIBLEVALUE
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual PRBool GetAllowsAnonChildAccessibles();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
 protected:
   already_AddRefed<nsIContent> GetSliderNode();
 
   nsresult GetSliderAttr(nsIAtom *aName, nsAString& aValue);
   nsresult SetSliderAttr(nsIAtom *aName, const nsAString& aValue);
 
   nsresult GetSliderAttr(nsIAtom *aName, double *aValue);
   nsresult SetSliderAttr(nsIAtom *aName, double aValue);
--- a/accessible/src/xul/nsXULTabAccessible.cpp
+++ b/accessible/src/xul/nsXULTabAccessible.cpp
@@ -60,20 +60,20 @@ nsXULTabAccessible::
   nsXULTabAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTabAccessible: nsIAccessible
 
-NS_IMETHODIMP nsXULTabAccessible::GetNumActions(PRUint8 *_retval)
+PRUint8
+nsXULTabAccessible::ActionCount()
 {
-  *_retval = 1;
-  return NS_OK;
+  return 1;
 }
 
 /** Return the name of our only action  */
 NS_IMETHODIMP nsXULTabAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Switch) {
     aName.AssignLiteral("switch"); 
     return NS_OK;
@@ -186,23 +186,20 @@ nsXULTabsAccessible::
 }
 
 PRUint32
 nsXULTabsAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_PAGETABLIST;
 }
 
-NS_IMETHODIMP
-nsXULTabsAccessible::GetNumActions(PRUint8 *aCount)
+PRUint8
+nsXULTabsAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aCount);
-  *aCount = 0;
-
-  return NS_OK;
+  return 0;
 }
 
 /** no value */
 NS_IMETHODIMP nsXULTabsAccessible::GetValue(nsAString& _retval)
 {
   return NS_OK;
 }
 
--- a/accessible/src/xul/nsXULTabAccessible.h
+++ b/accessible/src/xul/nsXULTabAccessible.h
@@ -49,45 +49,49 @@
 class nsXULTabAccessible : public nsAccessibleWrap
 {
 public:
   enum { eAction_Switch = 0 };
 
   nsXULTabAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
                                nsIAccessibleRelation **aRelation);
 
   // nsAccessible
   virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                           PRInt32 *aSetSize);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 
 /**
  * A container of tab objects, xul:tabs element.
  */
 class nsXULTabsAccessible : public nsXULSelectableAccessible
 {
 public:
   nsXULTabsAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
-  NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetValue(nsAString& _retval);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 };
 
 
 /** 
  * A container of tab panels, xul:tabpanels element.
  */
 class nsXULTabpanelsAccessible : public nsAccessibleWrap
 {
--- a/accessible/src/xul/nsXULTextAccessible.cpp
+++ b/accessible/src/xul/nsXULTextAccessible.cpp
@@ -184,23 +184,20 @@ nsXULLinkAccessible::NativeRole()
 
 
 PRUint64
 nsXULLinkAccessible::NativeState()
 {
   return nsHyperTextAccessible::NativeState() | states::LINKED;
 }
 
-NS_IMETHODIMP
-nsXULLinkAccessible::GetNumActions(PRUint8 *aNumActions)
+PRUint8
+nsXULLinkAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aNumActions);
-  
-  *aNumActions = 1;
-  return NS_OK;
+  return 1;
 }
 
 NS_IMETHODIMP
 nsXULLinkAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   if (aIndex != eAction_Jump)
--- a/accessible/src/xul/nsXULTextAccessible.h
+++ b/accessible/src/xul/nsXULTextAccessible.h
@@ -82,25 +82,27 @@ class nsXULLinkAccessible : public nsHyp
 public:
   nsXULLinkAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
 
-  NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
   // HyperLinkAccessible
   virtual bool IsLink();
   virtual PRUint32 StartOffset();
   virtual PRUint32 EndOffset();
   virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);
 
 protected:
   enum { eAction_Jump = 0 };
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -779,29 +779,22 @@ nsXULTreeItemAccessibleBase::GetRelation
     }
 
     return NS_OK;
   }
 
   return nsAccessible::GetRelationByType(aRelationType, aRelation);
 }
 
-NS_IMETHODIMP
-nsXULTreeItemAccessibleBase::GetNumActions(PRUint8 *aActionsCount)
+PRUint8
+nsXULTreeItemAccessibleBase::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aActionsCount);
-  *aActionsCount = 0;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
   // "activate" action is available for all treeitems, "expand/collapse" action
   // is avaible for treeitem which is container.
-  *aActionsCount = IsExpandable() ? 2 : 1;
-  return NS_OK;
+  return IsExpandable() ? 2 : 1;
 }
 
 NS_IMETHODIMP
 nsXULTreeItemAccessibleBase::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
--- a/accessible/src/xul/nsXULTreeAccessible.h
+++ b/accessible/src/xul/nsXULTreeAccessible.h
@@ -190,30 +190,32 @@ public:
 
   NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
                                nsIAccessibleRelation **aRelation);
 
   NS_IMETHOD GroupPosition(PRInt32 *aGroupLevel,
                            PRInt32 *aSimilarItemsInGroup,
                            PRInt32 *aPositionInGroup);
 
-  NS_IMETHOD GetNumActions(PRUint8 *aCount);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessNode
   virtual bool IsDefunct() const;
   virtual void Shutdown();
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual PRUint64 NativeState();
   virtual PRInt32 IndexInParent() const;
   virtual nsAccessible* FocusedChild();
 
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
+
   // nsXULTreeItemAccessibleBase
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEITEMBASEACCESSIBLE_IMPL_CID)
 
   /**
    * Return row index associated with the accessible.
    */
   PRInt32 GetRowIndex() const { return mRow; }
 
--- a/accessible/src/xul/nsXULTreeGridAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeGridAccessible.cpp
@@ -914,38 +914,30 @@ nsXULTreeGridCellAccessible::GetBounds(P
   *aX = presContext->CSSPixelsToDevPixels(x);
   *aY = presContext->CSSPixelsToDevPixels(y);
   *aWidth = presContext->CSSPixelsToDevPixels(width);
   *aHeight = presContext->CSSPixelsToDevPixels(height);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsXULTreeGridCellAccessible::GetNumActions(PRUint8 *aActionsCount)
+PRUint8
+nsXULTreeGridCellAccessible::ActionCount()
 {
-  NS_ENSURE_ARG_POINTER(aActionsCount);
-  *aActionsCount = 0;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
   PRBool isCycler = PR_FALSE;
   mColumn->GetCycler(&isCycler);
-  if (isCycler) {
-    *aActionsCount = 1;
-    return NS_OK;
-  }
+  if (isCycler)
+    return 1;
 
   PRInt16 type;
   mColumn->GetType(&type);
   if (type == nsITreeColumn::TYPE_CHECKBOX && IsEditable())
-    *aActionsCount = 1;
+    return 1;
 
-  return NS_OK;
+  return 0;
 }
 
 NS_IMETHODIMP
 nsXULTreeGridCellAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   if (aIndex != eAction_Click)
--- a/accessible/src/xul/nsXULTreeGridAccessible.h
+++ b/accessible/src/xul/nsXULTreeGridAccessible.h
@@ -143,34 +143,36 @@ public:
                                            nsLeafAccessible)
 
   // nsIAccessible
 
   NS_IMETHOD GetName(nsAString& aName);
   NS_IMETHOD GetBounds(PRInt32 *aX, PRInt32 *aY,
                        PRInt32 *aWidth, PRInt32 *aHeight);
 
-  NS_IMETHOD GetNumActions(PRUint8 *aCount);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsIAccessibleTableCell
   NS_DECL_NSIACCESSIBLETABLECELL
 
   // nsAccessNode
   virtual bool IsDefunct() const;
   virtual PRBool Init();
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual nsAccessible* FocusedChild();
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
+  virtual PRInt32 IndexInParent() const;
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
-  virtual PRInt32 IndexInParent() const;
+
+  // ActionAccessible
+  virtual PRUint8 ActionCount();
 
   // nsXULTreeGridCellAccessible
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEGRIDCELLACCESSIBLE_IMPL_CID)
 
   /**
    * Return index of the column.
    */
   PRInt32 GetColumnIndex() const;
--- a/accessible/tests/mochitest/events/test_text_alg.html
+++ b/accessible/tests/mochitest/events/test_text_alg.html
@@ -19,46 +19,62 @@
           src="../events.js"></script>
 
   <script type="application/javascript">
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
     const kRemoval = 0;
     const kInsertion = 1;
+    const kUnexpected = true;
 
     function changeText(aContainerID, aValue, aEventList)
     {
       this.containerNode = getNode(aContainerID);
       this.textNode = this.containerNode.firstChild;
       this.textData = this.textNode.data;
 
       this.eventSeq = [ ];
+      this.unexpectedEventSeq = [ ];
+
       for (var i = 0; i < aEventList.length; i++) {
-        var isInserted = aEventList[i][0];
-        var str = aEventList[i][1];
-        var offset = aEventList[i][2];
+        var event = aEventList[i];
+
+        var isInserted = event[0];
+        var str = event[1];
+        var offset = event[2];
         var checker = new textChangeChecker(this.containerNode, offset,
                                             offset + str.length, str,
                                             isInserted);
-        this.eventSeq.push(checker);
+
+        if (eventItem[3] == kUnexpected)
+          this.unexpectedEventSeq.push(checker);
+        else
+          this.eventSeq.push(checker);
       }
 
       this.invoke = function changeText_invoke()
       {
         this.textNode.data = aValue;
       }
 
       this.getID = function changeText_getID()
       {
         return "change text '" + this.textData + "' -> " + this.textNode.data +
           "for " + prettyName(this.containerNode);
       }
     }
 
+    function expStr(x, doublings)
+    {
+      for (var i = 0; i < doublings; ++i)
+        x = x + x;
+      return x;
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests
 
     //gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true;
 
     var gQueue = null;
     function doTests()
@@ -162,16 +178,44 @@
         [ kRemoval, "m", 0 ], // meilenstein -> eilenstein
         [ kInsertion, "l", 0], // eilenstein -> leilenstein
         [ kRemoval, "il", 2 ], // leilenstein -> leenstein
         [ kInsertion, "v", 2], // leenstein -> levenstein
         [ kInsertion, "h", 6 ], // levenstein -> levenshtein
       ];
       gQueue.push(new changeText("p11", "levenshtein", events));
 
+      //////////////////////////////////////////////////////////////////////////
+      // long strings, remove/insert pair as the old string was replaced on
+      // new one
+
+      var longStr1 = expStr("x", 16);
+      var longStr2 = expStr("X", 16);
+
+      var newStr = "a" + longStr1 + "b", insStr = longStr1, rmStr = "";
+      events = [
+        [ kRemoval, rmStr, 1, kUnexpected ],
+        [ kInsertion, insStr, 1 ]
+      ];
+      gQueue.push(new changeText("p12", newStr, events));
+
+      newStr = "a" + longStr2 + "b", insStr = longStr2, rmStr = longStr1;
+      events = [
+        [ kRemoval, rmStr, 1 ],
+        [ kInsertion, insStr, 1]
+      ];
+      gQueue.push(new changeText("p12", newStr, events));
+
+      newStr = "ab", insStr = "", rmStr = longStr2;
+      events = [
+        [ kRemoval, rmStr, 1 ],
+        [ kInsertion, insStr, 1, kUnexpected ]
+      ];
+      gQueue.push(new changeText("p12", newStr, events));
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
@@ -196,10 +240,11 @@
   <p id="p4">abcabc</p>
   <p id="p5">abc</p>
   <p id="p6">abc</p>
   <p id="p7">defabc</p>
   <p id="p8">abcdef</p>
   <p id="p9">abcDEFabc</p>
   <p id="p10">!abcdef@</p>
   <p id="p11">meilenstein</p>
+  <p id="p12">ab</p>
 </body>
 </html>
--- a/accessible/tests/mochitest/relations/test_general.xul
+++ b/accessible/tests/mochitest/relations/test_general.xul
@@ -64,16 +64,20 @@
       testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
 
       // 'node child of' relation for outlineitem role
       testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
 
+      // no relation node_child_of for accessible contained in an unexpected
+      // parent
+      testRelation("treeitem6", RELATION_NODE_CHILD_OF, null);
+
       // 'node child of' relation for the document having window, returns
       // direct accessible parent (fixed in bug 419770).
       var iframeElmObj = {};
       var iframeAcc = getAccessible("iframe", null, iframeElmObj);
       var iframeDoc = iframeElmObj.value.contentDocument;
       var iframeDocAcc = getAccessible(iframeDoc);
       testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
 
@@ -141,16 +145,21 @@
 
   <vbox style="overflow: auto;" flex="1">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=475298"
          title="mochitests for accessible relations">
         Mozilla Bug 475298
       </a><br/>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=673389"
+         title="node_child_of on an item not in a proper container">
+        Mozilla Bug 67389
+      </a><br/>
 
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
@@ -187,16 +196,18 @@
     <description role="treeitem" id="treeitem1">Yellow</description>
     <description role="treeitem" id="treeitem2">Orange</description>
     <vbox id="tree" role="tree" aria-owns="treeitem1 treeitem2">
       <description role="treeitem" id="treeitem3">Blue</description>
       <description role="treeitem" id="treeitem4" aria-level="1">Green</description>
       <description role="treeitem" id="treeitem5" aria-level="2">Light green</description>
     </vbox>
 
+    <description role="treeitem" id="treeitem6">Dark green</description>
+
     <iframe id="iframe"/>
 
     <hbox id="tablist" role="tablist">
       <description id="tab" role="tab" aria-controls="tabpanel">tab</description>
     </hbox>
     <description id="tabpanel" role="tabpanel">tabpanel</description>
 
     <description id="lr1" aria-live="assertive">1</description>
--- a/accessible/tests/mochitest/relations/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/relations/test_tabbrowser.xul
@@ -19,16 +19,18 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../relations.js" />
+  <script type="application/javascript"
+          src="../events.js" />
 
   <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     const Ci = Components.interfaces;
@@ -44,29 +46,38 @@
     var gFindBar = {
       hidden: true
     };
 
     function doTest()
     {
       var tabBrowser = document.getElementById("tabbrowser");
 
-      var progressListener =
-      {
-        onStateChange: function onStateChange(aWebProgress,
-                                              aRequest,
-                                              aStateFlags,
-                                              aStatus)
-       {
-        if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)
-          testRelations();
-       }
+      // Load documents into tabs and wait for reorder events caused by these
+      // documents load before we start the test.
+      var docURIs = ["about:", "about:mozilla"];
+
+      var handler = {
+        handleEvent: function handleEvent(aEvent) {
+          var target = aEvent.accessible;
+          if (target.role == ROLE_INTERNAL_FRAME &&
+              target.parent.parent == getAccessible(this.tabBrowser.mTabBox.tabpanels)) {
+            this.reorderCnt++;
+          }
+
+          if (this.reorderCnt == docURIs.length) {
+            unregisterA11yEventListener(EVENT_REORDER, this);
+            testRelations();
+          }
+        },
+
+        tabBrowser: tabBrowser,
+        reorderCnt: 0
       };
-
-      tabBrowser.addProgressListener(progressListener);
+      registerA11yEventListener(EVENT_REORDER, handler);
 
       tabBrowser.loadTabs(["about:", "about:mozilla"], false, true);
     }
 
     function testRelations()
     {
       //////////////////////////////////////////////////////////////////////////
       // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
--- a/accessible/tests/mochitest/states/Makefile.in
+++ b/accessible/tests/mochitest/states/Makefile.in
@@ -53,16 +53,17 @@ include $(topsrcdir)/config/rules.mk
 		test_doc.html \
 		test_docarticle.html \
 		test_editablebody.html \
 		test_frames.html \
 		test_inputs.html \
 		test_inputs.xul \
 		test_link.html \
 		test_popup.xul \
+		test_stale.html \
 		test_textbox.xul \
 		test_tree.xul \
 		z_frames.html \
 		z_frames_article.html \
 		z_frames_checkbox.html \
 		z_frames_textbox.html \
 		z_frames_update.html \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_stale.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Stale state testing</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    function addChild(aContainerID)
+    {
+      this.containerNode = getNode(aContainerID);
+      this.childNode = null;
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.containerNode)
+      ];
+
+      this.invoke = function addChild_invoke()
+      {
+        this.childNode = document.createElement("div");
+        this.containerNode.appendChild(this.childNode);
+      }
+
+      this.finalCheck = function addChild_finalCheck()
+      {
+        // no stale state should be set
+        testStates(this.childNode, 0, 0, 0, EXT_STATE_STALE);
+      }
+
+      this.getID = function addChild_getID()
+      {
+        return "add child for " + prettyName(aContainerID);
+      }
+    }
+
+    function removeChildChecker(aInvoker)
+    {
+      this.type = EVENT_HIDE;
+      this.__defineGetter__("target", function() { return aInvoker.child; });
+
+      this.check = function removeChildChecker_check()
+      {
+        // stale state should be set
+        testStates(aInvoker.child, 0, EXT_STATE_STALE);
+      }
+    }
+
+    function removeChild(aContainerID)
+    {
+      this.containerNode = getNode(aContainerID);
+      this.child = null;
+
+      this.eventSeq = [
+        new removeChildChecker(this)
+      ];
+
+      this.invoke = function removeChild_invoke()
+      {
+        var childNode = this.containerNode.firstChild;
+        this.child = getAccessible(childNode);
+
+        this.containerNode.removeChild(childNode);
+      }
+
+      this.getID = function removeChild_getID()
+      {
+        return "remove child from " + prettyName(aContainerID);
+      }
+    }
+
+    //gA11yEventDumpToConsole = true; //debugging
+
+    var gQueue = null;
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new addChild("container"));
+      gQueue.push(new removeChild("container"));
+
+      gQueue.invoke(); // will call SimpleTest.finish()
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+
+<body role="">
+
+  <a target="_blank"
+     title="Expose stale state on accessibles unattached from tree"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=676267">Mozilla Bug 676267</a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="container"></div>
+
+</body>
+</html>
--- a/accessible/tests/mochitest/table.js
+++ b/accessible/tests/mochitest/table.js
@@ -43,17 +43,17 @@ function testTableStruct(aIdentifier, aC
                          aCaption, aSummary, aIsTreeTable)
 {
   var tableNode = getNode(aIdentifier);
   var isGrid = tableNode.getAttribute("role") == "grid" ||
     tableNode.getAttribute("role") == "treegrid" ||
     tableNode.localName == "tree";
 
   var rowCount = aCellsArray.length;
-  var colsCount = aCellsArray[0].length;
+  var colsCount = aCellsArray[0] ? aCellsArray[0].length : 0;
 
   // Test table accessible tree.
   var tableObj = {
     role: aIsTreeTable ? ROLE_TREE_TABLE : ROLE_TABLE,
     children: []
   };
 
   // caption accessible handling
--- a/accessible/tests/mochitest/table/test_struct_ariagrid.html
+++ b/accessible/tests/mochitest/table/test_struct_ariagrid.html
@@ -45,16 +45,24 @@
       // ARIA grid with HTML table elements
       cellsArray = [
         [kColHeaderCell, kColHeaderCell],
         [kDataCell,      kDataCell]
       ];
 
       testTableStruct("grid2", cellsArray);
 
+      //////////////////////////////////////////////////////////////////////////
+      // ARIA grid of wrong markup
+      cellsArray = [ ];
+      testTableStruct("grid3", cellsArray);
+
+      cellsArray = [ [] ];
+      testTableStruct("grid4", cellsArray);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
@@ -63,16 +71,19 @@
      title="ARIA grid based on HTML table"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=491683">Mozilla Bug 491683</a>
   <a target="_blank"
      title="implement IAccessibleTable2"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424">Mozilla Bug 512424</a>
   <a target="_blank"
      title="nsHTMLTableCellAccessible is used in dojo's crazy ARIA grid"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=513848">Mozilla Bug 513848</a>
+  <a target="_blank"
+     title="Crash [@ AccIterator::GetNext()]"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=675861">Mozilla Bug 675861</a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <!-- Not usual markup to avoid text accessible between cell accessibles -->
   <div id="table" role="grid">
@@ -128,10 +139,13 @@
         <tr>
           <td role="gridcell">cell1</td>
           <td role="gridcell" tabindex="-1">cell2</td>
         </tr>
       </table>
     </div>
   </div>
 
+  <!-- Wrong markup ARIA grid -->
+  <div role="grid" id="grid3"></div>
+  <div role="grid" id="grid4"><div role="row"></div></div>
 </body>
 </html>
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -18,16 +18,18 @@
           src="chrome://mochikit/content/MochiKit/packed.js" />
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
+  <script type="application/javascript"
+          src="../events.js" />
 
   <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     const Ci = Components.interfaces;
@@ -42,34 +44,41 @@
     var gFindBar = {
       hidden: true
     };
 
     function doTest()
     {
       var tabBrowser = document.getElementById("tabbrowser");
 
-      var progressListener =
-      {
-        onStateChange: function onStateChange(aWebProgress,
-                                              aRequest,
-                                              aStateFlags,
-                                              aStatus)
-        {
-          if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
-            tabBrowser.removeProgressListener(progressListener);
+      // Load documents into tabs and wait for reorder events caused by these
+      // documents load before we start the test.
+      var docURIs = ["about:", "about:mozilla"];
 
-            SimpleTest.executeSoon(testAccTree);
+      var handler = {
+        handleEvent: function handleEvent(aEvent) {
+          var target = aEvent.accessible;
+          if (target.role == ROLE_INTERNAL_FRAME &&
+              target.parent.parent == getAccessible(this.tabBrowser.mTabBox.tabpanels)) {
+            this.reorderCnt++;
           }
-        }
+
+          if (this.reorderCnt == docURIs.length) {
+            unregisterA11yEventListener(EVENT_REORDER, this);
+            testAccTree();
+          }
+        },
+
+        tabBrowser: tabBrowser,
+        reorderCnt: 0
       };
-      tabBrowser.addProgressListener(progressListener);
+      registerA11yEventListener(EVENT_REORDER, handler);
 
       // Test XUL and HTML documents.
-      tabBrowser.loadTabs(["about:", "about:mozilla"], false, true);
+      tabBrowser.loadTabs(docURIs, false, true);
     }
 
     function testAccTree()
     {
       var tabBrowser = document.getElementById("tabbrowser");
 
       ////////////////////
       // Tab bar
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -200,23 +200,16 @@ endif
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 libs::
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/mozicon128.png $(DIST)/bin/icons
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/default16.png  $(DIST)/bin/chrome/icons/default
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/default32.png  $(DIST)/bin/chrome/icons/default
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/default48.png  $(DIST)/bin/chrome/icons/default
 endif
 
-ifdef MOZ_SPLASHSCREEN
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-libs::
-	$(INSTALL) $(IFLAGS1) $(DIST)/branding/splash.bmp $(DIST)/bin
-endif
-endif
-
 libs:: $(srcdir)/profile/prefs.js
 	$(INSTALL) $(IFLAGS1) $^ $(DIST)/bin/defaults/profile
 
 libs:: $(srcdir)/blocklist.xml
 	$(INSTALL) $(IFLAGS1) $^ $(DIST)/bin
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -0,0 +1,213 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1311096050000">
+  <emItems>
+      <emItem  blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
+                        <versionRange  minVersion="0.1" maxVersion="4.3.0.00" severity="1">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i8" id="{B13721C7-F507-4982-B2E5-502A71474FED}">
+                        <versionRange  minVersion=" " 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="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
+                        <versionRange  minVersion="1.1b1" maxVersion="1.1b1">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i16" id="{27182e60-b5f3-411c-b545-b44205977502}">
+                        <versionRange  minVersion="1.0" maxVersion="1.0">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i39" id="{c2d64ff7-0ab8-4263-89c9-ea3b0f8f050c}">
+                        <versionRange  minVersion="0.1" maxVersion="4.3.0.00" severity="1">
+                    </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="i18" id="msntoolbar@msn.com">
+                        <versionRange  minVersion=" " maxVersion="6.*">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i13" id="{E8E88AB0-7182-11DF-904E-6045E0D72085}">
+                        </emItem>
+      <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="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}">
+                        <versionRange  minVersion="0.1" maxVersion="4.3.0.00" severity="1">
+                    </versionRange>
+                  </emItem>
+      <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="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="i5" id="support@daemon-tools.cc">
+                        <versionRange  minVersion=" " maxVersion="1.0.0.5">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i6" id="{3f963a5b-e555-4543-90e2-c3908898db71}">
+                        <versionRange  minVersion=" " maxVersion="8.5">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i12" id="masterfiler@gmail.com">
+                        <versionRange  severity="3">
+                    </versionRange>
+                  </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="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="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
+                        <versionRange  minVersion="2.2" maxVersion="2.2">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i3" id="langpack-vi-VN@firefox.mozilla.org">
+                        <versionRange  minVersion="2.0" maxVersion="2.0">
+                    </versionRange>
+                  </emItem>
+      <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
+                        </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" />              <versionRange >
+                  </versionRange>
+          </pluginItem>
+      <pluginItem  blockID="p31">
+                  <match name="filename" exp="NPMySrch.dll" />              <versionRange >
+                  </versionRange>
+          </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>
+    </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>
+    <gfxBlacklistEntry  blockID="g36">
+      <os>WINNT 6.1</os>
+      <vendor>0x10de</vendor>
+              <devices>
+                      <device>0x0a6c</device>
+                  </devices>
+            <feature>DIRECT3D_9_LAYERS</feature>
+      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+      <driverVersion>8.17.12.5896</driverVersion>
+      <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>
+    </gfxBlacklistEntry>
+    <gfxBlacklistEntry  blockID="g37">
+      <os>WINNT 5.1</os>
+      <vendor>0x10de</vendor>
+            <feature>DIRECT3D_9_LAYERS</feature>
+      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+      <driverVersion>7.0.0.0</driverVersion>
+      <driverVersionComparator>GREATER_THAN_OR_EQUAL</driverVersionComparator>
+    </gfxBlacklistEntry>
+    </gfxItems>
+
+
+</blocklist>
\ No newline at end of file
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -31,16 +31,17 @@
 # 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 *****
 
+      <menuseparator id="page-menu-separator"/>
       <menuitem id="spell-no-suggestions"
                 disabled="true"
                 label="&spellNoSuggestions.label;"/>
       <menuitem id="spell-add-to-dictionary"
                 label="&spellAddToDictionary.label;"
                 accesskey="&spellAddToDictionary.accesskey;"
                 oncommand="InlineSpellCheckerUI.addToDictionary();"/>
       <menuseparator id="spell-suggestions-separator"/>
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -215,28 +215,32 @@
 #
 # Search Command Key Logic works like this:
 # 
 # Unix: Ctrl+K (cross platform binding)
 #       Ctrl+J (in case of emacs Ctrl-K conflict)
 # Mac:  Cmd+K (cross platform binding)
 #       Cmd+Opt+F (platform convention)
 # Win:  Ctrl+K (cross platform binding)
+#       Ctrl+E (IE compat)
 #
 # We support Ctrl+K on all platforms now and advertise it in the menu since it is
 # our standard - it is a "safe" choice since it is near no harmful keys like "W" as
 # "E" is. People mourning the loss of Ctrl+K for emacs compat can switch their GTK
 # system setting to use emacs emulation, and we should respect it. Focus-Search-Box
 # is a fundamental keybinding and we are maintaining a XP binding so that it is easy
 # for people to switch to Linux.
 #
     <key id="key_search" key="&searchFocus.commandkey;" command="Tools:Search" modifiers="accel"/>
 #ifdef XP_MACOSX
     <key id="key_search2" key="&findOnCmd.commandkey;" command="Tools:Search" modifiers="accel,alt"/>
 #endif
+#ifdef XP_WIN
+    <key id="key_search2" key="&searchFocus.commandkey2;" command="Tools:Search" modifiers="accel"/>
+#endif
 #ifdef XP_GNOME
     <key id="key_search2" key="&searchFocusUnix.commandkey;" command="Tools:Search" modifiers="accel"/>
     <key id="key_openDownloads" key="&downloadsUnix.commandkey;" command="Tools:Downloads" modifiers="accel,shift"/>
 #else
     <key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/>
 #endif
     <key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
     <key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift" disabled="true"/>
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -35,70 +35,79 @@
 # 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 *****
 
 // gSyncUI handles updating the tools menu
 let gSyncUI = {
+  _obs: ["weave:service:sync:start",
+         "weave:service:sync:finish",
+         "weave:service:sync:error",
+         "weave:service:sync:delayed",
+         "weave:service:quota:remaining",
+         "weave:service:setup-complete",
+         "weave:service:login:start",
+         "weave:service:login:finish",
+         "weave:service:login:error",
+         "weave:service:logout:finish",
+         "weave:service:start-over"],
+
+  _unloaded: false,
+
   init: function SUI_init() {
     // Proceed to set up the UI if Sync has already started up.
     // Otherwise we'll do it when Sync is firing up.
     if (Weave.Status.ready) {
       this.initUI();
       return;
     }
 
     Services.obs.addObserver(this, "weave:service:ready", true);
 
     // Remove the observer if the window is closed before the observer
     // was triggered.
-    window.addEventListener("unload", function() {
-      window.removeEventListener("unload", arguments.callee, false);
+    window.addEventListener("unload", function onUnload() {
+      gSyncUI._unloaded = true;
+      window.removeEventListener("unload", onUnload, false);
       Services.obs.removeObserver(gSyncUI, "weave:service:ready");
+
+      if (Weave.Status.ready) {
+        gSyncUI._obs.forEach(function(topic) {
+          Services.obs.removeObserver(gSyncUI, topic);
+        });
+      }
     }, false);
   },
 
   initUI: function SUI_initUI() {
-    let obs = ["weave:service:sync:start",
-               "weave:service:sync:finish",
-               "weave:service:sync:error",
-               "weave:service:sync:delayed",
-               "weave:service:quota:remaining",
-               "weave:service:setup-complete",
-               "weave:service:login:start",
-               "weave:service:login:finish",
-               "weave:service:login:error",
-               "weave:service:logout:finish",
-               "weave:service:start-over"];
-
     // If this is a browser window?
     if (gBrowser) {
-      obs.push("weave:notification:added");
+      this._obs.push("weave:notification:added");
     }
 
-    obs.forEach(function(topic) {
+    this._obs.forEach(function(topic) {
       Services.obs.addObserver(this, topic, true);
     }, this);
 
     // Find the alltabs-popup, only if there is a gBrowser
     if (gBrowser) {
       let popup = document.getElementById("alltabs-popup");
       if (popup) {
         popup.addEventListener(
           "popupshowing", this.alltabsPopupShowing.bind(this), true);
       }
 
       if (Weave.Notifications.notifications.length)
         this.initNotifications();
     }
     this.updateUI();
   },
-  
+
   initNotifications: function SUI_initNotifications() {
     const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     let notificationbox = document.createElementNS(XULNS, "notificationbox");
     notificationbox.id = "sync-notifications";
     notificationbox.setAttribute("flex", "1");
 
     let bottombox = document.getElementById("browser-bottombox");
     bottombox.insertBefore(notificationbox, bottombox.firstChild);
@@ -146,17 +155,17 @@ let gSyncUI = {
     if (!Weave.Service.isLoggedIn || !Weave.Engines.get("tabs").enabled)
       return;
 
     let label = this._stringBundle.GetStringFromName("tabs.fromOtherComputers.label");
 
     let popup = document.getElementById("alltabs-popup");
     if (!popup)
       return;
-    
+
     let menuitem = document.createElement("menuitem");
     menuitem.setAttribute("id", "sync-tabs-menuitem");
     menuitem.setAttribute("label", label);
     menuitem.setAttribute("class", "alltabs-item");
     menuitem.setAttribute("oncommand", "BrowserOpenSyncTabs();");
 
     // Fake the tab object on the menu entries, so that we don't have to worry
     // about removing them ourselves. They will just get cleaned up by popup
@@ -290,17 +299,17 @@ let gSyncUI = {
                         "weaveSetup", "centerscreen,chrome,resizable=no");
     }
   },
 
   openQuotaDialog: function SUI_openQuotaDialog() {
     let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
     if (win)
       win.focus();
-    else 
+    else
       Services.ww.activeWindow.openDialog(
         "chrome://browser/content/syncQuota.xul", "",
         "centerscreen,chrome,dialog,modal");
   },
 
   openPrefs: function SUI_openPrefs() {
     openPreferences("paneSync");
   },
@@ -409,18 +418,23 @@ let gSyncUI = {
     if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
       title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
       Weave.Notifications.removeAll(title);
       this._wasDelayed = false;
     }
 
     this.updateUI();
   },
-  
+
   observe: function SUI_observe(subject, topic, data) {
+    if (this._unloaded) {
+      Cu.reportError("SyncUI observer called after unload: " + topic);
+      return;
+    }
+
     switch (topic) {
       case "weave:service:sync:start":
         this.onActivityStart();
         break;
       case "weave:service:sync:finish":
         this.onSyncFinish();
         break;
       case "weave:service:sync:error":
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -211,16 +211,22 @@ XPCOMUtils.defineLazyGetter(this, "Win7F
 });
 
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
                                    "@mozilla.org/xre/app-info;1",
                                    "nsICrashReporter");
 #endif
 
+XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
+  let tmp = {};
+  Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
+  return new tmp.PageMenu();
+});
+
 /**
 * We can avoid adding multiple load event listeners and save some time by adding
 * one listener that calls all real handlers.
 */
 function pageShowEventHandlers(event) {
   // Filter out events that are not about the document load we are interested in
   if (event.originalTarget == content.document) {
     charsetLoadListener(event);
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -229,24 +229,16 @@
            hidden="true"
            ignorekeys="true"
            noautofocus="true"
            noautohide="true"
            titlebar="normal"
            close="true"
            onpopuphiding="InspectorUI.closeInspectorUI();"
            label="&inspectPanelTitle.label;">
-      <toolbar id="inspector-toolbar"
-               nowindowdrag="true">
-        <toolbarbutton id="inspector-inspect-toolbutton"
-                       label="&inspectButton.label;"
-                       accesskey="&inspectButton.accesskey;"
-                       class="toolbarbutton-text"
-                       command="Inspector:Inspect"/>
-      </toolbar>
       <hbox id="tree-panel-resizer-box" align="end">
         <spacer flex="1" />
         <resizer dir="bottomend" />
       </hbox>
     </panel>
 
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event);">
@@ -276,20 +268,20 @@
                 accesskey="&fullScreenAutohide.accesskey;"
                 oncommand="FullScreen.setAutohide();"/>
       <menuseparator/>
       <menuitem label="&fullScreenExit.label;"
                 accesskey="&fullScreenExit.accesskey;"
                 oncommand="BrowserFullScreen();"/>
     </menupopup>
 
-    <menupopup id="contentAreaContextMenu"
+    <menupopup id="contentAreaContextMenu" pagemenu="start"
                onpopupshowing="if (event.target != this)
                                  return true;
-                               gContextMenu = new nsContextMenu(this, gBrowser);
+                               gContextMenu = new nsContextMenu(this, gBrowser, event.shiftKey);
                                if (gContextMenu.shouldDisplay)
                                  updateEditUIVisibility();
                                return gContextMenu.shouldDisplay;"
                onpopuphiding="if (event.target == this) { gContextMenu = null; updateEditUIVisibility(); }">
 #include browser-context.inc
     </menupopup>
 
     <menupopup id="placesContext"/>
@@ -966,16 +958,29 @@
                   autocompletepopup="PopupAutoComplete"
                   onclick="return contentAreaClick(event, false);"/>
       <statuspanel id="statusbar-display" label=""/>
     </vbox>
     <vbox id="browser-border-end" hidden="true" layer="true"/>
   </hbox>
 
   <vbox id="browser-bottombox" layer="true">
+    <toolbar id="inspector-toolbar"
+             nowindowdrag="true"
+             hidden="true">
+      <toolbarbutton id="inspector-inspect-toolbutton"
+                     label="&inspectButton.label;"
+                     accesskey="&inspectButton.accesskey;"
+                     class="toolbarbutton-text"
+                     command="Inspector:Inspect"/>
+      <toolbarseparator />
+      <hbox id="inspector-tools">
+        <!-- registered tools go here -->
+      </hbox>
+    </toolbar>
     <toolbar id="addon-bar"
              toolbarname="&addonBarCmd.label;" accesskey="&addonBarCmd.accesskey;"
              collapsed="true"
              class="toolbar-primary chromeclass-toolbar"
              context="toolbar-context-menu" toolboxid="navigator-toolbox"
              mode="icons" iconsize="small" defaulticonsize="small"
              lockiconsize="true"
              defaultset="addonbar-closebutton,spring,status-bar"
@@ -999,17 +1004,17 @@
       <svg:circle cx="-0.35" cy="0.5" r="0.58"/>
     </svg:mask>
   </svg:svg>
 #endif
 #ifdef XP_MACOSX
   <svg:svg height="0">
     <svg:mask id="pinstripe-keyhole-forward-mask" maskContentUnits="objectBoundingBox">
       <svg:rect x="0" y="0" width="1" height="1" fill="white"/>
-      <svg:circle cx="-0.46" cy="0.48" r="0.65"/>
+      <svg:circle cx="-0.41" cy="0.5" r="0.65"/>
     </svg:mask>
     <svg:mask id="pinstripe-tab-ontop-left-curve-mask" maskContentUnits="userSpaceOnUse">
       <svg:circle cx="9" cy="3" r="3" fill="white"/>
       <svg:rect x="9" y="0" width="3" height="3" fill="white"/>
       <svg:rect x="6" y="3" width="6" height="19" fill="white"/>
       <svg:rect x="1" y="17" width="5" height="5" fill="white"/>
       <svg:circle cx="1" cy="17" r="5"/>
       <svg:rect x="0" y="22" width="12" height="1" fill="white"/>
--- a/browser/base/content/inspector.js
+++ b/browser/base/content/inspector.js
@@ -217,16 +217,17 @@ Highlighter.prototype = {
     this.veilLeftBox = null;
     this.veilMiddleBox = null;
     this.veilTransparentBox = null;
     this.node = null;
     this.highlighterContainer.parentNode.removeChild(this.highlighterContainer);
     this.highlighterContainer = null;
     this.win = null
     this.browser = null;
+    this.toolbar = null;
   },
 
   /**
    * Is the highlighter highlighting? Public method for querying the state
    * of the highlighter.
    */
   get isHighlighting() {
     return this._highlighting;
@@ -753,23 +754,26 @@ var InspectorUI = {
    * resize, tabContainer.TabSelect and others.
    */
   openInspectorUI: function IUI_openInspectorUI()
   {
     // initialization
     this.browser = gBrowser.selectedBrowser;
     this.win = this.browser.contentWindow;
     this.winID = this.getWindowID(this.win);
+    this.toolbar = document.getElementById("inspector-toolbar");
+
     if (!this.domplate) {
       Cu.import("resource:///modules/domplate.jsm", this);
       this.domplateUtils.setDOM(window);
     }
 
     this.openTreePanel();
 
+    this.toolbar.hidden = false;
     this.inspectCmd.setAttribute("checked", true);
   },
 
   /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
@@ -813,16 +817,17 @@ var InspectorUI = {
    */
   closeInspectorUI: function IUI_closeInspectorUI(aKeepStore)
   {
     if (this.closing || !this.win || !this.browser) {
       return;
     }
 
     this.closing = true;
+    this.toolbar.hidden = true;
 
     if (!aKeepStore) {
       InspectorStore.deleteStore(this.winID);
       this.win.removeEventListener("pagehide", this, true);
     } else {
       // Update the store before closing.
       if (this.selection) {
         InspectorStore.setValue(this.winID, "selectedNode",
@@ -892,28 +897,30 @@ var InspectorUI = {
     this.attachPageListeners();
     this.inspecting = true;
     this.highlighter.veilTransparentBox.removeAttribute("locked");
   },
 
   /**
    * Stop inspecting webpage, detach page listeners, disable highlighter
    * event listeners.
+   * @param aPreventScroll
+   *        Prevent scroll in the HTML tree?
    */
-  stopInspecting: function IUI_stopInspecting()
+  stopInspecting: function IUI_stopInspecting(aPreventScroll)
   {
     if (!this.inspecting) {
       return;
     }
 
     document.getElementById("inspector-inspect-toolbutton").checked = false;
     this.detachPageListeners();
     this.inspecting = false;
     if (this.highlighter.node) {
-      this.select(this.highlighter.node, true, true);
+      this.select(this.highlighter.node, true, true, !aPreventScroll);
     } else {
       this.select(null, true, true);
     }
     this.highlighter.veilTransparentBox.setAttribute("locked", true);
   },
 
   /**
    * Select an object in the tree view.
@@ -1043,19 +1050,26 @@ var InspectorUI = {
     if (this.hasClass(target, "twisty")) {
       node = this.getRepObject(aEvent.target.nextSibling);
       hitTwisty = true;
     } else {
       node = this.getRepObject(aEvent.target);
     }
 
     if (node) {
-      if (hitTwisty)
+      if (hitTwisty) {
         this.ioBox.toggleObject(node);
-      this.select(node, false, false);
+      } else {
+        if (this.inspecting) {
+          this.stopInspecting(true);
+        } else {
+          this.select(node, true, false);
+          this.highlighter.highlightNode(node);
+        }
+      }
     }
   },
 
   /**
    * Attach event listeners to content window and child windows to enable
    * highlighting and click to stop inspection.
    */
   attachPageListeners: function IUI_attachPageListeners()
@@ -1272,25 +1286,25 @@ var InspectorUI = {
       aRegObj.panel.addEventListener("popupshowing",
         function IUI_toolPanelShowing() {
           btn.setAttribute("checked", "true");
         }, false);
 
       this.tools[id] = aRegObj;
     }
 
-    let toolbar = document.getElementById("inspector-toolbar");
+    let toolbox = document.getElementById("inspector-tools");
     let btn = document.createElement("toolbarbutton");
     btn.setAttribute("id", aRegObj.buttonId);
     btn.setAttribute("label", aRegObj.label);
     btn.setAttribute("tooltiptext", aRegObj.tooltiptext);
     btn.setAttribute("accesskey", aRegObj.accesskey);
     btn.setAttribute("class", "toolbarbutton-text");
     btn.setAttribute("image", aRegObj.icon || "");
-    toolbar.appendChild(btn);
+    toolbox.appendChild(btn);
 
     btn.addEventListener("click",
       function IUI_ToolButtonClick(aEvent) {
         if (btn.getAttribute("checked") == "true") {
           aRegObj.onHide.apply(aRegObj.context);
         } else {
           aRegObj.onShow.apply(aRegObj.context, [InspectorUI.selection]);
           aRegObj.onSelect.apply(aRegObj.context, [InspectorUI.selection]);
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -56,55 +56,66 @@
 # 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 *****
 
-function nsContextMenu(aXulMenu, aBrowser) {
+function nsContextMenu(aXulMenu, aBrowser, aIsShift) {
   this.shouldDisplay = true;
-  this.initMenu(aBrowser);
+  this.initMenu(aBrowser, aXulMenu, aIsShift);
 }
 
 // Prototype for nsContextMenu "class."
 nsContextMenu.prototype = {
-  initMenu: function CM_initMenu(aBrowser) {
+  initMenu: function CM_initMenu(aBrowser, aXulMenu, aIsShift) {
     // Get contextual info.
     this.setTarget(document.popupNode, document.popupRangeParent,
                    document.popupRangeOffset);
     if (!this.shouldDisplay)
       return;
 
     this.browser = aBrowser;
+
+    this.hasPageMenu = false;
+    if (!aIsShift) {
+      this.hasPageMenu = PageMenu.init(this.target, aXulMenu);
+    }
+
     this.isFrameImage = document.getElementById("isFrameImage");
     this.ellipsis = "\u2026";
     try {
       this.ellipsis = gPrefService.getComplexValue("intl.ellipsis",
                                                    Ci.nsIPrefLocalizedString).data;
     } catch (e) { }
     this.isTextSelected = this.isTextSelection();
     this.isContentSelected = this.isContentSelection();
 
     // Initialize (disable/remove) menu items.
     this.initItems();
   },
 
   initItems: function CM_initItems() {
+    this.initPageMenuSeparator();
     this.initOpenItems();
     this.initNavigationItems();
     this.initViewItems();
     this.initMiscItems();
     this.initSpellingItems();
     this.initSaveItems();
     this.initClipboardItems();
     this.initMediaPlayerItems();
   },
 
+  initPageMenuSeparator: function CM_initPageMenuSeparator() {
+    this.showItem("page-menu-separator", this.hasPageMenu);
+  },
+
   initOpenItems: function CM_initOpenItems() {
     var isMailtoInternal = false;
     if (this.onMailtoLink) {
       var mailtoHandler = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
                           getService(Ci.nsIExternalProtocolService).
                           getProtocolHandlerInfo("mailto");
       isMailtoInternal = (!mailtoHandler.alwaysAskBeforeHandling &&
                           mailtoHandler.preferredAction == Ci.nsIHandlerInfo.useHelperApp &&
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1367,29 +1367,25 @@
             // activeness in the tab switcher.
             b.docShellIsActive = false;
 
             // Check if we're opening a tab related to the current tab and
             // move it to after the current tab.
             // aReferrerURI is null or undefined if the tab is opened from
             // an external application or bookmark, i.e. somewhere other
             // than the current tab.
-            if (aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) {
+            if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
+                Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
               let newTabPos = (this._lastRelatedTab ||
                                this.selectedTab)._tPos + 1;
-
               if (this._lastRelatedTab)
                 this._lastRelatedTab.owner = null;
               else
                 t.owner = this.selectedTab;
-
-              if (!this.selectedTab.pinned &&
-                  Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent"))
-                this.moveTabTo(t, newTabPos);
-
+              this.moveTabTo(t, newTabPos);
               this._lastRelatedTab = t;
             }
 
             return t;
           ]]>
         </body>
       </method>
 
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -258,21 +258,20 @@ function GroupItem(listOfEls, options) {
 
   GroupItems.register(this);
 
   // ___ Position
   this.setBounds(rectToBe, immediately);
   if (options.dontPush) {
     this.setZ(drag.zIndex);
     drag.zIndex++; 
-  } else
+  } else {
     // Calling snap will also trigger pushAway
     this.snap(immediately);
-  if ($container)
-    this.setBounds(rectToBe, immediately);
+  }
 
   if (!options.immediately && listOfEls.length > 0)
     $container.hide().fadeIn();
 
   this._inited = true;
   this.save();
 
   GroupItems.updateGroupCloseButtons();
@@ -1651,21 +1650,22 @@ GroupItem.prototype = Utils.extend(new I
   // Helper routine for the constructor; adds various event handlers to the container.
   _addHandlers: function GroupItem__addHandlers(container) {
     let self = this;
     let lastMouseDownTarget;
 
     container.mousedown(function(e) {
       let target = e.target;
       // only set the last mouse down target if it is a left click, not on the
-      // close button, not on the new tab button, not on the title bar and its
-      // element
+      // close button, not on the expand button, not on the title bar and its
+      // elements
       if (Utils.isLeftClick(e) &&
           self.$closeButton[0] != target &&
           self.$titlebar[0] != target &&
+          self.$expander[0] != target &&
           !self.$titlebar.contains(target) &&
           !self.$appTabTray.contains(target)) {
         lastMouseDownTarget = target;
       } else {
         lastMouseDownTarget = null;
       }
     });
     container.mouseup(function(e) {
@@ -2228,22 +2228,24 @@ let GroupItems = {
     
     return (groupItemsData && !Utils.isEmptyObject(groupItemsData));
   },
 
   // ----------
   // Function: groupItemStorageSanity
   // Given persistent storage data for a groupItem, returns true if it appears to not be damaged.
   groupItemStorageSanity: function GroupItems_groupItemStorageSanity(groupItemData) {
-    // TODO: check everything
-    // Bug 586555
-    var sane = true;
-    if (!Utils.isRect(groupItemData.bounds)) {
+    let sane = true;
+    if (!groupItemData.bounds || !Utils.isRect(groupItemData.bounds)) {
       Utils.log('GroupItems.groupItemStorageSanity: bad bounds', groupItemData.bounds);
       sane = false;
+    } else if ((groupItemData.userSize && 
+               !Utils.isPoint(groupItemData.userSize)) ||
+               !groupItemData.id) {
+      sane = false;
     }
 
     return sane;
   },
 
   // ----------
   // Function: register
   // Adds the given <GroupItem> to the list of groupItems we're tracking.
--- a/browser/base/content/tabview/items.js
+++ b/browser/base/content/tabview/items.js
@@ -305,19 +305,24 @@ Item.prototype = {
 
   // ----------
   // Function: pushAway
   // Pushes all other items away so none overlap this Item.
   //
   // Parameters:
   //  immediately - boolean for doing the pushAway without animation
   pushAway: function Item_pushAway(immediately) {
+    var items = Items.getTopLevelItems();
+
+    // we need at least two top-level items to push something away
+    if (items.length < 2)
+      return;
+
     var buffer = Math.floor(Items.defaultGutter / 2);
 
-    var items = Items.getTopLevelItems();
     // setup each Item's pushAwayData attribute:
     items.forEach(function pushAway_setupPushAwayData(item) {
       var data = {};
       data.bounds = item.getBounds();
       data.startBounds = new Rect(data.bounds);
       // Infinity = (as yet) unaffected
       data.generation = Infinity;
       item.pushAwayData = data;
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -428,17 +428,18 @@ let UI = {
   // Parameters:
   //
   // options
   //  dontSetActiveTabInGroup bool for not setting active tab in group
   setActive: function UI_setActive(item, options) {
     Utils.assert(item, "item must be given");
 
     if (item.isATabItem) {
-      GroupItems.setActiveGroupItem(item.parent);
+      if (item.parent)
+        GroupItems.setActiveGroupItem(item.parent);
       this._setActiveTab(item);
     } else {
       GroupItems.setActiveGroupItem(item);
       if (!options || !options.dontSetActiveTabInGroup) {
         let activeTab = item.getActiveTab()
         if (activeTab)
           this._setActiveTab(activeTab);
       }
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -148,16 +148,17 @@ endif
                  browser_bug561636.js \
                  browser_bug562649.js \
                  browser_bug563588.js \
                  browser_bug565575.js \
                  browser_bug567306.js \
                  browser_zbug569342.js \
                  browser_bug575561.js \
                  browser_bug577121.js \
+                 browser_bug578534.js \
                  browser_bug579872.js \
                  browser_bug580638.js \
                  browser_bug580956.js \
                  browser_bug581242.js \
                  browser_bug581253.js \
                  browser_bug581947.js \
                  browser_bug585785.js \
                  browser_bug585830.js \
@@ -182,25 +183,16 @@ endif
                  browser_ctrlTab.js \
                  browser_customize_popupNotification.js \
                  browser_disablechrome.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_hide_removing.js \
-                 browser_scratchpad_initialization.js \
-                 browser_scratchpad_contexts.js \
-                 browser_scratchpad_tab_switch.js \
-                 browser_scratchpad_execute_print.js \
-                 browser_scratchpad_inspect.js \
-                 browser_scratchpad_files.js \
-                 browser_scratchpad_ui.js \
-                 browser_scratchpad_bug_646070_chrome_context_pref.js \
-                 browser_scratchpad_bug_660560_tab.js \
                  browser_overflowScroll.js \
                  browser_locationBarExternalLoad.js \
                  browser_pageInfo.js \
                  browser_page_style_menu.js \
                  browser_pinnedTabs.js \
                  browser_plainTextLinks.js \
                  browser_pluginnotification.js \
                  browser_relatedTabs.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug578534.js
@@ -0,0 +1,61 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is bug 578534 test.
+ *
+ * The Initial Developer of the Original Code is
+ * Sindre Dammann <sindrebugzilla@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function test() {
+  let uriString = "http://example.com/";
+  let cookieBehavior = "network.cookie.cookieBehavior";
+  let uriObj = Services.io.newURI(uriString, null, null)
+  let cp = Components.classes["@mozilla.org/cookie/permission;1"]
+                     .getService(Components.interfaces.nsICookiePermission);
+  
+  Services.prefs.setIntPref(cookieBehavior, 2);
+
+  cp.setAccess(uriObj, cp.ACCESS_ALLOW);
+  gBrowser.selectedTab = gBrowser.addTab(uriString);
+  waitForExplicitFinish();
+  gBrowser.selectedBrowser.addEventListener("load", onTabLoaded, true);
+  
+  function onTabLoaded() {
+    is(gBrowser.selectedBrowser.contentWindow.navigator.cookieEnabled, true,
+       "navigator.cookieEnabled should be true");
+    // Clean up
+    gBrowser.selectedBrowser.removeEventListener("load", onTabLoaded, true);
+    gBrowser.removeTab(gBrowser.selectedTab);
+    Services.prefs.setIntPref(cookieBehavior, 0);
+    cp.setAccess(uriObj, cp.ACCESS_DEFAULT);
+    finish();
+  }
+}
--- a/browser/base/content/test/browser_relatedTabs.js
+++ b/browser/base/content/test/browser_relatedTabs.js
@@ -73,19 +73,10 @@ function test() {
   testPosition(2, 5, "tab with referrer opened immediately to the right");
   testPosition(3, 1, "next tab with referrer opened further to the right");
   testPosition(4, 4, "tab selection changed, tab opens immediately to the right");
   testPosition(5, 6, "blank tab with referrer opens to the right of 3rd original tab where removed tab was");
   testPosition(6, 2, "tab has moved, new tab opens immediately to the right");
   testPosition(7, 8, "blank tab without referrer opens at the end");
   testPosition(8, 9, "tab without referrer opens at the end");
 
-  gBrowser.selectedTab = tabs[0];
-  gBrowser.pinTab(gBrowser.selectedTab);
-  addTab("http://mochi.test:8888/#8", gBrowser.currentURI);
-  testPosition(9, 10, "tab with referrer should open at the end when the selected tab is pinned");
-  gBrowser.selectedTab = tabs[9];
-  gBrowser.removeTab(tabs.pop());
-  is(gBrowser.selectedTab, tabs[0],
-     "opening a tab from a pinned tab, selecting it and closing it should go back to the pinned tab");
-
   tabs.forEach(gBrowser.removeTab, gBrowser);
 }
--- a/browser/base/content/test/inspector/browser_inspector_initialization.js
+++ b/browser/base/content/test/inspector/browser_inspector_initialization.js
@@ -47,16 +47,17 @@ function startInspectorTests()
 
 function runInspectorTests()
 {
   Services.obs.removeObserver(runInspectorTests,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
   Services.obs.addObserver(finishInspectorTests,
     INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
+  ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
   let iframe = document.getElementById("inspector-tree-iframe");
   is(InspectorUI.treeIFrame, iframe, "Inspector IFrame matches");
   ok(InspectorUI.inspecting, "Inspector is inspecting");
   ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
   ok(InspectorUI.highlighter, "Highlighter is up");
 
   executeSoon(function() {
     InspectorUI.closeInspectorUI();
@@ -66,16 +67,17 @@ function runInspectorTests()
 function finishInspectorTests()
 {
   Services.obs.removeObserver(finishInspectorTests,
     INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
   ok(!InspectorUI.highlighter, "Highlighter is gone");
   ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
   ok(!InspectorUI.inspecting, "Inspector is not inspecting");
+  ok(InspectorUI.toolbar.hidden, "toolbar is hidden");
 
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/inspector/browser_inspector_treePanel_click.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+function test() {
+
+  waitForExplicitFinish();
+
+  let doc;
+  let node1;
+  let node2;
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onload() {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    doc = content.document;
+    waitForFocus(setupTest, content);
+  }, true);
+
+  content.location = "data:text/html,<div><p></p></div>";
+
+  function setupTest() {
+    node1 = doc.querySelector("div");
+    node2 = doc.querySelector("p");
+    Services.obs.addObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
+    InspectorUI.toggleInspectorUI();
+  }
+
+  function runTests() {
+    Services.obs.removeObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED);
+    testNode1();
+  }
+
+  function testNode1() {
+    let box = InspectorUI.ioBox.createObjectBox(node1);
+    box.click();
+    executeSoon(function() {
+      is(InspectorUI.selection, node1, "selection matches node");
+      is(InspectorUI.highlighter.node, node1, "selection matches node");
+      testNode2();
+    });
+  }
+
+  function testNode2() {
+    let box = InspectorUI.ioBox.createObjectBox(node2);
+    box.click();
+    executeSoon(function() {
+      is(InspectorUI.selection, node2, "selection matches node");
+      is(InspectorUI.highlighter.node, node2, "selection matches node");
+      Services.obs.addObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED, false);
+      InspectorUI.closeInspectorUI();
+    });
+  }
+
+  function finishUp() {
+    Services.obs.removeObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED);
+    doc = node1 = node2 = null;
+    gBrowser.removeCurrentTab();
+    finish();
+  }
+}
--- a/browser/base/content/test/subtst_contextmenu.html
+++ b/browser/base/content/test/subtst_contextmenu.html
@@ -16,11 +16,45 @@ Browser context menu subtest.
 <video id="test-video-bad" src="bogus.duh" width="100" height="100" style="background-color: orange"></video>
 <video id="test-video-bad2" width="100" height="100" style="background-color: yellow">
   <source src="bogus.duh" type="video/durrrr;">
 </video>
 <iframe id="test-iframe" width="98"  height="98" style="border: 1px solid black"></iframe>
 <textarea id="test-textarea">chssseesbbbie</textarea> <!-- a weird word which generates only one suggestion -->
 <div id="test-contenteditable" contenteditable="true">chssseefsbbbie</div> <!-- a more weird word which generates no suggestions -->
 <input id="test-input-spellcheck" type="text" spellcheck="true" autofocus value="prodkjfgigrty"> <!-- this one also generates one suggestion -->
+<div contextmenu="myMenu">
+  <p id="test-pagemenu" hopeless="true">I've got a context menu!</p>
+  <menu id="myMenu" type="context">
+    <menuitem label="Plain item" onclick="document.getElementById('test-pagemenu').removeAttribute('hopeless');"></menuitem>
+    <menuitem label="Disabled item" disabled></menuitem>
+    <menu>
+      <menuitem type="checkbox" label="Checkbox" checked></menuitem>
+    </menu>
+    <menu>
+      <menuitem type="radio" label="Radio1" checked></menuitem>
+      <menuitem type="radio" label="Radio2"></menuitem>
+      <menuitem type="radio" label="Radio3"></menuitem>
+    </menu>
+    <menu>
+      <menuitem label="Item w/ icon" icon="favicon.ico"></menuitem>
+      <menuitem label="Item w/ bad icon" icon="data://www.mozilla.org/favicon.ico"></menuitem>
+    </menu>
+    <menu label="Submenu">
+      <menuitem type="radio" label="Radio1" radiogroup="rg"></menuitem>
+      <menuitem type="radio" label="Radio2" checked radiogroup="rg"></menuitem>
+      <menuitem type="radio" label="Radio3" radiogroup="rg"></menuitem>
+      <menu>
+        <menuitem type="checkbox" label="Checkbox"></menuitem>
+      </menu>
+    </menu>
+    <menu hidden>
+      <menuitem label="Bogus item"></menuitem>
+    </menu>
+    <menu>
+    </menu>
+    <menuitem label="Hidden item" hidden></menuitem>
+    <menuitem></menuitem>
+  </menu>
+</div>
 
 </body>
 </html>
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -145,16 +145,18 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug654941.js \
                  browser_tabview_bug655269.js \
                  browser_tabview_bug656778.js \
                  browser_tabview_bug656913.js \
                  browser_tabview_bug662266.js \
                  browser_tabview_bug663421.js \
                  browser_tabview_bug665502.js \
                  browser_tabview_bug669694.js \
+                 browser_tabview_bug673196.js \
+                 browser_tabview_bug673729.js \
                  browser_tabview_click_group.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_expander.js \
                  browser_tabview_firstrun_pref.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_multiwindow_search.js \
--- a/browser/base/content/test/tabview/browser_tabview_bug625269.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug625269.js
@@ -1,77 +1,77 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   waitForExplicitFinish();
-
-  newWindowWithTabView(onTabViewWindowLoaded);
+  newWindowWithTabView(onTabViewShown);
 }
 
-function onTabViewWindowLoaded(win) {
-  win.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
+function onTabViewShown(win) {
+  registerCleanupFunction(function () win.close());
 
-  ok(win.TabView.isVisible(), "Tab View is visible");
-
-  let contentWindow = win.document.getElementById("tab-view").contentWindow;
-  let [originalTab] = win.gBrowser.visibleTabs;
-
+  let contentWindow = win.TabView.getContentWindow();
   let currentGroup = contentWindow.GroupItems.getActiveGroupItem();
 
+  function checkResized(diffX, diffY, shouldResize, text, callback) {
+    let {width: origWidth, height: origHeight} = currentGroup.getBounds();
+
+    resizeWindow(win, diffX, diffY, function () {
+      let {width: newWidth, height: newHeight} = currentGroup.getBounds();
+      let resized = (origWidth != newWidth || origHeight != newHeight);
+
+      is(resized, shouldResize, text + ": The group should " +
+         (shouldResize ? "" : "not ") + "have been resized");
+
+      callback();
+    });
+  }
+
+  function next() {
+    let test = tests.shift();
+
+    if (test)
+      checkResized.apply(this, test.concat([next]));
+    else
+      finishTest();
+  }
+
+  function finishTest() {
+    // reset the usersize of the group, so this should clear the "cramped" feeling.
+    currentGroup.setSize(100, 100, true);
+    currentGroup.setUserSize();
+    checkResized(400, 400, false, "After clearing the cramp", finish);
+  }
+
+  let tests = [
+    // diffX, diffY, shouldResize, text
+    [ -50,  -50, false, "A little smaller"],
+    [  50,   50, false, "A little bigger"],
+    [-400, -400, true,  "Much smaller"],
+    [ 400,  400, true,  "Bigger after much smaller"],
+    [-400, -400, true,  "Much smaller"]
+  ];
+
+  // setup
   currentGroup.setSize(600, 600, true);
   currentGroup.setUserSize();
 
-  let down1 = function down1(resized) {
-    checkResized(currentGroup, 50, 50, false, "A little bigger", up1, contentWindow, win);
-  };
-  
-  let up1 = function up1(resized) {
-    checkResized(currentGroup, -400, -400, true, "Much smaller", down2, contentWindow, win);    
-  }
-
-  let down2 = function down2(resized) {
-    checkResized(currentGroup, 400, 400, undefined,
-      "Bigger after much smaller: TODO (bug 625668): the group should be resized!",
-      up2, contentWindow, win);
-  };
-  
-  let up2 = function up2(resized) {
-    checkResized(currentGroup, -400, -400, undefined,
-      "Much smaller: TODO (bug 625668): the group should be resized!",
-      down3, contentWindow, win);    
-  }
-
-  let down3 = function down3(resized) {
-    // reset the usersize of the group, so this should clear the "cramped" feeling.
-    currentGroup.setSize(100,100,true);
-    currentGroup.setUserSize();
-    checkResized(currentGroup, 400, 400, false,
-      "After clearing the cramp",
-      up3, contentWindow, win);
-  };
-  
-  let up3 = function up3(resized) {
-    win.close();
-    finish();
-  }
-
-  // start by making it a little smaller.
-  checkResized(currentGroup, -50, -50, false, "A little smaller", down1, contentWindow, win);
+  // run the tests
+  next();
 }
 
-function simulateResizeBy(xDiff, yDiff, win) {
-  win = win || window;
-
-  win.resizeBy(xDiff, yDiff);
-}
+// ----------
+function resizeWindow(win, diffX, diffY, callback) {
+  let targetWidth = win.outerWidth + diffX;
+  let targetHeight = win.outerHeight + diffY;
 
-function checkResized(item, xDiff, yDiff, expectResized, note, callback, contentWindow, win) {
-  let originalBounds = new contentWindow.Rect(item.getBounds());
-  simulateResizeBy(xDiff, yDiff, win);
+  win.addEventListener("resize", function onResize() {
+    let {outerWidth: width, outerHeight: height} = win;
+    if (width != targetWidth || height != targetHeight)
+      return;
 
-  let newBounds = item.getBounds();
-  let resized = !newBounds.equals(originalBounds);
-  if (expectResized !== undefined)
-    is(resized, expectResized, note + ": The group should " + 
-      (expectResized ? "" : "not ") + "be resized");
-  callback(resized);
+    win.removeEventListener("resize", onResize, false);
+    executeSoon(callback);
+  }, false);
+
+  win.resizeBy(diffX, diffY);
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug673196.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  function onLoad(win) {
+    registerCleanupFunction(function () win.close());
+    win.gBrowser.addTab();
+  }
+
+  function onShow(win) {
+    let cw = win.TabView.getContentWindow();
+    let group = cw.GroupItems.groupItems[0];
+
+    // shrink the group to make some room for dragging
+    group.setSize(200, 200, true);
+
+    waitForFocus(function () {
+      let target = group.getChild(0).container;
+      EventUtils.synthesizeMouseAtCenter(target, {type: "mousedown"}, cw);
+      EventUtils.synthesizeMouse(target, 0, 300, {type: "mousemove"}, cw);
+      EventUtils.synthesizeMouseAtCenter(target, {type: "mouseup"}, cw);
+
+      let newGroup = cw.GroupItems.groupItems[1];
+      let groupBounds = newGroup.getBounds();
+
+      let safeWindowBounds = cw.Items.getSafeWindowBounds();
+      ok(safeWindowBounds.contains(groupBounds),
+         "new group is within safe window bounds");
+
+      finish();
+    }, cw);
+  }
+
+  waitForExplicitFinish();
+  newWindowWithTabView(onShow, onLoad);
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug673729.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  newWindowWithTabView(function (win) {
+    let cw = win.TabView.getContentWindow();
+
+    // turn off zoom animations
+    cw.gPrefBranch.setBoolPref("animate_zoom", false);
+
+    registerCleanupFunction(function () {
+      cw.gPrefBranch.clearUserPref("animate_zoom");
+      win.close();
+    });
+
+    let group = cw.GroupItems.groupItems[0];
+    group.setSize(100, 100, true);
+
+    while (!group.isStacked())
+      win.gBrowser.addTab();
+
+    waitForFocus(function () {
+      whenGroupIsExpanded(group, function () {
+        ok(win.TabView.isVisible(), "tabview is visible");
+        finish();
+      });
+
+      let expander = group.$expander[0];
+      EventUtils.synthesizeMouseAtCenter(expander, {}, cw);
+    }, cw);
+  });
+}
+
+// ----------
+function whenGroupIsExpanded(group, callback) {
+  group.addSubscriber("expanded", function onExpanded() {
+    group.removeSubscriber("expanded", onExpanded);
+    executeSoon(callback);
+  });
+}
--- a/browser/base/content/test/tabview/browser_tabview_privatebrowsing.js
+++ b/browser/base/content/test/tabview/browser_tabview_privatebrowsing.js
@@ -17,17 +17,17 @@ function test() {
   showTabView(onTabViewLoadedAndShown);
 }
 
 // -----------
 function onTabViewLoadedAndShown() {
   ok(TabView.isVisible(), "Tab View is visible");
 
   // Establish initial state
-  contentWindow = document.getElementById("tab-view").contentWindow;
+  contentWindow = TabView.getContentWindow();
   verifyCleanState("start");
 
   // register a clean up for private browsing just in case
   registerCleanupFunction(function() {
     pb.privateBrowsingEnabled = false;
   });
 
   // create a group
@@ -45,50 +45,47 @@ function onTabViewLoadedAndShown() {
   // collect the group titles
   let count = contentWindow.GroupItems.groupItems.length;
   for (let a = 0; a < count; a++) {
     let gi = contentWindow.GroupItems.groupItems[a];
     groupTitles[a] = gi.getTitle();
   }
 
   // Create a second tab
-  gBrowser.loadOneTab("about:robots", { inBackground: false });
+  gBrowser.addTab("about:robots");
   is(gBrowser.tabs.length, 2, "we now have 2 tabs");
   registerCleanupFunction(function() {
     gBrowser.removeTab(gBrowser.tabs[1]);
   });
 
   afterAllTabsLoaded(function() {
-    showTabView(function() {
-      // Get normal tab urls
-      for (let a = 0; a < gBrowser.tabs.length; a++)
-        normalURLs.push(gBrowser.tabs[a].linkedBrowser.currentURI.spec);
+    // Get normal tab urls
+    for (let a = 0; a < gBrowser.tabs.length; a++)
+      normalURLs.push(gBrowser.tabs[a].linkedBrowser.currentURI.spec);
 
-      // verify that we're all set up for our test
-      verifyNormal();
+    // verify that we're all set up for our test
+    verifyNormal();
 
-      // go into private browsing and make sure Tab View becomes hidden
-      togglePBAndThen(function() {
-        whenTabViewIsHidden(function() {
-          ok(!TabView.isVisible(), "Tab View is no longer visible");
-
-          verifyPB();
+    // go into private browsing and make sure Tab View becomes hidden
+    togglePBAndThen(function() {
+      whenTabViewIsHidden(function() {
+        ok(!TabView.isVisible(), "Tab View is no longer visible");
+        verifyPB();
 
-          // exit private browsing and make sure Tab View is shown again
-          togglePBAndThen(function() {
-            whenTabViewIsShown(function() {
-              ok(TabView.isVisible(), "Tab View is visible again");
-              verifyNormal();
+        // exit private browsing and make sure Tab View is shown again
+        togglePBAndThen(function() {
+          whenTabViewIsShown(function() {
+            ok(TabView.isVisible(), "Tab View is visible again");
+            verifyNormal();
 
-              hideTabView(onTabViewHidden);
-            });
+            hideTabView(onTabViewHidden);
           });
         });
       });
-    }); 
+    });
   });
 }
 
 // -----------
 function onTabViewHidden() {
   ok(!TabView.isVisible(), "Tab View is not visible");
   
   // go into private browsing and make sure Tab View remains hidden
@@ -98,16 +95,18 @@ function onTabViewHidden() {
     
     // turn private browsing back off
     togglePBAndThen(function() {
       verifyNormal();
       
       // end game
       ok(!TabView.isVisible(), "we finish with Tab View not visible");
       registerCleanupFunction(verifyCleanState); // verify after all cleanups
+
+      gBrowser.selectedTab = gBrowser.tabs[0];
       finish();
     });
   });
 }
 
 // ----------
 function verifyCleanState(mode) {
   let prefix = "we " + (mode || "finish") + " with ";
--- a/browser/base/content/test/test_contextmenu.html
+++ b/browser/base/content/test/test_contextmenu.html
@@ -19,21 +19,21 @@ Browser context menu tests.
 
 /** Test for Login Manager: multiple login autocomplete. **/
 
 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
-function openContextMenuFor(element) {
+function openContextMenuFor(element, shiftkey) {
     // Context menu should be closed before we open it again.
     is(contextMenu.state, "closed", "checking if popup is closed");
 
-    var eventDetails = { type : "contextmenu", button : 2 };
+    var eventDetails = { type : "contextmenu", button : 2, shiftKey : shiftkey };
     synthesizeMouse(element, 2, 2, eventDetails, element.ownerDocument.defaultView);
 }
 
 function closeContextMenu() {
     contextMenu.hidePopup();
 }
 
 function executeCopyCommand(command, expectedValue)
@@ -45,113 +45,171 @@ function executeCopyCommand(command, exp
   // The easiest way to check the clipboard is to paste the contents into a
   // textbox
   input.focus();
   input.value = "";
   input.controllers.getControllerForCommand("cmd_paste").doCommand("cmd_paste");
   is(input.value, expectedValue, "paste for command " + command);
 }
 
-function getVisibleMenuItems(aMenu) {
+function invokeItemAction(ident)
+{
+  var item = contextMenu.getElementsByAttribute("ident", ident)[0];
+  ok(item, "Got generated XUL menu item");
+  item.doCommand();
+  is(pagemenu.hasAttribute("hopeless"), false, "attribute got removed");
+}
+
+function getVisibleMenuItems(aMenu, aData) {
     var items = [];
     var accessKeys = {};
     for (var i = 0; i < aMenu.childNodes.length; i++) {
         var item = aMenu.childNodes[i];
         if (item.hidden)
             continue;
 
         var key = item.accessKey;
         if (key)
             key = key.toLowerCase();
 
+        var isGenerated = item.hasAttribute("generated");
+
         if (item.nodeName == "menuitem") {
             var isSpellSuggestion = item.className == "spell-suggestion";
             if (isSpellSuggestion) {
               is(item.id, "", "child menuitem #" + i + " is a spelling suggestion");
+            } else if (isGenerated) {
+              is(item.id, "", "child menuitem #" + i + " is a generated item");
             } else {
               ok(item.id, "child menuitem #" + i + " has an ID");
             }
             var label = item.getAttribute("label");
             ok(label.length, "menuitem " + item.id + " has a label");
             if (isSpellSuggestion) {
               is(key, "", "Spell suggestions shouldn't have an access key");
               items.push("*" + label);
+            } else if (isGenerated) {
+              items.push("+" + label);
             } else if (item.id.indexOf("spell-check-dictionary-") != 0 &&
                        item.id != "spell-no-suggestions") {
               ok(key, "menuitem " + item.id + " has an access key");
               if (accessKeys[key])
                   ok(false, "menuitem " + item.id + " has same accesskey as " + accessKeys[key]);
               else
                   accessKeys[key] = item.id;
             }
-            if (!isSpellSuggestion) {
+            if (!isSpellSuggestion && !isGenerated) {
               items.push(item.id);
             }
-            items.push(!item.disabled);
+            if (isGenerated) {
+              var p = {};
+              p.type = item.getAttribute("type");
+              p.icon = item.getAttribute("image");
+              p.checked = item.hasAttribute("checked");
+              p.disabled = item.hasAttribute("disabled");
+              items.push(p);
+            } else {
+              items.push(!item.disabled);
+            }
         } else if (item.nodeName == "menuseparator") {
             ok(true, "--- seperator id is " + item.id);
             items.push("---");
             items.push(null);
         } else if (item.nodeName == "menu") {
+            if (isGenerated) {
+                item.id = "generated-submenu-" + aData.generatedSubmenuId++;
+            }
             ok(item.id, "child menu #" + i + " has an ID");
-            ok(key, "menu has an access key");
-            if (accessKeys[key])
-                ok(false, "menu " + item.id + " has same accesskey as " + accessKeys[key]);
-            else
-                accessKeys[key] = item.id;
+            if (!isGenerated) {
+                ok(key, "menu has an access key");
+                if (accessKeys[key])
+                    ok(false, "menu " + item.id + " has same accesskey as " + accessKeys[key]);
+                else
+                    accessKeys[key] = item.id;
+            }
             items.push(item.id);
             items.push(!item.disabled);
             // Add a dummy item to that the indexes in checkMenu are the same
             // for expectedItems and actualItems.
             items.push([]);
             items.push(null);
         } else {
             ok(false, "child #" + i + " of menu ID " + aMenu.id +
                       " has an unknown type (" + item.nodeName + ")");
         }
     }
     return items;
 }
 
 function checkContextMenu(expectedItems) {
     is(contextMenu.state, "open", "checking if popup is open");
-    checkMenu(contextMenu, expectedItems);
+    var data = { generatedSubmenuId: 1 };
+    checkMenu(contextMenu, expectedItems, data);
 }
 
 /*
  * checkMenu - checks to see if the specified <menupopup> contains the
  * expected items and state.
  * expectedItems is a array of (1) item IDs and (2) a boolean specifying if
  * the item is enabled or not (or null to ignore it). Submenus can be checked
  * by providing a nested array entry after the expected <menu> ID.
  * For example: ["blah", true,              // item enabled
  *               "submenu", null,           // submenu
  *                   ["sub1", true,         // submenu contents
  *                    "sub2", false], null, // submenu contents
  *               "lol", false]              // item disabled
  * 
  */
-function checkMenu(menu, expectedItems) {
-    var actualItems = getVisibleMenuItems(menu);
+function checkMenu(menu, expectedItems, data) {
+    var actualItems = getVisibleMenuItems(menu, data);
     //ok(false, "Items are: " + actualItems);
     for (var i = 0; i < expectedItems.length; i+=2) {
         var actualItem   = actualItems[i];
         var actualEnabled = actualItems[i + 1];
         var expectedItem = expectedItems[i];
         var expectedEnabled = expectedItems[i + 1];
         if (expectedItem instanceof Array) {
             ok(true, "Checking submenu...");
             var menuID = expectedItems[i - 2]; // The last item was the menu ID.
             var submenu = menu.getElementsByAttribute("id", menuID)[0];
             ok(submenu && submenu.nodeName == "menu", "got expected submenu element");
-            checkMenu(submenu.menupopup, expectedItem);
+            checkMenu(submenu.menupopup, expectedItem, data);
         } else {
             is(actualItem, expectedItem,
                "checking item #" + i/2 + " (" + expectedItem + ") name");
-            if (expectedEnabled != null)
+
+            if (typeof expectedEnabled == "object" && expectedEnabled != null ||
+                typeof actualEnabled == "object" && actualEnabled != null) {
+
+                ok(!(actualEnabled == null), "actualEnabled is not null");
+                ok(!(expectedEnabled == null), "expectedEnabled is not null");
+                is(typeof actualEnabled, typeof expectedEnabled, "checking types");
+
+                if (typeof actualEnabled != typeof expectedEnabled ||
+                    actualEnabled == null || expectedEnabled == null)
+                  continue;
+
+                is(actualEnabled.type, expectedEnabled.type,
+                   "checking item #" + i/2 + " (" + expectedItem + ") type attr value");
+                var icon = actualEnabled.icon;
+                if (icon) {
+                  var tmp = "";
+                  var j = icon.length - 1;
+                  while (j && icon[j] != "/") {
+                    tmp = icon[j--] + tmp;
+                  }
+                  icon = tmp;
+                }
+                is(icon, expectedEnabled.icon,
+                   "checking item #" + i/2 + " (" + expectedItem + ") icon attr value");
+                is(actualEnabled.checked, expectedEnabled.checked,
+                   "checking item #" + i/2 + " (" + expectedItem + ") has checked attr");
+                is(actualEnabled.disabled, expectedEnabled.disabled,
+                   "checking item #" + i/2 + " (" + expectedItem + ") has disabled attr");
+            } else if (expectedEnabled != null)
                 is(actualEnabled, expectedEnabled,
                    "checking item #" + i/2 + " (" + expectedItem + ") enabled state");
         }
     }
     // Could find unexpected extra items at the end...
     is(actualItems.length, expectedItems.length, "checking expected number of menu entries");
 }
 
@@ -403,19 +461,80 @@ function runTest(testNum) {
                               ["spell-check-dictionary-en-US", true,
                                "---",                          null,
                                "spell-add-dictionaries",       true], null]);
 
         closeContextMenu();
         openContextMenuFor(link); // Invoke context menu for next test.
         break;
 
-  case 15:
+    case 15:
         executeCopyCommand("cmd_copyLink", "http://mozilla.com/");
         closeContextMenu();
+        openContextMenuFor(pagemenu); // Invoke context menu for next test.
+        break;
+
+    case 16:
+        // Context menu for element with assigned content context menu
+        checkContextMenu(["+Plain item",          {type: "", icon: "", checked: false, disabled: false},
+                          "+Disabled item",       {type: "", icon: "", checked: false, disabled: true},
+                          "---",                  null,
+                          "+Checkbox",            {type: "checkbox", icon: "", checked: true, disabled: false},
+                          "---",                  null,
+                          "+Radio1",              {type: "checkbox", icon: "", checked: true, disabled: false},
+                          "+Radio2",              {type: "checkbox", icon: "", checked: false, disabled: false},
+                          "+Radio3",              {type: "checkbox", icon: "", checked: false, disabled: false},
+                          "---",                  null,
+                          "+Item w/ icon",        {type: "", icon: "favicon.ico", checked: false, disabled: false},
+                          "+Item w/ bad icon",    {type: "", icon: "", checked: false, disabled: false},
+                          "---",                  null,
+                          "generated-submenu-1",  true,
+                              ["+Radio1",             {type: "checkbox", icon: "", checked: false, disabled: false},
+                               "+Radio2",             {type: "checkbox", icon: "", checked: true, disabled: false},
+                               "+Radio3",             {type: "checkbox", icon: "", checked: false, disabled: false},
+                               "---",                 null,
+                               "+Checkbox",           {type: "checkbox", icon: "", checked: false, disabled: false}], null,
+                          "---",                  null,
+                          "context-back",         false,
+                          "context-forward",      false,
+                          "context-reload",       true,
+                          "context-stop",         false,
+                          "---",                  null,
+                          "context-bookmarkpage", true,
+                          "context-savepage",     true,
+                          "context-sendpage",     true,
+                          "---",                  null,
+                          "context-viewbgimage",  false,
+                          "context-selectall",    true,
+                          "---",                  null,
+                          "context-viewsource",   true,
+                          "context-viewinfo",     true]);
+
+        invokeItemAction("0");
+        closeContextMenu();
+        openContextMenuFor(pagemenu, true); // Invoke context menu for next test.
+        break;
+
+    case 17:
+        // Context menu for element with assigned content context menu
+        // The shift key should bypass content context menu processing
+        checkContextMenu(["context-back",         false,
+                          "context-forward",      false,
+                          "context-reload",       true,
+                          "context-stop",         false,
+                          "---",                  null,
+                          "context-bookmarkpage", true,
+                          "context-savepage",     true,
+                          "context-sendpage",     true,
+                          "---",                  null,
+                          "context-viewbgimage",  false,
+                          "context-selectall",    true,
+                          "---",                  null,
+                          "context-viewsource",   true,
+                          "context-viewinfo",     true]);
 
         subwindow.close();
         SimpleTest.finish();
         return;
 
     /*
      * Other things that would be nice to test:
      *  - selected text
@@ -432,17 +551,17 @@ function runTest(testNum) {
   }
 
 }
 
 
 var testNum = 1;
 var subwindow, chromeWin, contextMenu;
 var text, link, mailto, input, img, canvas, video_ok, video_bad, video_bad2,
-    iframe, textarea, contenteditable, inputspell;
+    iframe, textarea, contenteditable, inputspell, pagemenu;
 
 function startTest() {
     netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
     chromeWin = subwindow
                     .QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIWebNavigation)
                     .QueryInterface(Ci.nsIDocShellTreeItem)
                     .rootTreeItem
@@ -465,16 +584,17 @@ function startTest() {
     canvas = subwindow.document.getElementById("test-canvas");
     video_ok   = subwindow.document.getElementById("test-video-ok");
     video_bad  = subwindow.document.getElementById("test-video-bad");
     video_bad2 = subwindow.document.getElementById("test-video-bad2");
     iframe = subwindow.document.getElementById("test-iframe");
     textarea = subwindow.document.getElementById("test-textarea");
     contenteditable = subwindow.document.getElementById("test-contenteditable");
     inputspell = subwindow.document.getElementById("test-input-spellcheck");
+    pagemenu = subwindow.document.getElementById("test-pagemenu");
 
     contextMenu.addEventListener("popupshown", function() { runTest(++testNum); }, false);
     runTest(1);
 }
 
 // We open this in a separate window, because the Mochitests run inside a frame.
 // The frame causes an extra menu item, and prevents running the test
 // standalone (ie, clicking the test name in the Mochitest window) to see
--- a/browser/base/content/web-panels.xul
+++ b/browser/base/content/web-panels.xul
@@ -75,20 +75,20 @@
              oncommand="getPanelBrowser().webNavigation.goForward();"
              disabled="true"/>
     <command id="Browser:Stop" oncommand="PanelBrowserStop();"/>
     <command id="Browser:Reload" oncommand="PanelBrowserReload();"/>
   </commandset>
 
   <popupset id="mainPopupSet">
     <tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
-    <menupopup id="contentAreaContextMenu"
+    <menupopup id="contentAreaContextMenu" pagemenu="start"
                onpopupshowing="if (event.target != this)
                                  return true;
-                               gContextMenu = new nsContextMenu(this, getPanelBrowser());
+                               gContextMenu = new nsContextMenu(this, getPanelBrowser(), event.shiftKey);
                                if (gContextMenu.shouldDisplay)
                                  document.popupNode = this.triggerNode;
                                return gContextMenu.shouldDisplay;"
                onpopuphiding="if (event.target == this)
                                 gContextMenu = null;">
 #include browser-context.inc
     </menupopup>
   </popupset>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -27,18 +27,16 @@ browser.jar:
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 *       content/browser/content.js                    (content/content.js)
 *       content/browser/fullscreen-video.xhtml        (content/fullscreen-video.xhtml)
 *       content/browser/inspector.html                (content/inspector.html)
-*       content/browser/scratchpad.xul                (content/scratchpad.xul)
-*       content/browser/scratchpad.js                 (content/scratchpad.js)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
 *       content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
 *       content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
 *       content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
 *       content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
 *       content/browser/pageinfo/feeds.xml            (content/pageinfo/feeds.xml)
 *       content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
 *       content/browser/pageinfo/security.js          (content/pageinfo/security.js)
--- a/browser/branding/aurora/Makefile.in
+++ b/browser/branding/aurora/Makefile.in
@@ -55,20 +55,16 @@ WINDOWS_BRANDING_FILES = \
 	firefox.ico \
 	document.ico \
 	branding.nsi \
 	wizHeader.bmp \
 	wizHeaderRTL.bmp \
 	wizWatermark.bmp \
 	$(NULL)
 
-ifdef MOZ_SPLASHSCREEN
-WINDOWS_BRANDING_FILES += splash.bmp
-endif
-
 OSX_BRANDING_FILES = \
 	background.png \
 	firefox.icns \
 	disk.icns \
 	document.icns \
 	dsstore \
 	$(NULL)
 
deleted file mode 100644
index 2ba119890cd21cd4498d6a8bb3274c7d41fc6cbc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/branding/nightly/Makefile.in
+++ b/browser/branding/nightly/Makefile.in
@@ -55,20 +55,16 @@ WINDOWS_BRANDING_FILES = \
 	firefox.ico \
 	document.ico \
 	branding.nsi \
 	wizHeader.bmp \
 	wizHeaderRTL.bmp \
 	wizWatermark.bmp \
 	$(NULL)
 
-ifdef MOZ_SPLASHSCREEN
-WINDOWS_BRANDING_FILES += splash.bmp
-endif
-
 OSX_BRANDING_FILES = \
 	background.png \
 	firefox.icns \
 	disk.icns \
 	document.icns \
 	dsstore \
 	$(NULL)
 
deleted file mode 100644
index 2ba119890cd21cd4498d6a8bb3274c7d41fc6cbc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/branding/official/Makefile.in
+++ b/browser/branding/official/Makefile.in
@@ -55,20 +55,16 @@ WINDOWS_BRANDING_FILES = \
 	firefox.ico \
 	document.ico \
 	branding.nsi \
 	wizHeader.bmp \
 	wizHeaderRTL.bmp \
 	wizWatermark.bmp \
 	$(NULL)
 
-ifdef MOZ_SPLASHSCREEN
-WINDOWS_BRANDING_FILES += splash.bmp
-endif
-
 OSX_BRANDING_FILES = \
 	background.png \
 	firefox.icns \
 	disk.icns \
 	document.icns \
 	dsstore \
 	$(NULL)
 
deleted file mode 100644
index a2b5e8b0867aebfa15f48f78a73549dc13662d69..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/branding/unofficial/Makefile.in
+++ b/browser/branding/unofficial/Makefile.in
@@ -55,20 +55,16 @@ WINDOWS_BRANDING_FILES = \
 	firefox.ico \
 	document.ico \
 	branding.nsi \
 	wizHeader.bmp \
 	wizHeaderRTL.bmp \
 	wizWatermark.bmp \
 	$(NULL)
 
-ifdef MOZ_SPLASHSCREEN
-WINDOWS_BRANDING_FILES += splash.bmp
-endif
-
 OSX_BRANDING_FILES = \
 	background.png \
 	firefox.icns \
 	disk.icns \
 	document.icns \
 	dsstore \
 	$(NULL)
 
deleted file mode 100644
index d2afefdf82ea339497d69adeeae4ae9d3f25e467..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/components/migration/src/nsSeamonkeyProfileMigrator.cpp
+++ b/browser/components/migration/src/nsSeamonkeyProfileMigrator.cpp
@@ -56,17 +56,16 @@
 
 #define FILE_NAME_BOOKMARKS       NS_LITERAL_STRING("bookmarks.html")
 #define FILE_NAME_COOKIES         NS_LITERAL_STRING("cookies.txt")
 #define FILE_NAME_SITEPERM_OLD    NS_LITERAL_STRING("cookperm.txt")
 #define FILE_NAME_SITEPERM_NEW    NS_LITERAL_STRING("hostperm.1")
 #define FILE_NAME_CERT8DB         NS_LITERAL_STRING("cert8.db")
 #define FILE_NAME_KEY3DB          NS_LITERAL_STRING("key3.db")
 #define FILE_NAME_SECMODDB        NS_LITERAL_STRING("secmod.db")
-#define FILE_NAME_HISTORY         NS_LITERAL_STRING("history.dat")
 #define FILE_NAME_MIMETYPES       NS_LITERAL_STRING("mimeTypes.rdf")
 #define FILE_NAME_DOWNLOADS       NS_LITERAL_STRING("downloads.rdf")
 #define FILE_NAME_PREFS           NS_LITERAL_STRING("prefs.js")
 #define FILE_NAME_USER_PREFS      NS_LITERAL_STRING("user.js")
 #define FILE_NAME_USERCONTENT     NS_LITERAL_STRING("userContent.css")
 #define DIR_NAME_CHROME           NS_LITERAL_STRING("chrome")
 
 NS_IMPL_ISUPPORTS1(nsSeamonkeyProfileMigrator, nsIBrowserProfileMigrator)
@@ -95,17 +94,16 @@ nsSeamonkeyProfileMigrator::Migrate(PRUi
   }
   if (!mSourceProfile)
     GetSourceProfile(aProfile);
 
   NOTIFY_OBSERVERS(MIGRATION_STARTED, nsnull);
 
   COPY_DATA(CopyPreferences,  aReplace, nsIBrowserProfileMigrator::SETTINGS);
   COPY_DATA(CopyCookies,      aReplace, nsIBrowserProfileMigrator::COOKIES);
-  COPY_DATA(CopyHistory,      aReplace, nsIBrowserProfileMigrator::HISTORY);
   COPY_DATA(CopyPasswords,    aReplace, nsIBrowserProfileMigrator::PASSWORDS);
   COPY_DATA(CopyOtherData,    aReplace, nsIBrowserProfileMigrator::OTHERDATA);
 
   // Need to do startup before trying to copy bookmarks, since bookmarks
   // import requires a profile. Can't do it earlier because services might
   // end up creating the files we try to copy above.
   if (aStartup) {
     rv = aStartup->DoStartup();
@@ -146,19 +144,16 @@ nsSeamonkeyProfileMigrator::GetMigrateDa
                              nsIBrowserProfileMigrator::SETTINGS,
                              PR_TRUE },
                            { ToNewUnicode(FILE_NAME_USER_PREFS),
                              nsIBrowserProfileMigrator::SETTINGS,
                              PR_TRUE },
                            { ToNewUnicode(FILE_NAME_COOKIES),
                              nsIBrowserProfileMigrator::COOKIES,
                              PR_FALSE },
-                           { ToNewUnicode(FILE_NAME_HISTORY),
-                             nsIBrowserProfileMigrator::HISTORY,
-                             PR_TRUE },
                            { ToNewUnicode(FILE_NAME_BOOKMARKS),
                              nsIBrowserProfileMigrator::BOOKMARKS,
                              PR_FALSE },
                            { ToNewUnicode(FILE_NAME_DOWNLOADS),
                              nsIBrowserProfileMigrator::OTHERDATA,
                              PR_TRUE },
                            { ToNewUnicode(FILE_NAME_MIMETYPES),
                              nsIBrowserProfileMigrator::OTHERDATA,
@@ -639,22 +634,16 @@ nsSeamonkeyProfileMigrator::CopyCookies(
     seamonkeyCookiesFile->Append(FILE_NAME_COOKIES);
 
     rv = ImportNetscapeCookies(seamonkeyCookiesFile);
   }
   return rv;
 }
 
 nsresult
-nsSeamonkeyProfileMigrator::CopyHistory(PRBool aReplace)
-{
-  return aReplace ? CopyFile(FILE_NAME_HISTORY, FILE_NAME_HISTORY) : NS_OK;
-}
-
-nsresult
 nsSeamonkeyProfileMigrator::CopyPasswords(PRBool aReplace)
 {
   nsresult rv;
 
   nsCString signonsFileName;
   GetSignonFileName(aReplace, getter_Copies(signonsFileName));
 
   if (signonsFileName.IsEmpty())
--- a/browser/components/migration/src/nsSeamonkeyProfileMigrator.h
+++ b/browser/components/migration/src/nsSeamonkeyProfileMigrator.h
@@ -86,17 +86,16 @@ protected:
   void     ReadFontsBranch(nsIPrefService* aPrefService,
                            nsTArray<FontPref>* aPrefs);
   void     WriteFontsBranch(nsIPrefService* aPrefService,
                             nsTArray<FontPref>* aPrefs);
 
   nsresult CopyUserContentSheet();
 
   nsresult CopyCookies(PRBool aReplace);
-  nsresult CopyHistory(PRBool aReplace);
   nsresult CopyPasswords(PRBool aReplace);
   nsresult LocateSignonsFile(char** aResult);
   nsresult CopyBookmarks(PRBool aReplace);
   nsresult CopyOtherData(PRBool aReplace);
 
 private:
   nsCOMPtr<nsISupportsArray> mProfileNames;
   nsCOMPtr<nsISupportsArray> mProfileLocations;
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -67,17 +67,17 @@ var PlacesOrganizer = {
   },
 
   init: function PO_init() {
     this._places = document.getElementById("placesList");
     this._content = document.getElementById("placeContent");
     this._initFolderTree();
 
     var leftPaneSelection = "AllBookmarks"; // default to all-bookmarks
-    if ("arguments" in window && window.arguments.length > 0)
+    if (window.arguments && window.arguments[0])
       leftPaneSelection = window.arguments[0];
 
     this.selectLeftPaneQuery(leftPaneSelection);
     // clear the back-stack
     this._backHistory.splice(0);
     document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);
 
     var view = this._content.treeBoxObject.view;
--- a/browser/components/places/src/PlacesUIUtils.jsm
+++ b/browser/components/places/src/PlacesUIUtils.jsm
@@ -51,17 +51,17 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
   Cu.import("resource://gre/modules/PlacesUtils.jsm");
   return PlacesUtils;
 });
 
 var PlacesUIUtils = {
-  ORGANIZER_LEFTPANE_VERSION: 6,
+  ORGANIZER_LEFTPANE_VERSION: 7,
   ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
   ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",
 
   LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar",
   DESCRIPTION_ANNO: "bookmarkProperties/description",
 
   TYPE_TAB_DROP: "application/x-moz-tabbrowser-tab",
 
@@ -807,18 +807,20 @@ var PlacesUIUtils = {
       args.AppendElement(uriList);      
       browserWindow = Services.ww.openWindow(aWindow,
                                              "chrome://browser/content/browser.xul",
                                              null, "chrome,dialog=no,all", args);
       return;
     }
 
     var loadInBackground = where == "tabshifted" ? true : false;
-    var replaceCurrentTab = where == "tab" ? false : true;
-    browserWindow.gBrowser.loadTabs(urls, loadInBackground, replaceCurrentTab);
+    // For consistency, we want all the bookmarks to open in new tabs, instead
+    // of having one of them replace the currently focused tab.  Hence we call
+    // loadTabs with aReplace set to false.
+    browserWindow.gBrowser.loadTabs(urls, loadInBackground, false);
   },
 
   /**
    * Helper method for methods which are forced to take a view/window
    * parameter as an optional parameter.  It will be removed post Fx4.
    */
   _getWindow: function PUIU__getWindow(aView) {
     if (aView) {
@@ -981,33 +983,34 @@ var PlacesUIUtils = {
     // Shortcuts to services.
     let bs = PlacesUtils.bookmarks;
     let as = PlacesUtils.annotations;
 
     // This is the list of the left pane queries.
     let queries = {
       "PlacesRoot": { title: "" },
       "History": { title: this.getString("OrganizerQueryHistory") },
+      "Downloads": { title: this.getString("OrganizerQueryDownloads") },
       "Tags": { title: this.getString("OrganizerQueryTags") },
       "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") },
       "BookmarksToolbar":
         { title: null,
           concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"),
           concreteId: PlacesUtils.toolbarFolderId },
       "BookmarksMenu":
         { title: null,
           concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"),
           concreteId: PlacesUtils.bookmarksMenuFolderId },
       "UnfiledBookmarks":
         { title: null,
           concreteTitle: PlacesUtils.getString("UnsortedBookmarksFolderTitle"),
           concreteId: PlacesUtils.unfiledBookmarksFolderId },
     };
     // All queries but PlacesRoot.
-    const EXPECTED_QUERY_COUNT = 6;
+    const EXPECTED_QUERY_COUNT = 7;
 
     // Removes an item and associated annotations, ignoring eventual errors.
     function safeRemoveItem(aItemId) {
       try {
         if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) &&
             !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) {
           // Some extension annotated their roots with our query annotation,
           // so we should not delete them.
@@ -1170,17 +1173,22 @@ var PlacesUIUtils = {
 
         // History Query.
         this.create_query("History", leftPaneRoot,
                           "place:type=" +
                           Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY +
                           "&sort=" +
                           Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
 
-        // XXX: Downloads.
+        // Downloads.
+        this.create_query("Downloads", leftPaneRoot,
+                          "place:transition=" +
+                          Ci.nsINavHistoryService.TRANSITION_DOWNLOAD +
+                          "&sort=" +
+                          Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
 
         // Tags Query.
         this.create_query("Tags", leftPaneRoot,
                           "place:type=" +
                           Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
                           "&sort=" +
                           Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
 
--- a/browser/components/places/tests/browser/Makefile.in
+++ b/browser/components/places/tests/browser/Makefile.in
@@ -69,12 +69,13 @@ include $(topsrcdir)/config/rules.mk
 	browser_markPageAsFollowedLink.js \
 	framedPage.html \
 	frameLeft.html \
 	frameRight.html \
 	browser_toolbar_migration.js \
 	browser_library_batch_delete.js \
 	browser_555547.js \
 	browser_416459_cut.js \
+	browser_library_downloads.js \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_library_downloads.js
@@ -0,0 +1,89 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Places test code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mehdi Mulani <mmulani@mozilla.com> (original author)
+ *   Jared Wein <jwein@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Tests bug 564900: Add folder specifically for downloads to Library left pane.
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=564900
+ * This test visits various pages then opens the Library and ensures
+ * that both the Downloads folder shows up and that the correct visits
+ * are shown in it.
+ */
+
+let now = Date.now();
+
+function test() {
+  waitForExplicitFinish();
+
+  function onLibraryReady(win) {
+    // Add visits to compare contents with.
+    fastAddVisit("http://mozilla.com",
+                  PlacesUtils.history.TRANSITION_TYPED);
+    fastAddVisit("http://google.com",
+                  PlacesUtils.history.TRANSITION_DOWNLOAD);
+    fastAddVisit("http://en.wikipedia.org",
+                  PlacesUtils.history.TRANSITION_TYPED);
+    fastAddVisit("http://ubuntu.org",
+                  PlacesUtils.history.TRANSITION_DOWNLOAD);
+
+    // Make sure Downloads is present.
+    isnot(win.PlacesOrganizer._places.selectedNode, null,
+          "Downloads is present and selected");
+
+    // Make sure content in right pane exists.
+    let tree = win.document.getElementById("placeContent");
+    isnot(tree, null, "placeContent tree exists");
+
+    // Check results.
+    var contentRoot = tree.result.root;
+    var len = contentRoot.childCount;
+    var testUris = ["http://ubuntu.org/", "http://google.com/"];
+    for (var i = 0; i < len; i++) {
+      is(contentRoot.getChild(i).uri, testUris[i],
+          "Comparing downloads shown at index " + i);
+    }
+
+    win.close();
+    waitForClearHistory(finish);
+  }
+
+  openLibrary(onLibraryReady, "Downloads");
+}
+
+function fastAddVisit(uri, transition) {
+  PlacesUtils.history.addVisit(PlacesUtils._uri(uri), now++ * 1000,
+                               null, transition, false, 0);
+}
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -1,28 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
 
 // We need to cache this before test runs...
 let cachedLeftPaneFolderIdGetter;
 let (getter = PlacesUIUtils.__lookupGetter__("leftPaneFolderId")) {
   if (!cachedLeftPaneFolderIdGetter && typeof(getter) == "function")
     cachedLeftPaneFolderIdGetter = getter;
 }
 // ...And restore it when test ends.
 registerCleanupFunction(function(){
   let (getter = PlacesUIUtils.__lookupGetter__("leftPaneFolderId")) {
     if (cachedLeftPaneFolderIdGetter && typeof(getter) != "function")
       PlacesUIUtils.__defineGetter__("leftPaneFolderId",
                                      cachedLeftPaneFolderIdGetter);
   }
 });
 
-
-function openLibrary(callback) {
+function openLibrary(callback, aLeftPaneRoot) {
   let library = window.openDialog("chrome://browser/content/places/places.xul",
-                                  "", "chrome,toolbar=yes,dialog=no,resizable");
+                                  "", "chrome,toolbar=yes,dialog=no,resizable",
+                                  aLeftPaneRoot);
   waitForFocus(function () {
     callback(library);
   }, library);
 
   return library;
 }
 
-Components.utils.import("resource://gre/modules/NetUtil.jsm");
+function waitForClearHistory(aCallback) {
+  Services.obs.addObserver(function observeCH(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(observeCH, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
+    aCallback();
+  }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
+  PlacesUtils.bhistory.removeAllPages();
+}
--- a/browser/components/places/tests/chrome/test_0_bug510634.xul
+++ b/browser/components/places/tests/chrome/test_0_bug510634.xul
@@ -92,47 +92,59 @@
       return results;
     }
 
     function runTest() {
       // We need to cache and restore this getter in order to simulate
       // Bug 510634
       let cachedLeftPaneFolderIdGetter =
         PlacesUIUtils.__lookupGetter__("leftPaneFolderId");
+      // Must also cache and restore this getter as it is affected by
+      // leftPaneFolderId, from bug 564900.
+      let cachedAllBookmarksFolderIdGetter =
+        PlacesUIUtils.__lookupGetter__("allBookmarksFolderId");
 
       let leftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
 
       // restore the getter
       PlacesUIUtils.__defineGetter__("leftPaneFolderId", cachedLeftPaneFolderIdGetter);
 
       // Setup the places tree contents.
       let tree = document.getElementById("tree");
       tree.place = "place:queryType=1&folder=" + leftPaneFolderId;
 
-      // Open All Bookmarks
-      PlacesUtils.asContainer(tree.view.nodeForTreeIndex(2)).containerOpen = true;
-      
       // The query-property is set on the title column for each row.
       let titleColumn = tree.treeBoxObject.columns.getColumnAt(0);
 
-      ["History", "Tags", "AllBookmarks", "BookmarksToolbar",
+      // Open All Bookmarks
+      tree.selectItems([PlacesUIUtils.leftPaneQueries["AllBookmarks"]]);
+      PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
+      is(PlacesUIUtils.allBookmarksFolderId, tree.selectedNode.itemId,
+         "Opened All Bookmarks");
+
+      ["History", "Downloads", "Tags", "AllBookmarks", "BookmarksToolbar",
        "BookmarksMenu", "UnfiledBookmarks"].forEach(
-         function(aQueryName, aRow) {
-           let rowProperties = createSupportsArray();
-           tree.view.getCellProperties(aRow, titleColumn, rowProperties);
-           rowProperties = convertPropertiesToJSArray(rowProperties);
-           ok(rowProperties.indexOf("OrganizerQuery_" + aQueryName) != -1,
-             "OrganizerQuery_" + aQueryName + " is set");
-         }
-       );
+        function(aQueryName, aRow) {
+          let found = false;
+          for (let i = 0; i < tree.view.rowCount && !found; i++) {
+            let rowProperties = createSupportsArray();
+            tree.view.getCellProperties(i, titleColumn, rowProperties);
+            rowProperties = convertPropertiesToJSArray(rowProperties);
+            found = rowProperties.indexOf("OrganizerQuery_" + aQueryName) != -1;
+          }
+          ok(found, "OrganizerQuery_" + aQueryName + " is set");
+        }
+      );
 
       // Close the root node
       tree.result.root.containerOpen = false;
 
-      // Restore the getter for the next test.
+      // Restore the getters for the next test.
       PlacesUIUtils.__defineGetter__("leftPaneFolderId", cachedLeftPaneFolderIdGetter);
+      PlacesUIUtils.__defineGetter__("allBookmarksFolderId",
+                                     cachedAllBookmarksFolderIdGetter);
 
       SimpleTest.finish();
     }
 
   ]]>
   </script>
 </window>
--- a/browser/components/places/tests/chrome/test_bug427633_no_newfolder_if_noip.xul
+++ b/browser/components/places/tests/chrome/test_bug427633_no_newfolder_if_noip.xul
@@ -80,17 +80,17 @@
                getService(Ci.nsINavBookmarksService);
       var ios = Cc["@mozilla.org/network/io-service;1"].
                 getService(Ci.nsIIOService);
       function uri(spec) {
         return ios.newURI(spec, null, null);
       }
 
       // Add a bookmark.
-      var itemId = bs.insertBookmark(bs.toolbarFolder,
+      var itemId = bs.insertBookmark(PlacesUtils.toolbarFolderId,
                                      uri("http://www.example.com/"),
                                      bs.DEFAULT_INDEX,
                                      "mozilla");
 
       // Init panel.
       ok(gEditItemOverlay, "gEditItemOverlay is in context");
       gEditItemOverlay.initPanel(itemId);
       ok(gEditItemOverlay._initialized, "gEditItemOverlay is initialized");
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -87,28 +87,38 @@ Site.prototype = {
   /**
    * Gets the favicon to use for the site. The callback only gets called if
    * a favicon is found for either the http URI or the https URI.
    *
    * @param aCallback
    *        A callback function that takes a favicon image URL as a parameter.
    */
   getFavicon: function Site_getFavicon(aCallback) {
+    let callbackExecuted = false;
     function faviconDataCallback(aURI, aDataLen, aData, aMimeType) {
+      // We don't need a second callback, so we can ignore it to avoid making
+      // a second database query for the favicon data.
+      if (callbackExecuted) {
+        return;
+      }
       try {
-        aCallback(aURI.spec);
+        // Use getFaviconLinkForIcon to get image data from the database instead
+        // of using the favicon URI to fetch image data over the network.
+        aCallback(gFaviconService.getFaviconLinkForIcon(aURI).spec);
+        callbackExecuted = true;
       } catch (e) {
         Cu.reportError("AboutPermissions: " + e);
       }
     }
 
     // Try to find favicion for both URIs. Callback will only be called if a
-    // favicon URI is found, so this means we'll always prefer the https favicon.
+    // favicon URI is found. We'll ignore the second callback if it is called,
+    // so this means we'll always prefer the https favicon.
+    gFaviconService.getFaviconURLForPage(this.httpsURI, faviconDataCallback);
     gFaviconService.getFaviconURLForPage(this.httpURI, faviconDataCallback);
-    gFaviconService.getFaviconURLForPage(this.httpsURI, faviconDataCallback);
   },
 
   /**
    * Gets the number of history visits for the site.
    *
    * @param aCallback
    *        A function that takes the visit count (a number) as a parameter.
    */
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -498,51 +498,53 @@ PrivateBrowsingService.prototype = {
     // Allowing observers to set the private browsing status from their
     // notification handlers is not desired, because it will change the
     // status of the service while it's in the process of another transition.
     // So, we detect a reentrant call here and throw an error.
     // This is documented in nsIPrivateBrowsingService.idl.
     if (this._currentStatus != STATE_IDLE)
       throw Cr.NS_ERROR_FAILURE;
 
+    if (val == this._inPrivateBrowsing)
+      return;
+
     try {
+      if (val) {
+        if (!this._canEnterPrivateBrowsingMode())
+          return;
+      }
+      else {
+        if (!this._canLeavePrivateBrowsingMode())
+          return;
+      }
+
+      this._ensureCanCloseWindows();
+
+      // start the transition now that we know that we can
       this._currentStatus = STATE_TRANSITION_STARTED;
 
-      if (val != this._inPrivateBrowsing) {
-        if (val) {
-          if (!this._canEnterPrivateBrowsingMode())
-            return;
-        }
-        else {
-          if (!this._canLeavePrivateBrowsingMode())
-            return;
-        }
+      this._autoStarted = this._prefs.getBoolPref("browser.privatebrowsing.autostart");
+      this._inPrivateBrowsing = val != false;
 
-        this._ensureCanCloseWindows();
+      let data = val ? "enter" : "exit";
 
-        this._autoStarted = this._prefs.getBoolPref("browser.privatebrowsing.autostart");
-        this._inPrivateBrowsing = val != false;
+      let quitting = Cc["@mozilla.org/supports-PRBool;1"].
+                     createInstance(Ci.nsISupportsPRBool);
+      quitting.data = this._quitting;
 
-        let data = val ? "enter" : "exit";
-
-        let quitting = Cc["@mozilla.org/supports-PRBool;1"].
-                       createInstance(Ci.nsISupportsPRBool);
-        quitting.data = this._quitting;
-
-        // notify observers of the pending private browsing mode change
-        this._obs.notifyObservers(quitting, "private-browsing-change-granted", data);
+      // notify observers of the pending private browsing mode change
+      this._obs.notifyObservers(quitting, "private-browsing-change-granted", data);
 
-        // destroy the current session and start initial cleanup
-        this._onBeforePrivateBrowsingModeChange();
+      // destroy the current session and start initial cleanup
+      this._onBeforePrivateBrowsingModeChange();
 
-        this._obs.notifyObservers(quitting, "private-browsing", data);
+      this._obs.notifyObservers(quitting, "private-browsing", data);
 
-        // load the appropriate session
-        this._onAfterPrivateBrowsingModeChange();
-      }
+      // load the appropriate session
+      this._onAfterPrivateBrowsingModeChange();
     } catch (ex) {
       // We aborted the transition to/from private browsing, we must restore the
       // beforeunload handling on all the windows for which we switched it off.
       for (let i = 0; i < this._windowsToClose.length; i++)
         this._windowsToClose[i].docShell.contentViewer.resetCloseWindow();
       // We don't log an error when the transition is canceled from beforeunload
       if (ex != Cr.NS_ERROR_ABORT)
         Cu.reportError("Exception thrown while processing the " +
--- a/browser/components/privatebrowsing/test/unit/head_privatebrowsing.js
+++ b/browser/components/privatebrowsing/test/unit/head_privatebrowsing.js
@@ -42,17 +42,16 @@ const Cu = Components.utils;
 
 const kPrivateBrowsingNotification = "private-browsing";
 const kPrivateBrowsingCancelVoteNotification = "private-browsing-cancel-vote";
 const kPrivateBrowsingTransitionCompleteNotification = "private-browsing-transition-complete";
 const kEnter = "enter";
 const kExit = "exit";
 
 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
-const NS_APP_HISTORY_50_FILE = "UHist";
 
 function LOG(aMsg) {
   aMsg = ("*** PRIVATEBROWSING TESTS: " + aMsg);
   Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
                                       logStringMessage(aMsg);
   print(aMsg);
 }
 
@@ -60,36 +59,16 @@ function uri(spec) {
   return Cc["@mozilla.org/network/io-service;1"].
          getService(Ci.nsIIOService).
          newURI(spec, null, null);
 }
 
 var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
 var profileDir = do_get_profile();
 
-var provider = {
-  getFile: function(prop, persistent) {
-    persistent.value = true;
-    if (prop == NS_APP_HISTORY_50_FILE) {
-      var histFile = profileDir.clone();
-      histFile.append("history.dat");
-      return histFile;
-    }
-    throw Cr.NS_ERROR_FAILURE;
-  },
-  QueryInterface: function(iid) {
-    if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
-        iid.equals(Ci.nsISupports)) {
-      return this;
-    }
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
-};
-dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
-
 // Do not attempt to restore any session since we don't have any windows
 Cc["@mozilla.org/preferences-service;1"].
   getService(Ci.nsIPrefBranch).
   setBoolPref("browser.privatebrowsing.keep_current_session", true);
 
 /**
  * Removes any files that could make our tests fail.
  */
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -43,22 +43,18 @@ var gTreeData;
 
 // Page initialization
 
 window.onload = function() {
   // the crashed session state is kept inside a textbox so that SessionStore picks it up
   // (for when the tab is closed or the session crashes right again)
   var sessionData = document.getElementById("sessionData");
   if (!sessionData.value) {
-    var ss = Cc["@mozilla.org/browser/sessionstartup;1"].getService(Ci.nsISessionStartup);
-    sessionData.value = ss.state;
-    if (!sessionData.value) {
-      document.getElementById("errorTryAgain").disabled = true;
-      return;
-    }
+    document.getElementById("errorTryAgain").disabled = true;
+    return;
   }
 
   // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
   if (sessionData.value.charAt(0) == '(')
     sessionData.value = sessionData.value.slice(1, -1);
   try {
     gStateObject = JSON.parse(sessionData.value);
   }
--- a/browser/components/sessionstore/content/aboutSessionRestore.xhtml
+++ b/browser/components/sessionstore/content/aboutSessionRestore.xhtml
@@ -81,17 +81,16 @@
           <ul>
             <li>&restorepage.restoreSome;</li>
             <li>&restorepage.startNew;</li>
           </ul>
         </div>
 
         <!-- Short Description -->
         <div id="errorTrailerDesc">
-          <p>&nbsp;</p>
           <tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
                 id="tabList" flex="1" seltype="single" hidecolumnpicker="true"
                 onclick="onListClick(event);" onkeydown="onListKeyDown(event);"
                 _window_label="&restorepage.windowLabel;">
             <treecols>
               <treecol cycler="true" id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
               <splitter class="tree-splitter"/>
               <treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
--- a/browser/components/sessionstore/src/nsSessionStartup.js
+++ b/browser/components/sessionstore/src/nsSessionStartup.js
@@ -107,18 +107,19 @@ SessionStartup.prototype = {
     let prefBranch = Cc["@mozilla.org/preferences-service;1"].
                      getService(Ci.nsIPrefService).getBranch("browser.");
 
     // get file references
     var dirService = Cc["@mozilla.org/file/directory_service;1"].
                      getService(Ci.nsIProperties);
     let sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
     sessionFile.append("sessionstore.js");
-    
-    let doResumeSession = prefBranch.getBoolPref("sessionstore.resume_session_once") ||
+
+    let doResumeSessionOnce = prefBranch.getBoolPref("sessionstore.resume_session_once");
+    let doResumeSession = doResumeSessionOnce ||
                           prefBranch.getIntPref("startup.page") == 3;
 
     // only continue if the session file exists
     if (!sessionFile.exists())
       return;
 
     // get string containing session state
     let iniString = this._readStateFile(sessionFile);
@@ -132,16 +133,20 @@ SessionStartup.prototype = {
         iniString = iniString.slice(1, -1);
       try {
         this._initialState = JSON.parse(iniString);
       }
       catch (exJSON) {
         var s = new Cu.Sandbox("about:blank");
         this._initialState = Cu.evalInSandbox("(" + iniString + ")", s);
       }
+
+      // If this is a normal restore then throw away any previous session
+      if (!doResumeSessionOnce)
+        delete this._initialState.lastSessionState;
     }
     catch (ex) { debug("The session file is invalid: " + ex); }
 
     let resumeFromCrash = prefBranch.getBoolPref("sessionstore.resume_from_crash");
     let lastSessionCrashed =
       this._initialState && this._initialState.session &&
       this._initialState.session.state &&
       this._initialState.session.state == STATE_RUNNING_STR;
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -230,17 +230,20 @@ SessionStoreService.prototype = {
   _tabsRestoringCount: 0,
 
   // number of tabs to restore concurrently, pref controlled.
   _maxConcurrentTabRestores: null,
   
   // whether to restore hidden tabs or not, pref controlled.
   _restoreHiddenTabs: null,
 
-  // The state from the previous session (after restoring pinned tabs)
+  // The state from the previous session (after restoring pinned tabs). This
+  // state is persisted and passed through to the next session during an app
+  // restart to make the third party add-on warning not trash the deferred
+  // session
   _lastSessionState: null,
 
   // Whether we've been initialized
   _initialized: false,
 
   // The original "sessionstore.resume_session_once" preference value before it
   // was modified by saveState.  saveState will set the
   // "sessionstore.resume_session_once" to true when the
@@ -326,16 +329,20 @@ SessionStoreService.prototype = {
           if (iniState.windows.length)
             this._initialState = iniState;
           else
             this._initialState = null;
           if (remainingState.windows.length)
             this._lastSessionState = remainingState;
         }
         else {
+          // Get the last deferred session in case the user still wants to
+          // restore it
+          this._lastSessionState = this._initialState.lastSessionState;
+
           let lastSessionCrashed =
             this._initialState.session && this._initialState.session.state &&
             this._initialState.session.state == STATE_RUNNING_STR;
           if (lastSessionCrashed) {
             this._recentCrashes = (this._initialState.session &&
                                    this._initialState.session.recentCrashes || 0) + 1;
             
             if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
@@ -483,16 +490,22 @@ SessionStoreService.prototype = {
         // if the sessionstore.resume_session_once preference was changed by
         // saveState because crash recovery is disabled then restore the
         // preference back to the value it was prior to that.  This will prevent
         // SessionStore from always restoring the session when crash recovery is
         // disabled.
         this._prefBranch.setBoolPref("sessionstore.resume_session_once",
                                      this._resume_session_once_on_shutdown);
       }
+
+      if (aData != "restart") {
+        // Throw away the previous session on shutdown
+        this._lastSessionState = null;
+      }
+
       this._loadState = STATE_QUITTING; // just to be sure
       this._uninit();
       break;
     case "browser:purge-session-history": // catch sanitization 
       this._clearDisk();
       // If the browser is shutting down, simply return after clearing the
       // session data on disk as this notification fires after the
       // quit-application notification so the browser is about to exit.
@@ -1854,19 +1867,17 @@ SessionStoreService.prototype = {
         // We can stop doing base64 encoding once our serialization into JSON
         // is guaranteed to handle all chars in strings, including embedded
         // nulls.
         entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes));
       }
       catch (ex) { debug(ex); }
     }
 
-    if (aEntry.docIdentifier) {
-      entry.docIdentifier = aEntry.docIdentifier;
-    }
+    entry.docIdentifier = aEntry.BFCacheEntry.ID;
 
     if (aEntry.stateData != null) {
       entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
       entry.structuredCloneVersion = aEntry.stateData.formatVersion;
     }
 
     if (!(aEntry instanceof Ci.nsISHContainer)) {
       return entry;
@@ -2964,17 +2975,16 @@ SessionStoreService.prototype = {
                        getEntryAtIndex(activeIndex, false).
                        QueryInterface(Ci.nsISHEntry);
 
       // restore those aspects of the currently active documents which are not
       // preserved in the plain history entries (mainly scroll state and text data)
       browser.__SS_restore_data = tabData.entries[activeIndex] || {};
       browser.__SS_restore_pageStyle = tabData.pageStyle || "";
       browser.__SS_restore_tab = aTab;
-      browser.__SS_restore_docIdentifier = curSHEntry.docIdentifier;
 
       didStartLoad = true;
       try {
         // In order to work around certain issues in session history, we need to
         // force session history to update its internal index and call reload
         // instead of gotoIndex. See bug 597315.
         browser.webNavigation.sessionHistory.getEntryAtIndex(activeIndex, true);
         browser.webNavigation.sessionHistory.reloadCurrentEntry();
@@ -3117,34 +3127,26 @@ SessionStoreService.prototype = {
       var postdata = atob(aEntry.postdata_b64);
       var stream = Cc["@mozilla.org/io/string-input-stream;1"].
                    createInstance(Ci.nsIStringInputStream);
       stream.setData(postdata, postdata.length);
       shEntry.postData = stream;
     }
 
     if (aEntry.docIdentifier) {
-      // Get a new document identifier for this entry to ensure that history
-      // entries after a session restore are considered to have different
-      // documents from the history entries before the session restore.
-      // Document identifiers are 64-bit ints, so JS will loose precision and
-      // start assigning all entries the same doc identifier if these ever get
-      // large enough.
-      //
-      // It's a potential security issue if document identifiers aren't
-      // globally unique, but shEntry.setUniqueDocIdentifier() below guarantees
-      // that we won't re-use a doc identifier within a given instance of the
-      // application.
-      let ident = aDocIdentMap[aEntry.docIdentifier];
-      if (!ident) {
-        shEntry.setUniqueDocIdentifier();
-        aDocIdentMap[aEntry.docIdentifier] = shEntry.docIdentifier;
+      // If we have a serialized document identifier, try to find an SHEntry
+      // which matches that doc identifier and adopt that SHEntry's
+      // BFCacheEntry.  If we don't find a match, insert shEntry as the match
+      // for the document identifier.
+      let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
+      if (!matchingEntry) {
+        aDocIdentMap[aEntry.docIdentifier] = shEntry;
       }
       else {
-        shEntry.docIdentifier = ident;
+        shEntry.adoptBFCacheEntry(matchingEntry);
       }
     }
 
     if (aEntry.owner_b64) {
       var ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].
                        createInstance(Ci.nsIStringInputStream);
       var binaryData = atob(aEntry.owner_b64);
       ownerInput.setData(binaryData, binaryData.length);
@@ -3270,29 +3272,22 @@ SessionStoreService.prototype = {
     // don't restore text data and scrolling state if the user has navigated
     // away before the loading completed (except for in-page navigation)
     if (hasExpectedURL(aEvent.originalTarget, aBrowser.__SS_restore_data.url)) {
       var content = aEvent.originalTarget.defaultView;
       restoreTextDataAndScrolling(content, aBrowser.__SS_restore_data, "");
       aBrowser.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle";
     }
 
-    if (aBrowser.__SS_restore_docIdentifier) {
-      let sh = aBrowser.webNavigation.sessionHistory;
-      sh.getEntryAtIndex(sh.index, false).QueryInterface(Ci.nsISHEntry).
-         docIdentifier = aBrowser.__SS_restore_docIdentifier;
-    }
-
     // notify the tabbrowser that this document has been completely restored
     this._sendTabRestoredNotification(aBrowser.__SS_restore_tab);
 
     delete aBrowser.__SS_restore_data;
     delete aBrowser.__SS_restore_pageStyle;
     delete aBrowser.__SS_restore_tab;
-    delete aBrowser.__SS_restore_docIdentifier;
   },
 
   /**
    * Restore visibility and dimension features to a window
    * @param aWindow
    *        Window reference
    * @param aWinData
    *        Object containing session data for the window
@@ -3480,16 +3475,20 @@ SessionStoreService.prototype = {
     oState.session = {
       state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR,
       lastUpdate: Date.now(),
       startTime: this._sessionStartTime
     };
     if (this._recentCrashes)
       oState.session.recentCrashes = this._recentCrashes;
 
+    // Persist the last session if we deferred restoring it
+    if (this._lastSessionState)
+      oState.lastSessionState = this._lastSessionState;
+
     this._saveStateObject(oState);
   },
 
   /**
    * write a state object to disk
    */
   _saveStateObject: function sss_saveStateObject(aStateObj) {
     var stateString = Cc["@mozilla.org/supports-string;1"].
--- a/browser/components/sessionstore/test/browser/browser_500328.js
+++ b/browser/components/sessionstore/test/browser/browser_500328.js
@@ -111,23 +111,23 @@ function test() {
 
     tabBrowser.loadURI("http://example.com", null, null);
 
     tabBrowser.addEventListener("load", function(aEvent) {
       tabBrowser.removeEventListener("load", arguments.callee, true);
 
       // After these push/replaceState calls, the window should have three
       // history entries:
-      //   testURL (state object: null)      <-- oldest
-      //   testURL (state object: {obj1:1})
-      //   page2   (state object: {obj3:/^a$/})  <-- newest
+      //   testURL        (state object: null)          <-- oldest
+      //   testURL        (state object: {obj1:1})
+      //   testURL?page2  (state object: {obj3:/^a$/})  <-- newest
       let contentWindow = tab.linkedBrowser.contentWindow;
       let history = contentWindow.history;
       history.pushState({obj1:1}, "title-obj1");
-      history.pushState({obj2:2}, "title-obj2", "page2");
+      history.pushState({obj2:2}, "title-obj2", "?page2");
       history.replaceState({obj3:/^a$/}, "title-obj3");
 
       let state = ss.getTabState(tab);
       gBrowser.removeTab(tab);
 
       // Restore the state into a new tab.  Things don't work well when we
       // restore into the old tab, but that's not a real use case anyway.
       let tab2 = gBrowser.addTab("about:blank");
--- a/browser/components/sessionstore/test/browser/browser_588426.js
+++ b/browser/components/sessionstore/test/browser/browser_588426.js
@@ -2,24 +2,40 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   let state = { windows: [{ tabs: [
       {entries: [{url: "about:mozilla"}], hidden: true},
       {entries: [{url: "about:robots"}], hidden: true}
   ] }] };
 
-  let finalState = { windows: [{ tabs: [
-      {entries: [{url: "about:blank"}]}
-  ] }] };
-
   waitForExplicitFinish();
 
-  waitForBrowserState(state, function () {
-    is(gBrowser.tabs.length, 2, "two tabs were restored");
-    is(gBrowser.visibleTabs.length, 1, "one tab is visible");
+  newWindowWithState(state, function (win) {
+    registerCleanupFunction(function () win.close());
 
-    let tab = gBrowser.visibleTabs[0];
+    is(win.gBrowser.tabs.length, 2, "two tabs were restored");
+    is(win.gBrowser.visibleTabs.length, 1, "one tab is visible");
+
+    let tab = win.gBrowser.visibleTabs[0];
     is(tab.linkedBrowser.currentURI.spec, "about:mozilla", "visible tab is about:mozilla");
 
-    waitForBrowserState(finalState, finish);
+    finish();
   });
 }
+
+function newWindowWithState(state, callback) {
+  let opts = "chrome,all,dialog=no,height=800,width=800";
+  let win = window.openDialog(getBrowserURL(), "_blank", opts);
+
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+
+    executeSoon(function () {
+      win.addEventListener("SSWindowStateReady", function onReady() {
+        win.removeEventListener("SSWindowStateReady", onReady, false);
+        executeSoon(function () callback(win));
+      }, false);
+
+      ss.setWindowState(win, JSON.stringify(state), true);
+    });
+  }, false);
+}
--- a/browser/components/sessionstore/test/browser/browser_590563.js
+++ b/browser/components/sessionstore/test/browser/browser_590563.js
@@ -1,65 +1,78 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let stateBackup = ss.getBrowserState();
-
 function test() {
-  waitForExplicitFinish();
-
   let oldState = {
     windows: [{
       tabs: [
         { entries: [{ url: "about:robots" }], hidden: true },
         { entries: [{ url: "about:blank" }], hidden: false }
       ]
     }]
   };
   let pageData = {
     url: "about:sessionrestore",
     formdata: { "#sessionData": "(" + JSON.stringify(oldState) + ")" }
   };
   let state = { windows: [{ tabs: [{ entries: [pageData] }] }] };
 
-  // The form data will be restored before SSTabRestored, so we want to listen
-  // for that on the currently selected tab (it will be reused)
-  gBrowser.selectedTab.addEventListener("SSTabRestored", onSSTabRestored, true);
+  waitForExplicitFinish();
+
+  newWindowWithState(state, function (win) {
+    registerCleanupFunction(function () win.close());
+
+    is(gBrowser.tabs.length, 1, "The total number of tabs should be 1");
+    is(gBrowser.visibleTabs.length, 1, "The total number of visible tabs should be 1");
 
-  ss.setBrowserState(JSON.stringify(state));
+    executeSoon(function () {
+      waitForFocus(function () {
+        middleClickTest(win);
+        finish();
+      }, win);
+    });
+  });
 }
 
-function onSSTabRestored(aEvent) {
-  gBrowser.selectedTab.removeEventListener("SSTabRestored", onSSTabRestored, true);
-
-  is(gBrowser.tabs.length, 1, "The total number of tabs should be 1");
-  is(gBrowser.visibleTabs.length, 1, "The total number of visible tabs should be 1");
-
-  executeSoon(middleClickTest);
-}
-
-function middleClickTest() {
-  let tree = gBrowser.selectedBrowser.contentDocument.getElementById("tabList");
+function middleClickTest(win) {
+  let browser = win.gBrowser.selectedBrowser;
+  let tree = browser.contentDocument.getElementById("tabList");
   is(tree.view.rowCount, 3, "There should be three items");
 
   let x = {}, y = {}, width = {}, height = {};
 
   // click on the first tab item
   tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[1], "text", x, y, width, height);
   EventUtils.synthesizeMouse(tree.body, x.value, y.value, { button: 1 },
-                             gBrowser.selectedBrowser.contentWindow);
+                             browser.contentWindow);
   // click on the second tab item
   tree.treeBoxObject.getCoordsForCellItem(2, tree.columns[1], "text", x, y, width, height);
   EventUtils.synthesizeMouse(tree.body, x.value, y.value, { button: 1 },
-                             gBrowser.selectedBrowser.contentWindow);
+                             browser.contentWindow);
 
-  is(gBrowser.tabs.length, 3,
+  is(win.gBrowser.tabs.length, 3,
      "The total number of tabs should be 3 after restoring 2 tabs by middle click.");
-  is(gBrowser.visibleTabs.length, 3,
+  is(win.gBrowser.visibleTabs.length, 3,
      "The total number of visible tabs should be 3 after restoring 2 tabs by middle click");
-
-  cleanup();
 }
 
-function cleanup() {
-   ss.setBrowserState(stateBackup);
-   executeSoon(finish);
+function newWindowWithState(state, callback) {
+  let opts = "chrome,all,dialog=no,height=800,width=800";
+  let win = window.openDialog(getBrowserURL(), "_blank", opts);
+
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+
+    let tab = win.gBrowser.selectedTab;
+
+    // The form data will be restored before SSTabRestored, so we want to listen
+    // for that on the currently selected tab (it will be reused)
+    tab.addEventListener("SSTabRestored", function onRestored() {
+      tab.removeEventListener("SSTabRestored", onRestored, true);
+      callback(win);
+    }, true);
+
+    executeSoon(function () {
+      ss.setWindowState(win, JSON.stringify(state), true);
+    });
+  }, false);
 }
--- a/browser/devtools/Makefile.in
+++ b/browser/devtools/Makefile.in
@@ -42,15 +42,16 @@ srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/config.mk
 
 DIRS = \
   webconsole \
+  scratchpad \
   $(NULL)
 
 ifdef ENABLE_TESTS
 # DIRS += test # no tests yet
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -1,2 +1,4 @@
 browser.jar:
     content/browser/NetworkPanel.xhtml            (webconsole/NetworkPanel.xhtml)
+*   content/browser/scratchpad.xul                (scratchpad/scratchpad.xul)
+*   content/browser/scratchpad.js                 (scratchpad/scratchpad.js)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/Makefile.in
@@ -0,0 +1,52 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is  HUDService code.
+#
+# The Initial Developer of the Original Code is Mozilla Corporation.
+# 
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Rob Campbell <rcampbell@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+ifdef ENABLE_TESTS
+ifneq (mobile,$(MOZ_BUILD_APP))
+	DIRS += test
+endif
+endif
+
+include $(topsrcdir)/config/rules.mk
rename from browser/base/content/scratchpad.js
rename to browser/devtools/scratchpad/scratchpad.js
rename from browser/base/content/scratchpad.xul
rename to browser/devtools/scratchpad/scratchpad.xul
--- a/browser/base/content/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -181,22 +181,22 @@
                 accesskey="&closeCmd.accesskey;"
                 command="sp-cmd-close"/>
     </menupopup>
   </menu>
 
   <menu id="sp-edit-menu" label="&editMenu.label;"
         accesskey="&editMenu.accesskey;">
     <menupopup id="sp-menu_editpopup">
-      <menuitem id="sp-menu_undo"
+      <menuitem id="sp-menu-undo"
                 label="&undoCmd.label;"
                 key="key_undo"
                 accesskey="&undoCmd.accesskey;"
                 disabled="true"
-                oncommand="cmd_undo"/>
+                command="cmd_undo"/>
       <menuitem id="sp-menu-redo"
                 label="&redoCmd.label;"
                 key="key_redo"
                 disabled="true"
                 accesskey="&redoCmd.accesskey;"
                 command="cmd_redo"/>
       <menuseparator/>
       <menuitem id="sp-menu-cut"
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/Makefile.in
@@ -0,0 +1,58 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is HUD test code.
+#
+# The Initial Developer of the Original Code is Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Rob Campbell <rcampbell@mozilla.com> (Original Author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH			= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH			= @srcdir@
+relativesrcdir  = browser/devtools/scratchpad/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_TEST_FILES = \
+		browser_scratchpad_initialization.js \
+		browser_scratchpad_contexts.js \
+		browser_scratchpad_tab_switch.js \
+		browser_scratchpad_execute_print.js \
+		browser_scratchpad_inspect.js \
+		browser_scratchpad_files.js \
+		browser_scratchpad_ui.js \
+		browser_scratchpad_bug_646070_chrome_context_pref.js \
+		browser_scratchpad_bug_660560_tab.js \
+
+libs:: $(_BROWSER_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
rename from browser/base/content/test/browser_scratchpad_bug_646070_chrome_context_pref.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_bug_646070_chrome_context_pref.js
rename from browser/base/content/test/browser_scratchpad_bug_660560_tab.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_bug_660560_tab.js
rename from browser/base/content/test/browser_scratchpad_contexts.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_contexts.js
rename from browser/base/content/test/browser_scratchpad_execute_print.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_execute_print.js
rename from browser/base/content/test/browser_scratchpad_files.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_files.js
rename from browser/base/content/test/browser_scratchpad_initialization.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_initialization.js
rename from browser/base/content/test/browser_scratchpad_inspect.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_inspect.js
rename from browser/base/content/test/browser_scratchpad_tab_switch.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_tab_switch.js
rename from browser/base/content/test/browser_scratchpad_ui.js
rename to browser/devtools/scratchpad/test/browser_scratchpad_ui.js
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -153,16 +153,17 @@ const SEVERITY_LOG = 3;
 // A mapping from the console API log event levels to the Web Console
 // severities.
 const LEVELS = {
   error: SEVERITY_ERROR,
   warn: SEVERITY_WARNING,
   info: SEVERITY_INFO,
   log: SEVERITY_LOG,
   trace: SEVERITY_LOG,
+  dir: SEVERITY_LOG
 };
 
 // The lowest HTTP response code (inclusive) that is considered an error.
 const MIN_HTTP_ERROR_CODE = 400;
 // The highest HTTP response code (exclusive) that is considered an error.
 const MAX_HTTP_ERROR_CODE = 600;
 
 // HTTP status codes.
@@ -460,17 +461,16 @@ ResponseListener.prototype =
     // Call update on all panels.
     this.httpActivity.panels.forEach(function(weakRef) {
       let panel = weakRef.get();
       if (panel) {
         panel.update();
       }
     });
     this.httpActivity.response.isDone = true;
-    this.httpActivity.response.listener = null;
     this.httpActivity = null;
     this.receivedData = "";
     this.request = null;
     this.sink = null;
     this.inputStream = null;
   },
 
   /**
@@ -1230,16 +1230,20 @@ function pruneConsoleOutputIfNecessary(a
     if (messageNodes[i].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");
       }
       delete hudRef.cssNodes[desc + location];
     }
+    else if (messageNodes[i].classList.contains("webconsole-msg-inspector")) {
+      hudRef.pruneConsoleDirNode(messageNodes[i]);
+      continue;
+    }
     messageNodes[i].parentNode.removeChild(messageNodes[i]);
   }
 
   if (!scrolledToBottom && removeNodes > 0 &&
       oldScrollHeight != scrollBox.scrollHeight) {
     scrollBox.scrollTop -= oldScrollHeight - scrollBox.scrollHeight;
   }
 
@@ -1268,16 +1272,47 @@ function HUD_SERVICE()
 
   // Remembers the last console height, in pixels.
   this.lastConsoleHeight = Services.prefs.getIntPref("devtools.hud.height");
 
   // Network response bodies are piped through a buffer of the given size (in
   // bytes).
   this.responsePipeSegmentSize =
     Services.prefs.getIntPref("network.buffer.cache.size");
+
+  /**
+   * Collection of HUDIds that map to the tabs/windows/contexts
+   * that a HeadsUpDisplay can be activated for.
+   */
+  this.activatedContexts = [];
+
+  /**
+   * Collection of outer window IDs mapping to HUD IDs.
+   */
+  this.windowIds = {};
+
+  /**
+   * Each HeadsUpDisplay has a set of filter preferences
+   */
+  this.filterPrefs = {};
+
+  /**
+   * Keeps a reference for each HeadsUpDisplay that is created
+   */
+  this.hudReferences = {};
+
+  /**
+   * Requests that haven't finished yet.
+   */
+  this.openRequests = {};
+
+  /**
+   * Response headers for requests that haven't finished yet.
+   */
+  this.openResponseHeaders = {};
 };
 
 HUD_SERVICE.prototype =
 {
   /**
    * L10N shortcut function
    *
    * @param string aName
@@ -1304,38 +1339,22 @@ HUD_SERVICE.prototype =
    *
    * @returns object
    */
   get consoleUI() {
     return HeadsUpDisplayUICommands;
   },
 
   /**
-   * Collection of HUDIds that map to the tabs/windows/contexts
-   * that a HeadsUpDisplay can be activated for.
-   */
-  activatedContexts: [],
-
-  /**
-   * Collection of outer window IDs mapping to HUD IDs.
-   */
-  windowIds: {},
-
-  /**
    * The sequencer is a generator (after initialization) that returns unique
    * integers
    */
   sequencer: null,
 
   /**
-   * Each HeadsUpDisplay has a set of filter preferences
-   */
-  filterPrefs: {},
-
-  /**
    * 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;
@@ -1672,21 +1691,16 @@ HUD_SERVICE.prototype =
         node.classList.add("hud-filtered-by-string");
       }
     }
 
     this.regroupOutput(outputNode);
   },
 
   /**
-   * Keeps a reference for each HeadsUpDisplay that is created
-   */
-  hudReferences: {},
-
-  /**
    * 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);
@@ -1720,16 +1734,18 @@ HUD_SERVICE.prototype =
   {
     let hud = this.getHudReferenceById(aHUDId);
 
     // Remove children from the output. If the output is not cleared, there can
     // be leaks as some nodes has node.onclick = function; set and GC can't
     // remove the nodes then.
     hud.jsterm.clearOutput();
 
+    hud.destroy();
+
     // Make sure that the console panel does not try to call
     // deactivateHUDForContext() again.
     hud.consoleWindowUnregisterOnHide = false;
 
     // Remove the HUDBox and the consolePanel if the Web Console is inside a
     // floating panel.
     hud.HUDBox.parentNode.removeChild(hud.HUDBox);
     if (hud.consolePanel) {
@@ -1816,16 +1832,18 @@ HUD_SERVICE.prototype =
     this.openRequests = {};
     this.openResponseHeaders = {};
 
     // delete the storage as it holds onto channels
     delete this.storage;
     delete this.defaultFilterPrefs;
     delete this.defaultGlobalConsolePrefs;
 
+    delete this.lastFinishedRequestCallback;
+
     HUDWindowObserver.uninit();
     HUDConsoleObserver.uninit();
     ConsoleAPIObserver.shutdown();
   },
 
   /**
    * Shutdown all HeadsUpDisplays on xpcom-shutdown
    *
@@ -1964,28 +1982,36 @@ HUD_SERVICE.prototype =
           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;
+
       default:
         Cu.reportError("Unknown Console API log level: " + level);
         return;
     }
 
     let node = ConsoleUtils.createMessageNode(hud.outputNode.ownerDocument,
                                               CATEGORY_WEBDEV,
                                               LEVELS[level],
                                               body,
                                               sourceURL,
                                               sourceLine,
-                                              clipboardText);
+                                              clipboardText,
+                                              level);
 
     // 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");
@@ -2009,16 +2035,24 @@ HUD_SERVICE.prototype =
                                                        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.
@@ -2099,26 +2133,16 @@ HUD_SERVICE.prototype =
   /**
    * Registry of ApplicationHooks used by specified Gecko Apps
    *
    * @returns Specific Gecko 'ApplicationHooks' Object/Mixin
    */
   applicationHooks: null,
 
   /**
-   * Requests that haven't finished yet.
-   */
-  openRequests: {},
-
-  /**
-   * Response headers for requests that haven't finished yet.
-   */
-  openResponseHeaders: {},
-
-  /**
    * Assign a function to this property to listen for finished httpRequests.
    * Used by unit tests.
    */
   lastFinishedRequestCallback: null,
 
   /**
    * Opens a NetworkPanel.
    *
@@ -2213,41 +2237,40 @@ HUD_SERVICE.prototype =
             let loggedNode = self.logNetActivity(httpActivity);
 
             // In some cases loggedNode can be undefined (e.g. if an image was
             // requested). Don't continue in such a case.
             if (!loggedNode) {
               return;
             }
 
-            // Add listener for the response body.
-            let newListener = new ResponseListener(httpActivity);
             aChannel.QueryInterface(Ci.nsITraceableChannel);
 
-            httpActivity.response.listener = newListener;
-
-            let tee = Cc["@mozilla.org/network/stream-listener-tee;1"].
-                      createInstance(Ci.nsIStreamListenerTee);
-
             // The response will be written into the outputStream of this pipe.
             // This allows us to buffer the data we are receiving and read it
             // asynchronously.
             // Both ends of the pipe must be blocking.
             let sink = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
 
             // The streams need to be blocking because this is required by the
             // stream tee.
             sink.init(false, false, HUDService.responsePipeSegmentSize,
                       PR_UINT32_MAX, null);
 
+            // Add listener for the response body.
+            let newListener = new ResponseListener(httpActivity);
+
             // Remember the input stream, so it isn't released by GC.
             newListener.inputStream = sink.inputStream;
+            newListener.sink = sink;
+
+            let tee = Cc["@mozilla.org/network/stream-listener-tee;1"].
+                      createInstance(Ci.nsIStreamListenerTee);
 
             let originalListener = aChannel.setNewListener(tee);
-            newListener.sink = sink;
 
             tee.init(originalListener, sink.outputStream, newListener);
 
             // Copy the request header data.
             aChannel.visitRequestHeaders({
               visitHeader: function(aName, aValue) {
                 httpActivity.request.header[aName] = aValue;
               }
@@ -2281,17 +2304,17 @@ HUD_SERVICE.prototype =
                 this._panelOpen = true;
               }
             }, false);
           }
           else {
             // Iterate over all currently ongoing requests. If aChannel can't
             // be found within them, then exit this function.
             let httpActivity = null;
-            for each (var item in self.openRequests) {
+            for each (let item in self.openRequests) {
               if (item.channel !== aChannel) {
                 continue;
               }
               httpActivity = item;
               break;
             }
 
             if (!httpActivity) {
@@ -2394,17 +2417,18 @@ HUD_SERVICE.prototype =
                 let fullStatusText = "[" + statusText + " " + timeText + "]";
                 statusNode.setAttribute("value", fullStatusText);
 
                 let clipboardTextPieces =
                   [ httpActivity.method, httpActivity.url, fullStatusText ];
                 msgObject.messageNode.clipboardText =
                   clipboardTextPieces.join(" ");
 
-                delete self.openRequests[item.id];
+                delete httpActivity.messageObject;
+                delete self.openRequests[httpActivity.id];
                 updatePanel = true;
                 break;
               }
             }
 
             if (updatePanel) {
               httpActivity.panels.forEach(function(weakRef) {
                 let panel = weakRef.get();
@@ -2631,24 +2655,35 @@ HUD_SERVICE.prototype =
    * around.
    *
    * @param nsIDOMEvent aEvent
    *        The dispatched event.
    * @returns void
    */
   onWindowUnload: function HS_onWindowUnload(aEvent)
   {
-    let gBrowser = aEvent.target.defaultView.gBrowser;
+    let window = aEvent.target.defaultView;
+
+    window.removeEventListener("unload", this.onWindowUnload, false);
+
+    let gBrowser = window.gBrowser;
     let tabContainer = gBrowser.tabContainer;
 
+    tabContainer.removeEventListener("TabClose", this.onTabClose, false);
+
     let tab = tabContainer.firstChild;
     while (tab != null) {
       this.deactivateHUDForContext(tab, false);
       tab = tab.nextSibling;
     }
+
+    if (window.webConsoleCommandController) {
+      window.controllers.removeController(window.webConsoleCommandController);
+      window.webConsoleCommandController = null;
+    }
   },
 
   /**
    * windowInitializer - checks what Gecko app is running and inits the HUD
    *
    * @param nsIDOMWindow aContentWindow
    * @returns void
    */
@@ -2664,23 +2699,18 @@ HUD_SERVICE.prototype =
     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;
     }
 
-    xulWindow.addEventListener("unload", this.onWindowUnload, false);
-
     let gBrowser = xulWindow.gBrowser;
 
-    let container = gBrowser.tabContainer;
-    container.addEventListener("TabClose", this.onTabClose, false);
-
     let _browser = gBrowser.
       getBrowserForDocument(aContentWindow.top.document);
     let nBox = gBrowser.getNotificationBox(_browser);
     let nBoxId = nBox.getAttribute("id");
     let hudId = "hud_" + nBoxId;
     let windowUI = nBox.ownerDocument.getElementById("console_window_" + hudId);
     if (windowUI) {
       // The Web Console popup is already open, no need to continue.
@@ -2690,16 +2720,19 @@ HUD_SERVICE.prototype =
       }
       return;
     }
 
     if (!this.canActivateContext(hudId)) {
       return;
     }
 
+    xulWindow.addEventListener("unload", this.onWindowUnload, false);
+    gBrowser.tabContainer.addEventListener("TabClose", this.onTabClose, false);
+
     this.registerDisplay(hudId);
 
     let hudNode;
     let childNodes = nBox.childNodes;
 
     for (let i = 0; i < childNodes.length; i++) {
       let id = childNodes[i].getAttribute("id");
       // `id` is a string with the format "hud_<number>".
@@ -2749,19 +2782,20 @@ HUD_SERVICE.prototype =
    * Adds the command controller to the XUL window if it's not already present.
    *
    * @param nsIDOMWindow aWindow
    *        The browser XUL window.
    * @returns void
    */
   createController: function HUD_createController(aWindow)
   {
-    if (aWindow.commandController == null) {
-      aWindow.commandController = new CommandController(aWindow);
-      aWindow.controllers.insertControllerAt(0, aWindow.commandController);
+    if (aWindow.webConsoleCommandController == null) {
+      aWindow.webConsoleCommandController = new CommandController(aWindow);
+      aWindow.controllers.insertControllerAt(0,
+        aWindow.webConsoleCommandController);
     }
   },
 
   /**
    * Animates the Console appropriately.
    *
    * @param string aHUDId The ID of the console.
    * @param string aDirection Whether to animate the console appearing
@@ -3585,51 +3619,54 @@ HeadsUpDisplay.prototype = {
    * Creates the UI for re-positioning the console
    *
    * @return nsIDOMNode
    *         The toolbarbutton which holds the menu that allows the user to
    *         change the console position.
    */
   createPositionUI: function HUD_createPositionUI()
   {
-    let self = this;
+    this._positionConsoleAbove = (function HUD_positionAbove() {
+      this.positionConsole("above");
+    }).bind(this);
+
+    this._positionConsoleBelow = (function HUD_positionBelow() {
+      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"));
 
     let menuPopup = this.makeXULNode("menupopup");
     button.appendChild(menuPopup);
 
     let itemAbove = this.makeXULNode("menuitem");
     itemAbove.setAttribute("label", this.getStr("webConsolePositionAbove"));
     itemAbove.setAttribute("type", "checkbox");
     itemAbove.setAttribute("autocheck", "false");
-    itemAbove.addEventListener("command", function() {
-      self.positionConsole("above");
-    }, false);
+    itemAbove.addEventListener("command", this._positionConsoleAbove, false);
     menuPopup.appendChild(itemAbove);
 
     let itemBelow = this.makeXULNode("menuitem");
     itemBelow.setAttribute("label", this.getStr("webConsolePositionBelow"));
     itemBelow.setAttribute("type", "checkbox");
     itemBelow.setAttribute("autocheck", "false");
-    itemBelow.addEventListener("command", function() {
-      self.positionConsole("below");
-    }, false);
+    itemBelow.addEventListener("command", this._positionConsoleBelow, false);
     menuPopup.appendChild(itemBelow);
 
     let itemWindow = this.makeXULNode("menuitem");
     itemWindow.setAttribute("label", this.getStr("webConsolePositionWindow"));
     itemWindow.setAttribute("type", "checkbox");
     itemWindow.setAttribute("autocheck", "false");
-    itemWindow.addEventListener("command", function() {
-      self.positionConsole("window");
-    }, false);
+    itemWindow.addEventListener("command", this._positionConsoleWindow, false);
     menuPopup.appendChild(itemWindow);
 
     this.positionMenuitems = {
       last: null,
       above: itemAbove,
       below: itemBelow,
       window: itemWindow,
     };
@@ -3745,26 +3782,27 @@ HeadsUpDisplay.prototype = {
    * Creates the close button on the toolbar.
    *
    * @param nsIDOMNode aParent
    *        The toolbar to attach the close button to.
    * @return void
    */
   makeCloseButton: function HUD_makeCloseButton(aToolbar)
   {
-    let onCommand = (function HUD_closeButton_onCommand() {
+    this.closeButtonOnCommand = (function HUD_closeButton_onCommand() {
       HUDService.animate(this.hudId, ANIMATE_OUT, (function() {
         HUDService.deactivateHUDForContext(this.tab, true);
       }).bind(this));
     }).bind(this);
 
-    let closeButton = this.makeXULNode("toolbarbutton");
-    closeButton.classList.add("webconsole-close-button");
-    closeButton.addEventListener("command", onCommand, false);
-    aToolbar.appendChild(closeButton);
+    this.closeButton = this.makeXULNode("toolbarbutton");
+    this.closeButton.classList.add("webconsole-close-button");
+    this.closeButton.addEventListener("command",
+      this.closeButtonOnCommand, false);
+    aToolbar.appendChild(this.closeButton);
   },
 
   /**
    * Creates the "Clear Console" button.
    *
    * @param nsIDOMNode aParent
    *        The toolbar to attach the "Clear Console" button to.
    * @param string aHUDId
@@ -3782,16 +3820,34 @@ HeadsUpDisplay.prototype = {
     clearButton.setAttribute("label", this.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
+   * cleanup for the tree widget and removes it from the DOM.
+   *
+   * @param nsIDOMNode aMessageNode
+   *        The message node that contains the property inspector from a
+   *        console.dir call.
+   */
+  pruneConsoleDirNode: function HUD_pruneConsoleDirNode(aMessageNode)
+  {
+    aMessageNode.parentNode.removeChild(aMessageNode);
+    let tree = aMessageNode.querySelector("tree");
+    tree.parentNode.removeChild(tree);
+    aMessageNode.propertyTreeView = null;
+    tree.view = null;
+    tree = null;
+  },
+
+  /**
    * Create the Web Console UI.
    *
    * @return nsIDOMNode
    *         The Web Console container element (HUDBox).
    */
   createHUD: function HUD_createHUD()
   {
     if (!this.HUDBox) {
@@ -3815,17 +3871,36 @@ HeadsUpDisplay.prototype = {
   {
     return this.outputNode.childNodes;
   },
 
   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"
-  }
+  },
+
+  /**
+   * Destroy the HUD object. Call this method to avoid memory leaks when the Web
+   * Console is closed.
+   */
+  destroy: function HUD_destroy()
+  {
+    this.jsterm.destroy();
+
+    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 = {
@@ -4218,16 +4293,35 @@ function JSTermHelper(aJSTerm)
     catch (ex) {
       aJSTerm.console.error(ex.message);
     }
 
     return nodes;
   };
 
   /**
+   * Returns the currently selected object in the highlighter.
+   *
+   * @returns nsIDOMNode or null
+   */
+  Object.defineProperty(aJSTerm.sandbox, "$0", {
+    get: function() {
+      let mw = HUDService.currentContext();
+      try {
+        return mw.InspectorUI.selection;
+      }
+      catch (ex) {
+        aJSTerm.console.error(ex.message);
+      }
+    },
+    enumerable: true,
+    configurable: false
+  });
+
+  /**
    * Clears the output of the JSTerm.
    */
   aJSTerm.sandbox.clear = function JSTH_clear()
   {
     aJSTerm.helperEvaluated = true;
     aJSTerm.clearOutput();
   };
 
@@ -4396,22 +4490,25 @@ JSTerm.prototype = {
   init: function JST_init()
   {
     this.createSandbox();
 
     this.inputNode = this.mixins.inputNode;
     this.outputNode = this.mixins.outputNode;
     this.completeNode = this.mixins.completeNode;
 
+    this._keyPress = this.keyPress.bind(this);
+    this._inputEventHandler = this.inputEventHandler.bind(this);
+
     this.inputNode.addEventListener("keypress",
-      this.keyPress.bind(this), false);
+      this._keyPress, false);
     this.inputNode.addEventListener("input",
-      this.inputEventHandler.bind(this), false);
+      this._inputEventHandler, false);
     this.inputNode.addEventListener("keyup",
-      this.inputEventHandler.bind(this), false);
+      this._inputEventHandler, false);
   },
 
   get codeInputString()
   {
     return this.inputNode.value;
   },
 
   generateUI: function JST_generateUI()
@@ -4770,18 +4867,25 @@ JSTerm.prototype = {
     return type.toLowerCase();
   },
 
   clearOutput: function JST_clearOutput()
   {
     let hud = HUDService.getHudReferenceById(this.hudId);
     hud.cssNodes = {};
 
-    while (hud.outputNode.firstChild) {
-      hud.outputNode.removeChild(hud.outputNode.firstChild);
+    let node = hud.outputNode;
+    while (node.firstChild) {
+      if (node.firstChild.classList &&
+          node.firstChild.classList.contains("webconsole-msg-inspector")) {
+        hud.pruneConsoleDirNode(node.firstChild);
+      }
+      else {
+        hud.outputNode.removeChild(node.firstChild);
+      }
     }
 
     hud.HUDBox.lastTimestamp = 0;
   },
 
   /**
    * Updates the size of the input field (command line) to fit its contents.
    *
@@ -5175,16 +5279,26 @@ JSTerm.prototype = {
    *        The proposed suffix for the inputNode value.
    */
   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;
   },
+
+  /**
+   * 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);
+  },
 };
 
 /**
  * Generates and attaches the JS Terminal part of the Web Console, which
  * essentially consists of the interactive JavaScript input facility.
  *
  * @param nsWeakPtr<nsIDOMWindow> aContext
  *        A weak pointer to the DOM window that contains the Web Console.
@@ -5376,24 +5490,26 @@ ConsoleUtils = {
    *        The URL of the source file that emitted the error.
    * @param number aSourceLine [optional]
    *        The line number on which the error occurred. If zero or omitted,
    *        there is no line number associated with this message.
    * @param string aClipboardText [optional]
    *        The text that should be copied to the clipboard when this node is
    *        copied. If omitted, defaults to the body text. If `aBody` is not
    *        a string, then the clipboard text must be supplied.
+   * @param number aLevel [optional]
+   *        The level of the console API message.
    * @return nsIDOMNode
    *         The message node: a XUL richlistitem ready to be inserted into
    *         the Web Console output node.
    */
   createMessageNode:
   function ConsoleUtils_createMessageNode(aDocument, aCategory, aSeverity,
                                           aBody, aSourceURL, aSourceLine,
-                                          aClipboardText) {
+                                          aClipboardText, aLevel) {
     if (aBody instanceof Ci.nsIDOMNode && aClipboardText == null) {
       throw new Error("HUDService.createMessageNode(): DOM node supplied " +
                       "without any clipboard text");
     }
 
     // Make the icon container, which is a vertical box. Its purpose is to
     // ensure that the icon stays anchored at the top of the message even for
     // long multi-line messages.
@@ -5411,22 +5527,25 @@ ConsoleUtils = {
     spacer.setAttribute("flex", "1");
     iconContainer.appendChild(spacer);
 
     // Create the message body, which contains the actual text of the message.
     let bodyNode = aDocument.createElementNS(XUL_NS, "description");
     bodyNode.setAttribute("flex", "1");
     bodyNode.classList.add("webconsole-msg-body");
 
+    // 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 ?
+    aBody = aBody instanceof Ci.nsIDOMNode && !(aLevel == "dir") ?
             aBody : aDocument.createTextNode(aBody);
 
     bodyNode.appendChild(aBody);
 
     let repeatContainer = aDocument.createElementNS(XUL_NS, "hbox");
     repeatContainer.setAttribute("align", "start");
     let repeatNode = aDocument.createElementNS(XUL_NS, "label");
     repeatNode.setAttribute("value", "1");
@@ -5451,22 +5570,57 @@ ConsoleUtils = {
     // Create the containing node and append all its elements to it.
     let node = aDocument.createElementNS(XUL_NS, "richlistitem");
     node.clipboardText = aClipboardText;
     node.classList.add("hud-msg-node");
 
     node.timestamp = timestamp;
     ConsoleUtils.setMessageType(node, aCategory, aSeverity);
 
-    node.appendChild(timestampNode);  // childNode[0]
-    node.appendChild(iconContainer);  // childNode[1]
-    node.appendChild(bodyNode);       // childNode[2]
-    node.appendChild(repeatContainer);  // childNode[3]
+    node.appendChild(timestampNode);
+    node.appendChild(iconContainer);
+    // Display the object tree after the message node.
+    if (aLevel == "dir") {
+      // Make the body container, which is a vertical box, for grouping the text
+      // and tree widgets.
+      let bodyContainer = aDocument.createElement("vbox");
+      bodyContainer.setAttribute("flex", "1");
+      bodyContainer.appendChild(bodyNode);
+      // Create the tree.
+      let tree = createElement(aDocument, "tree", {
+        flex: 1,
+        hidecolumnpicker: "true"
+      });
+
+      let treecols = aDocument.createElement("treecols");
+      let treecol = createElement(aDocument, "treecol", {
+        primary: "true",
+        flex: 1,
+        hideheader: "true",
+        ignoreincolumnpicker: "true"
+      });
+      treecols.appendChild(treecol);
+      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;
+      tree.setAttribute("rows", treeView.rowCount);
+    }
+    else {
+      node.appendChild(bodyNode);
+    }
+    node.appendChild(repeatContainer);
     if (locationNode) {
-      node.appendChild(locationNode); // childNode[4]
+      node.appendChild(locationNode);
     }
 
     node.setAttribute("id", "console-msg-" + HUDService.sequenceId());
 
     return node;
   },
 
   /**
@@ -5659,17 +5813,17 @@ ConsoleUtils = {
    * @return boolean
    *         true if the message is filtered, false otherwise.
    */
   filterRepeatedConsole:
   function ConsoleUtils_filterRepeatedConsole(aNode, aOutput) {
     let lastMessage = aOutput.lastChild;
 
     // childNodes[2] is the description element
-    if (lastMessage &&
+    if (lastMessage && !aNode.classList.contains("webconsole-msg-inspector") &&
         aNode.childNodes[2].textContent ==
         lastMessage.childNodes[2].textContent) {
       this.mergeFilteredMessageNode(lastMessage, aNode);
       return true;
     }
 
     return false;
   },
@@ -5693,17 +5847,17 @@ ConsoleUtils = {
     if (aNode.classList.contains("webconsole-msg-cssparser")) {
       isRepeated = this.filterRepeatedCSS(aNode, outputNode, aHUDId);
     }
 
     if (!isRepeated &&
         (aNode.classList.contains("webconsole-msg-console") ||
          aNode.classList.contains("webconsole-msg-exception") ||
          aNode.classList.contains("webconsole-msg-error"))) {
-      isRepeated = this.filterRepeatedConsole(aNode, outputNode, aHUDId);
+      isRepeated = this.filterRepeatedConsole(aNode, outputNode);
     }
 
     if (!isRepeated) {
       outputNode.appendChild(aNode);
     }
 
     HUDService.regroupOutput(outputNode);
 
@@ -6289,18 +6443,17 @@ CommandController.prototype = {
    */
   selectAll: function CommandController_selectAll(aOutputNode)
   {
     aOutputNode.selectAll();
   },
 
   supportsCommand: function CommandController_supportsCommand(aCommand)
   {
-    return this.isCommandEnabled(aCommand) &&
-           this._getFocusedOutputNode() != null;
+    return this.isCommandEnabled(aCommand);
   },
 
   isCommandEnabled: function CommandController_isCommandEnabled(aCommand)
   {
     let outputNode = this._getFocusedOutputNode();
     if (!outputNode) {
       return false;
     }
--- a/browser/devtools/webconsole/Makefile.in
+++ b/browser/devtools/webconsole/Makefile.in
@@ -44,16 +44,17 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
 		HUDService.jsm \
 		PropertyPanel.jsm \
 		NetworkHelper.jsm \
 		AutocompletePopup.jsm \
+		gcli.jsm \
 		$(NULL)
 
 ifdef ENABLE_TESTS
 ifneq (mobile,$(MOZ_BUILD_APP))
 	DIRS += test
 endif
 endif
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/gcli.jsm
@@ -0,0 +1,567 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is GCLI.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Joe Walker <jwalker@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *********************************** WARNING ***********************************
+ *
+ * Do not edit this file without understanding where it comes from,
+ * Your changes are likely to be overwritten without warning.
+ *
+ * The original source for this file is:
+ *  https://github.com/mozilla/gcli/
+ *
+ *******************************************************************************
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * This build of GCLI for Firefox is really 4 bits of code:
+ * - Browser support code - Currently just an implementation of the console
+ *   object that uses dump. We may need to add other browser shims to this.
+ * - A very basic commonjs AMD (Asynchronous Modules Definition) 'require'
+ *   implementation (which is just good enough to load GCLI). For more, see
+ *   http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition.
+ *   This alleviates the need for requirejs (http://requirejs.org/) which is
+ *   used when running in the browser.
+ *   This section of code is a copy of mini_require.js without the header and
+ *   footers. Changes to one should be reflected in the other.
+ * - A build of GCLI itself, packaged using dryice (for more details see the
+ *   project https://github.com/mozilla/dryice and the build file in this
+ *   project at Makefile.dryice.js)
+ * - Lastly, code to require the gcli object as needed by EXPORTED_SYMBOLS.
+ */
+
+var EXPORTED_SYMBOLS = [ "gcli" ];
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * This creates a console object that somewhat replicates Firebug's console
+ * object. It currently writes to dump(), but should write to the web
+ * console's chrome error section (when it has one)
+ */
+
+
+/**
+ * String utility to ensure that strings are a specified length. Strings
+ * that are too long are truncated to the max length and the last char is
+ * set to "_". Strings that are too short are left padded with spaces.
+ *
+ * @param {string} aStr
+ *        The string to format to the correct length
+ * @param {number} aMaxLen
+ *        The maximum allowed length of the returned string
+ * @param {number} aMinLen (optional)
+ *        The minimum allowed length of the returned string. If undefined,
+ *        then aMaxLen will be used
+ * @param {object} aOptions (optional)
+ *        An object allowing format customization. The only customization
+ *        allowed currently is 'truncate' which can take the value "start" to
+ *        truncate strings from the start as opposed to the end.
+ * @return {string}
+ *        The original string formatted to fit the specified lengths
+ */
+function fmt(aStr, aMaxLen, aMinLen, aOptions) {
+  if (aMinLen == undefined) {
+    aMinLen = aMaxLen;
+  }
+  if (aStr == null) {
+    aStr = "";
+  }
+  if (aStr.length > aMaxLen) {
+    if (aOptions && aOptions.truncate == "start") {
+      return "_" + aStr.substring(aStr.length - aMaxLen + 1);
+    }
+    else {
+      return aStr.substring(0, aMaxLen - 1) + "_";
+    }
+  }
+  if (aStr.length < aMinLen) {
+    return Array(aMinLen - aStr.length + 1).join(" ") + aStr;
+  }
+  return aStr;
+}
+
+/**
+ * Utility to extract the constructor name of an object.
+ * Object.toString gives: "[object ?????]"; we want the "?????".
+ *
+ * @param {object} aObj
+ *        The object from which to extract the constructor name
+ * @return {string}
+ *        The constructor name
+ */
+function getCtorName(aObj) {
+  return Object.prototype.toString.call(aObj).slice(8, -1);
+}
+
+/**
+ * A single line stringification of an object designed for use by humans
+ *
+ * @param {any} aThing
+ *        The object to be stringified
+ * @return {string}
+ *        A single line representation of aThing, which will generally be at
+ *        most 60 chars long
+ */
+function stringify(aThing) {
+  if (aThing === undefined) {
+    return "undefined";
+  }
+
+  if (aThing === null) {
+    return "null";
+  }
+
+  if (typeof aThing == "object") {
+    try {
+      return getCtorName(aThing) + " " + fmt(JSON.stringify(aThing), 50, 0);
+    }
+    catch (ex) {
+      return "[stringify error]";
+    }
+  }
+
+  var str = aThing.toString().replace(/\s+/g, " ");
+  return fmt(str, 60, 0);
+}
+
+/**
+ * A multi line stringification of an object, designed for use by humans
+ *
+ * @param {any} aThing
+ *        The object to be stringified
+ * @return {string}
+ *        A multi line representation of aThing
+ */
+function log(aThing) {
+  if (aThing == null) {
+    return "null";
+  }
+
+  if (aThing == undefined) {
+    return "undefined";
+  }
+
+  if (typeof aThing == "object") {
+    var reply = "";
+    var type = getCtorName(aThing);
+    if (type == "Error") {
+      reply += "  " + aThing.message + "\n";
+      reply += logProperty("stack", aThing.stack);
+    }
+    else {
+      var keys = Object.getOwnPropertyNames(aThing);
+      if (keys.length > 0) {
+        reply += type + "\n";
+        keys.forEach(function(aProp) {
+          reply += logProperty(aProp, aThing[aProp]);
+        }, this);
+      }
+      else {
+        reply += type + " (enumerated with for-in)\n";
+        var prop;
+        for (prop in aThing) {
+          reply += logProperty(prop, aThing[prop]);
+        }
+      }
+    }
+
+    return reply;
+  }
+
+  return "  " + aThing.toString() + "\n";
+}
+
+/**
+ * Helper for log() which converts a property/value pair into an output
+ * string
+ *
+ * @param {string} aProp
+ *        The name of the property to include in the output string
+ * @param {object} aValue
+ *        Value assigned to aProp to be converted to a single line string
+ * @return {string}
+ *        Multi line output string describing the property/value pair
+ */
+function logProperty(aProp, aValue) {
+  var reply = "";
+  if (aProp == "stack" && typeof value == "string") {
+    var trace = parseStack(aValue);
+    reply += formatTrace(trace);
+  }
+  else {
+    reply += "    - " + aProp + " = " + stringify(aValue) + "\n";
+  }
+  return reply;
+}
+
+/**
+ * Parse a stack trace, returning an array of stack frame objects, where
+ * each has file/line/call members
+ *
+ * @param {string} aStack
+ *        The serialized stack trace
+ * @return {object[]}
+ *        Array of { file: "...", line: NNN, call: "..." } objects
+ */
+function parseStack(aStack) {
+  var trace = [];
+  aStack.split("\n").forEach(function(line) {
+    if (!line) {
+      return;
+    }
+    var at = line.lastIndexOf("@");
+    var posn = line.substring(at + 1);
+    trace.push({
+      file: posn.split(":")[0],
+      line: posn.split(":")[1],
+      call: line.substring(0, at)
+    });
+  }, this);
+  return trace;
+}
+
+/**
+ * parseStack() takes output from an exception from which it creates the an
+ * array of stack frame objects, this has the same output but using data from
+ * Components.stack
+ *
+ * @param {string} aFrame
+ *        The stack frame from which to begin the walk
+ * @return {object[]}
+ *        Array of { file: "...", line: NNN, call: "..." } objects
+ */
+function getStack(aFrame) {
+  if (!aFrame) {
+    aFrame = Components.stack.caller;
+  }
+  var trace = [];
+  while (aFrame) {
+    trace.push({
+      file: aFrame.filename,
+      line: aFrame.lineNumber,
+      call: aFrame.name
+    });
+    aFrame = aFrame.caller;
+  }
+  return trace;
+};
+
+/**
+ * Take the output from parseStack() and convert it to nice readable
+ * output
+ *
+ * @param {object[]} aTrace
+ *        Array of trace objects as created by parseStack()
+ * @return {string} Multi line report of the stack trace
+ */
+function formatTrace(aTrace) {
+  var reply = "";
+  aTrace.forEach(function(frame) {
+    reply += fmt(frame.file, 20, 20, { truncate: "start" }) + " " +
+             fmt(frame.line, 5, 5) + " " +
+             fmt(frame.call, 75, 75) + "\n";
+  });
+  return reply;
+}
+
+/**
+ * Create a function which will output a concise level of output when used
+ * as a logging function
+ *
+ * @param {string} aLevel
+ *        A prefix to all output generated from this function detailing the
+ *        level at which output occurred
+ * @return {function}
+ *        A logging function
+ * @see createMultiLineDumper()
+ */
+function createDumper(aLevel) {
+  return function() {
+    var args = Array.prototype.slice.call(arguments, 0);
+    var data = args.map(function(arg) {
+      return stringify(arg);
+    });
+    dump(aLevel + ": " + data.join(", ") + "\n");
+  };
+}
+
+/**
+ * Create a function which will output more detailed level of output when
+ * used as a logging function
+ *
+ * @param {string} aLevel
+ *        A prefix to all output generated from this function detailing the
+ *        level at which output occurred
+ * @return {function}
+ *        A logging function
+ * @see createDumper()
+ */
+function createMultiLineDumper(aLevel) {
+  return function() {
+    dump(aLevel + "\n");
+    var args = Array.prototype.slice.call(arguments, 0);
+    args.forEach(function(arg) {
+      dump(log(arg));
+    });
+  };
+}
+
+/**
+ * The console object to expose
+ */
+var console = {
+  debug: createMultiLineDumper("debug"),
+  log: createDumper("log"),
+  info: createDumper("info"),
+  warn: createDumper("warn"),
+  error: createMultiLineDumper("error"),
+  trace: function Console_trace() {
+    var trace = getStack(Components.stack.caller);
+    dump(formatTrace(trace) + "\n");
+  },
+  clear: function Console_clear() {},
+
+  dir: createMultiLineDumper("dir"),
+  dirxml: createMultiLineDumper("dirxml"),
+  group: createDumper("group"),
+  groupEnd: createDumper("groupEnd")
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+// There are 2 virtually identical copies of this code:
+// - $GCLI_HOME/build/prefix-gcli.jsm
+// - $GCLI_HOME/build/mini_require.js
+// They should both be kept in sync
+
+var debugDependencies = false;
+
+/**
+ * Define a module along with a payload.
+ * @param {string} moduleName Name for the payload
+ * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec
+ * @param {function} payload Function with (require, exports, module) params
+ */
+function define(moduleName, deps, payload) {
+  if (typeof moduleName != "string") {
+    console.error(this.depth + " Error: Module name is not a string.");
+    console.trace();
+    return;
+  }
+
+  if (arguments.length == 2) {
+    payload = deps;
+  }
+
+  if (debugDependencies) {
+    console.log("define: " + moduleName + " -> " + payload.toString()
+        .slice(0, 40).replace(/\n/, '\\n').replace(/\r/, '\\r') + "...");
+  }
+
+  if (moduleName in define.modules) {
+    console.error(this.depth + " Error: Redefining module: " + moduleName);
+  }
+  define.modules[moduleName] = payload;
+};
+
+/**
+ * The global store of un-instantiated modules
+ */
+define.modules = {};
+
+
+/**
+ * We invoke require() in the context of a Domain so we can have multiple
+ * sets of modules running separate from each other.
+ * This contrasts with JSMs which are singletons, Domains allows us to
+ * optionally load a CommonJS module twice with separate data each time.
+ * Perhaps you want 2 command lines with a different set of commands in each,
+ * for example.
+ */
+function Domain() {
+  this.modules = {};
+
+  if (debugDependencies) {
+    this.depth = "";
+  }
+}
+
+/**
+ * Lookup module names and resolve them by calling the definition function if
+ * needed.
+ * There are 2 ways to call this, either with an array of dependencies and a
+ * callback to call when the dependencies are found (which can happen
+ * asynchronously in an in-page context) or with a single string an no callback
+ * where the dependency is resolved synchronously and returned.
+ * The API is designed to be compatible with the CommonJS AMD spec and
+ * RequireJS.
+ * @param {string[]|string} deps A name, or names for the payload
+ * @param {function|undefined} callback Function to call when the dependencies
+ * are resolved
+ * @return {undefined|object} The module required or undefined for
+ * array/callback method
+ */
+Domain.prototype.require = function(deps, callback) {
+  if (Array.isArray(deps)) {
+    var params = deps.map(function(dep) {
+      return this.lookup(dep);
+    }, this);
+    if (callback) {
+      callback.apply(null, params);
+    }
+    return undefined;
+  }
+  else {
+    return this.lookup(deps);
+  }
+};
+
+/**
+ * Lookup module names and resolve them by calling the definition function if
+ * needed.
+ * @param {string} moduleName A name for the payload to lookup
+ * @return {object} The module specified by aModuleName or null if not found.
+ */
+Domain.prototype.lookup = function(moduleName) {
+  if (moduleName in this.modules) {
+    var module = this.modules[moduleName];
+    if (debugDependencies) {
+      console.log(this.depth + " Using module: " + moduleName);
+    }
+    return module;
+  }
+
+  if (!(moduleName in define.modules)) {
+    console.error(this.depth + " Missing module: " + moduleName);
+    return null;
+  }
+
+  var module = define.modules[moduleName];
+
+  if (debugDependencies) {
+    console.log(this.depth + " Compiling module: " + moduleName);
+  }
+
+  if (typeof module == "function") {
+    if (debugDependencies) {
+      this.depth += ".";
+    }
+
+    var exports = {};
+    try {
+      module(this.require.bind(this), exports, { id: moduleName, uri: "" });
+    }
+    catch (ex) {
+      console.error("Error using module: " + moduleName, ex);
+      throw ex;
+    }
+    module = exports;
+
+    if (debugDependencies) {
+      this.depth = this.depth.slice(0, -1);
+    }
+  }
+
+  // cache the resulting module object for next time
+  this.modules[moduleName] = module;
+
+  return module;
+};
+
+/**
+ * Expose the Domain constructor and a global domain (on the define function
+ * to avoid exporting more than we need. This is a common pattern with require
+ * systems)
+ */
+define.Domain = Domain;
+define.globalDomain = new Domain();
+
+/**
+ * Expose a default require function which is the require of the global
+ * sandbox to make it easy to use.
+ */
+var require = define.globalDomain.require.bind(define.globalDomain);
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * The API of interest to people wanting to create GCLI commands is as
+ * follows. The implementation of this API is left to bug 659061 and other
+ * bugs.
+ */
+
+define('gcli/index', [ ], function(require, exports, module) {
+
+  exports.addCommand = function() { /* implementation goes here */ };
+  exports.removeCommand = function() { /* implementation goes here */ };
+  exports.startup = function() { /* implementation goes here */ };
+  exports.shutdown = function() { /* implementation goes here */ };
+
+});
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * require GCLI so it can be exported as declared in EXPORTED_SYMBOLS
+ * The dependencies specified here should be the same as in Makefile.dryice.js
+ */
+var gcli = require("gcli/index");
+gcli.createView = require("gcli/ui/start/firefox");
+gcli._internal = { require: require, define: define, console: console };
+
--- a/browser/devtools/webconsole/test/browser/Makefile.in
+++ b/browser/devtools/webconsole/test/browser/Makefile.in
@@ -103,16 +103,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_webconsole_bug_592442_closing_brackets.js \
 	browser_webconsole_bug_593003_iframe_wrong_hud.js \
 	browser_webconsole_bug_601909_remember_height.js \
 	browser_webconsole_bug_613013_console_api_iframe.js \
 	browser_webconsole_bug_597756_reopen_closed_tab.js \
 	browser_webconsole_bug_600183_charset.js \
 	browser_webconsole_bug_601177_log_levels.js \
 	browser_webconsole_bug_597460_filter_scroll.js \
+	browser_webconsole_gcli_require.js \
 	browser_webconsole_console_extras.js \
 	browser_webconsole_bug_598357_jsterm_output.js \
 	browser_webconsole_bug_603750_websocket.js \
 	browser_webconsole_abbreviate_source_url.js \
 	browser_webconsole_view_source.js \
 	browser_webconsole_bug_602572_log_bodies_checkbox.js \
 	browser_webconsole_bug_614793_jsterm_scroll.js \
 	browser_webconsole_bug_599725_response_headers.js \
@@ -136,16 +137,18 @@ include $(topsrcdir)/config/rules.mk
 	browser_webconsole_bug_646025_console_file_location.js \
 	browser_webconsole_position_ui.js \
 	browser_webconsole_bug_642615_autocomplete.js \
 	browser_webconsole_bug_585991_autocomplete_popup.js \
 	browser_webconsole_bug_585991_autocomplete_keys.js \
 	browser_webconsole_bug_663443_panel_title.js \
 	browser_webconsole_bug_660806_history_nav.js \
 	browser_webconsole_bug_651501_document_body_autocomplete.js \
+	browser_webconsole_bug_653531_highlighter_console_helper.js \
+	browser_webconsole_bug_659907_console_dir.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	test-console.html \
 	test-network.html \
 	test-network-request.html \
 	test-mutation.html \
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_586388_select_all.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_586388_select_all.js
@@ -31,17 +31,17 @@ function testSelectionWhenMovingBetweenB
 
   outputNode = jsterm.outputNode;
 
   ok(outputNode.childNodes.length >= 3, "the output node has children after " +
      "executing some JavaScript");
 
   // Test that the global Firefox "Select All" functionality (e.g. Edit >
   // Select All) works properly in the Web Console.
-  let commandController = window.commandController;
+  let commandController = window.webConsoleCommandController;
   ok(commandController != null, "the window has a command controller object");
 
   commandController.selectAll(outputNode);
   is(outputNode.selectedCount, outputNode.childNodes.length, "all console " +
      "messages are selected after performing a regular browser select-all " +
      "operation");
 
   outputNode.selectedIndex = -1;
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_626484_output_copy_order.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_626484_output_copy_order.js
@@ -1,11 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
-let itemsSet;
+let itemsSet, HUD;
 
 function test() {
   addTab("data:text/html,Web Console test for bug 626484");
   browser.addEventListener("load", tabLoaded, true);
 }
 
 function tabLoaded(aEvent) {
   browser.removeEventListener(aEvent.type, arguments.callee, true);
@@ -25,16 +25,17 @@ function tabLoaded(aEvent) {
 
   nextTest();
 }
 
 function nextTest() {
   if (itemsSet.length === 0) {
     outputNode.clearSelection();
     HUD.jsterm.clearOutput();
+    HUD = null;
     finish();
   }
   else {
     outputNode.clearSelection();
     let items = itemsSet.shift();
     items.forEach(function (index) {
       outputNode.addItemToSelection(outputNode.getItemAtIndex(index));
     });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_653531_highlighter_console_helper.js
@@ -0,0 +1,148 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Inspector Highlighter Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Campbell <rcampbell@mozilla.com>
+ *   Mihai Sucan <mihai.sucan@gmail.com>
+ *   Panos Astithas <past@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * 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 ***** */
+
+// Tests that the $0 console helper works as intended.
+
+let doc;
+let h1;
+
+function createDocument()
+{
+  let div = doc.createElement("div");
+  let h1 = doc.createElement("h1");
+  let p1 = doc.createElement("p");
+  let p2 = doc.createElement("p");
+  let div2 = doc.createElement("div");
+  let p3 = doc.createElement("p");
+  doc.title = "Inspector Tree Selection Test";
+  h1.textContent = "Inspector Tree Selection Test";
+  p1.textContent = "This is some example text";
+  p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
+    "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
+    "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
+    "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
+    "dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+    "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
+    "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+  p3.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
+    "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
+    "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
+    "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
+    "dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+    "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
+    "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+  div.appendChild(h1);
+  div.appendChild(p1);
+  div.appendChild(p2);
+  div2.appendChild(p3);
+  doc.body.appendChild(div);
+  doc.body.appendChild(div2);
+  setupHighlighterTests();
+}
+
+function setupHighlighterTests()
+{
+  h1 = doc.querySelectorAll("h1")[0];
+  ok(h1, "we have the header node");
+  Services.obs.addObserver(runSelectionTests,
+    INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.toggleInspectorUI();
+}
+
+function runSelectionTests()
+{
+  Services.obs.removeObserver(runSelectionTests,
+    INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+  executeSoon(function() {
+    Services.obs.addObserver(performTestComparisons,
+      INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
+  });
+}
+
+function performTestComparisons(evt)
+{
+  Services.obs.removeObserver(performTestComparisons,
+    INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+
+  InspectorUI.stopInspecting();
+  ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting");
+  is(InspectorUI.selection, h1, "selection matches node");
+
+  HUDService.activateHUDForContext(gBrowser.selectedTab);
+  let hudId = HUDService.getHudIdByWindow(content);