Merge mozilla-central to services-central.
authorRichard Newman <rnewman@mozilla.com>
Fri, 05 Aug 2011 18:20:32 -0700
changeset 74076 bc57027802ab05974762dfa8a1e030e18bf36fd2
parent 74075 7f68a26515829f1d63b934203acdf5e72a77fd46 (current diff)
parent 73826 5d742d2e4304f8e5052cbd3defc222108ca3a3f1 (diff)
child 74077 3d020dccebbeb3dda8b51a0dc25e9e1df8217d1b
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone8.0a1
Merge mozilla-central to services-central.
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
extensions/spellcheck/hunspell/tests/suggestiontest/Makefile.orig
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
--- 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/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/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -1844,32 +1844,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 +2136,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/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/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/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/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);">
@@ -966,16 +958,28 @@
                   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"
+             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"
--- 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 = null;
     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/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) {
--- 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/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/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/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/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
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.
@@ -1230,16 +1231,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;
   }
 
@@ -1964,28 +1969,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 +2022,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.
@@ -3782,16 +3803,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) {
@@ -4218,16 +4257,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();
   };
 
@@ -4770,18 +4828,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.
    *
@@ -5376,24 +5441,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 +5478,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 +5521,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 +5764,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 +5798,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);
 
--- a/browser/devtools/webconsole/test/browser/Makefile.in
+++ b/browser/devtools/webconsole/test/browser/Makefile.in
@@ -136,16 +136,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 \
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);
+  let hud = HUDService.hudReferences[hudId];
+  let jsterm = hud.jsterm;
+  outputNode = hud.outputNode;
+
+  jsterm.clearOutput();
+  jsterm.execute("$0");
+  findLogEntry("[object HTMLHeadingElement");
+
+  jsterm.clearOutput();
+  let msg = "foo";
+  jsterm.execute("$0.textContent = '" + msg + "'");
+  findLogEntry(msg);
+  is(InspectorUI.selection.textContent, msg, "node successfully updated");
+
+  doc = h1 = null;
+  executeSoon(finishUp);
+}
+
+function finishUp() {
+  InspectorUI.closeInspectorUI();
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,test for highlighter helper in web console";
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_659907_console_dir.js
@@ -0,0 +1,45 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that console.dir works as intended.
+
+function test() {
+  addTab("data:text/html,Web Console test for bug 659907: Expand console " +
+         "object with a dir method");
+  browser.addEventListener("load", onLoad, true);
+}
+
+function onLoad(aEvent) {
+  browser.removeEventListener(aEvent.type, arguments.callee, true);
+
+  openConsole();
+  let hudId = HUDService.getHudIdByWindow(content);
+  let hud = HUDService.hudReferences[hudId];
+  outputNode = hud.outputNode;
+  content.console.dir(content.document);
+  findLogEntry("[object HTMLDocument");
+  let msg = outputNode.querySelectorAll(".webconsole-msg-inspector");
+  is(msg.length, 1, "one message node displayed");
+  let rows = msg[0].propertyTreeView._rows;
+  let foundQSA = false;
+  let foundLocation = false;
+  let foundWrite = false;
+  for (let i = 0; i < rows.length; i++) {
+    if (rows[i].display == "querySelectorAll: function querySelectorAll()") {
+      foundQSA = true;
+    }
+    else if (rows[i].display  == "location: Object") {
+      foundLocation = true;
+    }
+    else if (rows[i].display  == "write: function write()") {
+      foundWrite = true;
+    }
+  }
+  ok(foundQSA, "found document.querySelectorAll");
+  ok(foundLocation, "found document.location");
+  ok(foundWrite, "found document.write");
+  finishTest();
+}
--- a/browser/devtools/webconsole/test/browser/test-console-extras.html
+++ b/browser/devtools/webconsole/test/browser/test-console-extras.html
@@ -4,17 +4,16 @@
     <script type="text/javascript">
       function test() {
         console.log("start");
         console.time();
         console.timeEnd()
         console.exception()
         console.assert()
         console.clear()
-        console.dir()
         console.dirxml()
         console.group()
         console.groupCollapsed()
         console.groupEnd()
         console.profile()
         console.profileEnd()
         console.count()
         console.table()
--- a/build/unix/elfhack/elf.cpp
+++ b/build/unix/elfhack/elf.cpp
@@ -561,16 +561,17 @@ void ElfSegment::addSection(ElfSection *
     assert(!((type == PT_GNU_RELRO) && (section->isRelocatable())));
 
     //TODO: Check overlapping sections
     std::list<ElfSection *>::iterator i;
     for (i = sections.begin(); i != sections.end(); ++i)
         if ((*i)->getAddr() > section->getAddr())
             break;
     sections.insert(i, section);
+    section->addToSegment(this);
 }
 
 unsigned int ElfSegment::getFileSize()
 {
     if (type == PT_GNU_RELRO)
         return filesz;
 
     if (sections.empty())
@@ -631,18 +632,20 @@ ElfSegment *ElfSegment::splitBefore(ElfS
     phdr.p_vaddr = 0;
     phdr.p_paddr = phdr.p_vaddr + v_p_diff;
     phdr.p_flags = flags;
     phdr.p_align = 0x1000;
     phdr.p_filesz = (unsigned int)-1;
     phdr.p_memsz = (unsigned int)-1;
     ElfSegment *segment = new ElfSegment(&phdr);
 
-    for (rm = i; i != sections.end(); ++i)
+    for (rm = i; i != sections.end(); ++i) {
+        (*i)->removeFromSegment(this);
         segment->addSection(*i);
+    }
     sections.erase(rm, sections.end());
 
     return segment;
 }
 
 ElfValue *ElfDynamic_Section::getValueForType(unsigned int tag)
 {
     for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++)
--- a/build/unix/elfhack/elfxx.h
+++ b/build/unix/elfhack/elfxx.h
@@ -36,16 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include <stdexcept>
 #include <list>
 #include <vector>
 #include <cstring>
 #include <iostream>
 #include <fstream>
+#include <algorithm>
 #include <elf.h>
 #include <asm/byteorder.h>
 
 // Technically, __*_to_cpu and __cpu_to* function are equivalent,
 // so swap can use either of both.
 #define def_swap(endian, type, bits) \
 static inline type ## bits ## _t swap(type ## bits ## _t i) { \
     return __ ## endian ## bits ## _to_cpu(i); \
@@ -365,17 +366,18 @@ public:
                 (getType() == SHT_RELA) ||
                 (getType() == SHT_HASH) ||
                 (getType() == SHT_NOTE) ||
                 (getType() == SHT_REL) ||
                 (getType() == SHT_DYNSYM) ||
                 (getType() == SHT_GNU_HASH) ||
                 (getType() == SHT_GNU_verdef) ||
                 (getType() == SHT_GNU_verneed) ||
-                (getType() == SHT_GNU_versym)) &&
+                (getType() == SHT_GNU_versym) ||
+                isInSegmentType(PT_INTERP)) &&
                 (getFlags() & SHF_ALLOC);
     }
 
     void insertAfter(ElfSection *section, bool dirty = true) {
         if (previous != NULL)
             previous->next = next;
         if (next != NULL)
             next->previous = previous;
@@ -405,25 +407,40 @@ public:
 
     virtual void serialize(std::ofstream &file, char ei_class, char ei_data)
     {
         if (getType() == SHT_NOBITS)
             return;
         file.seekp(getOffset());
         file.write(data, getSize());
     }
+
+private:
+    friend class ElfSegment;
+
+    void addToSegment(ElfSegment *segment) {
+        segments.push_back(segment);
+    }
+
+    void removeFromSegment(ElfSegment *segment) {
+        std::vector<ElfSegment *>::iterator i = std::find(segments.begin(), segments.end(), segment);
+        segments.erase(i, i + 1);
+    }
+
+    bool isInSegmentType(unsigned int type);
 protected:
     Elf_Shdr shdr;
     char *data;
     const char *name;
 private:
     ElfSection *link;
     SectionInfo info;
     ElfSection *next, *previous;
     int index;
+    std::vector<ElfSegment *> segments;
 };
 
 class ElfSegment {
 public:
     ElfSegment(Elf_Phdr *phdr);
 
     unsigned int getType() { return type; }
     unsigned int getFlags() { return flags; }
@@ -631,16 +648,23 @@ inline char Elf::getMachine() {
 
 inline unsigned int Elf::getSize() {
     ElfSection *section;
     for (section = shdr_section /* It's usually not far from the end */;
         section->getNext() != NULL; section = section->getNext());
     return section->getOffset() + section->getSize();
 }
 
+inline bool ElfSection::isInSegmentType(unsigned int type) {
+    for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
+        if ((*seg)->getType() == type)
+            return true;
+    return false;
+}
+
 inline ElfLocation::ElfLocation(ElfSection *section, unsigned int off, enum position pos)
 : section(section) {
     if ((pos == ABSOLUTE) && section)
         offset = off - section->getAddr();
     else
         offset = off;
 }
 
--- a/caps/src/nsJSPrincipals.cpp
+++ b/caps/src/nsJSPrincipals.cpp
@@ -63,17 +63,17 @@ nsGlobalPrivilegesEnabled(JSContext *cx,
 }
 
 static JSBool
 nsJSPrincipalsSubsume(JSPrincipals *jsprin, JSPrincipals *other)
 {
     nsJSPrincipals *nsjsprin = static_cast<nsJSPrincipals *>(jsprin);
     nsJSPrincipals *nsother  = static_cast<nsJSPrincipals *>(other);
 
-    JSBool result;
+    PRBool result;
     nsresult rv = nsjsprin->nsIPrincipalPtr->Subsumes(nsother->nsIPrincipalPtr,
                                                       &result);
     return NS_SUCCEEDED(rv) && result;
 }
 
 static void
 nsDestroyJSPrincipals(JSContext *cx, struct JSPrincipals *jsprin)
 {
--- a/caps/src/nsSecurityManagerFactory.cpp
+++ b/caps/src/nsSecurityManagerFactory.cpp
@@ -123,17 +123,17 @@ getUTF8StringArgument(JSContext *cx, JSO
 
 static JSBool
 netscape_security_isPrivilegeEnabled(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     if (!obj)
         return JS_FALSE;
 
-    JSBool result = JS_FALSE;
+    PRBool result = PR_FALSE;
     if (JSString *str = getStringArgument(cx, obj, 0, argc, JS_ARGV(cx, vp))) {
         JSAutoByteString cap(cx, str);
         if (!cap)
             return JS_FALSE;
 
         nsresult rv;
         nsCOMPtr<nsIScriptSecurityManager> securityManager = 
                  do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
--- a/configure.in
+++ b/configure.in
@@ -9391,16 +9391,30 @@ HAVE_WCRTOMB
 "
 
 AC_CONFIG_HEADER(
 netwerk/necko-config.h
 xpcom/xpcom-config.h
 xpcom/xpcom-private.h
 )
 
+# Hack around an Apple bug that effects the egrep that comes with OS X 10.7.
+# "arch -arch i386 egrep" always uses the 32-bit Intel part of the egrep fat
+# binary, even on 64-bit systems.  It should work on OS X 10.4.5 and up.  We
+# (apparently) only need this hack when egrep's "pattern" is particularly
+# long (as in the following code).  See bug 655339.
+case "$host" in
+x86_64-apple-darwin*)
+    FIXED_EGREP="arch -arch i386 egrep"
+    ;;
+*)
+    FIXED_EGREP="egrep"
+    ;;
+esac
+
 # Save the defines header file before autoconf removes it.
 # (Do not add AC_DEFINE calls after this line.)
   _CONFIG_TMP=confdefs-tmp.h
   _CONFIG_DEFS_H=mozilla-config.h
 
   cat > $_CONFIG_TMP <<\EOF
 /* List of defines generated by configure. Included with preprocessor flag,
  * -include, to avoid long list of -D defines on the compile command-line.
@@ -9414,17 +9428,17 @@ EOF
 _EGREP_PATTERN='^#define ('
 if test -n "$_NON_GLOBAL_ACDEFINES"; then
     for f in $_NON_GLOBAL_ACDEFINES; do
         _EGREP_PATTERN="${_EGREP_PATTERN}$f|"
     done
 fi
 _EGREP_PATTERN="${_EGREP_PATTERN}dummy_never_defined)"
 
-  sort confdefs.h | egrep -v "$_EGREP_PATTERN" >> $_CONFIG_TMP
+  sort confdefs.h | $FIXED_EGREP -v "$_EGREP_PATTERN" >> $_CONFIG_TMP
 
   if test "$?" != 0; then
     AC_MSG_ERROR([Error outputting config definitions])
   fi
 
   cat >> $_CONFIG_TMP <<\EOF
 
 /* The c99 defining the limit macros (UINT32_MAX for example), says:
@@ -9445,17 +9459,17 @@ EOF
 
     echo ==== $_CONFIG_DEFS_H =================================
     cat $_CONFIG_DEFS_H
   fi
 
 dnl Probably shouldn't call this manually but we always want the output of DEFS
 rm -f confdefs.h.save
 mv confdefs.h confdefs.h.save
-egrep -v "$_EGREP_PATTERN" confdefs.h.save > confdefs.h
+$FIXED_EGREP -v "$_EGREP_PATTERN" confdefs.h.save > confdefs.h
 if test "$?" != 0; then
   AC_MSG_ERROR([Error outputting confdefs.h])
 fi
 AC_OUTPUT_MAKE_DEFS()
 MOZ_DEFINES=$DEFS
 AC_SUBST(MOZ_DEFINES)
 rm -f confdefs.h
 mv confdefs.h.save confdefs.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1850,17 +1850,18 @@ private:
   static nsIBidiKeyboard* sBidiKeyboard;
 #endif
 
   static PRBool sInitialized;
   static PRUint32 sScriptBlockerCount;
 #ifdef DEBUG
   static PRUint32 sDOMNodeRemovedSuppressCount;
 #endif
-  static nsCOMArray<nsIRunnable>* sBlockedScriptRunners;
+  // Not an nsCOMArray because removing elements from those is slower
+  static nsTArray< nsCOMPtr<nsIRunnable> >* sBlockedScriptRunners;
   static PRUint32 sRunnersCountAtFirstBlocker;
   static PRUint32 sScriptBlockerCountWhereRunnersPrevented;
 
   static nsIInterfaceRequestor* sSameOriginChecker;
 
   static PRBool sIsHandlingKeyBoardEvent;
   static PRBool sAllowXULXBL_for_file;
 
--- a/content/base/public/nsIMozWebSocket.idl
+++ b/content/base/public/nsIMozWebSocket.idl
@@ -38,28 +38,36 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIDOMEventListener;
 interface nsIPrincipal;
 interface nsIScriptContext;
 interface nsPIDOMWindow;
+interface nsIDOMDOMStringList;
+
+%{C++
+#include "nsTArray.h"
+class nsString;
+%}
+[ref] native nsStringTArrayRef(nsTArray<nsString>);
 
 /**
  * The nsIMozWebSocket interface enables Web applications to maintain
  * bidirectional communications with server-side processes as described in:
  *
  * http://dev.w3.org/html5/websockets/
  *
  */
-[scriptable, uuid(eae76132-e4a4-4765-8494-5e136521c846)]
+[scriptable, uuid(5b124f54-7d46-4bc0-8507-e58ed22c19b9)]
 interface nsIMozWebSocket : nsISupports
 {
   readonly attribute DOMString url;
+  readonly attribute DOMString extensions;
   readonly attribute DOMString protocol;
 
   //ready state
   const unsigned short CONNECTING = 0;
   const unsigned short OPEN = 1;
   const unsigned short CLOSING = 2;
   const unsigned short CLOSED = 3;
   readonly attribute unsigned short readyState;
@@ -74,37 +82,41 @@ interface nsIMozWebSocket : nsISupports
 
   /**
    * Transmits data using the connection.
    *
    * @param data The data to be transmited.
    * @return if the connection is still established (and the data was queued or
    *         sent successfully).
    */
-  boolean send(in DOMString data);
+  void send(in DOMString data);
 
   /**
    * Closes the Web Socket connection or connection attempt, if any.
    * If the connection is already closed, it does nothing.
    */
-  void close();
+  [optional_argc] void close([optional] in unsigned short code,
+                             [optional] in DOMString reason);
 
   /**
    * Initialize the object for use from C++ code with the principal, script
    * context, and owner window that should be used.
    *
    * @param principal The principal to use for the request. This must not be
    *                  null.
    * @param scriptContext The script context to use for the request. May be
    *                      null.
    * @param ownerWindow The associated window for the request. May be null.
    * @param url The url for opening the socket. This must not be empty, and
    *            must have an absolute url, using either the ws or wss schemes.
-   * @param protocol  Specifies a sub-protocol that the server must support for
-   *                  the connection to be successful. If empty, no protocol is
-   *                  specified.
+   * @param protocol  Specifies array of sub-protocols acceptable to the client.
+   *                  If the length of the array is at least one, the server
+   *                  must select one of the listed sub-protocols for the
+   *                  connection to be successful. If empty, no sub-protocol is
+   *                  specified. The server selected sub-protocol can be read
+   *                  from the protocol attribute after connection.
    */
   [noscript] void init(in nsIPrincipal principal,
                        in nsIScriptContext scriptContext,
                        in nsPIDOMWindow ownerWindow,
                        in DOMString url,
-                       in DOMString protocol);
+                       in nsStringTArrayRef protocol);
 };
--- a/content/base/public/nsTreeSanitizer.h
+++ b/content/base/public/nsTreeSanitizer.h
@@ -132,20 +132,21 @@ class NS_STACK_CLASS nsTreeSanitizer {
 
     /**
      * Remove the named URL attribute from the element if the URL fails a
      * security check.
      *
      * @param aElement the element whose attribute to possibly modify
      * @param aNamespace the namespace of the URL attribute
      * @param aLocalName the local name of the URL attribute
+     * @return true if the attribute was removed and false otherwise
      */
-    void SanitizeURL(mozilla::dom::Element* aElement,
-                     PRInt32 aNamespace,
-                     nsIAtom* aLocalName);
+    PRBool SanitizeURL(mozilla::dom::Element* aElement,
+                       PRInt32 aNamespace,
+                       nsIAtom* aLocalName);
 
     /**
      * Checks a style rule for the presence of the 'binding' CSS property and
      * removes that property from the rule and reserializes in case the
      * property was found.
      *
      * @param aRule The style rule to check
      * @param aRuleText the serialized mutated rule if the method returns true
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -244,17 +244,17 @@ PRInt32 nsContentUtils::sScriptRootCount
 PRUint32 nsContentUtils::sJSGCThingRootCount;
 #ifdef IBMBIDI
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull;
 #endif
 PRUint32 nsContentUtils::sScriptBlockerCount = 0;
 #ifdef DEBUG
 PRUint32 nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
 #endif
-nsCOMArray<nsIRunnable>* nsContentUtils::sBlockedScriptRunners = nsnull;
+nsTArray< nsCOMPtr<nsIRunnable> >* nsContentUtils::sBlockedScriptRunners = nsnull;
 PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
 PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
 
 PRBool nsContentUtils::sIsHandlingKeyBoardEvent = PR_FALSE;
 PRBool nsContentUtils::sAllowXULXBL_for_file = PR_FALSE;
 
 nsString* nsContentUtils::sShiftText = nsnull;
@@ -377,18 +377,17 @@ nsContentUtils::Init()
     if (!PL_DHashTableInit(&sEventListenerManagersHash, &hash_table_ops,
                            nsnull, sizeof(EventListenerManagerMapEntry), 16)) {
       sEventListenerManagersHash.ops = nsnull;
 
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
-  sBlockedScriptRunners = new nsCOMArray<nsIRunnable>;
-  NS_ENSURE_TRUE(sBlockedScriptRunners, NS_ERROR_OUT_OF_MEMORY);
+  sBlockedScriptRunners = new nsTArray< nsCOMPtr<nsIRunnable> >;
 
   Preferences::AddBoolVarCache(&sAllowXULXBL_for_file,
                                "dom.allow_XUL_XBL_for_file");
 
   sInitialized = PR_TRUE;
 
   return NS_OK;
 }
@@ -1222,17 +1221,17 @@ nsContentUtils::Shutdown()
 
     if (sEventListenerManagersHash.entryCount == 0) {
       PL_DHashTableFinish(&sEventListenerManagersHash);
       sEventListenerManagersHash.ops = nsnull;
     }
   }
 
   NS_ASSERTION(!sBlockedScriptRunners ||
-               sBlockedScriptRunners->Count() == 0,
+               sBlockedScriptRunners->Length() == 0,
                "How'd this happen?");
   delete sBlockedScriptRunners;
   sBlockedScriptRunners = nsnull;
 
   delete sShiftText;
   sShiftText = nsnull;
   delete sControlText;  
   sControlText = nsnull;
@@ -3872,17 +3871,16 @@ nsContentUtils::SetNodeTextContent(nsICo
   mozAutoDocUpdate updateBatch(aContent->GetCurrentDoc(),
     UPDATE_CONTENT_MODEL, PR_TRUE);
 
   PRUint32 childCount = aContent->GetChildCount();
 
   if (aTryReuse && !aValue.IsEmpty()) {
     PRUint32 removeIndex = 0;
 
-    // i is unsigned, so i >= is always true
     for (PRUint32 i = 0; i < childCount; ++i) {
       nsIContent* child = aContent->GetChildAt(removeIndex);
       if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) {
         nsresult rv = child->SetText(aValue, PR_TRUE);
         NS_ENSURE_SUCCESS(rv, rv);
 
         removeIndex = 1;
       }
@@ -3891,19 +3889,18 @@ nsContentUtils::SetNodeTextContent(nsICo
       }
     }
 
     if (removeIndex == 1) {
       return NS_OK;
     }
   }
   else {
-    // i is unsigned, so i >= is always true
-    for (PRUint32 i = childCount; i-- != 0; ) {
-      aContent->RemoveChildAt(i, PR_TRUE);
+    for (PRUint32 i = 0; i < childCount; ++i) {
+      aContent->RemoveChildAt(0, PR_TRUE);
     }
   }
 
   if (aValue.IsEmpty()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIContent> textContent;
@@ -4491,17 +4488,17 @@ nsContentUtils::GetAccessKeyCandidates(n
 
 /* static */
 void
 nsContentUtils::AddScriptBlocker()
 {
   if (!sScriptBlockerCount) {
     NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
                  "Should not already have a count");
-    sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Count();
+    sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Length();
   }
   ++sScriptBlockerCount;
 }
 
 /* static */
 void
 nsContentUtils::AddScriptBlockerAndPreventAddingRunners()
 {
@@ -4520,49 +4517,49 @@ nsContentUtils::RemoveScriptBlocker()
   if (sScriptBlockerCount < sScriptBlockerCountWhereRunnersPrevented) {
     sScriptBlockerCountWhereRunnersPrevented = 0;
   }
   if (sScriptBlockerCount) {
     return;
   }
 
   PRUint32 firstBlocker = sRunnersCountAtFirstBlocker;
-  PRUint32 lastBlocker = (PRUint32)sBlockedScriptRunners->Count();
+  PRUint32 lastBlocker = sBlockedScriptRunners->Length();
   PRUint32 originalFirstBlocker = firstBlocker;
   PRUint32 blockersCount = lastBlocker - firstBlocker;
   sRunnersCountAtFirstBlocker = 0;
   NS_ASSERTION(firstBlocker <= lastBlocker,
                "bad sRunnersCountAtFirstBlocker");
 
   while (firstBlocker < lastBlocker) {
     nsCOMPtr<nsIRunnable> runnable = (*sBlockedScriptRunners)[firstBlocker];
     ++firstBlocker;
 
     runnable->Run();
     NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
                  "Bad count");
     NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
   }
-  sBlockedScriptRunners->RemoveObjectsAt(originalFirstBlocker, blockersCount);
+  sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
 }
 
 /* static */
 PRBool
 nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
 {
   if (!aRunnable) {
     return PR_FALSE;
   }
 
   if (sScriptBlockerCount) {
     if (sScriptBlockerCountWhereRunnersPrevented > 0) {
       NS_ERROR("Adding a script runner when that is prevented!");
       return PR_FALSE;
     }
-    return sBlockedScriptRunners->AppendObject(aRunnable);
+    return sBlockedScriptRunners->AppendElement(aRunnable) != nsnull;
   }
   
   nsCOMPtr<nsIRunnable> run = aRunnable;
   run->Run();
 
   return PR_TRUE;
 }
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1575,16 +1575,18 @@ nsDocument::~nsDocument()
     PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
            ("DOCUMENT %p destroyed", this));
 #endif
 
 #ifdef DEBUG
   nsCycleCollector_DEBUG_wasFreed(static_cast<nsIDocument*>(this));
 #endif
 
+  NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
+
   mInDestructor = PR_TRUE;
   mInUnlinkOrDeletion = PR_TRUE;
 
   // Clear mObservers to keep it in sync with the mutationobserver list
   mObservers.Clear();
 
   if (mStyleSheetSetList) {
     mStyleSheetSetList->Disconnect();
--- a/content/base/src/nsTreeSanitizer.cpp
+++ b/content/base/src/nsTreeSanitizer.cpp
@@ -1222,17 +1222,22 @@ nsTreeSanitizer::SanitizeAttributes(mozi
           }
         }
         continue;
       }
       if (aAllowDangerousSrc && nsGkAtoms::src == attrLocal) {
         continue;
       }
       if (IsURL(aURLs, attrLocal)) {
-        SanitizeURL(aElement, attrNs, attrLocal);
+        if (SanitizeURL(aElement, attrNs, attrLocal)) {
+          // in case the attribute removal shuffled the attribute order, start
+          // the loop again.
+          --ac;
+          i = ac; // i will be decremented immediately thanks to the for loop
+        }
         continue;
       }
       if (aAllowed->GetEntry(attrLocal) &&
           !(attrLocal == nsGkAtoms::rel &&
             aElement->IsHTML(nsGkAtoms::link)) &&
           !(attrLocal == nsGkAtoms::name &&
             aElement->IsHTML(nsGkAtoms::meta))) {
         // name="" and rel="" are whitelisted, but treat them as blacklisted
@@ -1247,26 +1252,36 @@ nsTreeSanitizer::SanitizeAttributes(mozi
       if (*localStr == '_' || (attrLocal->GetLength() > 5 && localStr[0] == 'd'
           && localStr[1] == 'a' && localStr[2] == 't' && localStr[3] == 'a'
           && localStr[4] == '-')) {
         continue;
       }
       // else not allowed
     } else if (kNameSpaceID_XML == attrNs) {
       if (nsGkAtoms::base == attrLocal) {
-        SanitizeURL(aElement, attrNs, attrLocal);
+        if (SanitizeURL(aElement, attrNs, attrLocal)) {
+          // in case the attribute removal shuffled the attribute order, start
+          // the loop again.
+          --ac;
+          i = ac; // i will be decremented immediately thanks to the for loop
+        }
         continue;
       }
       if (nsGkAtoms::lang == attrLocal || nsGkAtoms::space == attrLocal) {
         continue;
       }
       // else not allowed
     } else if (aAllowXLink && kNameSpaceID_XLink == attrNs) {
       if (nsGkAtoms::href == attrLocal) {
-        SanitizeURL(aElement, attrNs, attrLocal);
+        if (SanitizeURL(aElement, attrNs, attrLocal)) {
+          // in case the attribute removal shuffled the attribute order, start
+          // the loop again.
+          --ac;
+          i = ac; // i will be decremented immediately thanks to the for loop
+        }
         continue;
       }
       if (nsGkAtoms::type == attrLocal || nsGkAtoms::title == attrLocal
           || nsGkAtoms::show == attrLocal || nsGkAtoms::actuate == attrLocal) {
         continue;
       }
       // else not allowed
     }
@@ -1283,17 +1298,17 @@ nsTreeSanitizer::SanitizeAttributes(mozi
       aElement->IsHTML(nsGkAtoms::audio)) {
     aElement->SetAttr(kNameSpaceID_None,
                       nsGkAtoms::controls,
                       EmptyString(),
                       PR_FALSE);
   }
 }
 
-void
+PRBool
 nsTreeSanitizer::SanitizeURL(mozilla::dom::Element* aElement,
                              PRInt32 aNamespace,
                              nsIAtom* aLocalName)
 {
   nsAutoString value;
   aElement->GetAttr(aNamespace, aLocalName, value);
 
   // Get value and remove mandatory quotes
@@ -1307,17 +1322,19 @@ nsTreeSanitizer::SanitizeURL(mozilla::do
   nsCOMPtr<nsIURI> baseURI = aElement->GetBaseURI();
   nsCOMPtr<nsIURI> attrURI;
   nsresult rv = NS_NewURI(getter_AddRefs(attrURI), v, nsnull, baseURI);
   if (NS_SUCCEEDED(rv)) {
     rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags);
   }
   if (NS_FAILED(rv)) {
     aElement->UnsetAttr(aNamespace, aLocalName, PR_FALSE);
+    return PR_TRUE;
   }
+  return PR_FALSE;
 }
 
 void
 nsTreeSanitizer::Sanitize(nsIContent* aFragment) {
   // If you want to relax these preconditions, be sure to check the code in
   // here that notifies / does not notify or that fires mutation events if
   // in tree.
   NS_PRECONDITION(aFragment->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT),
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -72,16 +72,17 @@
 #include "nsJSUtils.h"
 #include "nsIScriptError.h"
 #include "nsNetUtil.h"
 #include "nsIWebSocketChannel.h"
 #include "nsIWebSocketListener.h"
 #include "nsILoadGroup.h"
 #include "nsIRequest.h"
 #include "mozilla/Preferences.h"
+#include "nsDOMLists.h"
 
 using namespace mozilla;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsWebSocketEstablishedConnection
 ////////////////////////////////////////////////////////////////////////////////
 
 #define UTF_8_REPLACEMENT_CHAR    static_cast<PRUnichar>(0xFFFD)
@@ -290,24 +291,29 @@ nsWebSocketEstablishedConnection::Init(n
   rv = GetLoadGroup(getter_AddRefs(loadGroup));
   if (loadGroup) {
     rv = mWebSocketChannel->SetLoadGroup(loadGroup);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = loadGroup->AddRequest(this, nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (!mOwner->mProtocol.IsEmpty())
-    rv = mWebSocketChannel->SetProtocol(mOwner->mProtocol);
+  if (!mOwner->mRequestedProtocolList.IsEmpty()) {
+    rv = mWebSocketChannel->SetProtocol(mOwner->mRequestedProtocolList);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCString asciiOrigin;
+  rv = nsContentUtils::GetASCIIOrigin(mOwner->mPrincipal, asciiOrigin);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCString utf8Origin;
-  CopyUTF16toUTF8(mOwner->mUTF16Origin, utf8Origin);
+  ToLowerCase(asciiOrigin);
+
   rv = mWebSocketChannel->AsyncOpen(mOwner->mURI,
-                                     utf8Origin, this, nsnull);
+                                    asciiOrigin, this, nsnull);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 nsWebSocketEstablishedConnection::PrintErrorOnConsole(const char *aBundleURI,
                                                       const PRUnichar *aError,
@@ -370,31 +376,31 @@ nsWebSocketEstablishedConnection::Close(
   if (!mOwner)
     return NS_OK;
 
   // Disconnect() can release this object, so we keep a
   // reference until the end of the method
   nsRefPtr<nsWebSocketEstablishedConnection> kungfuDeathGrip = this;
 
   if (mOwner->mReadyState == nsIMozWebSocket::CONNECTING) {
-    mOwner->SetReadyState(nsIMozWebSocket::CLOSING);
     mOwner->SetReadyState(nsIMozWebSocket::CLOSED);
     Disconnect();
     return NS_OK;
   }
 
   mOwner->SetReadyState(nsIMozWebSocket::CLOSING);
 
   if (mStatus == CONN_CLOSED) {
     mOwner->SetReadyState(nsIMozWebSocket::CLOSED);
     Disconnect();
     return NS_OK;
   }
 
-  return mWebSocketChannel->Close();
+  return mWebSocketChannel->Close(mOwner->mClientReasonCode,
+                                  mOwner->mClientReason);
 }
 
 nsresult
 nsWebSocketEstablishedConnection::ConsoleError()
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
   if (!mOwner) return NS_OK;
@@ -412,17 +418,17 @@ nsWebSocketEstablishedConnection::Consol
                           NS_LITERAL_STRING("connectionFailure").get(),
                           formatStrings, NS_ARRAY_LENGTH(formatStrings));
     } else {
       PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                           NS_LITERAL_STRING("netInterrupt").get(),
                           formatStrings, NS_ARRAY_LENGTH(formatStrings));
     }
   }
-  /// todo some sepcific errors - like for message too large
+  /// todo some specific errors - like for message too large
   return rv;
 }
 
 
 nsresult
 nsWebSocketEstablishedConnection::FailConnection()
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
@@ -499,18 +505,20 @@ nsWebSocketEstablishedConnection::OnBina
 
 NS_IMETHODIMP
 nsWebSocketEstablishedConnection::OnStart(nsISupports *aContext)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (!mOwner)
     return NS_OK;
 
-  if (!mOwner->mProtocol.IsEmpty())
-    mWebSocketChannel->GetProtocol(mOwner->mProtocol);
+  if (!mOwner->mRequestedProtocolList.IsEmpty())
+    mWebSocketChannel->GetProtocol(mOwner->mEstablishedProtocol);
+
+  mWebSocketChannel->GetExtensions(mOwner->mEstablishedExtensions);
 
   mStatus = CONN_CONNECTED_AND_READY;
   mOwner->SetReadyState(nsIMozWebSocket::OPEN);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebSocketEstablishedConnection::OnStop(nsISupports *aContext,
@@ -525,17 +533,17 @@ nsWebSocketEstablishedConnection::OnStop
   if (aStatusCode == NS_BASE_STREAM_CLOSED && 
       mOwner->mReadyState >= nsIMozWebSocket::CLOSING) {
     // don't generate an error event just because of an unclean close
     aStatusCode = NS_OK;
   }
 
   if (NS_FAILED(aStatusCode)) {
     ConsoleError();
-    if (mOwner && mOwner->mReadyState != nsIMozWebSocket::CONNECTING) {
+    if (mOwner) {
       nsresult rv =
         mOwner->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
       if (NS_FAILED(rv))
         NS_WARNING("Failed to dispatch the error event");
     }
   }
 
   mStatus = CONN_CLOSED;
@@ -555,19 +563,25 @@ nsWebSocketEstablishedConnection::OnAckn
   if (aSize > mOutgoingBufferedAmount)
     return NS_ERROR_UNEXPECTED;
   
   mOutgoingBufferedAmount -= aSize;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebSocketEstablishedConnection::OnServerClose(nsISupports *aContext)
+nsWebSocketEstablishedConnection::OnServerClose(nsISupports *aContext,
+                                                PRUint16 aCode,
+                                                const nsACString &aReason)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  if (mOwner) {
+    mOwner->mServerReasonCode = aCode;
+    CopyUTF8toUTF16(aReason, mOwner->mServerReason);
+  }
 
   Close();                                        /* reciprocate! */
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsWebSocketEstablishedConnection::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
@@ -605,16 +619,18 @@ nsWebSocketEstablishedConnection::GetInt
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsWebSocket
 ////////////////////////////////////////////////////////////////////////////////
 
 nsWebSocket::nsWebSocket() : mKeepingAlive(PR_FALSE),
                              mCheckMustKeepAlive(PR_TRUE),
                              mTriggeredCloseEvent(PR_FALSE),
+                             mClientReasonCode(0),
+                             mServerReasonCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
                              mReadyState(nsIMozWebSocket::CONNECTING),
                              mOutgoingBufferedAmount(0),
                              mScriptLine(0),
                              mWindowID(0)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 }
 
@@ -672,28 +688,29 @@ NS_IMPL_RELEASE_INHERITED(nsWebSocket, n
 
 //-----------------------------------------------------------------------------
 // nsWebSocket::nsIJSNativeInitializer methods:
 //-----------------------------------------------------------------------------
 
 /**
  * This Initialize method is called from XPConnect via nsIJSNativeInitializer.
  * It is used for constructing our nsWebSocket from JavaScript. It expects a URL
- * string parameter and an optional protocol parameter. It also initializes the
- * principal, the script context and the window owner.
+ * string parameter and an optional protocol parameter which may be a string or
+ * an array of strings. It also initializes the principal, the script context and
+ * the window owner.
  */
 NS_IMETHODIMP
 nsWebSocket::Initialize(nsISupports* aOwner,
                         JSContext* aContext,
                         JSObject* aObject,
                         PRUint32 aArgc,
                         jsval* aArgv)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
-  nsAutoString urlParam, protocolParam;
+  nsAutoString urlParam;
 
   if (!PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   if (aArgc != 1 && aArgc != 2) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
@@ -710,48 +727,85 @@ nsWebSocket::Initialize(nsISupports* aOw
   const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
   if (!chars) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   urlParam.Assign(chars, length);
   deleteProtector.clear();
 
-  if (aArgc == 2) {
-    jsstr = JS_ValueToString(aContext, aArgv[1]);
-    if (!jsstr) {
-      return NS_ERROR_DOM_SYNTAX_ERR;
-    }
-
-    deleteProtector.set(jsstr);
-    chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
-    if (!chars) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    protocolParam.Assign(chars, length);
-    if (protocolParam.IsEmpty()) {
-      return NS_ERROR_DOM_SYNTAX_ERR;
-    }
-  }
-
   nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
   NS_ENSURE_STATE(ownerWindow);
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
   NS_ENSURE_STATE(sgo);
   nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
   NS_ENSURE_STATE(scriptContext);
 
   nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aOwner));
   NS_ENSURE_STATE(scriptPrincipal);
   nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
   NS_ENSURE_STATE(principal);
 
-  return Init(principal, scriptContext, ownerWindow, urlParam, protocolParam);
+  nsTArray<nsString> protocolArray;
+
+  if (aArgc == 2) {
+    JSObject *jsobj;
+
+    if (JSVAL_IS_OBJECT(aArgv[1]) &&
+        (jsobj = JSVAL_TO_OBJECT(aArgv[1])) &&
+        JS_IsArrayObject(aContext, jsobj)) {
+      jsuint len;
+      JS_GetArrayLength(aContext, jsobj, &len);
+      
+      for (PRUint32 index = 0; index < len; ++index) {
+        jsval value;
+
+        if (!JS_GetElement(aContext, jsobj, index, &value))
+          return NS_ERROR_DOM_SYNTAX_ERR;
+
+        jsstr = JS_ValueToString(aContext, value);
+        if (!jsstr)
+          return NS_ERROR_DOM_SYNTAX_ERR;
+
+        deleteProtector.set(jsstr);
+        chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
+        if (!chars)
+          return NS_ERROR_OUT_OF_MEMORY;
+
+        nsDependentString protocolElement(chars, length);
+        if (protocolElement.IsEmpty())
+          return NS_ERROR_DOM_SYNTAX_ERR;
+        if (protocolArray.Contains(protocolElement))
+          return NS_ERROR_DOM_SYNTAX_ERR;
+        if (protocolElement.FindChar(',') != -1)  /* interferes w/list */
+          return NS_ERROR_DOM_SYNTAX_ERR;
+        protocolArray.AppendElement(protocolElement);
+        deleteProtector.clear();
+      }
+    } else {
+      jsstr = JS_ValueToString(aContext, aArgv[1]);
+      if (!jsstr)
+        return NS_ERROR_DOM_SYNTAX_ERR;
+      
+      deleteProtector.set(jsstr);
+      chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
+      if (!chars)
+        return NS_ERROR_OUT_OF_MEMORY;
+      
+      nsDependentString protocolElement(chars, length);
+      if (protocolElement.IsEmpty())
+        return NS_ERROR_DOM_SYNTAX_ERR;
+      if (protocolElement.FindChar(',') != -1)  /* interferes w/list */
+        return NS_ERROR_DOM_SYNTAX_ERR;
+      protocolArray.AppendElement(protocolElement);
+    }
+  }
+
+  return Init(principal, scriptContext, ownerWindow, urlParam, protocolArray);
 }
 
 //-----------------------------------------------------------------------------
 // nsWebSocket methods:
 //-----------------------------------------------------------------------------
 
 nsresult
 nsWebSocket::EstablishConnection()
@@ -760,41 +814,50 @@ nsWebSocket::EstablishConnection()
   NS_ABORT_IF_FALSE(!mConnection, "mConnection should be null");
 
   nsresult rv;
 
   nsRefPtr<nsWebSocketEstablishedConnection> conn =
     new nsWebSocketEstablishedConnection();
 
   rv = conn->Init(this);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   mConnection = conn;
+  if (NS_FAILED(rv)) {
+    Close(0, EmptyString(), 0);
+    mConnection = nsnull;
+    return rv;
+  }
 
   return NS_OK;
 }
 
 class nsWSCloseEvent : public nsRunnable
 {
 public:
-  nsWSCloseEvent(nsWebSocket *aWebSocket, PRBool aWasClean)
+nsWSCloseEvent(nsWebSocket *aWebSocket, PRBool aWasClean, 
+               PRUint16 aCode, const nsString &aReason)
     : mWebSocket(aWebSocket),
-      mWasClean(aWasClean)
+      mWasClean(aWasClean),
+      mCode(aCode),
+      mReason(aReason)
   {}
 
   NS_IMETHOD Run()
   {
-    nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mWasClean);
+    nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mWasClean,
+                                                          mCode, mReason);
     mWebSocket->UpdateMustKeepAlive();
     return rv;
   }
 
 private:
   nsRefPtr<nsWebSocket> mWebSocket;
   PRBool mWasClean;
+  PRUint16 mCode;
+  nsString mReason;
 };
 
 nsresult
 nsWebSocket::CreateAndDispatchSimpleEvent(const nsString& aName)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
 
@@ -872,17 +935,19 @@ nsWebSocket::CreateAndDispatchMessageEve
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
   rv = privateEvent->SetTrusted(PR_TRUE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
 }
 
 nsresult
-nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean)
+nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean,
+                                         PRUint16 aCode,
+                                         const nsString &aReason)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
 
   mTriggeredCloseEvent = PR_TRUE;
 
   rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
@@ -894,17 +959,17 @@ nsWebSocket::CreateAndDispatchCloseEvent
 
   nsCOMPtr<nsIDOMEvent> event;
   rv = NS_NewDOMCloseEvent(getter_AddRefs(event), nsnull, nsnull);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMCloseEvent> closeEvent = do_QueryInterface(event);
   rv = closeEvent->InitCloseEvent(NS_LITERAL_STRING("close"),
                                   PR_FALSE, PR_FALSE,
-                                  aWasClean);
+                                  aWasClean, aCode, aReason);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
   rv = privateEvent->SetTrusted(PR_TRUE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
 }
@@ -952,17 +1017,20 @@ nsWebSocket::SetReadyState(PRUint16 aNew
   }
 
   if (aNewReadyState == nsIMozWebSocket::CLOSED) {
     mReadyState = aNewReadyState;
 
     if (mConnection) {
       // The close event must be dispatched asynchronously.
       nsCOMPtr<nsIRunnable> event =
-        new nsWSCloseEvent(this, mConnection->ClosedCleanly());
+        new nsWSCloseEvent(this,
+                           mConnection->ClosedCleanly(),
+                           mServerReasonCode,
+                           mServerReason);
       mOutgoingBufferedAmount += mConnection->GetOutgoingBufferedAmount();
       mConnection = nsnull; // this is no longer necessary
 
       rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to dispatch the close event");
         mTriggeredCloseEvent = PR_TRUE;
         UpdateMustKeepAlive();
@@ -1012,33 +1080,29 @@ nsWebSocket::ParseURL(const nsString& aU
     filePath.AssignLiteral("/");
   }
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
 
   nsCAutoString query;
   rv = parsedURL->GetQuery(query);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
 
-  nsCString origin;
-  rv = nsContentUtils::GetASCIIOrigin(mPrincipal, origin);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
-
   if (scheme.LowerCaseEqualsLiteral("ws")) {
      mSecure = PR_FALSE;
      mPort = (port == -1) ? DEFAULT_WS_SCHEME_PORT : port;
   } else if (scheme.LowerCaseEqualsLiteral("wss")) {
     mSecure = PR_TRUE;
     mPort = (port == -1) ? DEFAULT_WSS_SCHEME_PORT : port;
   } else {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
-  ToLowerCase(origin);
-  CopyUTF8toUTF16(origin, mUTF16Origin);
-    
+  rv = nsContentUtils::GetUTFOrigin(parsedURL, mUTF16Origin);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
+
   mAsciiHost = host;
   ToLowerCase(mAsciiHost);
 
   mResource = filePath;
   if (!query.IsEmpty()) {
     mResource.AppendLiteral("?");
     mResource.Append(query);
   }
@@ -1051,36 +1115,16 @@ nsWebSocket::ParseURL(const nsString& aU
     }
   }
 
   mOriginalURL = aURL;
   mURI = parsedURL;
   return NS_OK;
 }
 
-nsresult
-nsWebSocket::SetProtocol(const nsString& aProtocol)
-{
-  if (aProtocol.IsEmpty()) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  PRUint32 length = aProtocol.Length();
-  PRUint32 i;
-  for (i = 0; i < length; ++i) {
-    if (aProtocol[i] < static_cast<PRUnichar>(0x0021) ||
-        aProtocol[i] > static_cast<PRUnichar>(0x007E)) {
-      return NS_ERROR_DOM_SYNTAX_ERR;
-    }
-  }
-
-  CopyUTF16toUTF8(aProtocol, mProtocol);
-  return NS_OK;
-}
-
 //-----------------------------------------------------------------------------
 // Methods that keep alive the WebSocket object when:
 //   1. the object has registered event listeners that can be triggered
 //      ("strong event listeners");
 //   2. there are outgoing not sent messages.
 //-----------------------------------------------------------------------------
 
 void
@@ -1187,19 +1231,26 @@ nsWebSocket::AddEventListener(const nsAS
 NS_IMETHODIMP
 nsWebSocket::GetUrl(nsAString& aURL)
 {
   aURL = mOriginalURL;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsWebSocket::GetExtensions(nsAString& aExtensions)
+{
+  CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsWebSocket::GetProtocol(nsAString& aProtocol)
 {
-  CopyUTF8toUTF16(mProtocol, aProtocol);
+  CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebSocket::GetReadyState(PRUint16 *aReadyState)
 {
   *aReadyState = mReadyState;
   return NS_OK;
@@ -1230,57 +1281,88 @@ nsWebSocket::GetBufferedAmount(PRUint32 
                                   _eventlistener, aEventListener);             \
   }
 
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(open, mOnOpenListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(close, mOnCloseListener)
 
+static PRBool
+ContainsUnpairedSurrogates(const nsAString& aData)
+{
+  // Check for unpaired surrogates.
+  PRUint32 i, length = aData.Length();
+  for (i = 0; i < length; ++i) {
+    if (NS_IS_LOW_SURROGATE(aData[i])) {
+      return PR_TRUE;
+    }
+    if (NS_IS_HIGH_SURROGATE(aData[i])) {
+      ++i;
+      if (i == length || !NS_IS_LOW_SURROGATE(aData[i])) {
+        return PR_TRUE;
+      }
+      continue;
+    }
+  }
+  return PR_FALSE;
+}
+
 NS_IMETHODIMP
-nsWebSocket::Send(const nsAString& aData, PRBool *aRet)
+nsWebSocket::Send(const nsAString& aData)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
-  *aRet = PR_FALSE;
 
   if (mReadyState == nsIMozWebSocket::CONNECTING) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
-  // Check for unpaired surrogates.
-  PRUint32 i, length = aData.Length();
-  for (i = 0; i < length; ++i) {
-    if (NS_IS_LOW_SURROGATE(aData[i])) {
-      return NS_ERROR_DOM_SYNTAX_ERR;
-    }
-    if (NS_IS_HIGH_SURROGATE(aData[i])) {
-      if (i + 1 == length || !NS_IS_LOW_SURROGATE(aData[i + 1])) {
-        return NS_ERROR_DOM_SYNTAX_ERR;
-      }
-      ++i;
-      continue;
-    }
-  }
+  if (ContainsUnpairedSurrogates(aData))
+    return NS_ERROR_DOM_SYNTAX_ERR;
 
   if (mReadyState == nsIMozWebSocket::CLOSING ||
       mReadyState == nsIMozWebSocket::CLOSED) {
     mOutgoingBufferedAmount += NS_ConvertUTF16toUTF8(aData).Length();
     return NS_OK;
   }
 
-  nsresult rv = mConnection->PostMessage(PromiseFlatString(aData));
-  *aRet = NS_SUCCEEDED(rv);
+  mConnection->PostMessage(PromiseFlatString(aData));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebSocket::Close()
+nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+
+  // the reason code is optional, but if provided it must be in a specific range
+  if (argc >= 1) {
+    if (code != 1000 && (code < 3000 || code > 4999))
+      return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+  }
+
+  nsCAutoString utf8Reason;
+  if (argc >= 2) {
+    if (ContainsUnpairedSurrogates(reason))
+      return NS_ERROR_DOM_SYNTAX_ERR;
+
+    CopyUTF16toUTF8(reason, utf8Reason);
+
+    // The API requires the UTF-8 string to be 123 or less bytes
+    if (utf8Reason.Length() > 123)
+      return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  // Format checks for reason and code both passed, they can now be assigned.
+  if (argc >= 1)
+    mClientReasonCode = code;
+  if (argc >= 2)
+    mClientReason = utf8Reason;
+  
   if (mReadyState == nsIMozWebSocket::CLOSING ||
       mReadyState == nsIMozWebSocket::CLOSED) {
     return NS_OK;
   }
 
   if (mReadyState == nsIMozWebSocket::CONNECTING) {
     // FailConnection() can release the object, so we keep a reference
     // before calling it
@@ -1301,17 +1383,17 @@ nsWebSocket::Close()
 /**
  * This Init method should only be called by C++ consumers.
  */
 NS_IMETHODIMP
 nsWebSocket::Init(nsIPrincipal* aPrincipal,
                   nsIScriptContext* aScriptContext,
                   nsPIDOMWindow* aOwnerWindow,
                   const nsAString& aURL,
-                  const nsAString& aProtocol)
+                  nsTArray<nsString> & protocolArray)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
 
   NS_ENSURE_ARG(aPrincipal);
 
   if (!PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
@@ -1359,20 +1441,27 @@ nsWebSocket::Init(nsIPrincipal* aPrincip
     // secure context (e.g. https). Check the security context of the document
     // associated with this script, which is the same as associated with mOwner.
     nsCOMPtr<nsIDocument> originDoc =
       nsContentUtils::GetDocumentFromScriptContext(mScriptContext);
     if (originDoc && originDoc->GetSecurityInfo())
       return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  // sets the protocol
-  if (!aProtocol.IsEmpty()) {
-    rv = SetProtocol(PromiseFlatString(aProtocol));
-    NS_ENSURE_SUCCESS(rv, rv);
+  // Assign the sub protocol list and scan it for illegal values
+  for (PRUint32 index = 0; index < protocolArray.Length(); ++index) {
+    for (PRUint32 i = 0; i < protocolArray[index].Length(); ++i) {
+      if (protocolArray[index][i] < static_cast<PRUnichar>(0x0021) ||
+          protocolArray[index][i] > static_cast<PRUnichar>(0x007E))
+        return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    if (!mRequestedProtocolList.IsEmpty())
+      mRequestedProtocolList.Append(NS_LITERAL_CSTRING(", "));
+    AppendUTF16toUTF8(protocolArray[index], mRequestedProtocolList);
   }
 
   // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
   // url parameter, so we don't care about the EstablishConnection result.
   EstablishConnection();
 
   return NS_OK;
 }
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -45,16 +45,17 @@
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIJSNativeInitializer.h"
 #include "nsIPrincipal.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMEventListener.h"
 #include "nsDOMEventTargetWrapperCache.h"
 #include "nsAutoPtr.h"
+#include "nsIDOMDOMStringList.h"
 
 #define DEFAULT_WS_SCHEME_PORT  80
 #define DEFAULT_WSS_SCHEME_PORT 443
 
 #define NS_WEBSOCKET_CID                            \
  { /* 7ca25214-98dc-40a6-bc1f-41ddbe41f46c */       \
   0x7ca25214, 0x98dc, 0x40a6,                       \
  {0xbc, 0x1f, 0x41, 0xdd, 0xbe, 0x41, 0xf4, 0x6c} }
@@ -99,22 +100,22 @@ public:
   static PRBool PrefEnabled();
 
   const PRUint64 WindowID() const { return mWindowID; }
   const nsCString& GetScriptFile() const { return mScriptFile; }
   const PRUint32 GetScriptLine() const { return mScriptLine; }
 
 protected:
   nsresult ParseURL(const nsString& aURL);
-  nsresult SetProtocol(const nsString& aProtocol);
   nsresult EstablishConnection();
 
   nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
   nsresult CreateAndDispatchMessageEvent(const nsACString& aData);
-  nsresult CreateAndDispatchCloseEvent(PRBool aWasClean);
+  nsresult CreateAndDispatchCloseEvent(PRBool aWasClean, PRUint16 aCode,
+                                       const nsString &aReason);
 
   // called from mConnection accordingly to the situation
   void SetReadyState(PRUint16 aNewReadyState);
 
   // if there are "strong event listeners" (see comment in nsWebSocket.cpp) or
   // outgoing not sent messages then this method keeps the object alive
   // when js doesn't have strong references to it.
   void UpdateMustKeepAlive();
@@ -131,23 +132,30 @@ protected:
   nsString mOriginalURL;
   PRPackedBool mSecure; // if true it is using SSL and the wss scheme,
                         // otherwise it is using the ws scheme with no SSL
 
   PRPackedBool mKeepingAlive;
   PRPackedBool mCheckMustKeepAlive;
   PRPackedBool mTriggeredCloseEvent;
 
+  nsCString mClientReason;
+  PRUint16  mClientReasonCode;
+  nsString  mServerReason;
+  PRUint16  mServerReasonCode;
+
   nsCString mAsciiHost;  // hostname
   PRUint32  mPort;
   nsCString mResource; // [filepath[?query]]
   nsString  mUTF16Origin;
   
   nsCOMPtr<nsIURI> mURI;
-  nsCString mProtocol;
+  nsCString mRequestedProtocolList;
+  nsCString mEstablishedProtocol;
+  nsCString mEstablishedExtensions;
 
   PRUint16 mReadyState;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   nsRefPtr<nsWebSocketEstablishedConnection> mConnection;
   PRUint32 mOutgoingBufferedAmount; // actually, we get this value from
                                     // mConnection when we are connected,
--- a/content/base/test/file_bug426646-1.html
+++ b/content/base/test/file_bug426646-1.html
@@ -1,27 +1,31 @@
 <html><head>
 <title>Bug 426646, Using location.replace breaks iframe history</title>
 <script type="text/javascript">
 var url1 = "data:text/html;charset=utf-8,1st%20page";
 
+function soon(f) {
+  return function() { setTimeout(f, 0); };
+}
+
 function doe() {
   document.body.innerHTML = "<iframe src='about:blank'></iframe>";
   document.body.innerHTML += "<iframe src='about:blank'></iframe>";
-  window.frames[0].frameElement.onload = doe2;
+  window.frames[0].frameElement.onload = soon(doe2);
   window.frames[0].location.replace(url1);
 }
 
 function doe2() {
   window.frames[0].location = 'data:text/html;charset=utf-8,2nd%20page';
-  window.frames[0].frameElement.onload = doe3;
+  window.frames[0].frameElement.onload = soon(doe3);
 }
 
 function doe3() {
-  window.frames[0].frameElement.onload = doe4;
+  window.frames[0].frameElement.onload = soon(doe4);
   history.go(-1);
 }
 
 function doe4() {
   opener.is(window.frames[0].location, url1, "History.go(-1) didn't work?");
   opener.is(window.frames[1].location, "about:blank",
             "History.go(-1) didn't work?");
   close();
--- a/content/base/test/file_bug426646-2.html
+++ b/content/base/test/file_bug426646-2.html
@@ -1,40 +1,64 @@
 <html><head>
 <title>Bug 426646, Using location.replace breaks iframe history</title>
 <script type="text/javascript">
 var url1 = "data:text/html;charset=utf-8,1st%20page";
 
 var win0 = null;
 
+function soon(f) {
+  return function() { setTimeout(f, 0); };
+}
+
 function doe() {
   document.body.innerHTML = "<iframe src='about:blank'></iframe>";
   document.body.innerHTML += "<iframe src='about:blank'></iframe>";
   win0 = window.frames[0];
-  win0.frameElement.onload = doe2;
+  win0.frameElement.onload = soon(doe2);
   win0.location.replace(url1);
 }
 
 function doe2() {
   // Add some iframes/docshells. Session history should still work.
   var ifr1 = document.createElement("iframe");
   document.body.insertBefore(ifr1, document.body.firstChild);
+  ifr1.onload = soon(doe3);
+
   var ifr2 = document.createElement("iframe");
   document.body.insertBefore(ifr2, document.body.firstChild);
+  ifr2.onload = soon(doe3);
+
   var ifr3 = document.createElement("iframe");
   document.body.insertBefore(ifr3, document.body.firstChild);
-  win0.frameElement.onload = doe3;
+  ifr3.onload = soon(doe3);
+}
+
+var doe3_count = 0;
+function doe3() {
+  // Wait until all three iframes have loaded about:blank before navigating
+  // win0.
+  doe3_count++;
+  if (doe3_count < 3) {
+    return;
+  }
+  if (doe3_count > 3) {
+    ok(false, 'Unexpected ' + doe3_count + 'th call to doe3.');
+    return;
+  }
+
+  win0.frameElement.onload = soon(doe4);
   win0.location = 'data:text/html;charset=utf-8,2nd%20page';
 }
 
-function doe3() {
-  win0.frameElement.onload = doe4;
+function doe4() {
+  win0.frameElement.onload = soon(doe5);
   history.go(-1);
 }
 
-function doe4() {
+function doe5() {
   opener.is(win0.location, url1, "History.go(-1) didn't work?");
   close();
 }
 </script>
 </head>
-<body onload="doe();" onunload="opener.nextTest();">
+<body onload="setTimeout(doe, 0);" onunload="opener.nextTest();">
 </body></html>
--- a/content/base/test/file_websocket_wsh.py
+++ b/content/base/test/file_websocket_wsh.py
@@ -2,34 +2,38 @@ from mod_pywebsocket import msgutil
 
 import time
 import sys
 
 # see the list of tests in test_websocket.html
 
 def web_socket_do_extra_handshake(request):
   # must set request.ws_protocol to the selected version from ws_requested_protocols
-  request.ws_protocol = request.ws_requested_protocols[0]
+  for x in request.ws_requested_protocols:
+    if x != "test-does-not-exist":
+      request.ws_protocol = x
+      break
 
   if request.ws_protocol == "test-2.1":
-    time.sleep(5)
+    time.sleep(3)
     pass
   elif request.ws_protocol == "test-9":
-    time.sleep(5)
+    time.sleep(3)
     pass
   elif request.ws_protocol == "test-10":
-    time.sleep(5)
+    time.sleep(3)
     pass
   elif request.ws_protocol == "test-19":
     raise ValueError('Aborting (test-19)')
   elif request.ws_protocol == "test-20" or request.ws_protocol == "test-17":
-    time.sleep(10)
+    time.sleep(3)
     pass
   elif request.ws_protocol == "test-22":
-    time.sleep(60)
+    # The timeout is 5 seconds
+    time.sleep(13)
     pass
   else:
     pass
 
 def web_socket_transfer_data(request):
   if request.ws_protocol == "test-2.1" or request.ws_protocol == "test-2.2":
     msgutil.close_connection(request)
   elif request.ws_protocol == "test-6":
@@ -42,28 +46,17 @@ def web_socket_transfer_data(request):
       resp = "4"
     msgutil.send_message(request, resp.decode('utf-8'))
     resp = "wrong message"
     if msgutil.receive_message(request) == "5":
       resp = "あいうえお"
     msgutil.send_message(request, resp.decode('utf-8'))
     msgutil.close_connection(request)
   elif request.ws_protocol == "test-7":
-    try:
-      while not request.client_terminated:
-        msgutil.receive_message(request)
-    except msgutil.ConnectionTerminatedException, e:
-      pass
-    msgutil.send_message(request, "server data")
-    msgutil.send_message(request, "server data")
-    msgutil.send_message(request, "server data")
-    msgutil.send_message(request, "server data")
-    msgutil.send_message(request, "server data")
-    time.sleep(30)
-    msgutil.close_connection(request, True)
+    msgutil.send_message(request, "test-7 data")
   elif request.ws_protocol == "test-10":
     msgutil.close_connection(request)
   elif request.ws_protocol == "test-11":
     resp = "wrong message"
     if msgutil.receive_message(request) == "client data":
       resp = "server data"
     msgutil.send_message(request, resp.decode('utf-8'))
     msgutil.close_connection(request)
@@ -77,21 +70,40 @@ def web_socket_transfer_data(request):
     msgutil.close_connection(request)
   elif request.ws_protocol == "test-14":
     msgutil.close_connection(request)
     msgutil.send_message(request, "server data")
   elif request.ws_protocol == "test-15":
     msgutil.close_connection(request, True)
     return
   elif request.ws_protocol == "test-17" or request.ws_protocol == "test-21":
-    time.sleep(5)
+    time.sleep(2)
     resp = "wrong message"
     if msgutil.receive_message(request) == "client data":
       resp = "server data"
     msgutil.send_message(request, resp.decode('utf-8'))
-    time.sleep(5)
+    time.sleep(2)
     msgutil.close_connection(request)
-    time.sleep(5)
   elif request.ws_protocol == "test-20":
     msgutil.send_message(request, "server data")
     msgutil.close_connection(request)
+  elif request.ws_protocol == "test-34":
+    request.ws_stream.close_connection(1001, "going away now")
+  elif request.ws_protocol == "test-35a":
+    while not request.client_terminated:
+      msgutil.receive_message(request)
+    global test35code
+    test35code = request.ws_close_code
+    global test35reason
+    test35reason = request.ws_close_reason
+  elif request.ws_protocol == "test-35b":
+    request.ws_stream.close_connection(test35code + 1, test35reason)
+  elif request.ws_protocol == "test-37b":
+    while not request.client_terminated:
+      msgutil.receive_message(request)
+    global test37code
+    test37code = request.ws_close_code
+    global test37reason
+    test37reason = request.ws_close_reason
+  elif request.ws_protocol == "test-37c":
+    request.ws_stream.close_connection(test37code, test37reason)
   while not request.client_terminated:
     msgutil.receive_message(request)
--- a/content/base/test/test_websocket.html
+++ b/content/base/test/test_websocket.html
@@ -18,18 +18,17 @@
 /*
  * tests:
  *  1. client tries to connect to a http scheme location;
  *  2. assure serialization of the connections;
  *  3. client tries to connect to an non-existent ws server;
  *  4. client tries to connect using a relative url;
  *  5. client uses an invalid protocol value;
  *  6. counter and encoding check;
- *  7. client calls close() and the server keeps sending messages and it doesn't
- *     send the close frame;
+ *  7. onmessage event origin property check
  *  8. client calls close() and the server sends the close frame in
  *     acknowledgement;
  *  9. client closes the connection before the ws connection is established;
  * 10. client sends a message before the ws connection is established;
  * 11. a simple hello echo;
  * 12. client sends a message with bad bytes;
  * 13. server sends an invalid message;
  * 14. server sends the close frame, it doesn't close the tcp connection and
@@ -40,24 +39,42 @@
  * 18. client tries to connect to an http resource;
  * 19. server closes the tcp connection before establishing the ws connection;
  * 20. see bug 572975 - only on error and onclose event listeners set
  * 21. see bug 572975 - same as test 17, but delete strong event listeners when
  *     receiving the message event;
  * 22. server takes too long to establish the ws connection;
  * 23. see bug 664692 - feature detection should detect MozWebSocket but not
  *     WebSocket on window object;
+ * 24. server rejects sub-protocol string
+ * 25. ctor with valid empty sub-protocol array
+ * 26. ctor with invalid sub-protocol array containing 1 empty element
+ * 27. ctor with invalid sub-protocol array containing an empty element in list
+ * 28. ctor using valid 1 element sub-protocol array
+ * 29. ctor using all valid 5 element sub-protocol array
+ * 30. ctor using valid 1 element sub-protocol array with element server will
+ *     reject
+ * 31. ctor using valid 2 element sub-protocol array with 1 element server
+ *     will reject and one server will accept.
+ * 32. ctor using invalid sub-protocol array that contains duplicate items
+ * 33. default close code test
+ * 34. test for receiving custom close code and reason
+ * 35. test for sending custom close code and reason
+ * 36. negative test for sending out of range close code
+ * 37. negative test for too long of a close reason
+ * 38. ensure extensions attribute is defined
+ * 39. a basic wss:// connectivity test
+ * 40. negative test for wss:// with no cert
  */
 
 var first_test = 1;
-var last_test = 23;
+var last_test = 40;
 
 var current_test = first_test;
 
-var timeoutToAbortTest = 60000;
 var all_ws = [];
 
 function shouldNotOpen(e)
 {
   var ws = e.target;
   ok(false, "onopen shouldn't be called on test " + ws._testNumber + "!");
 }
 
@@ -88,16 +105,20 @@ function shouldCloseCleanly(e)
 }
 
 function shouldCloseNotCleanly(e)
 {
   var ws = e.target;
   ok(!e.wasClean, "the ws connection in test " + ws._testNumber + " shouldn't be closed cleanly");
 }
 
+function ignoreError(e)
+{
+}
+
 function CreateTestWS(ws_location, ws_protocol)
 {
   var ws;
 
   try {
     if (ws_protocol == undefined) {
       ws = new MozWebSocket(ws_location);
     } else {
@@ -136,41 +157,31 @@ function forcegc()
   setTimeout(function()
   {
     SpecialPowers.gc();
   }, 1);
 }
 
 function doTest(number)
 {
-  if (doTest.timeoutId !== null) {
-    clearTimeout(doTest.timeoutId);
-    doTest.timeoutId = null;
-  }
-
   if (number > last_test) {
-    setTimeout(finishWSTest, 30000);  // wait for the close events be dispatched
+    ranAllTests = true;
+    maybeFinished();
     return;
   }
 
   $("feedback").innerHTML = "executing test: " + number + " of " + last_test + " tests.";
 
   var fnTest = eval("test" + number + "");
 
   if (fnTest._started === true) {
     doTest(number + 1);
     return;
   }
 
-  doTest.timeoutId = setTimeout(function()
-  {
-    ok(false, "test " + number + " took too long to finish!");
-    doTest(number + 1);
-  }, timeoutToAbortTest);
-
   fnTest._started = true;
   fnTest();
 }
 doTest.timeoutId = null;
 
 function test1()
 {
   try {
@@ -179,46 +190,77 @@ function test1()
   }
   catch (e) {
     ok(true, "test1 failed");
   }
   doTest(2);
 }
 
 // this test expects that the serialization list to connect to the proxy
-// is empty
+// is empty. Use different domain so we can run this in the background
+// and not delay other tests.
+
+var waitTest2Part1 = false;
+var waitTest2Part2 = false;
+
 function test2()
 {
-  var ws1 = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-2.1");
+  waitTest2Part1 = true;
+  waitTest2Part2 = true;
+
+  var ws1 = CreateTestWS("ws://sub2.test2.example.com/tests/content/base/test/file_websocket", "test-2.1");
   current_test--; // CreateTestWS incremented this
-  var ws2 = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-2.2");
+  var ws2 = CreateTestWS("ws://sub2.test2.example.com/tests/content/base/test/file_websocket", "test-2.2");
 
   var ws2CanConnect = false;
 
-  // the server will delay ws1 for 5 seconds
+  // the server will delay ws1 for 5 seconds, but the other tests can
+  // proceed in parallel
+  doTest(3);
 
   ws1.onopen = function()
   {
+    ok(true, "ws1 open in test 2");
     ws2CanConnect = true;
+    ws1.close();
   }
 
+  ws1.onclose = function(e)
+  {
+    waitTest2Part1 = false;
+    maybeFinished();
+  };
+
   ws2.onopen = function()
   {
     ok(ws2CanConnect, "shouldn't connect yet in test-2!");
-    doTest(3);
+    ws2.close();
   }
+
+  ws2.onclose = function(e)
+  {
+    waitTest2Part2 = false;
+    maybeFinished();
+  };
 }
 
 function test3()
 {
+  var hasError = false;
   var ws = CreateTestWS("ws://this.websocket.server.probably.does.not.exist");
   ws.onopen = shouldNotOpen;
+  ws.onerror = function (e)
+  {
+    hasError = true;
+  }
+
   ws.onclose = function(e)
   {
     shouldCloseNotCleanly(e);
+    ok(hasError, "rcvd onerror event");
     doTest(4);
   };
 }
 
 function test4()
 {
   try {
     var ws = CreateTestWS("file_websocket");
@@ -278,80 +320,103 @@ function test6()
       ws.send(counter);
     }
   }
   ws.onclose = shouldCloseCleanly;
 }
 
 function test7()
 {
-// with pywebsockets for -06 ths test no longer does anything useful
-// as the server handles the receipt of the close event directly, not
-// as part of the wsh - so we cannot fake the non-clean close which is
-// what we're trying to do here.
-
-  ok(true, "test disabled");
-  current_test++;
-  doTest(8);
+  var ws = CreateTestWS("ws://sub2.test2.example.org/tests/content/base/test/file_websocket", "test-7");
+  var gotmsg = false;
 
-//  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-7");
-//  ws.onopen = function()
-//  {
-//    ws.close();
-//  }
-//  ws.onclose = function(e)
-//  {
-//    shouldCloseNotCleanly(e);
-//    doTest(8);
-//  };
+  ws.onopen = function()
+  {
+    ok(true, "test 7 open");
+  }
+  ws.onmessage = function(e)
+  {
+    ok(true, "test 7 message");
+    ok(e.origin == "ws://sub2.test2.example.org", "onmessage origin set to ws:// host");
+    gotmsg = true;
+    ws.close();
+  }
+  ws.onclose = function(e)
+  {
+    ok(gotmsg, "recvd message in test 7 before close");
+    shouldCloseCleanly(e);
+    doTest(8);
+  };
 }
 
 function test8()
 {
   var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-8");
   ws.onopen = function()
   {
+    ok(ws.protocol == "test-8", "test-8 subprotocol selection");
     ws.close();
   }
   ws.onclose = function(e)
   {
     shouldCloseCleanly(e);
     doTest(9);
   };
 }
 
+var waitTest9 = false;
+
 function test9()
 {
-  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-9");
+  waitTest9 = true;
+
+  var ws = CreateTestWS("ws://test2.example.org/tests/content/base/test/file_websocket", "test-9");
   ws.onopen = shouldNotOpen;
   ws.onclose = function(e)
   {
     shouldCloseNotCleanly(e);
-    doTest(10);
+    waitTest9 = false;
+    maybeFinished();
   };
 
   ws.close();
+  
+  // the server injects a delay, so proceed with this in the background
+  doTest(10);
 }
 
+var waitTest10 = false;
+
 function test10()
 {
-  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-10");
-  ws.onclose = shouldCloseCleanly;
+  waitTest10 = true;
+
+  var ws = CreateTestWS("ws://sub1.test1.example.com/tests/content/base/test/file_websocket", "test-10");
+  ws.onclose = function(e)
+  {
+    shouldCloseCleanly(e);
+    waitTest10 = false;
+    maybeFinished();
+  }
 
   try {
     ws.send("client data");
     ok(false, "Couldn't send data before connecting!");
   }
   catch (e) {
     ok(true, "Couldn't send data before connecting!");
   }
   ws.onopen = function()
   {
-    doTest(11);
+    ok(true, "test 10 opened");
+    ws.close();
   }
+
+  // proceed with this test in the background
+  doTest(11);
 }
 
 function test11()
 {
   var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-11");
   ok(ws.readyState == 0, "create bad readyState in test-11!");
   ws.onopen = function()
   {
@@ -372,16 +437,18 @@ function test11()
     ok(ws.readyState == 3, "onclose bad readyState in test-11!");
     shouldCloseCleanly(e);
     doTest(12);
   }
 }
 
 function test12()
 {
+ ok(true,"test 12");
+
   var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-12");
   ws.onopen = function()
   {
     try {
       // send an unpaired surrogate
       ws.send(String.fromCharCode(0xD800));
       ok(false, "couldn't send an unpaired surrogate!");
     }
@@ -468,19 +535,23 @@ function test16()
   }
   ws.onclose = function()
   {
   }
 }
 
 var status_test17 = "not started";
 
+var waitTest17 = false;
+
 window._test17 = function()
 {
-  var local_ws = new MozWebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-17");
+  waitTest17 = true;
+
+  var local_ws = new MozWebSocket("ws://sub1.test2.example.org/tests/content/base/test/file_websocket", "test-17");
   local_ws._testNumber = "local17";
   local_ws._testNumber = current_test++;
 
   status_test17 = "started";
 
   local_ws.onopen = function(e)
   {
     status_test17 = "opened";
@@ -501,158 +572,581 @@ window._test17 = function()
   };
 
   local_ws.onclose = function(e)
   {
     ok(status_test17 == "got message", "Didn't got message in test-17!");
     shouldCloseCleanly(e);
     status_test17 = "closed";
     forcegc();
-    doTest(18);
-    forcegc();
+    waitTest17 = false;
+    maybeFinished();
   };
 
   local_ws = null;
   window._test17 = null;
   forcegc();
+
+// do this in the background
+  doTest(18);
+  forcegc();
 }
 
 function test17()
 {
   window._test17();
 }
 
 // The tests that expects that their websockets neither open nor close MUST
 // be in the end of the tests, i.e. HERE, in order to prevent blocking the other
 // tests.
 
 function test18()
 {
   var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket_http_resource.txt");
   ws.onopen = shouldNotOpen;
+  ws.onerror = ignoreError;
   ws.onclose = function(e)
   {
     shouldCloseNotCleanly(e);
     doTest(19);
   };
 }
 
 function test19()
 {
   var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-19");
   ws.onopen = shouldNotOpen;
+  ws.onerror = ignoreError;
   ws.onclose = function(e)
   {
     shouldCloseNotCleanly(e);
     doTest(20);
   };
 }
 
+var waitTest20 = false;
+
 window._test20 = function()
 {
-  var local_ws = new MozWebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-20");
+  waitTest20 = true;
+
+  var local_ws = new MozWebSocket("ws://sub1.test1.example.org/tests/content/base/test/file_websocket", "test-20");
   local_ws._testNumber = "local20";
   local_ws._testNumber = current_test++;
 
   local_ws.onerror = function()
   {
     ok(false, "onerror called on test " + e.target._testNumber + "!");
   };
 
   local_ws.onclose = function(e)
   {
-    shouldCloseCleanly(e);
-    doTest(21);
+    ok(true, "test 20 closed despite gc");
+    waitTest20 = false;
+    maybeFinished();
   };
 
   local_ws = null;
   window._test20 = null;
   forcegc();
+
+  // let test run in the background
+  doTest(21);
 }
 
 function test20()
 {
   window._test20();
 }
 
-var timeoutTest21;
+var waitTest21 = false;
 
 window._test21 = function()
 {
+  waitTest21 = true;
+
   var local_ws = new MozWebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-21");
   local_ws._testNumber = current_test++;
+  var received_message = false;
 
   local_ws.onopen = function(e)
   {
     e.target.send("client data");
-    timeoutTest21 = setTimeout(function()
-    {
-      ok(false, "Didn't received message on test-21!");
-    }, 15000);
     forcegc();
     e.target.onopen = null;
     forcegc();
   };
 
   local_ws.onerror = function()
   {
     ok(false, "onerror called on test " + e.target._testNumber + "!");
   };
 
   local_ws.onmessage = function(e)
   {
-    clearTimeout(timeoutTest21);
     ok(e.data == "server data", "Bad message in test-21");
+    received_message = true;
     forcegc();
     e.target.onmessage = null;
     forcegc();
   };
 
   local_ws.onclose = function(e)
   {
     shouldCloseCleanly(e);
-    doTest(22);
+    ok(received_message, "close transitioned through onmessage");
+    waitTest21 = false;
+    maybeFinished();
   };
 
   local_ws = null;
   window._test21 = null;
   forcegc();
+
+  doTest(22);
+
 }
 
 function test21()
 {
   window._test21();
 }
 
+var waitTest22 = false;
+
 function test22()
 {
-  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-22");
+  waitTest22 = true;
+
+  const pref_open = "network.websocket.timeout.open";
+  var oldpref_open_value = 20;
+  oldpref_open_value = SpecialPowers.getIntPref(pref_open);
+  SpecialPowers.setIntPref(pref_open, 5);
+
+  var ws = CreateTestWS("ws://sub2.test2.example.org/tests/content/base/test/file_websocket", "test-22");
   ws.onopen = shouldNotOpen;
+  ws.onerror = ignoreError;
   ws.onclose = function(e)
   {
     shouldCloseNotCleanly(e);
-    doTest(23);
+    waitTest22 = false;
+    maybeFinished();
   };
+
+  SpecialPowers.setIntPref(pref_open, oldpref_open_value);
+  doTest(23);
 }
 
 function test23()
 {
+  current_test++;
   is(false, "WebSocket" in window, "WebSocket shouldn't be available on window object");
   is(true, "MozWebSocket" in window, "MozWebSocket should be available on window object");
   doTest(24);
 }
 
-function finishWSTest()
+function test24()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-does-not-exist");
+  ws.onopen = shouldNotOpen;
+  ws.onclose = function(e)
+  {
+    shouldCloseNotCleanly(e);
+    doTest(25);
+  };
+  ws.onerror = function()
+  {
+  }
+}
+
+function test25()
+{
+  var prots=[];
+
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+
+  // This test errors because the server requires a sub-protocol, but
+  // the test just wants to ensure that the ctor doesn't generate an
+  // exception
+  ws.onerror = ignoreError;
+  ws.onopen = shouldNotOpen;
+
+  ws.onclose = function(e)
+  {
+    ok(ws.protocol == "", "test25 subprotocol selection");
+    ok(true, "test 25 protocol array close");
+    doTest(26);
+  };
+}
+
+function test26()
+{
+  var prots=[""];
+
+  try {
+    var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+    ok(false, "testing empty element sub protocol array");
+  }
+  catch (e) {
+    ok(true, "testing empty sub element protocol array");
+  }
+  doTest(27);
+}
+
+function test27()
+{
+  var prots=["test27", ""];
+
+  try {
+    var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+    ok(false, "testing empty element mixed sub protocol array");
+  }
+  catch (e) {
+    ok(true, "testing empty element mixed sub protocol array");
+  }
+  doTest(28);
+}
+
+function test28()
+{
+  var prots=["test28"];
+
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+  ws.onopen = function(e)
+  {
+    ok(true, "test 28 protocol array open");
+    ws.close();
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(ws.protocol == "test28", "test28 subprotocol selection");
+    ok(true, "test 28 protocol array close");
+    doTest(29);
+  };
+}
+
+function test29()
+{
+  var prots=["test29a", "test29b"];
+
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+  ws.onopen = function(e)
+  {
+    ok(true, "test 29 protocol array open");
+    ws.close();
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 29 protocol array close");
+    doTest(30);
+  };
+}
+
+function test30()
+{
+  var prots=["test-does-not-exist"];
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+
+  ws.onopen = shouldNotOpen;
+  ws.onclose = function(e)
+  {
+    shouldCloseNotCleanly(e);
+    doTest(31);
+  };
+  ws.onerror = function()
+  {
+  }
+}
+
+function test31()
+{
+  var prots=["test-does-not-exist", "test31"];
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+
+  ws.onopen = function(e)
+  {
+    ok(true, "test 31 protocol array open");
+    ws.close();
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(ws.protocol == "test31", "test31 subprotocol selection");
+    ok(true, "test 31 protocol array close");
+    doTest(32);
+  };
+}
+
+function test32()
+{
+  var prots=["test32","test32"];
+
+  try {
+    var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+    ok(false, "testing duplicated element sub protocol array");
+  }
+  catch (e) {
+    ok(true, "testing duplicated sub element protocol array");
+  }
+  doTest(33);
+}
+
+function test33()
+{
+  var prots=["test33"];
+
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+  ws.onopen = function(e)
+  {
+    ok(true, "test 33 open");
+    ws.close();
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 33 close");
+    ok(e.wasClean, "test 33 closed cleanly");
+    ok(e.code == 1000, "test 33 had normal 1000 error code");
+    doTest(34);
+  };
+}
+
+function test34()
+{
+  var prots=["test-34"];
+
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+  ws.onopen = function(e)
+  {
+    ok(true, "test 34 open");
+    ws.close();
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 34 close");
+    ok(e.wasClean, "test 34 closed cleanly");
+    ok(e.code == 1001, "test 34 custom server code");
+    ok(e.reason == "going away now", "test 34 custom server reason");
+    doTest(35);
+  };
+}
+
+function test35()
 {
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-35a");
+
+  ws.onopen = function(e)
+  {
+    ok(true, "test 35a open");
+    ws.close(3500, "my code");
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 35a close");
+    ok(e.wasClean, "test 35a closed cleanly");
+    current_test--; // CreateTestWS for 35a incremented this
+    var wsb = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-35b");
+
+  wsb.onopen = function(e)
+  {
+    ok(true, "test 35b open");
+    wsb.close();
+  };
+
+  wsb.onclose = function(e)
+  {
+    ok(true, "test 35b close");
+    ok(e.wasClean, "test 35b closed cleanly");
+    ok(e.code == 3501, "test 35 custom server code");
+    ok(e.reason == "my code", "test 35 custom server reason");
+    doTest(36);
+  };
+  }
+}
+
+function test36()
+{
+  var prots=["test-36"];
+
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+  ws.onopen = function(e)
+  {
+    ok(true, "test 36 open");
+
+    try {
+      ws.close(13200);
+      ok(false, "testing custom close code out of range");
+     }
+     catch (e) {
+       ok(true, "testing custom close code out of range");
+       ws.close(3200);
+     }
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 36 close");
+    ok(e.wasClean, "test 36 closed cleanly");
+    doTest(37);
+  };
+}
+
+function test37()
+{
+  var prots=["test-37"];
+
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+  ws.onopen = function(e)
+  {
+    ok(true, "test 37 open");
+
+    try {
+	ws.close(3100,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123");
+      ok(false, "testing custom close reason out of range");
+     }
+     catch (e) {
+       ok(true, "testing custom close reason out of range");
+       ws.close(3100,"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012");
+     }
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 37 close");
+    ok(e.wasClean, "test 37 closed cleanly");
+
+    current_test--; // CreateTestWS for 37 incremented this
+    var wsb = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-37b");
+
+    wsb.onopen = function(e)
+    {
+      // now test that a rejected close code and reason dont persist
+      ok(true, "test 37b open");
+      try {
+        wsb.close(3101,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123");
+        ok(false, "testing custom close reason out of range 37b");
+      }
+      catch (e) {
+        ok(true, "testing custom close reason out of range 37b");
+        wsb.close();
+     }
+    }
+
+    wsb.onclose = function(e)
+    {
+      ok(true, "test 37b close");
+      ok(e.wasClean, "test 37b closed cleanly");
+
+      current_test--; // CreateTestWS for 37 incremented this
+      var wsc = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-37c");
+
+      wsc.onopen = function(e)
+      {
+        ok(true, "test 37c open");
+        wsc.close();
+      }
+
+      wsc.onclose = function(e)
+      {
+         ok(e.code != 3101, "test 37c custom server code not present");
+         ok(e.reason == "", "test 37c custom server reason not present");
+         doTest(38);  
+      }
+    }
+  }
+}
+
+function test38()
+{
+  var prots=["test-38"];
+
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+  ws.onopen = function(e)
+  {
+    ok(true, "test 38 open");
+    ok(ws.extensions != undefined, "extensions attribute defined");
+    ok(ws.extensions == "deflate-stream", "extensions attribute deflate-stream");
+    ws.close();
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 38 close");
+    doTest(39);
+  };
+}
+
+function test39()
+{
+  var prots=["test-39"];
+
+  var ws = CreateTestWS("wss://example.com/tests/content/base/test/file_websocket", prots);
+  status_test39 = "started";
+  ws.onopen = function(e)
+  {
+    status_test39 = "opened";
+    ok(true, "test 39 open");
+    ws.close();
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 39 close");
+    ok(status_test39 == "opened", "test 39 did open"); 
+    doTest(40);
+  };
+}
+
+function test40()
+{
+  var prots=["test-40"];
+
+  var ws = CreateTestWS("wss://nocert.example.com/tests/content/base/test/file_websocket", prots);
+
+  status_test40 = "started";
+  ws.onerror = ignoreError;
+
+  ws.onopen = function(e)
+  {
+    status_test40 = "opened";
+    ok(false, "test 40 open");
+    ws.close();
+  };
+
+  ws.onclose = function(e)
+  {
+    ok(true, "test 40 close");
+    ok(status_test40 == "started", "test 40 did not open"); 
+    doTest(41);
+  };
+}
+
+var ranAllTests = false;
+
+function maybeFinished()
+{
+  if (!ranAllTests)
+    return;
+
+  if (waitTest2Part1 || waitTest2Part2 || waitTest9 || waitTest10 ||
+      waitTest17 || waitTest20 || waitTest21 || waitTest22)
+    return;
+
   for (i = 0; i < all_ws.length; ++i) {
     if (all_ws[i] != shouldNotReceiveCloseEvent &&
         !all_ws[i]._receivedCloseEvent) {
       ok(false, "didn't called close on test " + all_ws[i]._testNumber + "!");
     }
   }
+
   SimpleTest.finish();
 }
 
 function testWebSocket ()
 {
   doTest(first_test);
 }
 
--- a/content/events/src/nsDOMCloseEvent.cpp
+++ b/content/events/src/nsDOMCloseEvent.cpp
@@ -52,25 +52,43 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEve
 NS_IMETHODIMP
 nsDOMCloseEvent::GetWasClean(PRBool *aWasClean)
 {
   *aWasClean = mWasClean;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMCloseEvent::GetCode(PRUint16 *aCode)
+{
+  *aCode = mReasonCode;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMCloseEvent::GetReason(nsAString & aReason)
+{
+  aReason = mReason;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMCloseEvent::InitCloseEvent(const nsAString& aType,
                                 PRBool aCanBubble,
                                 PRBool aCancelable,
-                                PRBool aWasClean)
+                                PRBool aWasClean,
+                                PRUint16 aReasonCode,
+                                const nsAString &aReason)
 {
   nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mWasClean = aWasClean;
+  mReasonCode = aReasonCode;
+  mReason = aReason;
 
   return NS_OK;
 }
 
 nsresult
 NS_NewDOMCloseEvent(nsIDOMEvent** aInstancePtrResult,
                     nsPresContext* aPresContext,
                     nsEvent* aEvent) 
--- a/content/events/src/nsDOMCloseEvent.h
+++ b/content/events/src/nsDOMCloseEvent.h
@@ -48,24 +48,26 @@
  *
  * See http://dev.w3.org/html5/websockets/#closeevent for further details.
  */
 class nsDOMCloseEvent : public nsDOMEvent,
                         public nsIDOMCloseEvent
 {
 public:
   nsDOMCloseEvent(nsPresContext* aPresContext, nsEvent* aEvent)
-    : nsDOMEvent(aPresContext, aEvent), mWasClean(PR_FALSE)
-  {
-  }
+    : nsDOMEvent(aPresContext, aEvent),
+    mWasClean(PR_FALSE),
+    mReasonCode(1005) {}
                      
   NS_DECL_ISUPPORTS_INHERITED
 
   // Forward to base class
   NS_FORWARD_TO_NSDOMEVENT
 
   NS_DECL_NSIDOMCLOSEEVENT
 
 private:
   PRBool mWasClean;
+  PRUint16 mReasonCode;
+  nsString mReason;
 };
 
 #endif // nsDOMCloseEvent_h__
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -753,19 +753,19 @@ nsGenericHTMLElement::SetInnerHTML(const
   mozAutoSubtreeModified subtree(doc, nsnull);
 
   FireNodeRemovedForChildren();
 
   // Needed when innerHTML is used in combination with contenteditable
   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, PR_TRUE);
 
   // Remove childnodes.
-  // i is unsigned, so i >= is always true
-  for (PRUint32 i = GetChildCount(); i-- != 0; ) {
-    RemoveChildAt(i, PR_TRUE);
+  PRUint32 childCount = GetChildCount();
+  for (PRUint32 i = 0; i < childCount; ++i) {
+    RemoveChildAt(0, PR_TRUE);
   }
 
   nsCOMPtr<nsIDOMDocumentFragment> df;
 
   if (doc->IsHTML()) {
     PRInt32 oldChildCount = GetChildCount();
     nsContentUtils::ParseFragmentHTML(aInnerHTML,
                                       this,
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -576,28 +576,31 @@ nsHTMLCanvasElement::UpdateContext(nsIPr
     return NS_OK;
 
   nsresult rv = NS_OK;
   nsIntSize sz = GetWidthHeight();
 
   rv = mCurrentContext->SetIsOpaque(GetIsOpaque());
   if (NS_FAILED(rv)) {
     mCurrentContext = nsnull;
+    mCurrentContextId.AssignLiteral("");
     return rv;
   }
 
   rv = mCurrentContext->SetContextOptions(aNewContextOptions);
   if (NS_FAILED(rv)) {
     mCurrentContext = nsnull;
+    mCurrentContextId.AssignLiteral("");
     return rv;
   }
 
   rv = mCurrentContext->SetDimensions(sz.width, sz.height);
   if (NS_FAILED(rv)) {
     mCurrentContext = nsnull;
+    mCurrentContextId.AssignLiteral("");
     return rv;
   }
 
   return rv;
 }
 
 nsIFrame *
 nsHTMLCanvasElement::GetPrimaryCanvasFrame()
--- a/content/html/content/src/nsHTMLFontElement.cpp
+++ b/content/html/content/src/nsHTMLFontElement.cpp
@@ -234,17 +234,18 @@ MapAttributesIntoRule(const nsMappedAttr
       // color: color
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::color);
       nscolor color;
       if (value && value->GetColorValue(color)) {
         colorValue->SetColorValue(color);
       }
     }
   }
-  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) {
+  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset) &&
+      aData->mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
     // Make <a><font color="red">text</font></a> give the text a red underline
     // in quirks mode.  The NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL flag only
     // affects quirks mode rendering.
     const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::color);
     nscolor color;
     if (value && value->GetColorValue(color)) {
       nsCSSValue* decoration = aData->ValueForTextDecorationLine();
       PRInt32 newValue = NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL;
--- a/content/html/content/test/test_bug277724.html
+++ b/content/html/content/test/test_bug277724.html
@@ -32,33 +32,37 @@ var nodes = [
   [ "button input", HTMLInputElement ],
   [ "hidden", HTMLInputElement ],
   [ "file", HTMLInputElement ],
   [ "submit button", HTMLButtonElement ],
   [ "reset button", HTMLButtonElement ],
   [ "button", HTMLButtonElement ]
 ];
 
+function soon(f) {
+  return function() { setTimeout(f, 0); }
+}
+
 function startTest(frameid) {
   is(childUnloaded, false, "Child not unloaded yet");
 
   var doc = $(frameid).contentDocument;
   ok(doc instanceof Document, "Check for doc", "doc should be a document");
 
   for (var i = 0; i < nodes.length; ++i) {
     var id = nodes[i][0];
     var node = doc.getElementById(id);
     ok(node instanceof nodes[i][1],
        "Check for " + id, id + " should be a " + nodes[i][1]);
     is(node.disabled, false, "check for " + id + " state");
     node.disabled = true;
     is(node.disabled, true, "check for " + id + " state change");
   }
   
-  $(frameid).onload = function () { continueTest(frameid) };
+  $(frameid).onload = soon(function() { continueTest(frameid) });
 
   // Do this off a timeout so it's not treated like a replace load.
   function loadBlank() {
     $(frameid).contentWindow.location = "about:blank";
   }
   setTimeout(loadBlank, 0);
 }
 
@@ -69,17 +73,17 @@ function continueTest(frameid) {
 
   for (var i = 0; i < nodes.length; ++i) {
     var id = nodes[i][0];
     var node = doc.getElementById(id);
     ok(node === null,
        "Check for " + id, id + " should be null");
   }
   
-  $(frameid).onload = function() { finishTest(frameid) };
+  $(frameid).onload = soon(function() { finishTest(frameid); });
 
   // Do this off a timeout too.  Why, I'm not sure.  Something in session
   // history creates another history state if we don't.  :(
   function goBack() {
     $(frameid).contentWindow.history.back();
   }
   setTimeout(goBack, 0);
 }
@@ -112,27 +116,27 @@ function finishTest(frameid) {
   if (frameid == "frame2") {
     SimpleTest.finish();
   } else {
     childUnloaded = false;
 
     // XXXbz this is a nasty hack to deal with the content sink.  See above.
     testIs = flipper;
     
-    $("frame2").onload = function () { startTest("frame2") };
+    $("frame2").onload = soon(function() { startTest("frame2"); });
     $("frame2").src = "bug277724_iframe2.xhtml";
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 
 <!-- Don't use display:none, since we don't support framestate restoration
      without a frame tree -->
 <div id="content" style="visibility: hidden">
   <iframe src="bug277724_iframe1.html" id="frame1"
-          onload="startTest('frame1')"></iframe>
+          onload="setTimeout(function() { startTest('frame1') }, 0)"></iframe>
   <iframe src="" id="frame2"></iframe>
 </div>
 </body>
 </html>
 
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -1492,16 +1492,17 @@ nsHTMLDocument::SetCookie(const nsAStrin
   nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
   if (service && mDocumentURI) {
     nsCOMPtr<nsIPrompt> prompt;
     nsCOMPtr<nsPIDOMWindow> window = GetWindow();
     if (window) {
       window->GetPrompter(getter_AddRefs(prompt));
     }
 
+    // The for getting the URI matches nsNavigator::GetCookieEnabled
     nsCOMPtr<nsIURI> codebaseURI;
     NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
 
     if (!codebaseURI) {
       // Document's principal is not a codebase (may be system), so
       // can't set cookies
 
       return NS_OK;
--- a/content/media/ogg/nsOggCodecState.cpp
+++ b/content/media/ogg/nsOggCodecState.cpp
@@ -673,16 +673,22 @@ nsresult nsVorbisState::ReconstructVorbi
       blockSize = 0;
       mPrevVorbisBlockSize = 0;
     }
     long samples = mPrevVorbisBlockSize / 4 + blockSize / 4;
     mPrevVorbisBlockSize = blockSize;
     if (packet->granulepos == -1) {
       packet->granulepos = mGranulepos + samples;
     }
+
+    // Account for a partial last frame
+    if (packet->e_o_s && packet->granulepos >= mGranulepos) {
+       samples = packet->granulepos - mGranulepos;
+    }
+ 
     mGranulepos = packet->granulepos;
     RecordVorbisPacketSamples(packet, samples);
     return NS_OK;
   }
 
   PRBool unknownGranulepos = last->granulepos == -1;
   int totalSamples = 0;
   for (PRInt32 i = mUnstamped.Length() - 1; i > 0; i--) {
--- a/content/smil/nsSMILAnimationController.cpp
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -89,18 +89,18 @@ nsSMILAnimationController::~nsSMILAnimat
 }
 
 void
 nsSMILAnimationController::Disconnect()
 {
   NS_ABORT_IF_FALSE(mDocument, "disconnecting when we weren't connected...?");
   NS_ABORT_IF_FALSE(mRefCnt.get() == 1,
                     "Expecting to disconnect when doc is sole remaining owner");
-  NS_ABORT_IF_FALSE(mPauseState & nsSMILTimeContainer::PAUSE_PAGEHIDE,
-                    "Expecting to be paused for pagehide before disconnect");
+  NS_ASSERTION(mPauseState & nsSMILTimeContainer::PAUSE_PAGEHIDE,
+               "Expecting to be paused for pagehide before disconnect");
 
   StopSampling(GetRefreshDriver());
 
   mDocument = nsnull; // (raw pointer)
 }
 
 //----------------------------------------------------------------------
 // nsSMILTimeContainer methods:
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2430,16 +2430,39 @@ void
 nsXULElement::SetDrawsInTitlebar(PRBool aState)
 {
     nsIWidget* mainWidget = GetWindowWidget();
     if (mainWidget) {
         nsContentUtils::AddScriptRunner(new SetDrawInTitleBarEvent(mainWidget, aState));
     }
 }
 
+class MarginSetter : public nsRunnable
+{
+public:
+    MarginSetter(nsIWidget* aWidget) :
+        mWidget(aWidget), mMargin(-1, -1, -1, -1)
+    {}
+    MarginSetter(nsIWidget *aWidget, const nsIntMargin& aMargin) :
+        mWidget(aWidget), mMargin(aMargin)
+    {}
+
+    NS_IMETHOD Run()
+    {
+        // SetNonClientMargins can dispatch native events, hence doing
+        // it off a script runner.
+        mWidget->SetNonClientMargins(mMargin);
+        return NS_OK;
+    }
+
+private:
+    nsCOMPtr<nsIWidget> mWidget;
+    nsIntMargin mMargin;
+};
+
 void
 nsXULElement::SetChromeMargins(const nsAString* aValue)
 {
     if (!aValue)
         return;
 
     nsIWidget* mainWidget = GetWindowWidget();
     if (!mainWidget)
@@ -2448,29 +2471,28 @@ nsXULElement::SetChromeMargins(const nsA
     // top, right, bottom, left - see nsAttrValue
     nsAttrValue attrValue;
     nsIntMargin margins;
 
     nsAutoString data;
     data.Assign(*aValue);
     if (attrValue.ParseIntMarginValue(data) &&
         attrValue.GetIntMarginValue(margins)) {
-        mainWidget->SetNonClientMargins(margins);
+        nsContentUtils::AddScriptRunner(new MarginSetter(mainWidget, margins));
     }
 }
 
 void
 nsXULElement::ResetChromeMargins()
 {
     nsIWidget* mainWidget = GetWindowWidget();
     if (!mainWidget)
         return;
     // See nsIWidget
-    nsIntMargin margins(-1,-1,-1,-1);
-    mainWidget->SetNonClientMargins(margins);
+    nsContentUtils::AddScriptRunner(new MarginSetter(mainWidget));
 }
 
 PRBool
 nsXULElement::BoolAttrIsTrue(nsIAtom* aName)
 {
     const nsAttrValue* attr =
         FindLocalOrProtoAttr(kNameSpaceID_None, aName);
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1330,19 +1330,18 @@ nsDocShell::LoadURI(nsIURI * aURI,
                     // by session history, (if (!shEntry) condition succeeded) and mCurrentURI is not null,
                     // it is possible that a parent's onLoadHandler or even self's onLoadHandler is loading 
                     // a new page in this child. Check parent's and self's busy flag  and if it is set,
                     // we don't want this onLoadHandler load to get in to session history.
                     PRUint32 parentBusy = BUSY_FLAGS_NONE;
                     PRUint32 selfBusy = BUSY_FLAGS_NONE;
                     parentDS->GetBusyFlags(&parentBusy);                    
                     GetBusyFlags(&selfBusy);
-                    if (((parentBusy & BUSY_FLAGS_BUSY) ||
-                         (selfBusy & BUSY_FLAGS_BUSY)) &&
-                        shEntry) {
+                    if (parentBusy & BUSY_FLAGS_BUSY ||
+                        selfBusy & BUSY_FLAGS_BUSY) {
                         loadType = LOAD_NORMAL_REPLACE;
                         shEntry = nsnull; 
                     }
                 }
             } // parent
         } //parentDS
         else {  
             // This is the root docshell. If we got here while  
--- a/docshell/test/browser/Makefile.in
+++ b/docshell/test/browser/Makefile.in
@@ -59,12 +59,13 @@ include $(topsrcdir)/config/rules.mk
 		file_bug503832.html \
 		browser_bug554155.js \
 		browser_bug655273.js \
 		browser_bug655270.js \
 		file_bug655270.html \
 		favicon_bug655270.ico \
 		browser_bug670318.js \
 		file_bug670318.html \
+		browser_bug673467.js \
 		$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/browser_bug673467.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test for bug 673467.  In a new tab, load a page which inserts a new iframe
+// before the load and then sets its location during the load.  This should
+// create just one SHEntry.
+
+var doc = "data:text/html,<html><body onload='load()'>" +
+ "<script>" +
+ "  var iframe = document.createElement('iframe');" +
+ "  iframe.id = 'iframe';" +
+ "  document.documentElement.appendChild(iframe);" +
+ "  function load() {" +
+ "    iframe.src = 'data:text/html,Hello!';" +
+ "  }" +
+ "</script>" +
+ "</body></html>"
+
+function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(doc);
+  let tabBrowser = tab.linkedBrowser;
+
+  tabBrowser.addEventListener('load', function(aEvent) {
+    tabBrowser.removeEventListener('load', arguments.callee, true);
+
+    // The main page has loaded.  Now wait for the iframe to load.
+    let iframe = tabBrowser.contentWindow.document.getElementById('iframe');
+    iframe.addEventListener('load', function(aEvent) {
+
+      // Wait for the iframe to load the new document, not about:blank.
+      if (!iframe.src)
+        return;
+
+      iframe.removeEventListener('load', arguments.callee, true);
+      let shistory = tabBrowser.contentWindow
+                      .QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIWebNavigation)
+                      .sessionHistory;
+
+      is(shistory.count, 1, 'shistory count should be 1.');
+
+      gBrowser.removeTab(tab);
+      finish();
+
+    }, true);
+  }, true);
+}
--- a/dom/base/ConsoleAPI.js
+++ b/dom/base/ConsoleAPI.js
@@ -78,23 +78,28 @@ ConsoleAPI.prototype = {
         self.notifyObservers(id, "error", arguments);
       },
       debug: function CA_debug() {
         self.notifyObservers(id, "log", arguments);
       },
       trace: function CA_trace() {
         self.notifyObservers(id, "trace", self.getStackTrace());
       },
+      // Displays an interactive listing of all the properties of an object.
+      dir: function CA_dir() {
+        self.notifyObservers(id, "dir", arguments);
+      },
       __exposedProps__: {
         log: "r",
         info: "r",
         warn: "r",
         error: "r",
         debug: "r",
         trace: "r",
+        dir: "r"
       }
     };
 
     // We need to return an actual content object here, instead of a wrapped
     // chrome object. This allows things like console.log.bind() to work.
     let contentObj = Cu.createObjectIn(aWindow);
     function genPropDesc(fun) {
       return { enumerable: true, configurable: true, writable: true,
@@ -102,16 +107,17 @@ ConsoleAPI.prototype = {
     }
     const properties = {
       log: genPropDesc('log'),
       info: genPropDesc('info'),
       warn: genPropDesc('warn'),
       error: genPropDesc('error'),
       debug: genPropDesc('debug'),
       trace: genPropDesc('trace'),
+      dir: genPropDesc('dir'),
       __noSuchMethod__: { enumerable: true, configurable: true, writable: true,
                           value: function() {} },
       __mozillaConsole__: { value: true }
     };
 
     Object.defineProperties(contentObj, properties);
     Cu.makeObjectPropsNormal(contentObj);
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -89,16 +89,17 @@
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMPopStateEvent.h"
 #include "nsIDOMHashChangeEvent.h"
 #include "nsContentUtils.h"
 #include "nsDOMWindowUtils.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "mozilla/Preferences.h"
+#include "nsLocation.h"
 
 // Window scriptable helper includes
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIScriptExternalNameSet.h"
 #include "nsJSUtils.h"
 #include "nsIInterfaceRequestor.h"
@@ -6932,17 +6933,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
         return NS_OK;
       }
     }
 
     // Call GlobalResolve() after we call FindChildWithName() so
     // that named child frames will override external properties
     // which have been registered with the script namespace manager.
 
-    JSBool did_resolve = JS_FALSE;
+    PRBool did_resolve = PR_FALSE;
     rv = GlobalResolve(win, cx, obj, id, &did_resolve);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (did_resolve) {
       // GlobalResolve() resolved something, so we're done here.
       *objp = obj;
 
       return NS_OK;
@@ -7072,17 +7073,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
         isResolvingJavaProperties = PR_TRUE;
 
         // Tell the window to initialize the Java properties. The
         // window needs to do this as we need to do this only once,
         // and detecting that reliably from here is hard.
 
         win->InitJavaProperties(); 
 
-        PRBool hasProp;
+        JSBool hasProp;
         PRBool ok = ::JS_HasPropertyById(cx, obj, id, &hasProp);
 
         isResolvingJavaProperties = PR_FALSE;
 
         if (!ok) {
           return NS_ERROR_FAILURE;
         }
 
@@ -7790,17 +7791,18 @@ nsEventReceiverSH::NewResolve(nsIXPConne
 
     // If we're assigning to an on* property, just resolve to null for
     // now; the assignment will then set the right value. Only do this
     // in the case where the property isn't already defined on the
     // object's prototype chain though.
     JSAutoRequest ar(cx);
 
     JSObject *proto = ::JS_GetPrototype(cx, obj);
-    PRBool ok = PR_TRUE, hasProp = PR_FALSE;
+    PRBool ok = PR_TRUE;
+    JSBool hasProp = JS_FALSE;
     if (!proto || ((ok = ::JS_HasPropertyById(cx, proto, id, &hasProp)) &&
                    !hasProp)) {
       // Make sure the flags here match those in
       // nsJSContext::BindCompiledEventHandler
       if (!::JS_DefinePropertyById(cx, obj, id, JSVAL_NULL, nsnull, nsnull,
                                    JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
         return NS_ERROR_FAILURE;
       }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -185,16 +185,17 @@
 #include "nsEventDispatcher.h"
 #include "nsIObserverService.h"
 #include "nsIXULAppInfo.h"
 #include "nsNetUtil.h"
 #include "nsFocusManager.h"
 #include "nsIXULWindow.h"
 #include "nsEventStateManager.h"
 #include "nsITimedChannel.h"
+#include "nsICookiePermission.h"
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsIDOMXULControlElement.h"
 #include "nsMenuPopupFrame.h"
 #endif
 
 #include "xpcprivate.h"
 
@@ -237,16 +238,17 @@
 
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 
 #include "nsRefreshDriver.h"
 #include "mozAutoDocUpdate.h"
 
 #include "mozilla/Telemetry.h"
+#include "nsLocation.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
 #endif
 
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 using namespace mozilla;
@@ -10908,16 +10910,46 @@ nsNavigator::GetPlugins(nsIDOMPluginArra
 
 NS_IMETHODIMP
 nsNavigator::GetCookieEnabled(PRBool *aCookieEnabled)
 {
   *aCookieEnabled =
     (Preferences::GetInt("network.cookie.cookieBehavior",
                          COOKIE_BEHAVIOR_REJECT) != COOKIE_BEHAVIOR_REJECT);
 
+  // Check whether an exception overrides the global cookie behavior
+  // Note that the code for getting the URI here matches that in
+  // nsHTMLDocument::SetCookie.
+  nsCOMPtr<nsIDocument> doc = do_GetInterface(mDocShell);
+  if (!doc) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIURI> codebaseURI;
+  doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
+
+  if (!codebaseURI) {
+    // Not a codebase, so technically can't set cookies, but let's
+    // just return the default value.
+    return NS_OK;
+  }
+  
+  nsCOMPtr<nsICookiePermission> permMgr =
+    do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
+  NS_ENSURE_TRUE(permMgr, NS_OK);
+
+  // Pass null for the channel, just like the cookie service does
+  nsCookieAccess access;
+  nsresult rv = permMgr->CanAccess(codebaseURI, nsnull, &access);
+  NS_ENSURE_SUCCESS(rv, NS_OK);
+
+  if (access != nsICookiePermission::ACCESS_DEFAULT) {
+    *aCookieEnabled = access != nsICookiePermission::ACCESS_DENY;
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavigator::GetOnLine(PRBool* aOnline)
 {
   NS_PRECONDITION(aOnline, "Null out param");
   
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -61,17 +61,16 @@
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDOMClientInformation.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMNavigator.h"
 #include "nsIDOMNavigatorGeolocation.h"
 #include "nsIDOMNavigatorDesktopNotification.h"
-#include "nsIDOMLocation.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDOMJSWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptTimeoutHandler.h"
@@ -1085,55 +1084,14 @@ protected:
   nsIDocShell* mDocShell; // weak reference
 };
 
 nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
 nsresult NS_GetNavigatorPlatform(nsAString& aPlatform);
 nsresult NS_GetNavigatorAppVersion(nsAString& aAppVersion);
 nsresult NS_GetNavigatorAppName(nsAString& aAppName);
 
-class nsIURI;
-
-//*****************************************************************************
-// nsLocation: Script "location" object
-//*****************************************************************************
-
-class nsLocation : public nsIDOMLocation
-{
-public:
-  nsLocation(nsIDocShell *aDocShell);
-  virtual ~nsLocation();
-
-  NS_DECL_ISUPPORTS
-
-  void SetDocShell(nsIDocShell *aDocShell);
-  nsIDocShell *GetDocShell();
-
-  // nsIDOMLocation
-  NS_DECL_NSIDOMLOCATION
-
-protected:
-  // In the case of jar: uris, we sometimes want the place the jar was
-  // fetched from as the URI instead of the jar: uri itself.  Pass in
-  // PR_TRUE for aGetInnermostURI when that's the case.
-  nsresult GetURI(nsIURI** aURL, PRBool aGetInnermostURI = PR_FALSE);
-  nsresult GetWritableURI(nsIURI** aURL);
-  nsresult SetURI(nsIURI* aURL, PRBool aReplace = PR_FALSE);
-  nsresult SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
-                           PRBool aReplace);
-  nsresult SetHrefWithContext(JSContext* cx, const nsAString& aHref,
-                              PRBool aReplace);
-
-  nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
-  nsresult GetSourceDocument(JSContext* cx, nsIDocument** aDocument);
-
-  nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
-
-  nsString mCachedHash;
-  nsWeakPtr mDocShell;
-};
-
 /* factory function */
 nsresult
 NS_NewScriptGlobalObject(PRBool aIsChrome, PRBool aIsModalContentWindow,
                          nsIScriptGlobalObject **aResult);
 
 #endif /* nsGlobalWindow_h___ */
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1010,17 +1010,17 @@ nsJSContext::JSOptionChangedCallback(con
   ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSRUNOPTION_MASK);
 
   // Save the new defaults for the next page load (InitContext).
   context->mDefaultJSOptions = newDefaultJSOptions;
 
 #ifdef JS_GC_ZEAL
   PRInt32 zeal = Preferences::GetInt(js_zeal_option_str, -1);
   PRInt32 frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
-  PRBool compartment = Preferences::GetBool(js_zeal_compartment_str, JS_FALSE);
+  PRBool compartment = Preferences::GetBool(js_zeal_compartment_str, PR_FALSE);
   if (zeal >= 0)
     ::JS_SetGCZeal(context->mContext, (PRUint8)zeal, frequency, compartment);
 #endif
 
   return 0;
 }
 
 nsJSContext::nsJSContext(JSRuntime *aRuntime)
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -33,17 +33,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 ***** */
 
-#include "nsGlobalWindow.h"
+#include "nsLocation.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptContext.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIWebNavigation.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIURIFixup.h"
 #include "nsIURL.h"
@@ -51,32 +51,32 @@
 #include "nsIIOService.h"
 #include "nsIServiceManager.h"
 #include "nsNetUtil.h"
 #include "plstr.h"
 #include "prprf.h"
 #include "prmem.h"
 #include "nsCOMPtr.h"
 #include "nsEscape.h"
-#include "nsJSUtils.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMDocument.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIJSContextStack.h"
 #include "nsXPIDLString.h"
 #include "nsDOMError.h"
 #include "nsDOMClassInfo.h"
 #include "nsCRT.h"
 #include "nsIProtocolHandler.h"
 #include "nsReadableUtils.h"
 #include "nsITextToSubURI.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
+#include "jsdbgapi.h"
 
 static nsresult
 GetContextFromStack(nsIJSContextStack *aStack, JSContext **aContext)
 {
   nsCOMPtr<nsIJSContextStackIterator>
     iterator(do_CreateInstance("@mozilla.org/js/xpc/ContextStackIterator;1"));
   NS_ENSURE_TRUE(iterator, NS_ERROR_FAILURE);
 
new file mode 100644
--- /dev/null
+++ b/dom/base/nsLocation.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Travis Bogard <travis@netscape.com>
+ *   Dan Rosen <dr@netscape.com>
+ *   Vidur Apparao <vidur@netscape.com>
+ *   Johnny Stenback <jst@netscape.com>
+ *
+ * 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 ***** */
+
+#ifndef nsLocation_h__
+#define nsLocation_h__
+
+#include "nsIDOMLocation.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+
+
+class nsIURI;
+class nsIDocShell;
+struct JSContext;
+class nsIDocument;
+class nsIDocShellLoadInfo;
+
+//*****************************************************************************
+// nsLocation: Script "location" object
+//*****************************************************************************
+
+class nsLocation : public nsIDOMLocation
+{
+public:
+  nsLocation(nsIDocShell *aDocShell);
+  virtual ~nsLocation();
+
+  NS_DECL_ISUPPORTS
+
+  void SetDocShell(nsIDocShell *aDocShell);
+  nsIDocShell *GetDocShell();
+
+  // nsIDOMLocation
+  NS_DECL_NSIDOMLOCATION
+
+protected:
+  // In the case of jar: uris, we sometimes want the place the jar was
+  // fetched from as the URI instead of the jar: uri itself.  Pass in
+  // PR_TRUE for aGetInnermostURI when that's the case.
+  nsresult GetURI(nsIURI** aURL, PRBool aGetInnermostURI = PR_FALSE);
+  nsresult GetWritableURI(nsIURI** aURL);
+  nsresult SetURI(nsIURI* aURL, PRBool aReplace = PR_FALSE);
+  nsresult SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
+                           PRBool aReplace);
+  nsresult SetHrefWithContext(JSContext* cx, const nsAString& aHref,
+                              PRBool aReplace);
+
+  nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
+  nsresult GetSourceDocument(JSContext* cx, nsIDocument** aDocument);
+
+  nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
+
+  nsString mCachedHash;
+  nsWeakPtr mDocShell;
+};
+
+#endif // nsLocation_h__
+
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl
@@ -46,17 +46,17 @@
  * The nsIDOMCSS2Properties interface is a datatype for additional
  * reflection of data already provided in nsIDOMCSSStyleDeclaration in
  * the Document Object Model.
  *
  * For more information on this interface please see
  * http://www.w3.org/TR/DOM-Level-2-Style
  */
 
-[scriptable, uuid(7cf11a5f-4be5-4e31-b427-58d82746b5f5)]
+[scriptable, uuid(10f43750-b379-11e0-aff2-0800200c9a66)]
 interface nsIDOMCSS2Properties : nsISupports
 {
            attribute DOMString        background;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundAttachment;
                                         // raises(DOMException) on setting
 
@@ -676,16 +676,25 @@ interface nsIDOMCSS2Properties : nsISupp
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTransform;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTransformOrigin;
                                         // raises(DOMException) on setting 
 
+           attribute DOMString        MozPerspective;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozPerspectiveOrigin;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozBackfaceVisibility;
+                                        // raises(DOMException) on setting 
+
            attribute DOMString        MozWindowShadow;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundSize;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTextBlink;
                                         // raises(DOMException) on setting
--- a/dom/interfaces/events/nsIDOMCloseEvent.idl
+++ b/dom/interfaces/events/nsIDOMCloseEvent.idl
@@ -40,18 +40,22 @@
 
 /**
  * The nsIDOMCloseEvent interface is the interface to the event
  * close on a WebSocket object.
  *
  * For more information on this interface, please see
  * http://dev.w3.org/html5/websockets/#closeevent
  */
-[scriptable, uuid(a94d4379-eba2-45f4-be3a-6cc2fa1453a8)]
+[scriptable, uuid(f83d9d6d-6c0c-418c-b12a-438e76d5866b)]
 interface nsIDOMCloseEvent : nsIDOMEvent
 {
   readonly attribute boolean wasClean;
-  
+  readonly attribute unsigned short code;
+  readonly attribute DOMString reason;
+
   void initCloseEvent(in DOMString aType,
-                        in boolean aCanBubble,
-                        in boolean aCancelable,
-                        in boolean aWasClean);
+                      in boolean aCanBubble,
+                      in boolean aCancelable,
+                      in boolean aWasClean,
+                      in unsigned short aReasonCode,
+                      in DOMString aReason);
 };
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -103,8 +103,11 @@ RemoveChildWarning=Use of attributes' re
 AppendChildWarning=Use of attributes' appendChild() is deprecated. Use value instead.
 CloneNodeWarning=Use of attributes' cloneNode() is deprecated.
 OwnerDocumentWarning=Use of attributes' ownerDocument attribute is deprecated.
 NormalizeWarning=Use of attributes' normalize() is deprecated.
 IsSupportedWarning=Use of attributes' isSupported() is deprecated.
 IsEqualNodeWarning=Use of attributes' isEqualNode() is deprecated.
 TextContentWarning=Use of attributes' textContent attribute is deprecated. Use value instead.
 EnablePrivilegeWarning=Use of enablePrivilege is deprecated.  Please use code that runs with the system principal (e.g. an extension) instead.
+
+nsIJSONDecodeDeprecatedWarning=nsIJSON.decode is deprecated.  Please use JSON.parse instead.
+nsIJSONEncodeDeprecatedWarning=nsIJSON.encode is deprecated.  Please use JSON.stringify instead.
--- a/dom/src/json/nsJSON.cpp
+++ b/dom/src/json/nsJSON.cpp
@@ -47,16 +47,17 @@
 #include "nsIXPCScriptable.h"
 #include "nsStreamUtils.h"
 #include "nsIInputStream.h"
 #include "nsStringStream.h"
 #include "nsICharsetConverterManager.h"
 #include "nsXPCOMStrings.h"
 #include "nsNetUtil.h"
 #include "nsContentUtils.h"
+#include "nsIScriptError.h"
 #include "nsCRTGlue.h"
 #include "nsAutoPtr.h"
 #include "nsIScriptSecurityManager.h"
 
 static const char kXPConnectServiceCID[] = "@mozilla.org/js/xpc/XPConnect;1";
 
 #define JSON_STREAM_BUFSIZE 4096
 
@@ -71,21 +72,39 @@ NS_IMPL_RELEASE(nsJSON)
 nsJSON::nsJSON()
 {
 }
 
 nsJSON::~nsJSON()
 {
 }
 
+enum DeprecationWarning { EncodeWarning, DecodeWarning };
+
+static nsresult
+WarnDeprecatedMethod(DeprecationWarning warning)
+{
+  return nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
+                                         warning == EncodeWarning
+                                         ? "nsIJSONEncodeDeprecatedWarning"
+                                         : "nsIJSONDecodeDeprecatedWarning",
+                                         nsnull, 0,
+                                         nsnull,
+                                         EmptyString(), 0, 0,
+                                         nsIScriptError::warningFlag,
+                                         "DOM Core");
+}
+
 NS_IMETHODIMP
 nsJSON::Encode(nsAString &aJSON)
 {
   // This function should only be called from JS.
-  nsresult rv;
+  nsresult rv = WarnDeprecatedMethod(EncodeWarning);
+  if (NS_FAILED(rv))
+    return rv;
 
   nsJSONWriter writer;
   rv = EncodeInternal(&writer);
 
   // FIXME: bug 408838. Get exception types sorted out
   if (NS_SUCCEEDED(rv) || rv == NS_ERROR_INVALID_ARG) {
     rv = NS_OK;
     // if we didn't consume anything, it's not JSON, so return null
@@ -419,23 +438,27 @@ nsJSONWriter::WriteToStream(nsIOutputStr
   mDidWrite = PR_TRUE;
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsJSON::Decode(const nsAString& json)
 {
+  nsresult rv = WarnDeprecatedMethod(DecodeWarning);
+  if (NS_FAILED(rv))
+    return rv;
+
   const PRUnichar *data;
   PRUint32 len = NS_StringGetData(json, &data);
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
-                                      (const char*) data,
-                                      len * sizeof(PRUnichar),
-                                      NS_ASSIGNMENT_DEPEND);
+  rv = NS_NewByteInputStream(getter_AddRefs(stream),
+                             reinterpret_cast<const char*>(data),
+                             len * sizeof(PRUnichar),
+                             NS_ASSIGNMENT_DEPEND);
   NS_ENSURE_SUCCESS(rv, rv);
   return DecodeInternal(stream, len, PR_FALSE);
 }
 
 NS_IMETHODIMP
 nsJSON::DecodeFromStream(nsIInputStream *aStream, PRInt32 aContentLength)
 {
   return DecodeInternal(aStream, aContentLength, PR_TRUE);
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -1164,18 +1164,20 @@ DOMStorageImpl::GetKey(bool aCallerSecur
   // int, but the spec talks about what to do if a negative value is
   // passed in.
 
   // XXX: This does a linear search for the key at index, which would
   // suck if there's a large numer of indexes. Do we care? If so,
   // maybe we need to have a lazily populated key array here or
   // something?
 
-  if (UseDB())
+  if (UseDB()) {
+    mItemsCached = PR_FALSE;
     CacheKeysFromDB();
+  }
 
   IndexFinderData data(aCallerSecure, aIndex);
   mItems.EnumerateEntries(IndexFinder, &data);
 
   if (!data.mItem) {
     // aIndex was larger than the number of accessible keys. Throw.
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -159,30 +159,34 @@ function observeConsoleTest() {
   win.console.log("arg");
 
   expect("info", "arg", "extra arg");
   win.console.info("arg", "extra arg");
 
   expect("warn", "arg", "extra arg", 1);
   win.console.warn("arg", "extra arg", 1);
 
+  expect("dir", win.toString());
+  win.console.dir(win);
+
   expect("error", "arg");
   win.console.error("arg");
 }
 
 function consoleAPISanityTest() {
   let win = XPCNativeWrapper.unwrap(gWindow);
   ok(win.console, "we have a console attached");
   ok(win.console, "we have a console attached, 2nd attempt");
 
   ok(win.console.log, "console.log is here");
   ok(win.console.info, "console.info is here");
   ok(win.console.warn, "console.warn is here");
   ok(win.console.error, "console.error is here");
   ok(win.console.trace, "console.trace is here");
+  ok(win.console.dir, "console.dir is here");
 }
 
 var ConsoleObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   init: function CO_init() {
     Services.obs.addObserver(this, "console-api-log-event", false);
   },
--- a/dom/tests/mochitest/bugs/test_resize_move_windows.html
+++ b/dom/tests/mochitest/bugs/test_resize_move_windows.html
@@ -77,96 +77,85 @@ function hitEventLoop(condition, test, t
   }
 
   setTimeout(hitEventLoop, 0, condition, test, times - 1, next);
 }
 
 function checkChangeIsDisabled(aWindow, aNext)
 {
   // We want to check that nothing has changed. Having a high value would take
-  // too much time.
-  var hits = 10;
+  // too much time. Worse thing that could happen is random green.
+  var hits = 5;
 
   var originalWidth = aWindow.innerWidth;
   var originalHeight = aWindow.innerHeight;
 
   var originalX = aWindow.screenX;
   var originalY = aWindow.screenY;
 
   var oWidth = aWindow.outerWidth;
   var oHeight = aWindow.outerHeight;
 
-  function sizeChangeCondition() {
-    return aWindow.innerWidth != originalWidth || aWindow.innerHeight != originalHeight;
-  }
-
-  function sizeChangeTest() {
-    is(aWindow.innerWidth, originalWidth, "Window width shouldn't have changed");
-    is(aWindow.innerHeight, originalHeight, "Window height shouldn't have changed");
+  function changeCondition() {
+    return aWindow.innerWidth != originalWidth ||
+           aWindow.innerHeight != originalHeight ||
+           aWindow.screenX != originalX || aWindow.screenY != originalY ||
+           aWindow.outerWidth != oWidth || aWindow.outerHeight != oHeight;
   }
 
-  function posChangeCondition() {
-    return aWindow.screenX != originalX || aWindow.screenY != originalY;
-  }
-
-  function posChangeTest() {
+  function changeTest() {
+    is(aWindow.innerWidth, originalWidth, "Window width shouldn't have changed");
+    is(aWindow.innerHeight, originalHeight, "Window height shouldn't have changed");
     is(aWindow.screenX, originalX, "Window x position shouldn't have changed");
     is(aWindow.screenY, originalY, "Window y position shouldn't have changed");
-  }
-
-  function outerChangeCondition() {
-    return aWindow.outerWidth != oWidth || aWindow.outerHeight != oHeight;
-  }
-
-  function outerChangeTest() {
     is(aWindow.outerWidth, oWidth, "Window outerWidth shouldn't have changed");
     is(aWindow.outerHeight, oHeight, "Window outerHeight shouldn't have changed");
   }
 
   /**
-   * Size checks.
+   * Size changes.
    */
-  aWindow.innerWidth = getNewWidth(aWindow);
-  aWindow.innerHeight = getNewHeight(aWindow);
-  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
-    aWindow.resizeTo(getNewWidth(aWindow), getNewHeight(aWindow));
+  var newWidth = getNewWidth(aWindow);
+  var newHeight = getNewHeight(aWindow);
+
+  aWindow.innerWidth = newWidth;
+  aWindow.innerHeight = newHeight;
 
-  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
-    aWindow.resizeBy(getNewWidth(aWindow) - aWindow.innerWidth,
-                     getNewHeight(aWindow) - aWindow.innerHeight);
+  aWindow.resizeTo(newWidth, newHeight);
 
-  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
-    aWindow.sizeToContent();
+  aWindow.resizeBy(newWidth - aWindow.innerWidth,
+                   newHeight - aWindow.innerHeight);
 
-  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
+  aWindow.sizeToContent();
+
   /**
    * Position checks.
    */
-    aWindow.screenX = getNewX(aWindow);
-    aWindow.screenY = getNewY(aWindow);
-  hitEventLoop(posChangeCondition, posChangeTest, hits, function () {
-    aWindow.moveTo(getNewX(aWindow), getNewY(aWindow));
-  hitEventLoop(posChangeCondition, posChangeTest, hits, function () {
-    aWindow.moveBy(getNewX(aWindow) - aWindow.screenX,
-                   getNewY(aWindow) - aWindow.screenY);
-  hitEventLoop(posChangeCondition, posChangeTest, hits, function () {
+  var newX = getNewX(aWindow);
+  var newY = getNewY(aWindow);
+
+  aWindow.screenX = newX;
+  aWindow.screenY = newY;
+
+  aWindow.moveTo(newX, newY);
+
+  aWindow.moveBy(newX - aWindow.screenX,
+                 newY - aWindow.screenY);
+
   /**
    * Outer width/height checks.
    */
-    aWindow.outerWidth *= 2;
-    aWindow.outerHeight *= 2;
+  aWindow.outerWidth *= 2;
+  aWindow.outerHeight *= 2;
 
-  hitEventLoop(outerChangeCondition, outerChangeTest, hits, aNext);
-  });
-  });
-  });
-  });
-  });
-  });
-  });
+  // We did a lot of changes. Now, we are going to wait and see if something
+  // happens.
+  // NOTE: if this happens to fail, you will have to check manually which
+  // operation has been accepted.
+  hitEventLoop(changeCondition, changeTest, hits, aNext);
 }
 
 function checkChangeIsEnabled(aWindow, aNext)
 {
   // Something should happen. We are not going to go to the next test until
   // it does.
   var hits = -1;
 
--- a/dom/tests/mochitest/general/test_consoleAPI.html
+++ b/dom/tests/mochitest/general/test_consoleAPI.html
@@ -22,16 +22,17 @@ function doTest() {
 
   var expectedProps = {
     "log": "function",
     "info": "function",
     "warn": "function",
     "error": "function",
     "debug": "function",
     "trace": "function",
+    "dir": "function",
     "__noSuchMethod__": "function"
   };
 
   var foundProps = 0;
   for (var prop in console) {
     foundProps++;
     is(typeof(console[prop]), expectedProps[prop], "expect console prop " + prop + " exists");
   }
--- a/dom/tests/mochitest/localstorage/Makefile.in
+++ b/dom/tests/mochitest/localstorage/Makefile.in
@@ -44,16 +44,17 @@ relativesrcdir	= dom/tests/mochitest/loc
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES	= \
     frameBug624047.html \
     frameChromeSlave.html \
+    frameKeySync.html \
     frameMasterEqual.html \
     frameMasterNotEqual.html \
     frameSlaveEqual.html \
     frameSlaveNotEqual.html \
     frameReplace.html \
     frameQuota.html \
     frameQuotaSessionOnly.html \
     frameOrder.html \
@@ -62,16 +63,17 @@ include $(topsrcdir)/config/rules.mk
     interOriginTest2.js \
     pbSwitch.js \
     test_brokenUTF-16.html \
     test_bug624047.html \
     test_cookieBlock.html \
     test_cookieSession-phase1.html \
     test_cookieSession-phase2.html \
     test_embededNulls.html \
+    test_keySync.html \
     test_localStorageBase.html \
     test_localStorageBasePrivateBrowsing.html \
     test_localStorageBaseSessionOnly.html \
     test_localStorageCookieSettings.html \
     test_localStorageEnablePref.html \
     test_localStorageOriginsEquals.html \
     test_localStorageOriginsDiff.html \
     test_localStorageOriginsPortDiffs.html \
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/localstorage/frameKeySync.html
@@ -0,0 +1,51 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>frame for localStorage test</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = parseInt(location.search.substring(1));
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 1:
+      localStorage.clear();
+      break;
+
+    case 2:
+      localStorage.setItem("a", "1");
+      is(localStorage["a"], "1", "Value a=1 set");
+      break;
+
+    case 3:
+      try {
+        is(localStorage.key(0), "a", "Key 'a' present in 'key' array")
+      }
+      catch (exc) {
+        ok(false, "Shouldn't throw when accessing key(0) " + exc);
+      }
+      is(localStorage["a"], "1", "Value a=1 set");
+      break;
+
+    default:
+      return finishTest();
+  }
+
+  // Increase by two to as odd number are executed in a window separate from
+  // where even step are.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/localstorage/test_keySync.html
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>localStorage equal origins</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from the same origin, clears in one frame,
+  sets a single key in another and then checks key(0) in the first frame.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.org:80";
+  slaveFrameOrigin = "http://example.org:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameKeySync.html?1";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameKeySync.html?2";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -333,24 +333,17 @@ public:
 
     JS_TriggerAllOperationCallbacks(mRuntime);
 
     IterateData data;
     if (!CollectCompartmentStatsForRuntime(mRuntime, &data)) {
       return NS_ERROR_FAILURE;
     }
 
-    for (CompartmentStats *stats = data.compartmentStatsVector.begin();
-         stats != data.compartmentStatsVector.end();
-         ++stats)
-    {
-      ReportCompartmentStats(*stats, mPathPrefix, aCallback, aClosure);
-    }
-
-    ReportJSStackSizeForRuntime(mRuntime, mPathPrefix, aCallback, aClosure);
+    ReportJSRuntimeStats(data, mPathPrefix, aCallback, aClosure);
 
     return NS_OK;
   }
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter)
 
 class WorkerThreadRunnable : public nsRunnable
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -6766,41 +6766,48 @@ nsHTMLEditRules::ReturnInListItem(nsISel
   nsresult res = NS_OK;
   
   nsCOMPtr<nsIDOMNode> listitem;
   
   // sanity check
   NS_PRECONDITION(PR_TRUE == nsHTMLEditUtils::IsListItem(aListItem),
                   "expected a list item and didn't get one");
   
+  // get the listitem parent and the active editing host.
+  nsIContent* rootContent = mHTMLEditor->GetActiveEditingHost();
+  nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootContent);
+  nsCOMPtr<nsIDOMNode> list;
+  PRInt32 itemOffset;
+  res = nsEditor::GetNodeLocation(aListItem, address_of(list), &itemOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
   // if we are in an empty listitem, then we want to pop up out of the list
+  // but only if prefs says it's ok and if the parent isn't the active editing host.
   PRBool isEmpty;
   res = IsEmptyBlock(aListItem, &isEmpty, PR_TRUE, PR_FALSE);
   NS_ENSURE_SUCCESS(res, res);
-  if (isEmpty && mReturnInEmptyLIKillsList)   // but only if prefs says it's ok
-  {
-    nsCOMPtr<nsIDOMNode> list, listparent;
-    PRInt32 offset, itemOffset;
-    res = nsEditor::GetNodeLocation(aListItem, address_of(list), &itemOffset);
-    NS_ENSURE_SUCCESS(res, res);
-    res = nsEditor::GetNodeLocation(list, address_of(listparent), &offset);
-    NS_ENSURE_SUCCESS(res, res);
-    
+  if (isEmpty && (rootNode != list) && mReturnInEmptyLIKillsList)
+  {
     // are we the last list item in the list?
     PRBool bIsLast;
     res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast);
     NS_ENSURE_SUCCESS(res, res);
     if (!bIsLast)
     {
       // we need to split the list!
       nsCOMPtr<nsIDOMNode> tempNode;
       res = mHTMLEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
       NS_ENSURE_SUCCESS(res, res);
     }
+
     // are we in a sublist?
+    nsCOMPtr<nsIDOMNode> listparent;
+    PRInt32 offset;
+    res = nsEditor::GetNodeLocation(list, address_of(listparent), &offset);
+    NS_ENSURE_SUCCESS(res, res);
     if (nsHTMLEditUtils::IsList(listparent))  //in a sublist
     {
       // if so, move this list item out of this list and into the grandparent list
       res = mHTMLEditor->MoveNode(aListItem,listparent,offset+1);
       NS_ENSURE_SUCCESS(res, res);
       res = aSelection->Collapse(aListItem,0);
     }
     else
--- a/editor/libeditor/html/tests/Makefile.in
+++ b/editor/libeditor/html/tests/Makefile.in
@@ -50,17 +50,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug332636.html^headers^ \
 		test_bug366682.html \
 		test_bug372345.html \
 		test_bug410986.html \
 		test_bug414526.html \
 		test_bug417418.html \
 		test_bug432225.html \
 		test_bug439808.html \
- 		test_bug449243.html \
+		test_bug449243.html \
 		test_bug455992.html \
 		test_bug456244.html \
 		test_bug460740.html \
 		test_bug478725.html \
 		test_bug480972.html \
 		test_bug484181.html \
 		test_bug487524.html \
 		test_bug520189.html \
@@ -76,16 +76,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug607584.html \
 		test_bug611182.html \
 		test_bug612447.html \
 		test_bug620906.html \
 		test_bug622371.html \
 		test_bug629845.html \
 		test_bug640321.html \
 		test_bug668599.html \
+		test_bug674861.html \
 		test_CF_HTML_clipboard.html \
 		test_contenteditable_focus.html \
 		test_htmleditor_keyevent_handling.html \
 		test_select_all_without_body.html \
 		file_select_all_without_body.html \
 		test_root_element_replacement.html \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/tests/test_bug674861.html
@@ -0,0 +1,120 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=674861
+-->
+<head>
+  <title>Test for Bug 674861</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674861">Mozilla Bug 674861</a>
+<p id="display"></p>
+<div id="content">
+  <h2> Bullet List </h2>
+  <ul contenteditable>
+    <li> item 1 </li>
+    <li> item 2 </li>
+    <li> item 3 </li>
+  </ul>
+
+  <h2> Ordered List </h2>
+  <ol contenteditable>
+    <li> item 1 </li>
+    <li> item 2 </li>
+    <li> item 3 </li>
+  </ol>
+
+  <h2> Definition List </h2>
+  <dl contenteditable>
+    <dt> term 1 </dt>
+    <dd> definition 1 </dd>
+    <dt> term 2 </dt>
+    <dd> definition 2 </dd>
+    <dt> term 3 </dt>
+    <dd> definition 3 </dd>
+  </dl>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 674861 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+const CARET_BEGIN  = 0;
+const CARET_MIDDLE = 1;
+const CARET_END    = 2;
+
+function try2split(element, caretPos) {
+  // compute the requested position
+  var len = element.textContent.length;
+  var pos = -1;
+  switch (caretPos) {
+    case CARET_BEGIN:
+      pos = 0;
+      break;
+    case CARET_MIDDLE:
+      pos = Math.floor(len/2);
+      break;
+    case CARET_END:
+      pos = len;
+      break;
+  }
+
+  // put the caret on the requested position
+  var sel = window.getSelection();
+  for (var i = 0; i < sel.rangeCount; i++) {
+    var range = sel.getRangeAt(i);
+    sel.removeRange(range);
+  }
+  range = document.createRange();
+  range.setStart(element.firstChild, pos);
+  range.setEnd(element.firstChild, pos);
+  sel.addRange(range);
+  
+  // simulates two [Return] keypresses
+  synthesizeKey("VK_RETURN", {});
+  synthesizeKey("VK_RETURN", {});
+}
+
+function runTests() {
+  const ul = document.querySelector("#content ul");
+  const ol = document.querySelector("#content ol");
+  const dl = document.querySelector("#content dl");
+
+  // bullet list
+  ul.focus();
+  try2split(ul.querySelector("li"), CARET_END);
+  is(document.querySelectorAll("#content ul").length, 1,
+    "The <ul> list should not be splittable.");
+  is(ul.querySelectorAll("li").length, 5,
+    "Two new <li> elements should have been created.");
+
+  // ordered list
+  ol.focus();
+  try2split(ol.querySelector("li"), CARET_END);
+  is(document.querySelectorAll("#content ol").length, 1,
+    "The <ol> list should not be splittable.");
+  is(ol.querySelectorAll("li").length, 5,
+    "Two new <li> elements should have been created.");
+
+  // definition list
+  dl.focus();
+  try2split(dl.querySelector("dd"), CARET_END);
+  is(document.querySelectorAll("#content dl").length, 1,
+    "The <dl> list should not be splittable.");
+  is(dl.querySelectorAll("dt").length, 5,
+    "Two new <dt> elements should have been created.");
+
+  // done
+  SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -1162,17 +1162,17 @@ public class GeckoAppShell
             appearance.recycle();
         }
 
         return result;
     }
 
     public static void putChildInBackground() {
         try {
-            File cgroupFile = new File("/proc" + android.os.Process.myPid() + "/cgroup");
+            File cgroupFile = new File("/proc/" + android.os.Process.myPid() + "/cgroup");
             BufferedReader br = new BufferedReader(new FileReader(cgroupFile));
             String[] cpuLine = br.readLine().split("/");
             br.close();
             final String backgroundGroup = cpuLine.length == 2 ? cpuLine[1] : "";
             GeckoProcessesVisitor visitor = new GeckoProcessesVisitor() {
                 public boolean callback(int pid) {
                     if (pid != android.os.Process.myPid()) {
                         try {
deleted file mode 100644
--- a/extensions/spellcheck/hunspell/tests/suggestiontest/Makefile.orig
+++ /dev/null
@@ -1,11 +0,0 @@
-all:
-	./prepare
-	./test
-
-single:
-	./prepare2
-	./test
-
-clean:
-	rm *.[1-5] result.*
-
new file mode 100644
--- /dev/null
+++ b/gfx/2d/BasePoint3D.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@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 ***** */
+
+#ifndef MOZILLA_BASEPOINT3D_H_
+#define MOZILLA_BASEPOINT3D_H_
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub>
+struct BasePoint3D {
+  T x, y, z;
+
+  // Constructors
+  BasePoint3D() : x(0), y(0), z(0) {}
+  BasePoint3D(T aX, T aY, T aZ) : x(aX), y(aY), z(aZ) {}
+
+  void MoveTo(T aX, T aY, T aZ) { x = aX; y = aY; z = aZ; }
+  void MoveBy(T aDx, T aDy, T aDz) { x += aDx; y += aDy; z += aDz; }
+
+  // Note that '=' isn't defined so we'll get the
+  // compiler generated default assignment operator
+
+  bool operator==(const Sub& aPoint) const {
+    return x == aPoint.x && y == aPoint.y && z == aPoint.z;
+  }
+  bool operator!=(const Sub& aPoint) const {
+    return x != aPoint.x || y != aPoint.y || z != aPoint.z;
+  }
+
+  Sub operator+(const Sub& aPoint) const {
+    return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z);
+  }
+  Sub operator-(const Sub& aPoint) const {
+    return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z);
+  }
+  Sub& operator+=(const Sub& aPoint) {
+    x += aPoint.x;
+    y += aPoint.y;
+    z += aPoint.z;
+    return *static_cast<Sub*>(this);
+  }
+  Sub& operator-=(const Sub& aPoint) {
+    x -= aPoint.x;
+    y -= aPoint.y;
+    z -= aPoint.z;
+    return *static_cast<Sub*>(this);
+  }
+
+  Sub operator*(T aScale) const {
+    return Sub(x * aScale, y * aScale, z * aScale);
+  }
+  Sub operator/(T aScale) const {
+    return Sub(x / aScale, y / aScale, z / aScale);
+  }
+
+  Sub& operator*=(T aScale) {
+    x *= aScale;
+    y *= aScale;
+    z *= aScale;
+    return *static_cast<Sub*>(this);
+  }
+
+  Sub& operator/=(T aScale) {
+      x /= aScale;
+      y /= aScale;
+      z /= aScale;
+      return *static_cast<Sub*>(this);
+  }
+
+  Sub operator-() const {
+    return Sub(-x, -y, -z);
+  }
+
+  Sub CrossProduct(const Sub& aPoint) const {
+      return Sub(y * aPoint.z - aPoint.y * z,
+                 z * aPoint.x - aPoint.z * x,
+                 x * aPoint.y - aPoint.x * y);
+  }
+
+  T DotProduct(const Sub& aPoint) const {
+      return x * aPoint.x + y * aPoint.y + z * aPoint.z;
+  }
+
+  T Length() const {
+      return sqrt(x*x + y*y + z*z);
+  }
+
+  // Invalid for points with distance from origin of 0.
+  void Normalize() {
+      *this /= Length();
+  }
+};
+
+}
+}
+
+#endif /* MOZILLA_BASEPOINT3D_H_ */
--- a/gfx/2d/Makefile.in
+++ b/gfx/2d/Makefile.in
@@ -46,16 +46,17 @@ MODULE		= gfx2d
 LIBRARY_NAME	= gfx2d
 LIBXUL_LIBRARY	= 1
 EXPORT_LIBRARY	= 1
 
 EXPORTS_NAMESPACES = mozilla/gfx
 EXPORTS_mozilla/gfx	= \
         2D.h \
         BasePoint.h \
+	BasePoint3D.h \
         BaseMargin.h \
         BaseRect.h \
         BaseSize.h \
         Point.h \
         Matrix.h \
         Rect.h \
         Types.h \
 	$(NULL)
--- a/gfx/angle/README.mozilla
+++ b/gfx/angle/README.mozilla
@@ -5,16 +5,18 @@ Current revision: r653
 == Applied local patches ==
 
 In this order:
   angle-nspr-misc.patch - don't bother with ANGLE_OS detection with NSPR
   angle-renaming.patch - rename debug.h to compilerdebug.h to avoid conflict in our makefiles
   angle-intrinsic-msvc2005.patch - work around a MSVC 2005 compile error
   angle-amap-arev-fix.patch - plain bug fix, this is ANGLE r699
   angle-r702.patch - this is ANGLE r702
+  angle-limit-identifiers-to-250-chars.patch - see bug 675625
+  angle-r712.patch - this is ANGLE r712
 
 In addition to these patches, the Makefile.in files are ours, they're not present in upsteam ANGLE.
 
 == How to update this ANGLE copy ==
 
 1. Unapply patches
 2. Apply diff with new ANGLE version
 3. Reapply patches.
new file mode 100644
--- /dev/null
+++ b/gfx/angle/angle-limit-identifiers-to-250-chars.patch
@@ -0,0 +1,24 @@
+diff --git a/gfx/angle/src/compiler/preprocessor/scanner.h b/gfx/angle/src/compiler/preprocessor/scanner.h
+--- a/gfx/angle/src/compiler/preprocessor/scanner.h
++++ b/gfx/angle/src/compiler/preprocessor/scanner.h
+@@ -44,17 +44,19 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILI
+ //
+ // scanner.h
+ //
+ 
+ #if !defined(__SCANNER_H)
+ #define __SCANNER_H 1
+ 
+ // These lengths do not include the NULL terminator.
+-#define MAX_SYMBOL_NAME_LEN 256
++// see bug 675625: NVIDIA driver crash with lengths >= 253
++// this is only an interim fix, the real fix is name mapping, see ANGLE bug 144 / r619
++#define MAX_SYMBOL_NAME_LEN 250
+ #define MAX_STRING_LEN 511
+ 
+ #include "compiler/preprocessor/parser.h"
+ 
+ // Not really atom table stuff but needed first...
+ 
+ typedef struct SourceLoc_Rec {
+     unsigned short file, line;
new file mode 100644
--- /dev/null
+++ b/gfx/angle/angle-r712.patch
@@ -0,0 +1,42 @@
+# HG changeset patch
+# Parent 88a5c8710f5cffd568bc21226118cb567850ce28
+diff --git a/gfx/angle/src/libGLESv2/VertexDataManager.cpp b/gfx/angle/src/libGLESv2/VertexDataManager.cpp
+--- a/gfx/angle/src/libGLESv2/VertexDataManager.cpp
++++ b/gfx/angle/src/libGLESv2/VertexDataManager.cpp
+@@ -134,34 +134,34 @@ GLenum VertexDataManager::prepareVertexD
+                 if (staticBuffer->size() == 0)
+                 {
+                     int totalCount = buffer->size() / attribs[i].stride();
+                     staticBuffer->addRequiredSpace(spaceRequired(attribs[i], totalCount));
+                 }
+                 else if (staticBuffer->lookupAttribute(attribs[i]) == -1)
+                 {
+                     // This static buffer doesn't have matching attributes, so fall back to using the streaming buffer
+-                    buffer->invalidateStaticData();
+-
+                     // Add the space of all previous attributes belonging to the invalidated static buffer to the streaming buffer
+                     for (int previous = 0; previous < i; previous++)
+                     {
+                         if (translated[previous].active && attribs[previous].mArrayEnabled)
+                         {
+                             Buffer *previousBuffer = attribs[previous].mBoundBuffer.get();
+                             StaticVertexBuffer *previousStaticBuffer = previousBuffer ? previousBuffer->getStaticVertexBuffer() : NULL;
+ 
+                             if (staticBuffer == previousStaticBuffer)
+                             {
+                                 mStreamingBuffer->addRequiredSpace(spaceRequired(attribs[previous], count));
+                             }
+                         }
+                     }
+ 
+                     mStreamingBuffer->addRequiredSpace(spaceRequired(attribs[i], count));
++
++                    buffer->invalidateStaticData();
+                 }    
+             }
+             else
+             {
+                 mStreamingBuffer->addRequiredSpace(spaceRequired(attribs[i], count));
+             }
+         }
+     }
--- a/gfx/angle/src/compiler/preprocessor/scanner.h
+++ b/gfx/angle/src/compiler/preprocessor/scanner.h
@@ -44,17 +44,19 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILI
 //
 // scanner.h
 //
 
 #if !defined(__SCANNER_H)
 #define __SCANNER_H 1
 
 // These lengths do not include the NULL terminator.
-#define MAX_SYMBOL_NAME_LEN 256
+// see bug 675625: NVIDIA driver crash with lengths >= 253
+// this is only an interim fix, the real fix is name mapping, see ANGLE bug 144 / r619
+#define MAX_SYMBOL_NAME_LEN 250
 #define MAX_STRING_LEN 511
 
 #include "compiler/preprocessor/parser.h"
 
 // Not really atom table stuff but needed first...
 
 typedef struct SourceLoc_Rec {
     unsigned short file, line;
--- a/gfx/angle/src/libGLESv2/VertexDataManager.cpp
+++ b/gfx/angle/src/libGLESv2/VertexDataManager.cpp
@@ -134,34 +134,34 @@ GLenum VertexDataManager::prepareVertexD
                 if (staticBuffer->size() == 0)
                 {
                     int totalCount = buffer->size() / attribs[i].stride();
                     staticBuffer->addRequiredSpace(spaceRequired(attribs[i], totalCount));
                 }
                 else if (staticBuffer->lookupAttribute(attribs[i]) == -1)
                 {
                     // This static buffer doesn't have matching attributes, so fall back to using the streaming buffer
-                    buffer->invalidateStaticData();
-
                     // Add the space of all previous attributes belonging to the invalidated static buffer to the streaming buffer
                     for (int previous = 0; previous < i; previous++)
                     {
                         if (translated[previous].active && attribs[previous].mArrayEnabled)
                         {
                             Buffer *previousBuffer = attribs[previous].mBoundBuffer.get();
                             StaticVertexBuffer *previousStaticBuffer = previousBuffer ? previousBuffer->getStaticVertexBuffer() : NULL;
 
                             if (staticBuffer == previousStaticBuffer)
                             {
                                 mStreamingBuffer->addRequiredSpace(spaceRequired(attribs[previous], count));
                             }
                         }
                     }
 
                     mStreamingBuffer->addRequiredSpace(spaceRequired(attribs[i], count));
+
+                    buffer->invalidateStaticData();
                 }    
             }
             else
             {
                 mStreamingBuffer->addRequiredSpace(spaceRequired(attribs[i], count));
             }
         }
     }
--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
+++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
@@ -1101,16 +1101,17 @@ typedef struct {
     void *image_extra;
 } quartz_source_image_t;
 
 static void
 DataProviderReleaseCallback (void *info, const void *data, size_t size)
 {
     quartz_source_image_t *source_img = info;
     _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
+    cairo_surface_destroy (source_img->surface);
     free (source_img);
 }
 
 static cairo_status_t
 _cairo_surface_to_cgimage (cairo_surface_t *source,
 			   CGImageRef *image_out)
 {
     cairo_status_t status;
@@ -1140,20 +1141,21 @@ static cairo_status_t
             }
 	}
     }
 
     source_img = malloc (sizeof (quartz_source_image_t));
     if (source_img == NULL)
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    source_img->surface = source;
+    source_img->surface = cairo_surface_reference(source);
 
     status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
     if (status) {
+	cairo_surface_destroy (source_img->surface);
 	free (source_img);
 	return status;
     }
 
     if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
 	*image_out = NULL;
 	DataProviderReleaseCallback (source_img,
 				     source_img->image_out->data,
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -416,17 +416,19 @@ ContainerLayer::DefaultComputeEffectiveT
 
   PRBool useIntermediateSurface;
   float opacity = GetEffectiveOpacity();
   if (opacity != 1.0f && HasMultipleChildren()) {
     useIntermediateSurface = PR_TRUE;
   } else {
     useIntermediateSurface = PR_FALSE;
     gfxMatrix contTransform;
-    if (!mEffectiveTransform.Is2D(&contTransform) ||
+    if (!mEffectiveTransform.Is2D(&contTransform)) {
+     useIntermediateSurface = PR_TRUE;   
+    } else if (
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
         !contTransform.PreservesAxisAlignedRectangles()) {
 #else
         contTransform.HasNonIntegerTranslation()) {
 #endif
       for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
         const nsIntRect *clipRect = child->GetEffectiveClipRect();
         /* We can't (easily) forward our transform to children with a non-empty clip
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -485,17 +485,17 @@ LayerManagerD3D10::SetViewport(const nsI
 
   gfx3DMatrix projection;
   /*
    * Matrix to transform to viewport space ( <-1.0, 1.0> topleft,
    * <1.0, -1.0> bottomright)
    */
   projection._11 = 2.0f / aViewport.width;
   projection._22 = -2.0f / aViewport.height;
-  projection._33 = 1.0f;
+  projection._33 = 0.0f;
   projection._41 = -1.0f;
   projection._42 = 1.0f;
   projection._44 = 1.0f;
 
   HRESULT hr = mEffect->GetVariableByName("mProjection")->
     SetRawValue(&projection._11, 0, 64);
 
   if (FAILED(hr)) {
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -366,16 +366,17 @@ LayerManagerD3D9::SetupPipeline()
 
   gfx3DMatrix viewMatrix;
   /*
    * Matrix to transform to viewport space ( <-1.0, 1.0> topleft,
    * <1.0, -1.0> bottomright)
    */
   viewMatrix._11 = 2.0f / rect.width;
   viewMatrix._22 = -2.0f / rect.height;
+  viewMatrix._33 = 0.0f;
   viewMatrix._41 = -1.0f;
   viewMatrix._42 = 1.0f;
 
   HRESULT hr = device()->SetVertexShaderConstantF(CBmProjection,
                                                   &viewMatrix._11, 4);
 
   if (FAILED(hr)) {
     NS_WARNING("Failed to set projection shader constant!");
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -940,17 +940,20 @@ LayerManagerOGL::SetupPipeline(int aWidt
   viewMatrix.Translate(-gfxPoint(1.0, -1.0));
   viewMatrix.Scale(2.0f / float(aWidth), 2.0f / float(aHeight));
   viewMatrix.Scale(1.0f, -1.0f);
 
   if (aTransformPolicy == ApplyWorldTransform) {
     viewMatrix = mWorldMatrix * viewMatrix;
   }
 
-  SetLayerProgramProjectionMatrix(gfx3DMatrix::From2D(viewMatrix));
+  gfx3DMatrix matrix3d = gfx3DMatrix::From2D(viewMatrix);
+  matrix3d._33 = 0.0f;
+
+  SetLayerProgramProjectionMatrix(matrix3d);
 }
 
 void
 LayerManagerOGL::SetupBackBuffer(int aWidth, int aHeight)
 {
   if (mGLContext->IsDoubleBuffered()) {
     mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
     return;
--- a/gfx/tests/reftest/reftest.list
+++ b/gfx/tests/reftest/reftest.list
@@ -1,3 +1,3 @@
 # 468496-1 will also detect bugs in video drivers.
-fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) fails-if(Android) == 468496-1.html 468496-1-ref.html # bug 486761, 660740
+fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == 468496-1.html 468496-1-ref.html # bug 486761, 660740
 == 611498-1.html 611498-ref.html
--- a/gfx/thebes/GLContextProviderEGL.cpp
+++ b/gfx/thebes/GLContextProviderEGL.cpp
@@ -1166,33 +1166,31 @@ public:
                 mShaderType = RGBXLayerProgramType;
 #else
                 mUpdateFormat = gfxASurface::ImageFormatARGB32;
                 mShaderType = RGBALayerProgramType;
 #endif
             } else {
                 mShaderType = RGBALayerProgramType;
             }
+            Resize(aSize);
         } else {
             // Convert RGB24 to either ARGB32 on mobile.  We can't
             // generate GL_RGB data, so we'll always have an alpha byte
             // for RGB24.  No easy way to upload that to GL.
             // 
             // Note that if we start using RGB565 here, we'll need to
             // watch for a) setting mIsRGBFormat to TRUE; and b) getting
             // the stride right.
             if (mUpdateFormat == gfxASurface::ImageFormatRGB24) {
                 mUpdateFormat = gfxASurface::ImageFormatARGB32;
             }
             // We currently always use BGRA type textures
             mShaderType = BGRALayerProgramType;
         }
-
-	// We resize here so we should have a valid buffer after creation
-        Resize(aSize);
     }
 
     virtual ~TextureImageEGL()
     {
         GLContext *ctx = mGLContext;
         if (ctx->IsDestroyed() || !NS_IsMainThread()) {
             ctx = ctx->GetSharedContext();
         }
@@ -1377,22 +1375,32 @@ public:
         }
 
         mTextureState = Valid;
         return true;
     }
 
     virtual void BindTexture(GLenum aTextureUnit)
     {
+        // Ensure the texture is allocated before it is used.
+        if (mTextureState == Created) {
+            Resize(mSize);
+        }
+
         mGLContext->fActiveTexture(aTextureUnit);
         mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
         mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
     }
 
-    virtual GLuint GetTextureID() {
+    virtual GLuint GetTextureID() 
+    {
+        // Ensure the texture is allocated before it is used.
+        if (mTextureState == Created) {
+            Resize(mSize);
+        }
         return mTexture;
     };
 
     virtual PRBool InUpdate() const { return !!mUpdateSurface; }
 
     virtual void Resize(const nsIntSize& aSize)
     {
         NS_ASSERTION(!mUpdateSurface, "Resize() while in update?");
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -27,16 +27,17 @@ EXPORTS	= \
 	gfxFontUtils.h \
 	gfxFontTest.h \
 	gfxImageSurface.h \
 	gfxMatrix.h \
 	gfxPath.h \
 	gfxPattern.h \
 	gfxPlatform.h \
 	gfxPoint.h \
+	gfxPoint3D.h \
 	gfxRect.h \
 	gfxSkipChars.h \
 	gfxTeeSurface.h \
 	gfxTypes.h \
 	gfxTextRunCache.h \
 	gfxTextRunWordCache.h \
 	gfxUnicodeProperties.h \
 	gfxUtils.h \
--- a/gfx/thebes/gfx3DMatrix.cpp
+++ b/gfx/thebes/gfx3DMatrix.cpp
@@ -143,16 +143,27 @@ gfx3DMatrix::Translation(float aX, float
 
   matrix._41 = aX;
   matrix._42 = aY;
   matrix._43 = aZ;
   return matrix;
 }
 
 gfx3DMatrix
+gfx3DMatrix::Translation(const gfxPoint3D& aPoint)
+{
+  gfx3DMatrix matrix;
+
+  matrix._41 = aPoint.x;
+  matrix._42 = aPoint.y;
+  matrix._43 = aPoint.z;
+  return matrix;
+}
+
+gfx3DMatrix
 gfx3DMatrix::Scale(float aFactor)
 {
   gfx3DMatrix matrix;
 
   matrix._11 = matrix._22 = matrix._33 = aFactor;
   return matrix;
 }
 
@@ -264,23 +275,34 @@ gfx3DMatrix::Invert()
 
   *this /= det;
   return *this;
 }
 
 gfxPoint
 gfx3DMatrix::Transform(const gfxPoint& point) const
 {
-  gfxFloat x = point.x * _11 + point.y * _21 + _41;
-  gfxFloat y = point.x * _12 + point.y * _22 + _42;
-  gfxFloat w = point.x * _14 + point.y * _24 + _44;
+  gfxPoint3D vec3d(point.x, point.y, 0);
+  vec3d = Transform3D(vec3d);
+  return gfxPoint(vec3d.x, vec3d.y);
+}
+
+gfxPoint3D
+gfx3DMatrix::Transform3D(const gfxPoint3D& point) const
+{
+  gfxFloat x = point.x * _11 + point.y * _21 + point.z * _31 + _41;
+  gfxFloat y = point.x * _12 + point.y * _22 + point.z * _32 + _42;
+  gfxFloat z = point.x * _13 + point.y * _23 + point.z * _33 + _43;
+  gfxFloat w = point.x * _14 + point.y * _24 + point.z * _34 + _44;
+
   x /= w;
   y /= w;
-  /* Assume z is 0! */
-  return gfxPoint(x, y);
+  z /= w;
+
+  return gfxPoint3D(x, y, z);
 }
 
 gfxRect
 gfx3DMatrix::TransformBounds(const gfxRect& rect) const
 {
   gfxPoint points[4];
 
   points[0] = Transform(rect.TopLeft());
@@ -320,8 +342,78 @@ gfx3DMatrix::Is2D(gfxMatrix* aMatrix) co
     aMatrix->xy = _21;
     aMatrix->yy = _22;
     aMatrix->x0 = _41;
     aMatrix->y0 = _42;
   }
   return PR_TRUE;
 }
 
+gfxPoint gfx3DMatrix::ProjectPoint(const gfxPoint& aPoint) const
+{
+  // Define a ray of the form P + Ut where t is a real number
+  // w is assumed to always be 1 when transforming 3d points with our
+  // 4x4 matrix.
+  // p is our click point, q is another point on the same ray.
+  // 
+  // Note: since the transformation is a general projective transformation and is not
+  // necessarily affine, we can't just take a unit vector u, back-transform it, and use
+  // it as unit vector on the back-transformed ray. Instead, we really must take two points
+  // on the ray and back-transform them.
+  gfxPoint3D p(aPoint.x, aPoint.y, 0);
+  gfxPoint3D q(aPoint.x, aPoint.y, 1);
+
+  // Back transform the vectors (using w = 1) and normalize
+  // back into 3d vectors by dividing by the w component.
+  gfxPoint3D pback = Transform3D(p);
+  gfxPoint3D qback = Transform3D(q);
+  gfxPoint3D uback = qback - pback;
+
+  // Find the point where the back transformed line intersects z=0
+  // and find t.
+  
+  float t = -pback.z / uback.z;
+
+  gfxPoint result(pback.x + t*uback.x, pback.y + t*uback.y);
+
+  return result;
+}
+
+gfxRect gfx3DMatrix::ProjectRectBounds(const gfxRect& aRect) const
+{
+  gfxPoint points[4];
+
+  points[0] = ProjectPoint(aRect.TopLeft());
+  points[1] = ProjectPoint(gfxPoint(aRect.X() + aRect.Width(), aRect.Y()));
+  points[2] = ProjectPoint(gfxPoint(aRect.X(), aRect.Y() + aRect.Height()));
+  points[3] = ProjectPoint(gfxPoint(aRect.X() + aRect.Width(),
+                                    aRect.Y() + aRect.Height()));
+
+  gfxFloat min_x, max_x;
+  gfxFloat min_y, max_y;
+
+  min_x = max_x = points[0].x;
+  min_y = max_y = points[0].y;
+
+  for (int i=1; i<4; i++) {
+    min_x = min(points[i].x, min_x);
+    max_x = max(points[i].x, max_x);
+    min_y = min(points[i].y, min_y);
+    max_y = max(points[i].y, max_y);
+  }
+
+  return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
+}
+
+gfxPoint3D gfx3DMatrix::GetNormalVector() const
+{
+    // Define a plane in transformed space as the transformations
+    // of 3 points on the z=0 screen plane.
+    gfxPoint3D a = Transform3D(gfxPoint3D(0, 0, 0));
+    gfxPoint3D b = Transform3D(gfxPoint3D(0, 1, 0));
+    gfxPoint3D c = Transform3D(gfxPoint3D(1, 0, 0));
+
+    // Convert to two vectors on the surface of the plane.
+    gfxPoint3D ab = b - a;
+    gfxPoint3D ac = c - a;
+
+    return ac.CrossProduct(ab);
+}
--- a/gfx/thebes/gfx3DMatrix.h
+++ b/gfx/thebes/gfx3DMatrix.h
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_3DMATRIX_H
 #define GFX_3DMATRIX_H
 
 #include <gfxTypes.h>
+#include <gfxPoint3D.h>
 #include <gfxMatrix.h>
 
 /**
  * This class represents a 3D transformation. The matrix is laid
  * out as follows:
  *
  * _11 _12 _13 _14
  * _21 _22 _23 _24
@@ -106,35 +107,58 @@ public:
    */
   gfxPoint Transform(const gfxPoint& point) const;
 
   /**
    * Transforms a rectangle according to this matrix
    */
   gfxRect TransformBounds(const gfxRect& rect) const;
 
+  /** 
+   * Transforms a 3D vector according to this matrix.
+   */
+  gfxPoint3D Transform3D(const gfxPoint3D& point) const;
+
+  gfxPoint ProjectPoint(const gfxPoint& aPoint) const;
+  gfxRect ProjectRectBounds(const gfxRect& aRect) const;
+
+
   /**
    * Inverts this matrix, if possible. Otherwise, the matrix is left
    * unchanged.
    */
   gfx3DMatrix& Invert();
 
+  inline gfx3DMatrix Inverse() const
+  {
+    gfx3DMatrix temp = *this;
+    temp.Invert();
+    return temp;
+  }
+
+  /**
+   * Returns a unit vector that is perpendicular to the plane formed
+   * by transform the screen plane (z=0) by this matrix.
+   */
+  gfxPoint3D GetNormalVector() const;
+
   /**
    * Check if matrix is singular (no inverse exists).
    */
   PRBool IsSingular() const;
 
   /**
    * Create a translation matrix.
    *
    * \param aX Translation on X-axis.
    * \param aY Translation on Y-axis.
    * \param aZ Translation on Z-axis.
    */
   static gfx3DMatrix Translation(float aX, float aY, float aZ);
+  static gfx3DMatrix Translation(const gfxPoint3D& aPoint);
 
   /**
    * Create a scale matrix. Scales uniformly along all axes.
    *
    * \param aScale Scale factor
    */
   static gfx3DMatrix Scale(float aFactor);
 
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -153,21 +153,16 @@ gfxSurfaceDrawable::Draw(gfxContext* aCo
           filter = gfxPattern::FILTER_FAST;
         }
         nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
         gfxMatrix deviceSpaceToImageSpace =
             DeviceToImageTransform(aContext, aTransform);
         PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace,
                                         currentTarget, filter);
     }
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-    if (!mozilla::supports_neon()) {
-        pattern->SetFilter(gfxPattern::FILTER_FAST);
-    }
-#endif
     pattern->SetMatrix(gfxMatrix(aTransform).Multiply(mTransform));
     aContext->NewPath();
     aContext->SetPattern(pattern);
     aContext->Rectangle(aFillRect);
     aContext->Fill();
     return PR_TRUE;
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxPoint3D.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** 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 Oracle Corporation code.
+ *
+ * The Initial Developer of the Original Code is Oracle Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@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 ***** */
+
+#ifndef GFX_POINT3D_H
+#define GFX_POINT3D_H
+
+#include "mozilla/gfx/BasePoint3D.h"
+#include "gfxTypes.h"
+
+struct THEBES_API gfxPoint3D : public mozilla::gfx::BasePoint3D<gfxFloat, gfxPoint3D> {
+    typedef mozilla::gfx::BasePoint3D<gfxFloat, gfxPoint3D> Super;
+
+    gfxPoint3D() : Super() {}
+    gfxPoint3D(gfxFloat aX, gfxFloat aY, gfxFloat aZ) : Super(aX, aY, aZ) {}
+};
+
+#endif /* GFX_POINT3D_H */ 
--- a/intl/locale/src/charsetalias.properties
+++ b/intl/locale/src/charsetalias.properties
@@ -108,16 +108,17 @@ utf-7=UTF-7
 shift_jis=Shift_JIS
 big5=Big5
 euc-jp=EUC-JP
 euc-kr=EUC-KR
 gb2312=GB2312
 gb18030=gb18030
 viscii=VISCII
 koi8-r=KOI8-R
+koi8_r=KOI8-R
 koi8-u=KOI8-U
 tis-620=TIS-620
 t.61-8bit=T.61-8bit
 hz-gb-2312=HZ-GB-2312
 big5-hkscs=Big5-HKSCS
 gbk=gbk
 cns11643=x-euc-tw
 #
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -2568,17 +2568,17 @@ jsdService::AsyncOn (jsdIActivationCallb
     if (NS_FAILED(rv)) return rv;
 
     mActivationCallback = activationCallback;
     
     return xpc->SetDebugModeWhenPossible(PR_TRUE);
 }
 
 NS_IMETHODIMP
-jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, JSBool mode) {
+jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, PRBool mode) {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
   /* XPConnect now does this work itself, so this IDL entry point is no longer used. */
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 jsdService::DeactivateDebugger ()
 {
--- a/js/src/jit-test/tests/basic/testBug663789-2.js
+++ b/js/src/jit-test/tests/basic/testBug663789-2.js
@@ -1,11 +1,6 @@
 // |jit-test| debug;mjit
 
-/*
- * NOTE: this evalInFrame is explicitly exposing an optimization artifact that
- * InvokeSessionGuard leaves the callee frame on the stack between invocations.
- * If this ever gets fixed or InvokeSessionGuard gets removed, this test will
- * fail and it can be removed.
- */
-o = { toString:function() { return evalInFrame(1, "arguments; x") } }
+o = { toString:function() { return evalInFrame(1, "x") } }
+var x = 'C';
 var s = "aaaaaaaaaa".replace(/a/g, function() { var x = 'B'; return o });
-assertEq(s, "BBBBBBBBBB");
+assertEq(s, "CCCCCCCCCC");
--- a/js/src/jsapi-tests/testIndexToString.cpp
+++ b/js/src/jsapi-tests/testIndexToString.cpp
@@ -1,21 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sw=4 et tw=99:
  */
 
 #include "tests.h"
 
+#include "jscntxt.h"
+#include "jscompartment.h"
 #include "jsnum.h"
 
-#include "vm/String.h"
+#include "vm/String-inl.h"
 
 BEGIN_TEST(testIndexToString)
 {
-    struct TestPair {
+    const struct TestPair {
         uint32 num;
         const char *expected;
     } tests[] = {
         { 0, "0" },
         { 1, "1" },
         { 2, "2" },
         { 9, "9" },
         { 10, "10" },
@@ -38,19 +40,23 @@ BEGIN_TEST(testIndexToString)
         { 2147483647, "2147483647" },
         { 2147483648, "2147483648" },
         { 2147483649, "2147483649" },
         { 4294967294, "4294967294" },
         { 4294967295, "4294967295" },
     };
 
     for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) {
-        JSString *str = js::IndexToString(cx, tests[i].num);
+        uint32 u = tests[i].num;
+        JSString *str = js::IndexToString(cx, u);
         CHECK(str);
 
+        if (!JSAtom::hasUintStatic(u))
+            CHECK(cx->compartment->dtoaCache.lookup(10, u) == str);
+
         JSBool match = JS_FALSE;
         CHECK(JS_StringEqualsAscii(cx, str, tests[i].expected, &match));
         CHECK(match);
     }
 
     return true;
 }
 END_TEST(testIndexToString)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2683,16 +2683,18 @@ JS_GetGCParameter(JSRuntime *rt, JSGCPar
       case JSGC_STACKPOOL_LIFESPAN:
         return rt->gcEmptyArenaPoolLifespan;
       case JSGC_BYTES:
         return rt->gcBytes;
       case JSGC_MODE:
         return uint32(rt->gcMode);
       case JSGC_UNUSED_CHUNKS:
         return uint32(rt->gcChunksWaitingToExpire);
+      case JSGC_TOTAL_CHUNKS:
+        return uint32(rt->gcUserChunkSet.count() + rt->gcSystemChunkSet.count());
       default:
         JS_ASSERT(key == JSGC_NUMBER);
         return rt->gcNumber;
     }
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value)
@@ -4468,17 +4470,17 @@ CompileUCScriptForPrincipalsCommon(JSCon
 
     uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT;
     JSScript *script = Compiler::compileScript(cx, obj, NULL, principals, tcflags,
                                                chars, length, filename, lineno, version);
     JSObject *scriptObj = NULL;
     if (script) {
         scriptObj = js_NewScriptObject(cx, script);
         if (!scriptObj)
-            js_DestroyScript(cx, script);
+            js_DestroyScript(cx, script, 3);
     }
     LAST_FRAME_CHECKS(cx, scriptObj);
     return scriptObj;
 }
 
 extern JS_PUBLIC_API(JSObject *)
 JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
                                        JSPrincipals *principals,
@@ -4655,17 +4657,17 @@ CompileFileHelper(JSContext *cx, JSObjec
     script = Compiler::compileScript(cx, obj, NULL, principals, tcflags, buf, len, filename, 1,
                                      cx->findVersion());
     cx->free_(buf);
     if (!script)
         return NULL;
 
     JSObject *scriptObj = js_NewScriptObject(cx, script);
     if (!scriptObj)
-        js_DestroyScript(cx, script);
+        js_DestroyScript(cx, script, 4);
 
     return scriptObj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
@@ -4962,17 +4964,17 @@ EvaluateUCScriptForPrincipalsCommon(JSCo
     if (!script) {
         LAST_FRAME_CHECKS(cx, script);
         return false;
     }
     JS_ASSERT(script->getVersion() == compileVersion);
 
     bool ok = ExternalExecute(cx, script, *obj, Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
-    js_DestroyScript(cx, script);
+    js_DestroyScript(cx, script, 5);
     return ok;
 
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
                                         JSPrincipals *principals,
                                         const jschar *chars, uintN length,
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1666,17 +1666,17 @@ JS_CallTracer(JSTracer *trc, void *thing
  * for the following call to JS_CallTracer.
  *
  * When printer is null, arg must be const char * or char * C string naming
  * the reference and index must be either (size_t)-1 indicating that the name
  * alone describes the reference or it must be an index into some array vector
  * that stores the reference.
  *
  * When printer callback is not null, the arg and index arguments are
- * available to the callback as debugPrinterArg and debugPrintIndex fields
+ * available to the callback as debugPrintArg and debugPrintIndex fields
  * of JSTracer.
  *
  * The storage for name or callback's arguments needs to live only until
  * the following call to JS_CallTracer returns.
  */
 #ifdef DEBUG
 # define JS_SET_TRACING_DETAILS(trc, printer, arg, index)                     \
     JS_BEGIN_MACRO                                                            \
@@ -1826,17 +1826,20 @@ typedef enum JSGCParamKey {
 
     /* Max size of the code cache in bytes. */
     JSGC_MAX_CODE_CACHE_BYTES = 5,
 
     /* Select GC mode. */
     JSGC_MODE = 6,
 
     /* Number of GC chunks waiting to expire. */
-    JSGC_UNUSED_CHUNKS = 7
+    JSGC_UNUSED_CHUNKS = 7,
+
+    /* Total number of allocated GC chunks. */
+    JSGC_TOTAL_CHUNKS = 8
 } JSGCParamKey;
 
 typedef enum JSGCMode {
     /* Perform only global GCs. */
     JSGC_MODE_GLOBAL = 0,
 
     /* Perform per-compartment GCs until too much garbage has accumulated. */
     JSGC_MODE_COMPARTMENT = 1
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -118,16 +118,17 @@ const char *const js_common_atom_names[]
 #define JS_PROTO(name,code,init) js_##name##_str,
 #include "jsproto.tbl"
 #undef JS_PROTO
 
     js_anonymous_str,           /* anonymousAtom                */
     js_apply_str,               /* applyAtom                    */
     js_arguments_str,           /* argumentsAtom                */
     js_arity_str,               /* arityAtom                    */
+    js_BYTES_PER_ELEMENT_str,   /* BYTES_PER_ELEMENTAtom        */
     js_call_str,                /* callAtom                     */
     js_callee_str,              /* calleeAtom                   */
     js_caller_str,              /* callerAtom                   */
     js_class_prototype_str,     /* classPrototypeAtom           */
     js_constructor_str,         /* constructorAtom              */
     js_each_str,                /* eachAtom                     */
     js_eval_str,                /* evalAtom                     */
     js_fileName_str,            /* fileNameAtom                 */
@@ -238,16 +239,17 @@ JSAtomState::checkStaticInvariants()
 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) < 256);
 
 const size_t js_common_atom_count = JS_ARRAY_LENGTH(js_common_atom_names);
 
 const char js_anonymous_str[]       = "anonymous";
 const char js_apply_str[]           = "apply";
 const char js_arguments_str[]       = "arguments";
 const char js_arity_str[]           = "arity";
+const char js_BYTES_PER_ELEMENT_str[] = "BYTES_PER_ELEMENT";
 const char js_call_str[]            = "call";
 const char js_callee_str[]          = "callee";
 const char js_caller_str[]          = "caller";
 const char js_class_prototype_str[] = "prototype";
 const char js_constructor_str[]     = "constructor";
 const char js_each_str[]            = "each";
 const char js_eval_str[]            = "eval";
 const char js_fileName_str[]        = "fileName";
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -241,16 +241,17 @@ struct JSAtomState
     /* Standard class constructor or prototype names. */
     JSAtom              *classAtoms[JSProto_LIMIT];
 
     /* Various built-in or commonly-used atoms, pinned on first context. */
     JSAtom              *anonymousAtom;
     JSAtom              *applyAtom;
     JSAtom              *argumentsAtom;
     JSAtom              *arityAtom;
+    JSAtom              *BYTES_PER_ELEMENTAtom;
     JSAtom              *callAtom;
     JSAtom              *calleeAtom;
     JSAtom              *callerAtom;
     JSAtom              *classPrototypeAtom;
     JSAtom              *constructorAtom;
     JSAtom              *eachAtom;
     JSAtom              *evalAtom;
     JSAtom              *fileNameAtom;
@@ -408,16 +409,17 @@ extern const size_t      js_common_atom_
 #define JS_PROTO(name,code,init) extern const char js_##name##_str[];
 #include "jsproto.tbl"
 #undef JS_PROTO
 
 extern const char   js_anonymous_str[];
 extern const char   js_apply_str[];
 extern const char   js_arguments_str[];
 extern const char   js_arity_str[];
+extern const char   js_BYTES_PER_ELEMENT_str[];
 extern const char   js_call_str[];
 extern const char   js_callee_str[];
 extern const char   js_caller_str[];
 extern const char   js_class_prototype_str[];
 extern const char   js_close_str[];
 extern const char   js_constructor_str[];
 extern const char   js_count_str[];
 extern const char   js_etago_str[];
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1120,17 +1120,17 @@ JS_EvaluateUCInStackFrame(JSContext *cx,
                                                filename, lineno, cx->findVersion(),
                                                NULL, UpvarCookie::UPVAR_LEVEL_LIMIT);
 
     if (!script)
         return false;
 
     bool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, Valueify(rval));
 
-    js_DestroyScript(cx, script);
+    js_DestroyScript(cx, script, 6);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
                         const char *bytes, uintN length,
                         const char *filename, uintN lineno,
                         jsval *rval)
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -3088,17 +3088,17 @@ AllocateSwitchConstant(JSContext *cx)
     Value *pv;
     JS_ARENA_ALLOCATE_TYPE(pv, Value, &cx->tempPool);
     return pv;
 }
 
 /*
  * Sometimes, let-slots are pushed to the JS stack before we logically enter
  * the let scope. For example,
- *     for (let x = EXPR;;) BODY
+ *     let (x = EXPR) BODY
  * compiles to roughly {enterblock; EXPR; setlocal x; BODY; leaveblock} even
  * though EXPR is evaluated in the enclosing scope; it does not see x.
  *
  * In those cases we use TempPopScope around the code to emit EXPR. It
  * temporarily removes the let-scope from the JSCodeGenerator's scope stack and
  * emits extra bytecode to ensure that js::GetBlockChain also finds the correct
  * scope at run time.
  */
@@ -4179,20 +4179,16 @@ EmitVariables(JSContext *cx, JSCodeGener
      *
      * The same goes for let declarations in the head of any kind of for loop.
      * Unlike a let declaration 'let x = i' within a block, where x is hoisted
      * to the start of the block, a 'for (let x = i...) ...' loop evaluates i
      * in the containing scope, and puts x in the loop body's scope.
      */
     let = (pn->pn_op == JSOP_NOP);
     forInVar = (pn->pn_xflags & PNX_FORINVAR) != 0;
-#if JS_HAS_BLOCK_SCOPE
-    bool popScope = (inLetHead || (let && (cg->flags & TCF_IN_FOR_INIT)));
-    JS_ASSERT_IF(popScope, let);
-#endif
 
     off = noteIndex = -1;
     for (pn2 = pn->pn_head; ; pn2 = next) {
         first = pn2 == pn->pn_head;
         next = pn2->pn_next;
 
         if (pn2->pn_type != TOK_NAME) {
 #if JS_HAS_DESTRUCTURING
@@ -4315,33 +4311,21 @@ EmitVariables(JSContext *cx, JSCodeGener
                     JS_ASSERT(!let);
                     EMIT_INDEX_OP(JSOP_BINDGNAME, atomIndex);
                 }
                 if (pn->pn_op == JSOP_DEFCONST &&
                     !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, pn3)) {
                     return JS_FALSE;
                 }
 
-#if JS_HAS_BLOCK_SCOPE
-                /* Evaluate expr in the outer lexical scope if requested. */
-                TempPopScope tps;
-                if (popScope && !tps.popBlock(cx, cg))
-                    return JS_FALSE;
-#endif
-
                 oldflags = cg->flags;
                 cg->flags &= ~TCF_IN_FOR_INIT;
                 if (!js_EmitTree(cx, cg, pn3))
                     return JS_FALSE;
                 cg->flags |= oldflags & TCF_IN_FOR_INIT;
-
-#if JS_HAS_BLOCK_SCOPE
-                if (popScope && !tps.repushBlock(cx, cg))
-                    return JS_FALSE;
-#endif
             }
         }
 
         /*
          * The parser rewrites 'for (var x = i in o)' to hoist 'var x = i' --
          * likewise 'for (let x = i in o)' becomes 'i; for (let x in o)' using
          * a TOK_SEQ node to make the two statements appear as one. Therefore
          * if this declaration is part of a for-in loop head, we do not need to
@@ -6676,41 +6660,66 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
         if (!EmitLeaveBlock(cx, cg, op, objbox))
             return JS_FALSE;
 
         ok = js_PopStatementCG(cx, cg);
         break;
       }
 
 #if JS_HAS_BLOCK_SCOPE
-      case TOK_LET:
-        /* Let statements have their variable declarations on the left. */
+      case TOK_LET: {
+        /*
+         * pn represents one of these syntactic constructs:
+         *   let-expression:                        (let (x = y) EXPR)
+         *   let-statement:                         let (x = y) { ... }
+         *   let-declaration in statement context:  let x = y;
+         *   let-declaration in for-loop head:      for (let ...) ...
+         *
+         * Let-expressions and let-statements are represented as binary nodes
+         * with their variable declarations on the left and the body on the
+         * right.
+         */
         if (pn->pn_arity == PN_BINARY) {
             pn2 = pn->pn_right;
             pn = pn->pn_left;
         } else {
             pn2 = NULL;
         }
 
-        /* Non-null pn2 means that pn is the variable list from a let head. */
+        /*
+         * Non-null pn2 means that pn is the variable list from a let head.
+         *
+         * Use TempPopScope to evaluate the expressions in the enclosing scope.
+         * This also causes the initializing assignments to be emitted in the
+         * enclosing scope, but the assignment opcodes emitted here
+         * (essentially just setlocal, though destructuring assignment uses
+         * other additional opcodes) do not care about the block chain.
+         */
         JS_ASSERT(pn->pn_arity == PN_LIST);
+        TempPopScope tps;
+        bool popScope = pn2 || (cg->flags & TCF_IN_FOR_INIT);
+        if (popScope && !tps.popBlock(cx, cg))
+            return JS_FALSE;
         if (!EmitVariables(cx, cg, pn, pn2 != NULL, &noteIndex))
             return JS_FALSE;
+        tmp = CG_OFFSET(cg);
+        if (popScope && !tps.repushBlock(cx, cg))
+            return JS_FALSE;
 
         /* Thus non-null pn2 is the body of the let block or expression. */
-        tmp = CG_OFFSET(cg);
         if (pn2 && !js_EmitTree(cx, cg, pn2))
             return JS_FALSE;
 
         if (noteIndex >= 0 &&
             !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
                                  CG_OFFSET(cg) - tmp)) {
             return JS_FALSE;
         }
         break;
+      }
 #endif /* JS_HAS_BLOCK_SCOPE */
 
 #if JS_HAS_GENERATORS
       case TOK_ARRAYPUSH: {
         jsint slot;
 
         /*
          * The array object's stack index is in cg->arrayCompDepth. See below
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1581,18 +1581,24 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
     if (xdr->mode == JSXDR_DECODE) {
         fun->nargs = flagsword >> 16;
         JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
         fun->flags = uint16(flagsword);
         fun->u.i.skipmin = uint16(firstword >> 2);
         fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
     }
 
-    if (!js_XDRScript(xdr, &fun->u.i.script))
+    /*
+     * Don't directly store into fun->u.i.script because we want this to happen
+     * at the same time as we set the script's owner.
+     */
+    JSScript *script = fun->u.i.script;
+    if (!js_XDRScript(xdr, &script))
         return false;
+    fun->u.i.script = script;
 
     if (xdr->mode == JSXDR_DECODE) {
         *objp = FUN_OBJECT(fun);
         fun->u.i.script->setOwnerObject(fun);
 #ifdef CHECK_SCRIPT_OWNER
         fun->script()->owner = NULL;
 #endif
         JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
@@ -2469,19 +2475,20 @@ js_CloneFunctionObject(JSContext *cx, JS
         clone->setPrivate(cfun);
         if (cfun->isInterpreted()) {
             JSScript *script = cfun->script();
             JS_ASSERT(script);
             JS_ASSERT(script->compartment == fun->compartment());
             JS_ASSERT(script->compartment != cx->compartment);
             JS_OPT_ASSERT(script->ownerObject == fun);
 
-            cfun->u.i.script = js_CloneScript(cx, script);
-            if (!cfun->script())
+            JSScript *cscript = js_CloneScript(cx, script);
+            if (!cscript)
                 return NULL;
+            cfun->u.i.script = cscript;
             cfun->script()->setOwnerObject(cfun);
 #ifdef CHECK_SCRIPT_OWNER
             cfun->script()->owner = NULL;
 #endif
             js_CallNewScriptHook(cx, cfun->script(), cfun);
         }
     }
     return clone;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -704,16 +704,19 @@ InvokeSessionGuard::start(JSContext *cx,
     ContextStack &stack = cx->stack;
     if (!stack.pushInvokeArgs(cx, argc, &args_))
         return false;
 
     /* Callees may clobber 'this' or 'callee'. */
     savedCallee_ = args_.calleev() = calleev;
     savedThis_ = args_.thisv() = thisv;
 
+    /* If anyone (through jsdbgapi) finds this frame, make it safe. */
+    MakeRangeGCSafe(args_.argv(), args_.argc());
+
     do {
         /* Hoist dynamic checks from scripted Invoke. */
         if (!calleev.isObject())
             break;
         JSObject &callee = calleev.toObject();
         if (callee.getClass() != &js_FunctionClass)
             break;
         JSFunction *fun = callee.getFunctionPrivate();
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1264,47 +1264,48 @@ JSFixedString *
 NumberToString(JSContext *cx, jsdouble d)
 {
     if (JSString *str = js_NumberToStringWithBase(cx, d, 10))
         return &str->asFixed();
     return NULL;
 }
 
 JSFixedString *
-IndexToString(JSContext *cx, uint32 u)
+IndexToString(JSContext *cx, uint32 index)
 {
-    if (JSAtom::hasUintStatic(u))
-        return &JSAtom::uintStatic(u);
+    if (JSAtom::hasUintStatic(index))
+        return &JSAtom::uintStatic(index);
 
     JSCompartment *c = cx->compartment;
-    if (JSFixedString *str = c->dtoaCache.lookup(10, u))
+    if (JSFixedString *str = c->dtoaCache.lookup(10, index))
         return str;
 
     JSShortString *str = js_NewGCShortString(cx);
     if (!str)
         return NULL;
 
     /* +1, since MAX_LENGTH does not count the null char. */
     JS_STATIC_ASSERT(JSShortString::MAX_LENGTH + 1 >= sizeof("4294967295"));
 
     jschar *storage = str->inlineStorageBeforeInit();
     size_t length = JSShortString::MAX_SHORT_LENGTH;
     const RangedPtr<jschar> end(storage + length, storage, length + 1);
     RangedPtr<jschar> cp = end;
     *cp = '\0';
 
+    uint32 u = index;
     do {
-        jsuint newu = u / 10, digit = u % 10;
+        uint32 newu = u / 10, digit = u % 10;
         *--cp = '0' + digit;
         u = newu;
     } while (u > 0);
 
     str->initAtOffsetInBuffer(cp.get(), end - cp);
 
-    c->dtoaCache.cache(10, u, str);
+    c->dtoaCache.cache(10, index, str);
     return str;
 }
 
 bool JS_FASTCALL
 NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
 {
     /* Convert to C-string. */
     ToCStringBuf cbuf;
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -2522,16 +2522,25 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * NB: todo at this point indexes space in ss->sprinter
                      * that is liable to be overwritten.  The code below knows
                      * exactly how long rval lives, or else copies it down via
                      * SprintCString.
                      */
                     rval = OFF2STR(&ss->sprinter, todo);
                     todo = -2;
                     pc2 = pc + oplen;
+
+                    /* Skip a block chain annotation if one appears here. */
+                    if (*pc2 == JSOP_NOP) {
+                        if (pc2[JSOP_NOP_LENGTH] == JSOP_NULLBLOCKCHAIN)
+                            pc2 += JSOP_NOP_LENGTH + JSOP_NULLBLOCKCHAIN_LENGTH;
+                        else if (pc2[JSOP_NOP_LENGTH] == JSOP_BLOCKCHAIN)
+                            pc2 += JSOP_NOP_LENGTH + JSOP_BLOCKCHAIN_LENGTH;
+                    }
+
                     if (*pc2 == JSOP_NOP) {
                         sn = js_GetSrcNote(jp->script, pc2);
                         if (sn) {
                             if (SN_TYPE(sn) == SRC_FOR) {
                                 op = JSOP_NOP;
                                 pc = pc2;
                                 goto do_forloop;
                             }
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1137,17 +1137,17 @@ Compiler::compileScript(JSContext *cx, J
     return script;
 
   too_many_slots:
     parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
     /* Fall through. */
 
   late_error:
     if (script) {
-        js_DestroyScript(cx, script);
+        js_DestroyScript(cx, script, 7);
         script = NULL;
     }
     goto out;
 }
 
 bool
 Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
 {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -749,17 +749,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
             goto error;
     }
 
     xdr->script = oldscript;
     return JS_TRUE;
 
   error:
     if (xdr->mode == JSXDR_DECODE) {
-        js_DestroyScript(cx, script);
+        js_DestroyScript(cx, script, 1);
         *scriptp = NULL;
     }
     xdr->script = oldscript;
     return JS_FALSE;
 }
 
 #endif /* JS_HAS_XDR */
 
@@ -1265,17 +1265,17 @@ JSScript::NewScriptFromCG(JSContext *cx,
     }
 
     /* Tell the debugger about this compiled script. */
     js_CallNewScriptHook(cx, script, fun);
 
     return script;
 
 bad:
-    js_DestroyScript(cx, script);
+    js_DestroyScript(cx, script, 2);
     return NULL;
 }
 
 size_t
 JSScript::totalSize()
 {
     return code +
            length * sizeof(jsbytecode) +
@@ -1340,17 +1340,17 @@ CheckCompartmentScripts(JSCompartment *c
     {
         CheckScript(script, prev);
     }
 }
 
 } /* namespace js */
 
 static void
-DestroyScript(JSContext *cx, JSScript *script, JSObject *owner)
+DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
 {
     CheckScript(script, NULL);
     CheckScriptOwner(script, owner);
 
     if (script->principals)
         JSPRINCIPALS_DROP(cx, script->principals);
 
     GSNCache *gsnCache = GetGSNCache(cx);
@@ -1403,41 +1403,42 @@ DestroyScript(JSContext *cx, JSScript *s
 #endif
     JS_REMOVE_LINK(&script->links);
 
     script->pcCounters.destroy(cx);
 
     if (script->sourceMap)
         cx->free_(script->sourceMap);
 
-    memset(script, JS_FREE_PATTERN, script->totalSize());
+    memset(script, 0xdb, script->totalSize());
+    *(uint32 *)script = caller;
     cx->free_(script);
 }
 
 void
-js_DestroyScript(JSContext *cx, JSScript *script)
+js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller)
 {
     JS_ASSERT(!cx->runtime->gcRunning);
     js_CallDestroyScriptHook(cx, script);
-    DestroyScript(cx, script, JS_NEW_SCRIPT);
+    DestroyScript(cx, script, JS_NEW_SCRIPT, caller);
 }
 
 void
 js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner)
 {
     JS_ASSERT(cx->runtime->gcRunning);
     js_CallDestroyScriptHook(cx, script);
-    DestroyScript(cx, script, owner);
+    DestroyScript(cx, script, owner, 100);
 }
 
 void
 js_DestroyCachedScript(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(cx->runtime->gcRunning);
-    DestroyScript(cx, script, JS_CACHED_SCRIPT);
+    DestroyScript(cx, script, JS_CACHED_SCRIPT, 101);
 }
 
 void
 js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner)
 {
     CheckScript(script, NULL);
     if (owner)
         CheckScriptOwner(script, owner);
@@ -1466,16 +1467,20 @@ js_TraceScript(JSTracer *trc, JSScript *
 
     if (script->u.object)
         MarkObject(trc, *script->u.object, "object");
 
     if (IS_GC_MARKING_TRACER(trc) && script->filename)
         js_MarkScriptFilename(script->filename);
 
     script->bindings.trace(trc);
+
+#ifdef JS_METHODJIT
+    mjit::TraceScript(trc, script);
+#endif
 }
 
 JSObject *
 js_NewScriptObject(JSContext *cx, JSScript *script)
 {
     AutoScriptRooter root(cx, script);
 
     JS_ASSERT(!script->u.object);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -728,17 +728,17 @@ js_CallNewScriptHook(JSContext *cx, JSSc
 extern void
 js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
 
 /*
  * The function must be used only outside the GC for a script that was run
  * only on the current thread.
  */
 extern void
-js_DestroyScript(JSContext *cx, JSScript *script);
+js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller);
 
 extern void
 js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner);
 
 /*
  * Script objects may be cached and reused, in which case their JSD-visible
  * lifetimes may be shorter than their actual lifetimes. Destroy one such
  * script for real as part of a GC pass. From JSD's point of view, the script
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -65,29 +65,36 @@ JS_PUBLIC_DATA(JSUint32) OOM_counter = 0
  * Checks the assumption that JS_FUNC_TO_DATA_PTR and JS_DATA_TO_FUNC_PTR
  * macros uses to implement casts between function and data pointers.
  */
 JS_STATIC_ASSERT(sizeof(void *) == sizeof(void (*)()));
 
 static JS_NEVER_INLINE void
 CrashInJS()
 {
+    /*
+     * We write 123 here so that the machine code for this function is
+     * unique. Otherwise the linker, trying to be smart, might use the
+     * same code for CrashInJS and for some other function. That
+     * messes up the signature in minidumps.
+     */
+
 #if defined(WIN32)
     /*
      * We used to call DebugBreak() on Windows, but amazingly, it causes
      * the MSVS 2010 debugger not to be able to recover a call stack.
      */
-    *((int *) NULL) = 0;
+    *((int *) NULL) = 123;
     exit(3);
 #elif defined(__APPLE__)
     /*
      * On Mac OS X, Breakpad ignores signals. Only real Mach exceptions are
      * trapped.
      */
-    *((int *) NULL) = 0;  /* To continue from here in GDB: "return" then "continue". */
+    *((int *) NULL) = 123;  /* To continue from here in GDB: "return" then "continue". */
     raise(SIGABRT);  /* In case above statement gets nixed by the optimizer. */
 #else
     raise(SIGABRT);  /* To continue from here in GDB: "signal 0". */
 #endif
 }
 
 JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln)
 {
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -715,17 +715,17 @@ JS_XDRScriptObject(JSXDRState *xdr, JSOb
 
     if (!js_XDRScript(xdr, &script))
         return false;
 
     if (xdr->mode == JSXDR_DECODE) {
         js_CallNewScriptHook(xdr->cx, script, NULL);
         *scriptObjp = js_NewScriptObject(xdr->cx, script);
         if (!*scriptObjp) {
-            js_DestroyScript(xdr->cx, script);
+            js_DestroyScript(xdr->cx, script, 8);
             return false;
         }
     }
 
     return true;
 }
 
 #define CLASS_REGISTRY_MIN      8
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -217,17 +217,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 92)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 93)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -65,16 +65,18 @@
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsxml.h"
 #include "jsstaticcheck.h"
 #include "jsvector.h"
 
+#include "vm/GlobalObject.h"
+
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsstrinlines.h"
 
 #include "vm/Stack-inl.h"
 
 #ifdef DEBUG
 #include <string.h>     /* for #ifdef DEBUG memset calls */
@@ -202,17 +204,16 @@ namespace_equality(JSContext *cx, JSObje
     *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass)
           ? JS_FALSE
           : EqualStrings(obj->getNameURI(), obj2->getNameURI());
     return JS_TRUE;
 }
 
 JS_FRIEND_DATA(Class) js_NamespaceClass = {
     "Namespace",
-    JSCLASS_CONSTRUCT_PROTOTYPE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_CLASS_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
@@ -319,17 +320,16 @@ qname_equality(JSContext *cx, JSObject *
     *bp = (!obj2 || obj2->getClass() != &js_QNameClass)
           ? JS_FALSE
           : qname_identity(qn, obj2);
     return JS_TRUE;
 }
 
 JS_FRIEND_DATA(Class) js_QNameClass = {
     "QName",
-    JSCLASS_CONSTRUCT_PROTOTYPE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
@@ -354,32 +354,30 @@ JS_FRIEND_DATA(Class) js_QNameClass = {
 /*
  * Classes for the ECMA-357-internal types AttributeName and AnyName, which
  * are like QName, except that they have no property getters.  They share the
  * qname_toString method, and therefore are exposed as constructable objects
  * in this implementation.
  */
 JS_FRIEND_DATA(Class) js_AttributeNameClass = {
     js_AttributeName_str,
-    JSCLASS_CONSTRUCT_PROTOTYPE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     ConvertStub,
     FinalizeStub
 };
 
 JS_FRIEND_DATA(Class) js_AnyNameClass = {
     js_AnyName_str,
-    JSCLASS_CONSTRUCT_PROTOTYPE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
@@ -7103,80 +7101,134 @@ js_GetXMLObject(JSContext *cx, JSXML *xm
         return NULL;
     xml->object = obj;
     return obj;
 }
 
 JSObject *
 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
 {
-    return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2,
-                        NULL, namespace_methods, NULL, NULL);
+    JS_ASSERT(obj->isNative());
+
+    GlobalObject *global = obj->asGlobal();
+
+    JSObject *namespaceProto = global->createBlankPrototype(cx, &js_NamespaceClass);
+    if (!namespaceProto)
+        return NULL;
+    JSFlatString *empty = cx->runtime->emptyString;
+    namespaceProto->setNamePrefix(empty);
+    namespaceProto->setNameURI(empty);
+    namespaceProto->syncSpecialEquality();
+
+    const uintN NAMESPACE_CTOR_LENGTH = 2;
+    JSFunction *ctor = global->createConstructor(cx, Namespace, &js_NamespaceClass,
+                                                 CLASS_ATOM(cx, Namespace),
+                                                 NAMESPACE_CTOR_LENGTH);
+    if (!ctor)
+        return NULL;
+
+    if (!LinkConstructorAndPrototype(cx, ctor, namespaceProto))
+        return NULL;
+
+    if (!DefinePropertiesAndBrand(cx, namespaceProto, namespace_props, namespace_methods))
+        return NULL;
+
+    if (!DefineConstructorAndPrototype(cx, global, JSProto_Namespace, ctor, namespaceProto))
+        return NULL;
+
+    return namespaceProto;
 }
 
 JSObject *
 js_InitQNameClass(JSContext *cx, JSObject *obj)
 {
-    return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2,
-                        NULL, qname_methods, NULL, NULL);
+    JS_ASSERT(obj->isNative());
+
+    GlobalObject *global = obj->asGlobal();
+
+    JSObject *qnameProto = global->createBlankPrototype(cx, &js_QNameClass);
+    if (!qnameProto)
+        return NULL;
+    JSFlatString *empty = cx->runtime->emptyString;
+    if (!InitXMLQName(cx, qnameProto, empty, empty, empty))
+        return NULL;
+    qnameProto->syncSpecialEquality();
+
+    const uintN QNAME_CTOR_LENGTH = 2;
+    JSFunction *ctor = global->createConstructor(cx, QName, &js_QNameClass,
+                                                 CLASS_ATOM(cx, QName), QNAME_CTOR_LENGTH);
+    if (!ctor)
+        return NULL;
+
+    if (!LinkConstructorAndPrototype(cx, ctor, qnameProto))
+        return NULL;
+
+    if (!DefinePropertiesAndBrand(cx, qnameProto, NULL, qname_methods))
+        return NULL;
+
+    if (!DefineConstructorAndPrototype(cx, global, JSProto_QName, ctor, qnameProto))
+        return NULL;
+
+    return qnameProto;
 }
 
 JSObject *
 js_InitXMLClass(JSContext *cx, JSObject *obj)
 {
-    /* Define the isXMLName function. */
-    if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
+    JS_ASSERT(obj->isNative());
+
+    GlobalObject *global = obj->asGlobal();
+
+    JSObject *xmlProto = global->createBlankPrototype(cx, &js_XMLClass);
+    if (!xmlProto)
         return NULL;
-
-    /* Define the XML class constructor and prototype. */
-    JSObject *proto = js_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
-                                   NULL, xml_methods, xml_static_props, xml_static_methods);
-    if (!proto)
-        return NULL;
-
     JSXML *xml = js_NewXML(cx, JSXML_CLASS_TEXT);
     if (!xml)
         return NULL;
-    proto->setPrivate(xml);
-    xml->object = proto;
-
-    /*
-     * Prepare to set default settings on the XML constructor we just made.
-     * NB: We can't use JS_GetConstructor, because it calls
-     * JSObject::getProperty, which is xml_getProperty, which creates a new
-     * XMLList every time!  We must instead call js_LookupProperty directly.
-     */
-    JSObject *pobj;
-    JSProperty *prop;
-    if (!js_LookupProperty(cx, proto,
-                           ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
-                           &pobj, &prop)) {
+    xmlProto->setPrivate(xml);
+    xml->object = xmlProto;
+
+    const uintN XML_CTOR_LENGTH = 1;
+    JSFunction *ctor = global->createConstructor(cx, XML, &js_XMLClass, CLASS_ATOM(cx, XML),
+                                                 XML_CTOR_LENGTH);
+    if (!ctor)
+        return NULL;
+
+    if (!LinkConstructorAndPrototype(cx, ctor, xmlProto))
+        return NULL;
+
+    if (!DefinePropertiesAndBrand(cx, xmlProto, NULL, xml_methods) ||
+        !DefinePropertiesAndBrand(cx, ctor, xml_static_props, xml_static_methods))
+    {
+        return NULL;
+    }
+
+    if (!SetDefaultXMLSettings(cx, ctor))
         return NULL;
-    }
-    JS_ASSERT(prop);
-    Shape *shape = (Shape *) prop;
-    jsval cval = Jsvalify(pobj->nativeGetSlot(shape->slot));
-    JS_ASSERT(VALUE_IS_FUNCTION(cx, cval));
-
-    /* Set default settings. */
-    jsval vp[3];
-    vp[0] = JSVAL_NULL;
-    vp[1] = cval;
-    vp[2] = JSVAL_VOID;
-    if (!xml_setSettings(cx, 1, vp))
+
+    /* Define the XMLList function, and give it the same .prototype as XML. */
+    JSFunction *xmllist =
+        JS_DefineFunction(cx, global, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR);
+    if (!xmllist)
+        return NULL;
+    if (!xmllist->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
+                                 ObjectValue(*xmlProto), PropertyStub, StrictPropertyStub,
+                                 JSPROP_PERMANENT | JSPROP_READONLY))
+    {
         return NULL;
-
-    /* Define the XMLList function and give it the same prototype as XML. */
-    JSFunction *fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR);
-    if (!fun)
+    }
+
+    /* Define the isXMLName function. */
+    if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
         return NULL;
-    if (!LinkConstructorAndPrototype(cx, FUN_OBJECT(fun), proto))
+
+    if (!DefineConstructorAndPrototype(cx, global, JSProto_XML, ctor, xmlProto))
         return NULL;
 
-    return proto;
+    return xmlProto;
 }
 
 JSObject *
 js_InitXMLClasses(JSContext *cx, JSObject *obj)
 {
     if (!js_InitNamespaceClass(cx, obj))
         return NULL;
     if (!js_InitQNameClass(cx, obj))
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -108,16 +108,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
     getElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
     setElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
 #endif
     callPatches(CompilerAllocPolicy(cx, *thisFromCtor())),
     callSites(CompilerAllocPolicy(cx, *thisFromCtor())), 
     doubleList(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTableOffsets(CompilerAllocPolicy(cx, *thisFromCtor())),
+    rootedObjects(CompilerAllocPolicy(cx, *thisFromCtor())),
     stubcc(cx, *thisFromCtor(), frame, script),
     debugMode_(cx->compartment->debugMode),
 #if defined JS_TRACER
     addTraceHints(cx->traceJitEnabled),
 #endif
     oomInVector(false),
     applyTricks(NoApplyTricks)
 {
@@ -452,17 +453,18 @@ mjit::Compiler::finishThisUp(JITScript *
                       sizeof(ic::EqualityICInfo) * equalityICs.length() +
                       sizeof(ic::TraceICInfo) * traceICs.length() +
 #endif
 #if defined JS_POLYIC
                        sizeof(ic::PICInfo) * pics.length() +
                        sizeof(ic::GetElementIC) * getElemICs.length() +
                        sizeof(ic::SetElementIC) * setElemICs.length() +
 #endif
-                       sizeof(CallSite) * callSites.length();
+                       sizeof(CallSite) * callSites.length() +
+                       sizeof(JSObject *) * rootedObjects.length();
 
     uint8 *cursor = (uint8 *)cx->calloc_(dataSize);
     if (!cursor) {
         execPool->release();
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 
@@ -815,16 +817,23 @@ mjit::Compiler::finishThisUp(JITScript *
         CallSite &to = jitCallSites[i];
         InternalCallSite &from = callSites[i];
         uint32 codeOffset = from.ool
                             ? masm.size() + from.returnOffset
                             : from.returnOffset;
         to.initialize(codeOffset, from.pc - script->code, from.id);
     }
 
+    /* Build the list of objects rooted by the script. */
+    JSObject **jitRooted = (JSObject **)cursor;
+    jit->nRootedObjects = rootedObjects.length();
+    cursor += sizeof(JSObject *) * jit->nRootedObjects;
+    for (size_t i = 0; i < jit->nRootedObjects; i++)
+        jitRooted[i] = rootedObjects[i];
+
     JS_ASSERT(size_t(cursor - (uint8*)jit) == dataSize);
 
     *jitp = jit;
 
     return Compile_Okay;
 }
 
 class SrcNoteLineScanner {
@@ -3236,16 +3245,22 @@ mjit::Compiler::jsop_callprop_str(JSAtom
      * We must pass an explicit scope chain only because JSD calls into
      * here via the recompiler with a dummy context, and we need to use
      * the global object for the script we are now compiling.
      */
     JSObject *obj;
     if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj))
         return false;
 
+    /*
+     * Root the proto, since JS_ClearScope might overwrite the global object's
+     * copy.
+     */
+    rootedObjects.append(obj);
+
     /* Force into a register because getprop won't expect a constant. */
     RegisterID reg = frame.allocReg();
 
     masm.move(ImmPtr(obj), reg);
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
 
     /* Get the property. */
     if (!jsop_getprop(atom))
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -353,16 +353,17 @@ class Compiler : public BaseCompiler
     js::Vector<GetElementICInfo, 16, CompilerAllocPolicy> getElemICs;
     js::Vector<SetElementICInfo, 16, CompilerAllocPolicy> setElemICs;
 #endif
     js::Vector<CallPatchInfo, 64, CompilerAllocPolicy> callPatches;
     js::Vector<InternalCallSite, 64, CompilerAllocPolicy> callSites;
     js::Vector<DoublePatch, 16, CompilerAllocPolicy> doubleList;
     js::Vector<JumpTable, 16> jumpTables;
     js::Vector<uint32, 16> jumpTableOffsets;
+    js::Vector<JSObject *, 0, CompilerAllocPolicy> rootedObjects;
     StubCompiler stubcc;
     Label invokeLabel;
     Label arityLabel;
     bool debugMode_;
     bool addTraceHints;
     bool oomInVector;       // True if we have OOM'd appending to a vector. 
     enum { NoApplyTricks, LazyArgsObj } applyTricks;
 #ifdef DEBUG
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "MethodJIT.h"
 #include "Logging.h"
 #include "assembler/jit/ExecutableAllocator.h"
 #include "jstracer.h"
+#include "jsgcmark.h"
 #include "BaseAssembler.h"
 #include "Compiler.h"
 #include "MonoIC.h"
 #include "PolyIC.h"
 #include "TrampolineCompiler.h"
 #include "jscntxtinlines.h"
 #include "jscompartment.h"
 #include "jsscope.h"
@@ -747,17 +748,17 @@ NativeMapEntry *
 JITScript::nmap() const
 {
     return (NativeMapEntry *)((char*)this + sizeof(JITScript));
 }
 
 char *
 JITScript::nmapSectionLimit() const
 {
-    return (char *)nmap() + sizeof(NativeMapEntry) * nNmapPairs;
+    return (char *)&nmap()[nNmapPairs];
 }
 
 #ifdef JS_MONOIC
 ic::GetGlobalNameIC *
 JITScript::getGlobalNames() const
 {
     return (ic::GetGlobalNameIC *)nmapSectionLimit();
 }
@@ -767,36 +768,35 @@ JITScript::setGlobalNames() const
 {
     return (ic::SetGlobalNameIC *)((char *)nmapSectionLimit() +
             sizeof(ic::GetGlobalNameIC) * nGetGlobalNames);
 }
 
 ic::CallICInfo *
 JITScript::callICs() const
 {
-    return (ic::CallICInfo *)((char *)setGlobalNames() +
-            sizeof(ic::SetGlobalNameIC) * nSetGlobalNames);
+    return (ic::CallICInfo *)&setGlobalNames()[nSetGlobalNames];
 }
 
 ic::EqualityICInfo *
 JITScript::equalityICs() const
 {
-    return (ic::EqualityICInfo *)((char *)callICs() + sizeof(ic::CallICInfo) * nCallICs);
+    return (ic::EqualityICInfo *)&callICs()[nCallICs];
 }
 
 ic::TraceICInfo *
 JITScript::traceICs() const
 {
-    return (ic::TraceICInfo *)((char *)equalityICs() + sizeof(ic::EqualityICInfo) * nEqualityICs);
+    return (ic::TraceICInfo *)&equalityICs()[nEqualityICs];
 }
 
 char *
 JITScript::monoICSectionsLimit() const
 {
-    return (char *)traceICs() + sizeof(ic::TraceICInfo) * nTraceICs;
+    return (char *)&traceICs()[nTraceICs];
 }
 #else   // JS_MONOIC
 char *
 JITScript::monoICSectionsLimit() const
 {
     return nmapSectionLimit();
 }
 #endif  // JS_MONOIC
@@ -834,16 +834,22 @@ JITScript::polyICSectionsLimit() const
 #endif  // JS_POLYIC
 
 js::mjit::CallSite *
 JITScript::callSites() const
 {
     return (js::mjit::CallSite *)polyICSectionsLimit();
 }
 
+JSObject **
+JITScript::rootedObjects() const
+{
+    return (JSObject **)&callSites()[nCallSites];
+}
+
 template <typename T>
 static inline void Destroy(T &t)
 {
     t.~T();
 }
 
 mjit::JITScript::~JITScript()
 {
@@ -870,16 +876,23 @@ mjit::JITScript::~JITScript()
     }
     
     ic::CallICInfo *callICs_ = callICs();
     for (uint32 i = 0; i < nCallICs; i++)
         callICs_[i].releasePools();
 #endif
 }
 
+void
+mjit::JITScript::trace(JSTracer *trc)
+{
+    for (uint32 i = 0; i < nRootedObjects; ++i)
+        MarkObject(trc, *rootedObjects()[i], "mjit rooted object");
+}
+
 size_t
 JSScript::jitDataSize()
 {
     size_t n = 0;
     if (jitNormal)
         n += jitNormal->scriptDataSize(); 
     if (jitCtor)
         n += jitCtor->scriptDataSize(); 
@@ -925,16 +938,26 @@ mjit::ReleaseScriptCode(JSContext *cx, J
     if ((jscr = script->jitCtor)) {
         jscr->~JITScript();
         cx->free_(jscr);
         script->jitCtor = NULL;
         script->jitArityCheckCtor = NULL;
     }
 }
 
+void
+mjit::TraceScript(JSTracer *trc, JSScript *script)
+{
+    if (JITScript *jit = script->jitNormal)
+        jit->trace(trc);
+
+    if (JITScript *jit = script->jitCtor)
+        jit->trace(trc);
+}
+
 #ifdef JS_METHODJIT_PROFILE_STUBS
 void JS_FASTCALL
 mjit::ProfileStubCall(VMFrame &f)
 {
     JSOp op = JSOp(*f.regs.pc);
     StubCallsForOp[op]++;
 }
 #endif
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -377,16 +377,17 @@ struct JITScript {
     uint32          nTraceICs;
 #endif
 #ifdef JS_POLYIC
     uint32          nGetElems;
     uint32          nSetElems;
     uint32          nPICs;
 #endif
     uint32          nCallSites;
+    uint32          nRootedObjects;
 
 #ifdef JS_MONOIC
     // Additional ExecutablePools that IC stubs were generated into.
     typedef Vector<JSC::ExecutablePool *, 0, SystemAllocPolicy> ExecPoolVector;
     ExecPoolVector execPools;
 #endif
 
     NativeMapEntry *nmap() const;
@@ -398,30 +399,33 @@ struct JITScript {
     ic::TraceICInfo *traceICs() const;
 #endif
 #ifdef JS_POLYIC
     ic::GetElementIC *getElems() const;
     ic::SetElementIC *setElems() const;
     ic::PICInfo     *pics() const;
 #endif
     js::mjit::CallSite *callSites() const;
+    JSObject **rootedObjects() const;
 
     ~JITScript();
 
     bool isValidCode(void *ptr) {
         char *jitcode = (char *)code.m_code.executableAddress();
         char *jcheck = (char *)ptr;
         return jcheck >= jitcode && jcheck < jitcode + code.m_size;
     }
 
     void nukeScriptDependentICs();
     void sweepCallICs(JSContext *cx, bool purgeAll);
     void purgeMICs();
     void purgePICs();
 
+    void trace(JSTracer *trc);
+
     size_t scriptDataSize();
     jsbytecode *nativeToPC(void *returnAddress) const;
 
   private:
     /* Helpers used to navigate the variable-length sections. */
     char *nmapSectionLimit() const;
     char *monoICSectionsLimit() const;
     char *polyICSectionsLimit() const;
@@ -451,16 +455,19 @@ void JS_FASTCALL
 ProfileStubCall(VMFrame &f);
 
 CompileStatus JS_NEVER_INLINE
 TryCompile(JSContext *cx, StackFrame *fp);
 
 void
 ReleaseScriptCode(JSContext *cx, JSScript *script);
 
+void
+TraceScript(JSTracer *trc, JSScript *script);
+
 struct CallSite
 {
     uint32 codeOffset;
     uint32 pcOffset;
     uint32 id;
 
     // Normally, callsite ID is the __LINE__ in the program that added the
     // callsite. Since traps can be removed, we make sure they carry over
--- a/js/src/shell/Makefile.in
+++ b/js/src/shell/Makefile.in
@@ -44,16 +44,17 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 PROGRAM         = js$(BIN_SUFFIX)
 CPPSRCS		= \
   js.cpp \
   jsworkers.cpp \
   jsoptparse.cpp \
+  jsheaptools.cpp \
   $(NULL)
 
 DEFINES         += -DEXPORT_JS_API
 
 LIBS      = $(NSPR_LIBS) $(EDITLINE_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX)
 ifdef MOZ_NATIVE_FFI
 EXTRA_LIBS += $(MOZ_FFI_LIBS)
 endif
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -71,31 +71,33 @@
 #include "json.h"
 #include "jsparse.h"
 #include "jsreflect.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jstypedarray.h"
 #include "jsxml.h"
 #include "jsperf.h"
+#include "jshashtable.h"
 
 #include "prmjtime.h"
 
 #ifdef JSDEBUGGER
 #include "jsdebug.h"
 #ifdef JSDEBUGGER_JAVA_UI
 #include "jsdjava.h"
 #endif /* JSDEBUGGER_JAVA_UI */
 #ifdef JSDEBUGGER_C_UI
 #include "jsdb.h"
 #endif /* JSDEBUGGER_C_UI */
 #endif /* JSDEBUGGER */
 
 #include "jsoptparse.h"
 #include "jsworkers.h"
+#include "jsheaptools.h"
 
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 #include "methodjit/MethodJIT.h"
 
 #ifdef XP_UNIX
 #include <unistd.h>
@@ -4500,17 +4502,17 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("help",           Help,           0,0),
     JS_FN("quit",           Quit,           0,0),
     JS_FN("assertEq",       AssertEq,       2,0),
     JS_FN("assertJit",      AssertJit,      0,0),
     JS_FN("gc",             ::GC,           0,0),
     JS_FN("gcparam",        GCParameter,    2,0),
     JS_FN("countHeap",      CountHeap,      0,0),
     JS_FN("makeFinalizeObserver", MakeFinalizeObserver, 0,0),
-    JS_FN("finalizeCount",  FinalizeCount, 0,0),
+    JS_FN("finalizeCount",  FinalizeCount,  0,0),
 #ifdef JS_GC_ZEAL
     JS_FN("gczeal",         GCZeal,         2,0),
     JS_FN("schedulegc",     ScheduleGC,     1,0),
 #endif
     JS_FN("internalConst",  InternalConst,  1,0),
     JS_FN("setDebug",       SetDebug,       1,0),
     JS_FN("setDebuggerHandler", SetDebuggerHandler, 1,0),
     JS_FN("setThrowHook",   SetThrowHook,   1,0),
@@ -4525,16 +4527,17 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("disassemble",    DisassembleToString, 1,0),
     JS_FN("dis",            Disassemble,    1,0),
     JS_FN("disfile",        DisassFile,     1,0),
     JS_FN("dissrc",         DisassWithSrc,  1,0),
     JS_FN("dumpHeap",       DumpHeap,       0,0),
     JS_FN("dumpObject",     DumpObject,     1,0),
     JS_FN("notes",          Notes,          1,0),
     JS_FN("stats",          DumpStats,      1,0),
+    JS_FN("findReferences", FindReferences, 1,0),
 #endif
     JS_FN("dumpStack",      DumpStack,      1,0),
 #ifdef TEST_CVTARGS
     JS_FN("cvtargs",        ConvertArgs,    0,0),
 #endif
     JS_FN("build",          BuildDate,      0,0),
     JS_FN("clear",          Clear,          0,0),
     JS_FN("intern",         Intern,         1,0),
@@ -4658,16 +4661,18 @@ static const char *const shell_help_mess
 "    \"-r\" (disassemble recursively)\n"
 "    \"-l\" (show line numbers)",
 "dissrc([fun])            Disassemble functions with source lines",
 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
 "  Interface to JS_DumpHeap with output sent to file",
 "dumpObject()             Dump an internal representation of an object",
 "notes([fun])             Show source notes for functions",
 "stats([string ...])      Dump 'arena', 'atom', 'global' stats",
+"findReferences(target)\n"
+"  Walk the heap and return an object describing all references to target",
 #endif
 "dumpStack()              Dump the stack as an array of callees (youngest first)",
 #ifdef TEST_CVTARGS
 "cvtargs(arg1..., arg12)  Test argument formatter",
 #endif
 "build()                  Show build date and time",
 "clear([obj])             Clear properties of object",
 "intern(str)              Internalize str in the atom table",
new file mode 100644
--- /dev/null
+++ b/js/src/shell/jsheaptools.cpp
@@ -0,0 +1,570 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99:
+ *
+ * ***** 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 dis