merge the last green changeset on m-c to fx-team
authorTim Taubert <tim.taubert@gmx.de>
Thu, 04 Aug 2011 14:12:03 +0200
changeset 73946 6daba4de8d1416e1546adfc4c6d643978ffcb69f
parent 73945 71881e5b06ca0968dd72bf56dd28bef3fad1d3df (current diff)
parent 73826 5d742d2e4304f8e5052cbd3defc222108ca3a3f1 (diff)
child 73947 3f5229b206d5cf8439b9ee0070495a24755b149c
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone8.0a1
merge the last green changeset on m-c to fx-team
--- 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/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/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 \
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/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(662691db-2b99-4461-801b-fbb72d99a4b9)]
+[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;
@@ -80,31 +88,35 @@ interface nsIMozWebSocket : nsISupports
    *         sent successfully).
    */
   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/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/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,
@@ -383,17 +389,18 @@ nsWebSocketEstablishedConnection::Close(
   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;
@@ -498,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,
@@ -524,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;
@@ -554,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
 //-----------------------------------------------------------------------------
@@ -604,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");
 }
 
@@ -671,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;
   }
@@ -709,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()
@@ -761,42 +816,48 @@ nsWebSocket::EstablishConnection()
   nsresult rv;
 
   nsRefPtr<nsWebSocketEstablishedConnection> conn =
     new nsWebSocketEstablishedConnection();
 
   rv = conn->Init(this);
   mConnection = conn;
   if (NS_FAILED(rv)) {
-    Close();
+    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;
 
@@ -874,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)) {
@@ -896,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);
 }
@@ -954,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();
@@ -1014,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);
   }
@@ -1053,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
@@ -1189,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;
@@ -1232,55 +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)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   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;
   }
 
   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/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/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -6933,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;
@@ -7073,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;
         }
 
@@ -7791,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"
 
@@ -10909,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/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/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/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/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/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/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)
--- 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/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/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/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/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 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 JavaScript shell workers.
+ *
+ * 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):
+ *   Jason Orendorff <jorendorff@mozilla.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 ***** */
+
+#include <string.h>
+
+#include "jsapi.h"
+
+#include "jsalloc.h"
+#include "jscntxt.h"
+#include "jscompartment.h"
+#include "jsfun.h"
+#include "jshashtable.h"
+#include "jsobj.h"
+#include "jsprf.h"
+#include "jsutil.h"
+#include "jsvalue.h"
+#include "jsvector.h"
+
+using namespace js;
+
+#ifdef DEBUG
+
+
+/*** class HeapReverser **************************************************************************/
+
+/*
+ * A class for constructing a map of the JavaScript heap, with all
+ * reference edges reversed.
+ *
+ * Unfortunately, it's not possible to build the results for findReferences
+ * while visiting things solely in the order that JS_TraceRuntime and
+ * JS_TraceChildren reaches them. For example, as you work outward from the
+ * roots, suppose an edge from thing T reaches a "gray" thing G --- G being gray
+ * because you're still in the midst of traversing its descendants. At this
+ * point, you don't know yet whether G will be a referrer or not, and so you
+ * can't tell whether T should be a referrer either. And you won't visit T
+ * again.
+ *
+ * So we take a brute-force approach. We reverse the entire graph, and then walk
+ * outward from |target| to the representable objects that refer to it, stopping
+ * at such objects.
+ */
+
+/* A JSTracer that produces a map of the heap with edges reversed. */
+class HeapReverser : public JSTracer {
+  public:
+    struct Edge;
+
+    /* Metadata for a given Cell we have visited. */
+    class Node {
+      public:
+        Node() { }
+        Node(uint32 kind) : kind(kind), incoming(), marked(false) { }
+
+        /*
+         * Move constructor and move assignment. These allow us to store our
+         * incoming edge Vector in the hash table: Vectors support moves, but
+         * not assignments or copy construction.
+         */
+        Node(MoveRef<Node> rhs)
+          : kind(rhs->kind), incoming(Move(rhs->incoming)), marked(rhs->marked) { }
+        Node &operator=(MoveRef<Node> rhs) {
+            this->~Node();
+            new(this) Node(rhs);
+            return *this;
+        }
+
+        /* What kind of Cell this is. */
+        uint32 kind;
+
+        /*
+         * A vector of this Cell's incoming edges.
+         * This must use SystemAllocPolicy because HashMap requires its elements to
+         * be constructible with no arguments.
+         */
+        Vector<Edge, 0, SystemAllocPolicy> incoming;
+
+        /* A mark bit, for other traversals. */
+        bool marked;
+
+      private:
+        Node(const Node &);
+        Node &operator=(const Node &);
+    };
+
+    /* Metadata for a heap edge we have traversed. */
+    struct Edge {
+      public:
+        Edge(char *name, void *origin) : name(name), origin(origin) { }
+        ~Edge() { free(name); }
+
+        /*
+         * Move constructor and move assignment. These allow us to live in
+         * Vectors without needing to copy our name string when the vector is
+         * resized.
+         */
+        Edge(MoveRef<Edge> rhs) : name(rhs->name), origin(rhs->origin) {
+            rhs->name = NULL;
+        }
+        Edge &operator=(MoveRef<Edge> rhs) {
+            this->~Edge();
+            new(this) Edge(rhs);
+            return *this;
+        }
+
+        /* The name of this heap edge. Owned by this Edge. */
+        char *name;
+
+        /*
+         * The Cell from which this edge originates. NULL means a root. This is
+         * a cell address instead of a Node * because Nodes live in HashMap
+         * table entries; if the HashMap reallocates its table, all pointers to
+         * the Nodes it contains would become invalid. You should look up the
+         * address here in |map| to find its Node.
+         */
+        void *origin;
+    };
+
+    /*
+     * The result of a reversal is a map from Cells' addresses to Node
+     * structures describing their incoming edges.
+     */
+    typedef HashMap<void *, Node> Map;
+    Map map;
+
+    /* Construct a HeapReverser for |context|'s heap. */
+    HeapReverser(JSContext *cx) : map(cx), work(cx), parent(NULL) {
+        context = cx;
+        callback = traverseEdgeWithThis;
+    }
+
+    bool init() { return map.init(); }
+
+    /* Build a reversed map of the heap in |map|. */
+    bool reverseHeap();
+
+  private:    
+    /*
+     * Return the name of the most recent edge this JSTracer has traversed. The
+     * result is allocated with malloc; if we run out of memory, raise an error
+     * in this HeapReverser's context and return NULL.
+     *
+     * This may not be called after that edge's call to traverseEdge has
+     * returned.
+     */
+    char *getEdgeDescription();
+
+    /* Class for setting new parent, and then restoring the original. */
+    class AutoParent {
+      public:
+        AutoParent(HeapReverser *reverser, void *newParent) : reverser(reverser) {
+            savedParent = reverser->parent;
+            reverser->parent = newParent;
+        }
+        ~AutoParent() {
+            reverser->parent = savedParent; 
+        }
+      private:
+        HeapReverser *reverser;
+        void *savedParent;
+    };
+
+    /* A work item in the stack of nodes whose children we need to traverse. */
+    struct Child {
+        Child(void *cell, uint32 kind) : cell(cell), kind(kind) { }
+        void *cell;
+        uint32 kind;
+    };
+
+    /*
+     * A stack of work items. We represent the stack explicitly to avoid
+     * overflowing the C++ stack when traversing long chains of objects.
+     */
+    Vector<Child> work; 
+
+    /* When traverseEdge is called, the Cell and kind at which the edge originated. */
+    void *parent;
+
+    /* Traverse an edge. */
+    bool traverseEdge(void *cell, uint32 kind);
+
+    /*
+     * JS_TraceRuntime and JS_TraceChildren don't propagate error returns,
+     * and out-of-memory errors, by design, don't establish an exception in
+     * |context|, so traverseEdgeWithThis uses this to communicate the
+     * result of the traversal to reverseHeap.
+     */
+    bool traversalStatus;
+
+    /* Static member function wrapping 'traverseEdge'. */
+    static void traverseEdgeWithThis(JSTracer *tracer, void *cell, uint32 kind) {
+        HeapReverser *reverser = static_cast<HeapReverser *>(tracer);
+        reverser->traversalStatus = reverser->traverseEdge(cell, kind);
+    }
+};
+
+bool
+HeapReverser::traverseEdge(void *cell, uint32 kind) {
+    /* Capture this edge before the JSTracer members get overwritten. */
+    char *edgeDescription = getEdgeDescription();
+    if (!edgeDescription)
+        return false;
+    Edge e(edgeDescription, parent);
+
+    Map::AddPtr a = map.lookupForAdd(cell);
+    if (!a) {
+        /*
+         * We've never visited this cell before. Add it to the map (thus
+         * marking it as visited), and put it on the work stack, to be
+         * visited from the main loop.
+         */
+        Node n(kind);
+        uint32 generation = map.generation();
+        if (!map.add(a, cell, Move(n)) ||
+            !work.append(Child(cell, kind)))
+            return false;
+        /* If the map has been resized, re-check the pointer. */
+        if (map.generation() != generation)
+            a = map.lookupForAdd(cell);
+    }
+
+    /* Add this edge to the reversed map. */
+    return a->value.incoming.append(Move(e));
+}
+
+bool
+HeapReverser::reverseHeap() {
+    /* Prime the work stack with the roots of collection. */
+    JS_TraceRuntime(this);
+    if (!traversalStatus)
+        return false;
+
+    /* Traverse children until the stack is empty. */
+    while (!work.empty()) {
+        const Child child = work.popCopy();
+        AutoParent autoParent(this, child.cell);
+        JS_TraceChildren(this, child.cell, child.kind);
+        if (!traversalStatus)
+            return false;
+    }
+
+    return true;
+}
+
+char *
+HeapReverser::getEdgeDescription()
+{
+    if (!debugPrinter && debugPrintIndex == (size_t) -1) {
+        const char *arg = static_cast<const char *>(debugPrintArg);
+        char *name = static_cast<char *>(context->malloc_(strlen(arg) + 1));
+        if (!name)
+            return NULL;
+        strcpy(name, arg);
+        return name;
+    }
+
+    /* Lovely; but a fixed size is required by JSTraceNamePrinter. */
+    static const int nameSize = 200;
+    char *name = static_cast<char *>(context->malloc_(nameSize));
+    if (!name)
+        return NULL;
+    if (debugPrinter)
+        debugPrinter(this, name, nameSize);
+    else
+        JS_snprintf(name, nameSize, "%s[%lu]",
+                    static_cast<const char *>(debugPrintArg), debugPrintIndex);
+
+    /* Shrink storage to fit. */
+    return static_cast<char *>(context->realloc_(name, strlen(name) + 1));
+}
+
+
+/*** class ReferenceFinder ***********************************************************************/
+
+/* A class for finding an object's referrers, given a reversed heap map. */
+class ReferenceFinder {
+  public:
+    ReferenceFinder(JSContext *cx, const HeapReverser &reverser) 
+      : context(cx), reverser(reverser) { }
+
+    /* Produce an object describing all references to |target|. */
+    JSObject *findReferences(JSObject *target);
+
+  private:
+    /* The context in which to do allocation and error-handling. */
+    JSContext *context;
+
+    /* A reversed map of the current heap. */
+    const HeapReverser &reverser;
+
+    /* The results object we're currently building. */
+    JSObject *result;
+
+    /* A list of edges we've traversed to get to a certain point. */
+    class Path {
+      public:
+        Path(const HeapReverser::Edge &edge, Path *next) : edge(edge), next(next) { }
+        
+        /*
+         * Compute the full path represented by this Path. The result is
+         * owned by the caller.
+         */
+        char *computeName(JSContext *cx);
+
+      private:
+        const HeapReverser::Edge &edge;
+        Path *next;
+    };
+
+    struct AutoNodeMarker {
+        AutoNodeMarker(HeapReverser::Node *node) : node(node) { node->marked = true; }
+        ~AutoNodeMarker() { node->marked = false; }
+      private:
+        HeapReverser::Node *node;
+    };
+
+    /* 
+     * Given that we've reached |cell| via |path|, with all Nodes along that
+     * path marked, add paths from all reportable objects reachable from cell
+     * to |result|.
+     */
+    bool visit(void *cell, Path *path);
+
+    /*
+     * If |cell|, of |kind|, is representable as a JavaScript value, return that
+     * value; otherwise, return JSVAL_VOID.
+     */
+    jsval representable(void *cell, int kind) {
+        if (kind == JSTRACE_OBJECT) {
+            JSObject *object = static_cast<JSObject *>(cell);
+
+            /* Certain classes of object are for internal use only. */
+            JSClass *clasp = JS_GET_CLASS(context, object);
+            if (clasp == Jsvalify(&js_BlockClass) ||
+                clasp == Jsvalify(&js_CallClass) ||
+                clasp == Jsvalify(&js_WithClass) ||
+                clasp == Jsvalify(&js_DeclEnvClass))
+                return JSVAL_VOID;
+
+            /* Internal function objects should also not be revealed. */
+            if (JS_ObjectIsFunction(context, object) && IsInternalFunctionObject(object))
+                return JSVAL_VOID;
+
+            return OBJECT_TO_JSVAL(object);
+        }
+
+        return JSVAL_VOID;
+    }
+
+    /* Add |referrer| as something that refers to |target| via |path|. */
+    bool addReferrer(jsval referrer, Path *path);
+};
+
+bool
+ReferenceFinder::visit(void *cell, Path *path)
+{
+    /* In ReferenceFinder, paths will almost certainly fit on the C++ stack. */
+    JS_CHECK_RECURSION(context, return false);
+
+    /* Have we reached a root? Always report that. */
+    if (!cell)
+        return addReferrer(JSVAL_NULL, path);
+        
+    HeapReverser::Map::Ptr p = reverser.map.lookup(cell);
+    JS_ASSERT(p);
+    HeapReverser::Node *node = &p->value;
+
+    /* Is |cell| a representable cell, reached via a non-empty path? */
+    if (path != NULL) {
+        jsval representation = representable(cell, node->kind);
+        if (!JSVAL_IS_VOID(representation))
+            return addReferrer(representation, path);
+    }
+
+    /*
+     * If we've made a cycle, don't traverse further. We *do* want to include
+     * paths from the target to itself, so we don't want to do this check until
+     * after we've possibly reported this cell as a referrer.
+     */
+    if (node->marked)
+        return true;
+    AutoNodeMarker marker(node);
+
+    /* Visit the origins of all |cell|'s incoming edges. */
+    for (size_t i = 0; i < node->incoming.length(); i++) {
+        const HeapReverser::Edge &edge = node->incoming[i];
+        Path extendedPath(edge, path);
+        if (!visit(edge.origin, &extendedPath))
+            return false;
+    }
+
+    return true;
+}
+
+char *
+ReferenceFinder::Path::computeName(JSContext *cx)
+{
+    /* Walk the edge list and compute the total size of the path. */
+    size_t size = 6;
+    for (Path *l = this; l; l = l->next) 
+        size += strlen(l->edge.name) + (l->next ? 2 : 0);
+    size += 1;
+
+    char *path = static_cast<char *>(cx->malloc_(size));
+    if (!path)
+        return NULL;
+
+    /*
+     * Walk the edge list again, and copy the edge names into place, with
+     * appropriate separators. Note that we constructed the edge list from
+     * target to referrer, which means that the list links point *towards* the
+     * target, so we can walk the list and build the path from left to right.
+     */
+    strcpy(path, "edge: ");
+    char *next = path + 6;
+    for (Path *l = this; l; l = l->next) {
+        strcpy(next, l->edge.name);
+        next += strlen(next);
+        if (l->next) {
+            strcpy(next, "; ");
+            next += 2;
+        }
+    }
+    JS_ASSERT(next + 1 == path + size);
+
+    return path;
+}
+
+bool
+ReferenceFinder::addReferrer(jsval referrer, Path *path)
+{
+    if (!context->compartment->wrap(context, Valueify(&referrer)))
+        return NULL;
+
+    char *pathName = path->computeName(context);
+    if (!pathName)
+        return false;
+    AutoReleasePtr releasePathName(context, pathName);
+
+    /* Find the property of the results object named |pathName|. */
+    jsval v;
+    if (!JS_GetProperty(context, result, pathName, &v))
+        return false;
+    if (JSVAL_IS_VOID(v)) {
+        /* Create an array to accumulate referents under this path. */
+        JSObject *array = JS_NewArrayObject(context, 1, &referrer);
+        if (!array)
+            return false;
+        v = OBJECT_TO_JSVAL(array);
+        return !!JS_SetProperty(context, result, pathName, &v);
+    }
+
+    /* The property's value had better be an array. */
+    JS_ASSERT(JSVAL_IS_OBJECT(v) && !JSVAL_IS_NULL(v));
+    JSObject *array = JSVAL_TO_OBJECT(v);
+    JS_ASSERT(JS_IsArrayObject(context, array));
+
+    /* Append our referrer to this array. */
+    jsuint length;
+    return JS_GetArrayLength(context, array, &length) &&
+           JS_SetElement(context, array, length, &referrer);
+}
+
+JSObject *
+ReferenceFinder::findReferences(JSObject *target)
+{
+    result = JS_NewObject(context, NULL, NULL, NULL);
+    if (!result)
+        return NULL;
+    if (!visit(target, NULL))
+        return NULL;
+
+    return result;
+}
+
+/*
+ * findReferences(thing)
+ *
+ * Walk the entire heap, looking for references to |thing|, and return a
+ * "references object" describing what we found.
+ *
+ * Each property of the references object describes one kind of reference. The
+ * property's name is the label supplied to MarkObject, JS_CALL_TRACER, or what
+ * have you, prefixed with "edge: " to avoid collisions with system properties
+ * (like "toString" and "__proto__"). The property's value is an array of things
+ * that refer to |thing| via that kind of reference. Ordinary references from
+ * one object to another are named after the property name (with the "edge: "
+ * prefix).
+ *
+ * Garbage collection roots appear as references from 'null'. We use the name
+ * given to the root (with the "edge: " prefix) as the name of the reference.
+ *
+ * Note that the references object does record references from objects that are
+ * only reachable via |thing| itself, not just the references reachable
+ * themselves from roots that keep |thing| from being collected. (We could make
+ * this distinction if it is useful.)
+ *
+ * If any references are found by the conservative scanner, the references
+ * object will have a property named "edge: machine stack"; the referrers will
+ * be 'null', because they are roots.
+ */
+JSBool
+FindReferences(JSContext *cx, uintN argc, jsval *vp)
+{
+    if (argc < 1) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
+                             "findReferences", 1, "");
+        return false;
+    }
+
+    jsval target = JS_ARGV(cx, vp)[0];
+    if (!JSVAL_IS_OBJECT(target) || JSVAL_IS_NULL(target)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
+                             "argument", "not an object");
+        return false;
+    }
+
+    /* Walk the JSRuntime, producing a reversed map of the heap. */
+    HeapReverser reverser(cx);
+    if (!reverser.init() || !reverser.reverseHeap())
+        return false;
+
+    /* Given the reversed map, find the referents of target. */
+    ReferenceFinder finder(cx, reverser);
+    JSObject *references = finder.findReferences(JSVAL_TO_OBJECT(target));
+    if (!references)
+        return false;
+    
+    JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(references));
+    return true;
+}
+
+#endif /* DEBUG */
new file mode 100644
--- /dev/null
+++ b/js/src/shell/jsheaptools.h
@@ -0,0 +1,50 @@
+/* -*- 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 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 JavaScript shell workers.
+ *
+ * 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):
+ *   Jim Blandy <jimb@mozilla.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 jsheaptools_h___
+#define jsheaptools_h___
+
+#include "jsapi.h"
+
+#ifdef DEBUG
+JSBool FindReferences(JSContext *cx, uintN argc, jsval *vp);
+#endif /* DEBUG */
+
+#endif /* jsheaptools_h___ */
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/findReferences-01.js
@@ -0,0 +1,52 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+// Contributor: Jim Blandy
+
+if (typeof findReferences == "function") {
+    function C() {}
+    var o = new C;
+    o.x = {};               // via ordinary property
+    o[42] = {};             // via numeric property
+    o.myself = o;           // self-references should be reported
+    o.alsoMyself = o;       // multiple self-references should all be reported
+
+    assertEq(referencesVia(o, 'proto', C.prototype), true);
+    assertEq(referencesVia(o, 'parent', this), true);
+    assertEq(referencesVia(o, 'x', o.x), true);
+    assertEq(referencesVia(o, '42', o[42]), true);
+    assertEq(referencesVia(o, 'myself', o), true);
+    assertEq(referencesVia(o, 'alsoMyself', o), true);
+
+    function g() { return 42; }
+    function s(v) { }
+    var p = Object.defineProperty({}, 'a', { get:g, set:s });
+    assertEq(referencesVia(p, 'shape; a getter', g), true);
+    assertEq(referencesVia(p, 'shape; a setter', s), true);
+
+    // If there are multiple objects with the same shape referring to a getter
+    // or setter, findReferences should get all of them, even though the shape
+    // gets 'marked' the first time we visit it.
+    var q = Object.defineProperty({}, 'a', { get:g, set:s });
+    assertEq(referencesVia(p, 'shape; a getter', g), true);
+    assertEq(referencesVia(q, 'shape; a getter', g), true);
+
+    // If we extend each object's shape chain, both should still be able to
+    // reach the getter, even though the two shapes are each traversed twice.
+    p.b = 9;
+    q.b = 9;
+    assertEq(referencesVia(p, 'shape; a getter', g), true);
+    assertEq(referencesVia(q, 'shape; a getter', g), true);
+
+    // These are really just ordinary own property references.
+    assertEq(referencesVia(C, 'prototype', Object.getPrototypeOf(o)), true);
+    assertEq(referencesVia(Object.getPrototypeOf(o), 'constructor', C), true);
+
+    // Dense arrays should work, too.
+    a = [];
+    a[1] = o;
+    assertEq(referencesVia(a, 'element[1]', o), true);
+
+    reportCompare(true, true);
+} else {
+    reportCompare(true, true, "test skipped: findReferences is not a function");
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/findReferences-02.js
@@ -0,0 +1,28 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+// Contributor: Jim Blandy
+
+if (typeof findReferences == "function") {
+    (function f() {
+         assertEq(referencesVia(arguments, 'callee', f), true);
+     })();
+
+    var o = ({});
+
+    function returnFlat(x) { return function flat() { return x; }; }
+    assertEq(referencesVia(returnFlat(o), 'upvars[0]', o), true);
+
+    function returnHeavy(y) { eval(''); return function heavy() { return y; }; }
+    assertEq(referencesVia(returnHeavy(o), 'parent; y', o), true);
+    assertEq(referencesVia(returnHeavy(o), 'parent; parent', this), true);
+
+    function returnBlock(z) { eval(''); let(w = z) { return function block() { return w; }; }; }
+    assertEq(referencesVia(returnBlock(o), 'parent; w', o), true);
+
+    function returnWithObj(v) { with(v) return function withObj() { return u; }; }
+    assertEq(referencesVia(returnWithObj(o), 'parent; proto', o), true);
+
+    reportCompare(true, true);
+} else {
+    reportCompare(true, true, "test skipped: findReferences is not a function");
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/findReferences-03.js
@@ -0,0 +1,41 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+// Contributor: Jim Blandy
+
+if (typeof findReferences == "function") {
+
+    function makeGenerator(c) { eval(c); yield function generatorClosure() { return x; }; }
+    var generator = makeGenerator('var x = 42');
+    var closure = generator.next();
+    referencesVia(closure, 'parent; generator object', generator);
+
+    var o = {};
+
+    assertEq(function f() { return referencesVia(null, 'arguments', arguments); } (), true);
+
+    var rvalueCorrect;
+
+    function finallyHoldsRval() {
+        try {
+            return o;
+        } finally {
+            rvalueCorrect = referencesVia(null, 'rval', o);
+        }
+    }
+    rvalueCorrect = false;
+    finallyHoldsRval();
+    assertEq(rvalueCorrect, true);
+
+    // Because we don't distinguish between JavaScript stack marking and C++
+    // stack marking (both use the conservative scanner), we can't really write
+    // the following tests meaningfully:
+    //   generator frame -> generator object
+    //   stack frame -> local variables
+    //   stack frame -> this
+    //   stack frame -> callee
+    //   for(... in x) loop's reference to x
+
+    reportCompare(true, true);
+} else {
+    reportCompare(true, true, "test skipped: findReferences is not a function");
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/findReferences-04.js
@@ -0,0 +1,18 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+// Contributor: Jim Blandy
+
+if (typeof findReferences == "function") {
+
+    var global = newGlobal('new-compartment');
+    var o = ({});
+    global.o = o;
+
+    // Don't trip a cross-compartment reference assertion.
+    findReferences(o);
+
+    reportCompare(true, true);
+
+} else {
+    reportCompare(true, true, "test skipped: findReferences is not a function");
+}
--- a/js/src/tests/js1_8_5/extensions/jstests.list
+++ b/js/src/tests/js1_8_5/extensions/jstests.list
@@ -25,16 +25,20 @@ skip-if(!xulRuntime.shell) script clone-
 skip-if(!xulRuntime.shell) script clone-leaf-object.js
 skip-if(!xulRuntime.shell) script clone-object.js
 skip-if(!xulRuntime.shell) script clone-typed-array.js
 skip-if(!xulRuntime.shell) script clone-errors.js
 skip-if(!xulRuntime.shell) script clone-forge.js
 skip-if(!xulRuntime.shell) script clone-complex-object.js
 script set-property-non-extensible.js
 script recursion.js
+script findReferences-01.js
+script findReferences-02.js
+script findReferences-03.js
+script findReferences-04.js
 script regress-627859.js
 script regress-627984-1.js
 script regress-627984-2.js
 script regress-627984-3.js
 script regress-627984-4.js
 script regress-627984-5.js
 script regress-627984-6.js
 script regress-627984-7.js
--- a/js/src/tests/js1_8_5/extensions/shell.js
+++ b/js/src/tests/js1_8_5/extensions/shell.js
@@ -165,8 +165,32 @@ var Match =
 
         return matchObject(act, exp);
     }
 
     return { Pattern: Pattern,
              MatchError: MatchError };
 
 })();
+
+function referencesVia(from, edge, to) {
+    edge = "edge: " + edge;
+    var edges = findReferences(to);
+    if (edge in edges && edges[edge].indexOf(from) != -1)
+        return true;
+
+    // Be nice: make it easy to fix if the edge name has just changed.
+    var alternatives = [];
+    for (var e in edges) {
+        if (edges[e].indexOf(from) != -1)
+            alternatives.push(e);
+    }
+    if (alternatives.length == 0) {
+        print("referent not referred to by referrer after all");
+    } else {
+        print("referent is not referenced via: " + uneval(edge));
+        print("but it is referenced via:       " + uneval(alternatives));
+    }
+    print("all incoming edges, from any object:");
+    for (var e in edges)
+        print(e);
+    return false;
+}
--- a/js/src/tests/js1_8_5/regress/jstests.list
+++ b/js/src/tests/js1_8_5/regress/jstests.list
@@ -103,8 +103,11 @@ script regress-636364.js
 script regress-640075.js
 script regress-646820-1.js
 script regress-646820-2.js
 script regress-646820-3.js
 script regress-643222.js
 script regress-614714.js
 script regress-665355.js
 script regress-666599.js
+script regress-673070-1.js
+script regress-673070-2.js
+script regress-673070-3.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-673070-1.js
@@ -0,0 +1,8 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var q = [2];
+let ([q] = eval("q"))
+    assertEq(q, 2);
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-673070-2.js
@@ -0,0 +1,8 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var q = 1;
+let ([q] = [eval("q")])
+    assertEq(q, 1);
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-673070-3.js
@@ -0,0 +1,8 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+let (x = 1) {
+    let ([] = [<x/>], r = <x/>) {}
+}
+
+reportCompare(0, 0, 'ok');
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -463,20 +463,32 @@ NoteJSRoot(JSTracer *trc, void *thing, u
     }
 }
 #endif
 
 nsresult 
 nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
                                   bool explainLiveExpectedGarbage)
 {
+    // It is important not to call GetSafeJSContext while on the
+    // cycle-collector thread since this context will be destroyed
+    // asynchronously and race with the main thread. In particular, we must
+    // ensure that a context is passed to the XPCCallContext constructor.
+    JSContext *cx = mRuntime->GetJSCycleCollectionContext();
+    if (!cx)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    // Clear after mCycleCollectionContext is destroyed
+    JS_SetContextThread(cx);
+
     NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishTraverse?");
-    mCycleCollectionContext = new XPCCallContext(NATIVE_CALLER);
+    mCycleCollectionContext = new XPCCallContext(NATIVE_CALLER, cx);
     if (!mCycleCollectionContext->IsValid()) {
         mCycleCollectionContext = nsnull;
+        JS_ClearContextThread(cx);
         return NS_ERROR_FAILURE;
     }
 
     static bool gcHasRun = false;
     if(!gcHasRun)
     {
         JSRuntime* rt = JS_GetRuntime(mCycleCollectionContext->GetJSContext());
         if(!rt)
@@ -516,18 +528,21 @@ nsXPConnect::BeginCycleCollection(nsCycl
     GetRuntime()->AddXPConnectRoots(mCycleCollectionContext->GetJSContext(), cb);
 
     return NS_OK;
 }
 
 nsresult 
 nsXPConnect::FinishTraverse()
 {
-    if (mCycleCollectionContext)
+    if (mCycleCollectionContext) {
+        JSContext *cx = mCycleCollectionContext->GetJSContext();
         mCycleCollectionContext = nsnull;
+        JS_ClearContextThread(cx);
+    }
     return NS_OK;
 }
 
 nsresult 
 nsXPConnect::FinishCycleCollection()
 {
 #ifdef DEBUG_CC
     if(mJSRoots.ops)
--- a/js/src/xpconnect/src/qsgen.py
+++ b/js/src/xpconnect/src/qsgen.py
@@ -460,17 +460,17 @@ argumentUnboxingTemplates = {
         "    float ${name} = (float) ${name}_dbl;\n",
 
     'double':
         "    jsdouble ${name};\n"
         "    if (!JS_ValueToNumber(cx, ${argVal}, &${name}))\n"
         "        return JS_FALSE;\n",
 
     'boolean':
-        "    PRBool ${name};\n"
+        "    JSBool ${name};\n"
         "    JS_ValueToBoolean(cx, ${argVal}, &${name});\n",
 
     '[astring]':
         "    xpc_qsAString ${name}(cx, ${argVal}, ${argPtr});\n"
         "    if (!${name}.IsValid())\n"
         "        return JS_FALSE;\n",
 
     '[domstring]':
--- a/js/src/xpconnect/src/xpccomponents.cpp
+++ b/js/src/xpconnect/src/xpccomponents.cpp
@@ -55,17 +55,17 @@
 #include "nsNullPrincipal.h"
 #include "nsJSUtils.h"
 #include "mozJSComponentLoader.h"
 #include "nsContentUtils.h"
 
 /***************************************************************************/
 // stuff used by all
 
-static nsresult ThrowAndFail(uintN errNum, JSContext* cx, JSBool* retval)
+static nsresult ThrowAndFail(uintN errNum, JSContext* cx, PRBool* retval)
 {
     XPCThrower::Throw(errNum, cx);
     *retval = JS_FALSE;
     return NS_OK;
 }
 
 static JSBool
 JSValIsInterfaceOfType(JSContext *cx, jsval v, REFNSIID iid)
--- a/js/src/xpconnect/src/xpcconvert.cpp
+++ b/js/src/xpconnect/src/xpcconvert.cpp
@@ -542,16 +542,17 @@ XPCConvert::JSData2Native(XPCCallContext
 {
     NS_PRECONDITION(d, "bad param");
 
     JSContext* cx = ccx.GetJSContext();
 
     int32    ti;
     uint32   tu;
     jsdouble td;
+    JSBool   tb;
     JSBool isDOMString = JS_TRUE;
 
     if(pErr)
         *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
 
     switch(type.TagPart())
     {
     case nsXPTType::T_I8     :
@@ -621,17 +622,18 @@ XPCConvert::JSData2Native(XPCCallContext
             return JS_FALSE;
         *((float*)d) = (float) td;
         break;
     case nsXPTType::T_DOUBLE :
         if(!JS_ValueToNumber(cx, s, (double*)d))
             return JS_FALSE;
         break;
     case nsXPTType::T_BOOL   :
-        JS_ValueToBoolean(cx, s, (JSBool*)d);
+        JS_ValueToBoolean(cx, s, &tb);
+        *((PRBool*)d) = tb;
         break;
     case nsXPTType::T_CHAR   :
         {
             JSString* str = JS_ValueToString(cx, s);
             if(!str)
             {
                 return JS_FALSE;
             }
--- a/js/src/xpconnect/src/xpcinlines.h
+++ b/js/src/xpconnect/src/xpcinlines.h
@@ -119,26 +119,16 @@ XPCCallContext::GetXPCContext() const
 
 inline JSContext*
 XPCCallContext::GetJSContext() const
 {
     CHECK_STATE(HAVE_CONTEXT);
     return mJSContext;
 }
 
-inline JSContext*
-XPCCallContext::GetSafeJSContext() const
-{
-    CHECK_STATE(HAVE_CONTEXT);
-    JSContext* cx;
-    if(NS_SUCCEEDED(mThreadData->GetJSContextStack()->GetSafeJSContext(&cx)))
-        return cx;
-    return nsnull;
-}
-
 inline JSBool
 XPCCallContext::GetContextPopRequired() const
 {
     CHECK_STATE(HAVE_CONTEXT);
     return mContextPopRequired;
 }
 
 inline XPCContext::LangType
@@ -685,37 +675,16 @@ xpc_NewSystemInheritingJSObject(JSContex
     } else {
         obj = JS_NewObject(cx, clasp, proto, parent);
     }
     if (obj && JS_IsSystemObject(cx, parent) && !JS_MakeSystemObject(cx, obj))
         obj = NULL;
     return obj;
 }
 
-inline JSBool
-xpc_SameScope(XPCWrappedNativeScope *objectscope, XPCWrappedNativeScope *xpcscope,
-              JSBool *sameOrigin)
-{
-    if (objectscope == xpcscope)
-    {
-        *sameOrigin = JS_TRUE;
-        return JS_TRUE;
-    }
-
-    nsIPrincipal *objectprincipal = objectscope->GetPrincipal();
-    nsIPrincipal *xpcprincipal = xpcscope->GetPrincipal();
-    if(!objectprincipal || !xpcprincipal ||
-       NS_FAILED(objectprincipal->Equals(xpcprincipal, sameOrigin)))
-    {
-        *sameOrigin = JS_FALSE;
-    }
-
-    return JS_FALSE;
-}
-
 inline jsid
 GetRTIdByIndex(JSContext *cx, uintN index)
 {
   XPCJSRuntime *rt = nsXPConnect::FastGetXPConnect()->GetRuntime();
   return rt->GetStringID(index);
 }
 
 inline jsval
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -990,17 +990,17 @@ XPCJSRuntime::WatchdogMain(void *arg)
     }
 
     /* Wake up the main thread waiting for the watchdog to terminate. */
     PR_NotifyCondVar(self->mWatchdogWakeup);
 }
 
 //static
 void
-XPCJSRuntime::ActivityCallback(void *arg, PRBool active)
+XPCJSRuntime::ActivityCallback(void *arg, JSBool active)
 {
     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
     if (active) {
         self->mLastActiveTime = -1;
         if (self->mWatchdogHibernating)
         {
             self->mWatchdogHibernating = PR_FALSE;
             PR_NotifyCondVar(self->mWatchdogWakeup);
@@ -1052,16 +1052,28 @@ void XPCJSRuntime::SystemIsBeingShutDown
 {
     DOM_ClearInterfaces();
 
     if(mDetachedWrappedNativeProtoMap)
         mDetachedWrappedNativeProtoMap->
             Enumerate(DetachedWrappedNativeProtoShutdownMarker, cx);
 }
 
+JSContext *
+XPCJSRuntime::GetJSCycleCollectionContext()
+{
+    if(!mJSCycleCollectionContext) {
+        mJSCycleCollectionContext = JS_NewContext(mJSRuntime, 0);
+        if(!mJSCycleCollectionContext)
+            return nsnull;
+        JS_ClearContextThread(mJSCycleCollectionContext);
+    }
+    return mJSCycleCollectionContext;
+}
+
 XPCJSRuntime::~XPCJSRuntime()
 {
     if (mWatchdogWakeup)
     {
         // If the watchdog thread is running, tell it to terminate waking it
         // up if necessary and wait until it signals that it finished. As we
         // must release the lock before calling PR_DestroyCondVar, we use an
         // extra block here.
@@ -1072,16 +1084,22 @@ XPCJSRuntime::~XPCJSRuntime()
                 PR_NotifyCondVar(mWatchdogWakeup);
                 PR_WaitCondVar(mWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT);
             }
         }
         PR_DestroyCondVar(mWatchdogWakeup);
         mWatchdogWakeup = nsnull;
     }
 
+    if(mJSCycleCollectionContext)
+    {
+        JS_SetContextThread(mJSCycleCollectionContext);
+        JS_DestroyContextNoGC(mJSCycleCollectionContext);
+    }
+
 #ifdef XPC_DUMP_AT_SHUTDOWN
     {
     // count the total JSContexts in use
     JSContext* iter = nsnull;
     int count = 0;
     while(JS_ContextIterator(mJSRuntime, &iter))
         count ++;
     if(count)
@@ -1429,63 +1447,62 @@ MakeMemoryReporterPath(const nsACString 
 } // anonymous namespace
 
 class XPConnectGCChunkAllocator
     : public js::GCChunkAllocator
 {
 public:
     XPConnectGCChunkAllocator() {}
 
-    PRInt64 GetGCChunkBytesInUse() {
-        return mNumGCChunksInUse * js::GC_CHUNK_SIZE;
-    }
 private:
     virtual void *doAlloc() {
         void *chunk;
 #ifdef MOZ_MEMORY
         // posix_memalign returns zero on success, nonzero on failure.
         if (posix_memalign(&chunk, js::GC_CHUNK_SIZE, js::GC_CHUNK_SIZE))
             chunk = 0;
 #else
         chunk = js::AllocGCChunk();
 #endif
-        if (chunk)
-            mNumGCChunksInUse++;
         return chunk;
     }
 
     virtual void doFree(void *chunk) {
-        mNumGCChunksInUse--;
 #ifdef MOZ_MEMORY
         free(chunk);
 #else
         js::FreeGCChunk(chunk);
 #endif
     }
-
-protected:
-    PRUint32 mNumGCChunksInUse;
 };
 
 static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
 
 #ifdef MOZ_MEMORY
 #define JS_GC_HEAP_KIND  nsIMemoryReporter::KIND_HEAP
 #else
 #define JS_GC_HEAP_KIND  nsIMemoryReporter::KIND_NONHEAP
 #endif
 
 // We have per-compartment GC heap totals, so we can't put the total GC heap
 // size in the explicit allocations tree.  But it's a useful figure, so put it
 // in the "others" list.
+
+static PRInt64
+GetGCChunkTotalBytes()
+{
+    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
+    return PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * js::GC_CHUNK_SIZE;
+}
+
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
     "js-gc-heap",
     KIND_OTHER,
     nsIMemoryReporter::UNITS_BYTES,
-    gXPCJSChunkAllocator.GetGCChunkBytesInUse,
+    GetGCChunkTotalBytes,
     "Memory used by the garbage-collected JavaScript heap.")
 
 static PRInt64
 GetJSSystemCompartmentCount()
 {
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     size_t n = 0;
     for (size_t i = 0; i < rt->compartments.length(); i++) {
@@ -1586,28 +1603,76 @@ CollectCompartmentStatsForRuntime(JSRunt
     JSContext *cx = JS_NewContext(rt, 0);
     if(!cx)
     {
         NS_ERROR("couldn't create context for memory tracing");
         return false;
     }
 
     {
-      JSAutoRequest ar(cx);
+        JSAutoRequest ar(cx);
+
+        if (!data->compartmentStatsVector.reserve(rt->compartments.length()))
+            return false;
 
-      data->compartmentStatsVector.reserve(rt->compartments.length());
-      js::IterateCompartmentsArenasCells(cx, data, CompartmentCallback,
-                                         ArenaCallback, CellCallback);
+        data->gcHeapChunkCleanUnused =
+            PRInt64(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) *
+            js::GC_CHUNK_SIZE;
+        data->gcHeapChunkTotal =
+            PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
+            js::GC_CHUNK_SIZE;
+
+        js::IterateCompartmentsArenasCells(cx, data, CompartmentCallback,
+                                           ArenaCallback, CellCallback);
+
+        for(js::ThreadDataIter i(rt); !i.empty(); i.popFront())
+            data->stackSize += i.threadData()->stackSpace.committedSize();
     }
 
     JS_DestroyContextNoGC(cx);
+
+    // This is initialized to all bytes stored in used chunks, and then we
+    // subtract used space from it each time around the loop.
+    data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
+                                   data->gcHeapChunkCleanUnused;
+    data->gcHeapArenaUnused = 0;
+
+    for(CompartmentStats *stats = data->compartmentStatsVector.begin();
+        stats != data->compartmentStatsVector.end();
+        ++stats)
+    {
+        data->gcHeapChunkDirtyUnused -=
+            stats->gcHeapArenaHeaders + stats->gcHeapArenaPadding +
+            stats->gcHeapArenaUnused +
+            stats->gcHeapObjects + stats->gcHeapStrings +
+            stats->gcHeapShapes + stats->gcHeapXml;
+        
+        data->gcHeapArenaUnused += stats->gcHeapArenaUnused;
+    }
+
+    size_t numDirtyChunks = (data->gcHeapChunkTotal -
+                             data->gcHeapChunkCleanUnused) /
+                            js::GC_CHUNK_SIZE;
+    PRInt64 perChunkAdmin =
+        sizeof(js::gc::Chunk) - (sizeof(js::gc::Arena) * js::gc::ArenasPerChunk);
+    data->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
+    data->gcHeapChunkDirtyUnused -= data->gcHeapChunkAdmin;
+    
+    // Why 10000x?  100x because it's a percentage, and another 100x
+    // because nsIMemoryReporter requires that for percentage amounts so
+    // they can be fractional.
+    data->gcHeapUnusedPercentage = (data->gcHeapChunkCleanUnused +
+                                    data->gcHeapChunkDirtyUnused +
+                                    data->gcHeapArenaUnused) * 10000 /
+                                   data->gcHeapChunkTotal;
+
     return true;
 }
 
-void
+static void
 ReportCompartmentStats(const CompartmentStats &stats,
                        const nsACString &pathPrefix,
                        nsIMemoryMultiReporterCallback *callback,
                        nsISupports *closure)
 {
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "gc-heap/arena-headers"),
                        JS_GC_HEAP_KIND, stats.gcHeapArenaHeaders,
@@ -1734,30 +1799,55 @@ ReportCompartmentStats(const Compartment
                        stats.tjitDataAllocatorsReserve,
     "Memory used by the trace JIT and held in reserve for the compartment's "
     "VMAllocators in case of OOM.",
                        callback, closure);
 #endif
 }
 
 void
-ReportJSStackSizeForRuntime(JSRuntime *rt, const nsACString &pathPrefix,
-                            nsIMemoryMultiReporterCallback *callback,
-                            nsISupports *closure)
+ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
+                     nsIMemoryMultiReporterCallback *callback,
+                     nsISupports *closure)
 {
-    PRInt64 stackSize = 0;
-    for(js::ThreadDataIter i(rt); !i.empty(); i.popFront())
-        stackSize += i.threadData()->stackSpace.committedSize();
+    for(const CompartmentStats *stats = data.compartmentStatsVector.begin();
+        stats != data.compartmentStatsVector.end();
+        ++stats)
+    {
+        ReportCompartmentStats(*stats, pathPrefix, callback, closure);
+    }
 
     ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("stack"),
-                      nsIMemoryReporter::KIND_NONHEAP, stackSize,
+                      nsIMemoryReporter::KIND_NONHEAP, data.stackSize,
     "Memory used for the JavaScript stack.  This is the committed portion "
     "of the stack; any uncommitted portion is not measured because it "
     "hardly costs anything.",
                       callback, closure);
+
+    ReportMemoryBytes(pathPrefix +
+                      NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
+                      JS_GC_HEAP_KIND, data.gcHeapChunkDirtyUnused,
+    "Memory on the garbage-collected JavaScript heap, within chunks with at "
+    "least one allocated GC thing, that could be holding useful data but "
+    "currently isn't.",
+                      callback, closure);
+
+    ReportMemoryBytes(pathPrefix +
+                      NS_LITERAL_CSTRING("gc-heap-chunk-clean-unused"),
+                      JS_GC_HEAP_KIND, data.gcHeapChunkCleanUnused,
+    "Memory on the garbage-collected JavaScript heap taken by completely empty "
+     "chunks, that soon will be released unless claimed for new allocations.",
+                          callback, closure);
+
+    ReportMemoryBytes(pathPrefix +
+                      NS_LITERAL_CSTRING("gc-heap-chunk-admin"),
+                      JS_GC_HEAP_KIND, data.gcHeapChunkAdmin,
+    "Memory on the garbage-collected JavaScript heap, within chunks, that is "
+    "used to hold internal book-keeping information.",
+                          callback, closure);
 }
 
 } // namespace memory
 } // namespace xpconnect
 } // namespace mozilla
 
 class XPConnectJSCompartmentsMultiReporter : public nsIMemoryMultiReporter
 {
@@ -1773,91 +1863,51 @@ public:
         // data structure.  In the second step we pass all the stashed stats to
         // the callback.  Separating these steps is important because the
         // callback may be a JS function, and executing JS while getting these
         // stats seems like a bad idea.
         IterateData data;
         if(!CollectCompartmentStatsForRuntime(rt, &data))
             return NS_ERROR_FAILURE;
 
-        PRInt64 gcHeapChunkTotal = gXPCJSChunkAllocator.GetGCChunkBytesInUse();
-        // This is initialized to gcHeapChunkTotal, and then we subtract used
-        // space from it each time around the loop.
-        PRInt64 gcHeapChunkUnused = gcHeapChunkTotal;
-        PRInt64 gcHeapArenaUnused = 0;
-
         NS_NAMED_LITERAL_CSTRING(pathPrefix, "explicit/js/");
 
         // This is the second step (see above).
-        for(CompartmentStats *stats = data.compartmentStatsVector.begin();
-            stats != data.compartmentStatsVector.end();
-            ++stats)
-        {
-            gcHeapChunkUnused -=
-                stats->gcHeapArenaHeaders + stats->gcHeapArenaPadding +
-                stats->gcHeapArenaUnused +
-                stats->gcHeapObjects + stats->gcHeapStrings +
-                stats->gcHeapShapes + stats->gcHeapXml;
-
-            gcHeapArenaUnused += stats->gcHeapArenaUnused;
-
-            ReportCompartmentStats(*stats, pathPrefix, callback, closure);
-        }
+        ReportJSRuntimeStats(data, pathPrefix, callback, closure);
 
-        JS_ASSERT(gcHeapChunkTotal % js::GC_CHUNK_SIZE == 0);
-        size_t numChunks = gcHeapChunkTotal / js::GC_CHUNK_SIZE;
-        PRInt64 perChunkAdmin =
-            sizeof(js::gc::Chunk) - (sizeof(js::gc::Arena) * js::gc::ArenasPerChunk);
-        PRInt64 gcHeapChunkAdmin = numChunks * perChunkAdmin;
-        gcHeapChunkUnused -= gcHeapChunkAdmin;
-
-        // Why 10000x?  100x because it's a percentage, and another 100x
-        // because nsIMemoryReporter requires that for percentage amounts so
-        // they can be fractional.
-        PRInt64 gcHeapUnusedPercentage =
-            (gcHeapChunkUnused + gcHeapArenaUnused) * 10000 /
-            gXPCJSChunkAllocator.GetGCChunkBytesInUse();
-
-        ReportMemoryBytes(pathPrefix +
-                          NS_LITERAL_CSTRING("gc-heap-chunk-unused"),
-                          JS_GC_HEAP_KIND, gcHeapChunkUnused,
-    "Memory on the garbage-collected JavaScript heap, within chunks, that "
-    "could be holding useful data but currently isn't.",
+        ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-dirty-unused"),
+                          nsIMemoryReporter::KIND_OTHER,
+                          data.gcHeapChunkDirtyUnused,
+    "The same as 'explicit/js/gc-heap-chunk-dirty-unused'.  Shown here for "
+    "easy comparison with other 'js-gc' reporters.",
                           callback, closure);
 
-        ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-unused"),
-                          nsIMemoryReporter::KIND_OTHER, gcHeapChunkUnused,
-    "The same as 'explicit/js/gc-heap-chunk-unused'.  Shown here for "
-    "easy comparison with 'js-gc-heap' and 'js-gc-heap-arena-unused'.",
-                          callback, closure);
-
-        ReportMemoryBytes(pathPrefix +
-                          NS_LITERAL_CSTRING("gc-heap-chunk-admin"),
-                          JS_GC_HEAP_KIND, gcHeapChunkAdmin,
-    "Memory on the garbage-collected JavaScript heap, within chunks, that is "
-    "used to hold internal book-keeping information.",
+        ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-clean-unused"),
+                          nsIMemoryReporter::KIND_OTHER,
+                          data.gcHeapChunkCleanUnused,
+    "The same as 'explicit/js/gc-heap-chunk-clean-unused'.  Shown here for "
+    "easy comparison with other 'js-gc' reporters.",
                           callback, closure);
 
         ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-arena-unused"),
-                          nsIMemoryReporter::KIND_OTHER, gcHeapArenaUnused,
+                          nsIMemoryReporter::KIND_OTHER, data.gcHeapArenaUnused,
     "Memory on the garbage-collected JavaScript heap, within arenas, that "
     "could be holding useful data but currently isn't.  This is the sum of "
     "all compartments' 'gc-heap/arena-unused' numbers.",
                           callback, closure);
 
         ReportMemoryPercentage(NS_LITERAL_CSTRING("js-gc-heap-unused-fraction"),
                                nsIMemoryReporter::KIND_OTHER,
-                               gcHeapUnusedPercentage,
+                               data.gcHeapUnusedPercentage,
     "Fraction of the garbage-collected JavaScript heap that is unused. "
-    "Computed as ('js-gc-heap-chunk-unused' + 'js-gc-heap-arena-unused') / "
+    "Computed as ('js-gc-heap-chunk-clean-unused' + "
+    "'js-gc-heap-chunk-dirty-unused' + 'js-gc-heap-arena-unused') / "
     "'js-gc-heap'.",
                                callback, closure);
 
-        ReportJSStackSizeForRuntime(rt, pathPrefix, callback, closure);
-
         return NS_OK;
     }
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(
   XPConnectJSCompartmentsMultiReporter
 , nsIMemoryMultiReporter
 )
@@ -1868,16 +1918,17 @@ DiagnosticMemoryCallback(void *ptr, size
 {
     return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
 }
 #endif
 
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
  : mXPConnect(aXPConnect),
    mJSRuntime(nsnull),
+   mJSCycleCollectionContext(nsnull),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
    mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)),
    mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
    mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
--- a/js/src/xpconnect/src/xpcmaps.cpp
+++ b/js/src/xpconnect/src/xpcmaps.cpp
@@ -542,17 +542,17 @@ XPCNativeScriptableSharedMap::~XPCNative
 {
     if(mTable)
         JS_DHashTableDestroy(mTable);
 }
 
 JSBool
 XPCNativeScriptableSharedMap::GetNewOrUsed(JSUint32 flags,
                                            char* name,
-                                           JSBool isGlobal,
+                                           PRBool isGlobal,
                                            PRUint32 interfacesBitmap,
                                            XPCNativeScriptableInfo* si)
 {
     NS_PRECONDITION(name,"bad param");
     NS_PRECONDITION(si,"bad param");
 
     XPCNativeScriptableShared key(flags, name, interfacesBitmap);
     Entry* entry = (Entry*)
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -649,16 +649,17 @@ private:
 // no virtuals. no refcounting.
 class XPCJSRuntime
 {
 public:
     static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect);
 
     JSRuntime*     GetJSRuntime() const {return mJSRuntime;}
     nsXPConnect*   GetXPConnect() const {return mXPConnect;}
+    JSContext*     GetJSCycleCollectionContext();
 
     JSObject2WrappedJSMap*     GetWrappedJSMap()        const
         {return mWrappedJSMap;}
 
     IID2WrappedJSClassMap*     GetWrappedJSClassMap()   const
         {return mWrappedJSClassMap;}
 
     IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const
@@ -780,33 +781,34 @@ public:
 private:
    JSDHashTable* DEBUG_WrappedNativeHashtable;
 public:
 #endif
 
     void AddGCCallback(JSGCCallback cb);
     void RemoveGCCallback(JSGCCallback cb);
 
-    static void ActivityCallback(void *arg, PRBool active);
+    static void ActivityCallback(void *arg, JSBool active);
 
 private:
     XPCJSRuntime(); // no implementation
     XPCJSRuntime(nsXPConnect* aXPConnect);
 
     // The caller must be holding the GC lock
     void RescheduleWatchdog(XPCContext* ccx);
 
     static void WatchdogMain(void *arg);
 
     static const char* mStrings[IDX_TOTAL_COUNT];
     jsid mStrIDs[IDX_TOTAL_COUNT];
     jsval mStrJSVals[IDX_TOTAL_COUNT];
 
-    nsXPConnect* mXPConnect;
-    JSRuntime*  mJSRuntime;
+    nsXPConnect*             mXPConnect;
+    JSRuntime*               mJSRuntime;
+    JSContext*               mJSCycleCollectionContext;
     JSObject2WrappedJSMap*   mWrappedJSMap;
     IID2WrappedJSClassMap*   mWrappedJSClassMap;
     IID2NativeInterfaceMap*  mIID2NativeInterfaceMap;
     ClassInfo2NativeSetMap*  mClassInfo2NativeSetMap;
     NativeSetMap*            mNativeSetMap;
     IID2ThisTranslatorMap*   mThisTranslatorMap;
     XPCNativeScriptableSharedMap* mNativeScriptableSharedMap;
     XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
@@ -1029,17 +1031,16 @@ public:
 
     inline JSBool                       IsValid() const ;
 
     inline nsXPConnect*                 GetXPConnect() const ;
     inline XPCJSRuntime*                GetRuntime() const ;
     inline XPCPerThreadData*            GetThreadData() const ;
     inline XPCContext*                  GetXPCContext() const ;
     inline JSContext*                   GetJSContext() const ;
-    inline JSContext*                   GetSafeJSContext() const ;
     inline JSBool                       GetContextPopRequired() const ;
     inline XPCContext::LangType         GetCallerLanguage() const ;
     inline XPCContext::LangType         GetPrevCallerLanguage() const ;
     inline XPCCallContext*              GetPrevCallContext() const ;
 
     /*
      * The 'scope for new JSObjects' will be the scope for objects created when
      * carrying out a JS/C++ call. This member is only available if HAVE_SCOPE.
--- a/js/src/xpconnect/src/xpcpublic.h
+++ b/js/src/xpconnect/src/xpcpublic.h
@@ -219,33 +219,43 @@ struct CompartmentStats
     PRInt64 tjitDataAllocatorsMain;
     PRInt64 tjitDataAllocatorsReserve;
 #endif
 };
 
 struct IterateData
 {
     IterateData()
-    : compartmentStatsVector(), currCompartmentStats(NULL) { }
+      : stackSize(0),
+        gcHeapChunkTotal(0),
+        gcHeapChunkCleanUnused(0),
+        gcHeapChunkDirtyUnused(0),
+        gcHeapArenaUnused(0),
+        gcHeapChunkAdmin(0),
+        gcHeapUnusedPercentage(0),
+        compartmentStatsVector(),
+        currCompartmentStats(NULL) { }
+
+    PRInt64 stackSize;
+    PRInt64 gcHeapChunkTotal;
+    PRInt64 gcHeapChunkCleanUnused;
+    PRInt64 gcHeapChunkDirtyUnused;
+    PRInt64 gcHeapArenaUnused;
+    PRInt64 gcHeapChunkAdmin;
+    PRInt64 gcHeapUnusedPercentage;
 
     js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
     CompartmentStats *currCompartmentStats;
 };
 
 JSBool
 CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data);
 
 void
-ReportCompartmentStats(const CompartmentStats &stats,
-                       const nsACString &pathPrefix,
-                       nsIMemoryMultiReporterCallback *callback,
-                       nsISupports *closure);
-
-void
-ReportJSStackSizeForRuntime(JSRuntime *rt, const nsACString &pathPrefix,
-                            nsIMemoryMultiReporterCallback *callback,
-                            nsISupports *closure);
+ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
+                     nsIMemoryMultiReporterCallback *callback,
+                     nsISupports *closure);
 
 } // namespace memory
 } // namespace xpconnect
 } // namespace mozilla
 
 #endif
--- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
@@ -807,19 +807,21 @@ XPC_WN_Equality(JSContext *cx, JSObject 
         return JS_TRUE;
     }
 
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
     if(si && si->GetFlags().WantEquality())
     {
-        nsresult rv = si->GetCallback()->Equality(wrapper, cx, obj, v, bp);
+        PRBool res;
+        nsresult rv = si->GetCallback()->Equality(wrapper, cx, obj, v, &res);
         if(NS_FAILED(rv))
             return Throw(rv, cx);
+        *bp = res;
     }
     else if(!JSVAL_IS_PRIMITIVE(v))
     {
         JSObject *other = JSVAL_TO_OBJECT(v);
 
         *bp = (obj == other ||
                XPC_GetIdentityObject(cx, obj) ==
                XPC_GetIdentityObject(cx, other));
@@ -1060,18 +1062,20 @@ XPC_WN_Helper_Construct(JSContext *cx, u
     Construct(wrapper, cx, obj, argc, JS_ARGV(cx, vp), vp, &retval);
     POST_HELPER_STUB
 }
 
 static JSBool
 XPC_WN_Helper_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp)
 {
     SLIM_LOG_WILL_MORPH(cx, obj);
+    PRBool retval2;
     PRE_HELPER_STUB_NO_SLIM
-    HasInstance(wrapper, cx, obj, *valp, bp, &retval);
+    HasInstance(wrapper, cx, obj, *valp, &retval2, &retval);
+    *bp = retval2;
     POST_HELPER_STUB
 }
 
 static void
 XPC_WN_Helper_Finalize(JSContext *cx, JSObject *obj)
 {
     nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
     if(IS_SLIM_WRAPPER(obj))
@@ -1099,17 +1103,17 @@ XPC_WN_Helper_Trace(JSTracer *trc, JSObj
     MarkWrappedNative(trc, obj, true);
 }
 
 static JSBool
 XPC_WN_Helper_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                          JSObject **objp)
 {
     nsresult rv = NS_OK;
-    JSBool retval = JS_TRUE;
+    PRBool retval = JS_TRUE;
     JSObject* obj2FromScriptable = nsnull;
     if(IS_SLIM_WRAPPER(obj))
     {
         XPCNativeScriptableInfo *si =
             GetSlimWrapperProto(obj)->GetScriptableInfo();
         if(!si->GetFlags().WantNewResolve())
             return retval;
 
--- a/js/src/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp
@@ -202,17 +202,17 @@ holder_get(JSContext *cx, JSObject *wrap
 
     JSObject *holder = GetHolder(wrapper);
 
     XPCWrappedNative *wn = GetWrappedNativeFromHolder(holder);
     if (NATIVE_HAS_FLAG(wn, WantGetProperty)) {
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, holder))
             return false;
-        JSBool retval = true;
+        PRBool retval = true;
         nsresult rv = wn->GetScriptableCallback()->GetProperty(wn, cx, wrapper, id, vp, &retval);
         if (NS_FAILED(rv) || !retval) {
             if (retval)
                 XPCThrower::Throw(rv, cx);
             return false;
         }
     }
     return true;
@@ -228,17 +228,17 @@ holder_set(JSContext *cx, JSObject *wrap
         return true;
     }
 
     XPCWrappedNative *wn = GetWrappedNativeFromHolder(holder);
     if (NATIVE_HAS_FLAG(wn, WantSetProperty)) {
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, holder))
             return false;
-        JSBool retval = true;
+        PRBool retval = true;
         nsresult rv = wn->GetScriptableCallback()->SetProperty(wn, cx, wrapper, id, vp, &retval);
         if (NS_FAILED(rv) || !retval) {
             if (retval)
                 XPCThrower::Throw(rv, cx);
             return false;
         }
     }
     return true;
@@ -482,17 +482,17 @@ XrayWrapper<Base>::resolveOwnProperty(JS
         XPCWrappedNative *wn = GetWrappedNativeFromHolder(holder);
 
         // Run the resolve hook of the wrapped native.
         if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) {
             desc->obj = nsnull;
             return true;
         }
 
-        JSBool retval = true;
+        PRBool retval = true;
         JSObject *pobj = NULL;
         nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, wrapper, id,
                                                                          flags, &pobj, &retval);
         if (NS_FAILED(rv)) {
             if (retval)
                 XPCThrower::Throw(rv, cx);
             return false;
         }
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -3638,17 +3638,19 @@ nsCSSRendering::GetTextDecorationRectInt
   NS_ASSERTION(aStyle <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY,
                "Invalid aStyle value");
 
   if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE)
     return gfxRect(0, 0, 0, 0);
 
   PRBool canLiftUnderline = aDescentLimit >= 0.0;
 
-  gfxRect r(NS_floor(aPt.x + 0.5), 0, NS_round(aLineSize.width), 0);
+  const gfxFloat left  = NS_floor(aPt.x + 0.5),
+                 right = NS_floor(aPt.x + aLineSize.width + 0.5);
+  gfxRect r(left, 0, right - left, 0);
 
   gfxFloat lineHeight = NS_round(aLineSize.height);
   lineHeight = NS_MAX(lineHeight, 1.0);
 
   gfxFloat ascent = NS_round(aAscent);
   gfxFloat descentLimit = NS_floor(aDescentLimit);
 
   gfxFloat suggestedMaxRectHeight = NS_MAX(NS_MIN(ascent, descentLimit), 1.0);
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/673770.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html style="-moz-column-width: 1px;">
+  <head>
+    <script>
+      function boom()
+      {
+        document.documentElement.offsetHeight;
+        document.body.style.height = "8px";
+        document.documentElement.style.fontSize = "22050893469px";
+        document.documentElement.offsetHeight;
+        document.getElementById("x").style.counterReset = "chicken";
+        document.documentElement.offsetHeight;
+      }
+    </script>
+  </head>
+  <body style="-moz-column-width: 1px;" onload="boom();">
+    <hr size="100" color="blue"><div style="position: absolute;"></div><div id="x" style="height: 5px;"></div>
+  </body>
+</html>
+
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -363,8 +363,9 @@ load 650499-1.html
 load 660416.html
 load text-overflow-form-elements.html
 load text-overflow-iframe.html
 load text-overflow-bug666751-1.html
 load text-overflow-bug666751-2.html
 asserts(2) load text-overflow-bug670564.xhtml # asserts(2) for bug 436470
 load text-overflow-bug671796.xhtml
 load 667025.html
+asserts(14) load 673770.html # bug 569193 and bug 459597
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -4819,66 +4819,85 @@ nsBlockFrame::AddFrames(nsFrameList& aFr
     NS_ASSERTION(!aFrameList.ContainsFrame(mBullet),
                  "Trying to make mBullet prev sibling to itself");
     aPrevSibling = mBullet;
   }
   
   nsIPresShell *presShell = PresContext()->PresShell();
 
   // Attempt to find the line that contains the previous sibling
-  nsLineList::iterator prevSibLine = end_lines();
+  nsFrameList overflowFrames;
+  nsLineList* lineList = &mLines;
+  nsLineList::iterator prevSibLine = lineList->end();
   PRInt32 prevSiblingIndex = -1;
   if (aPrevSibling) {
     // XXX_perf This is technically O(N^2) in some cases, but by using
     // RFind instead of Find, we make it O(N) in the most common case,
     // which is appending content.
 
     // Find the line that contains the previous sibling
-    if (! nsLineBox::RFindLineContaining(aPrevSibling,
-                                         begin_lines(), prevSibLine,
-                                         mFrames.LastChild(),
-                                         &prevSiblingIndex)) {
-      // Note: defensive code! RFindLineContaining must not return
-      // false in this case, so if it does...
-      NS_NOTREACHED("prev sibling not in line list");
-      aPrevSibling = nsnull;
-      prevSibLine = end_lines();
+    if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
+                                        prevSibLine, mFrames.LastChild(),
+                                        &prevSiblingIndex)) {
+      // Not in mLines - try overflow lines.
+      lineList = GetOverflowLines();
+      if (lineList) {
+        prevSibLine = lineList->end();
+        prevSiblingIndex = -1;
+        overflowFrames = nsFrameList(lineList->front()->mFirstChild,
+                                     lineList->back()->LastChild());
+        if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
+                                            prevSibLine,
+                                            overflowFrames.LastChild(),
+                                            &prevSiblingIndex)) {
+          lineList = nsnull;
+        }
+      }
+      if (!lineList) {
+        // Note: defensive code! RFindLineContaining must not return
+        // false in this case, so if it does...
+        NS_NOTREACHED("prev sibling not in line list");
+        lineList = &mLines;
+        aPrevSibling = nsnull;
+        prevSibLine = lineList->end();
+      }
     }
   }
 
   // Find the frame following aPrevSibling so that we can join up the
   // two lists of frames.
   if (aPrevSibling) {
     // Split line containing aPrevSibling in two if the insertion
     // point is somewhere in the middle of the line.
     PRInt32 rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
     if (rem) {
       // Split the line in two where the frame(s) are being inserted.
       nsLineBox* line = NS_NewLineBox(presShell, aPrevSibling->GetNextSibling(), rem, PR_FALSE);
       if (!line) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
-      mLines.after_insert(prevSibLine, line);
+      lineList->after_insert(prevSibLine, line);
       prevSibLine->SetChildCount(prevSibLine->GetChildCount() - rem);
       // Mark prevSibLine dirty and as needing textrun invalidation, since
       // we may be breaking up text in the line. Its previous line may also
       // need to be invalidated because it may be able to pull some text up.
       MarkLineDirty(prevSibLine);
       // The new line will also need its textruns recomputed because of the
       // frame changes.
       line->MarkDirty();
       line->SetInvalidateTextRuns(PR_TRUE);
     }
   }
-  else if (! mLines.empty()) {
-    mLines.front()->MarkDirty();
-    mLines.front()->SetInvalidateTextRuns(PR_TRUE);
-  }
+  else if (! lineList->empty()) {
+    lineList->front()->MarkDirty();
+    lineList->front()->SetInvalidateTextRuns(PR_TRUE);
+  }
+  nsFrameList& frames = lineList == &mLines ? mFrames : overflowFrames;
   const nsFrameList::Slice& newFrames =
-    mFrames.InsertFrames(nsnull, aPrevSibling, aFrameList);
+    frames.InsertFrames(nsnull, aPrevSibling, aFrameList);
 
   // Walk through the new frames being added and update the line data
   // structures to fit.
   for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) {
     nsIFrame* newFrame = e.get();
     NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
                  "Unexpected aPrevSibling");
     NS_ASSERTION(newFrame->GetType() != nsGkAtoms::placeholderFrame ||
@@ -4888,33 +4907,33 @@ nsBlockFrame::AddFrames(nsFrameList& aFr
 
     PRBool isBlock = newFrame->GetStyleDisplay()->IsBlockOutside();
 
     // If the frame is a block frame, or if there is no previous line or if the
     // previous line is a block line we need to make a new line.  We also make
     // a new line, as an optimization, in the two cases we know we'll need it:
     // if the previous line ended with a <br>, or if it has significant whitespace
     // and ended in a newline.
-    if (isBlock || prevSibLine == end_lines() || prevSibLine->IsBlock() ||
+    if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
         (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
       // Create a new line for the frame and add its line to the line
       // list.
       nsLineBox* line = NS_NewLineBox(presShell, newFrame, 1, isBlock);
       if (!line) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
-      if (prevSibLine != end_lines()) {
+      if (prevSibLine != lineList->end()) {
         // Append new line after prevSibLine
-        mLines.after_insert(prevSibLine, line);
+        lineList->after_insert(prevSibLine, line);
         ++prevSibLine;
       }
       else {
         // New line is going before the other lines
-        mLines.push_front(line);
-        prevSibLine = begin_lines();
+        lineList->push_front(line);
+        prevSibLine = lineList->begin();
       }
     }
     else {
       prevSibLine->SetChildCount(prevSibLine->GetChildCount() + 1);
       // We're adding inline content to prevSibLine, so we need to mark it
       // dirty, ensure its textruns are recomputed, and possibly do the same
       // to its previous line since that line may be able to pull content up.
       MarkLineDirty(prevSibLine);
@@ -6079,80 +6098,16 @@ nsBlockFrame::IsVisibleInSelection(nsISe
     return PR_TRUE;
 
   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
   PRBool visible;
   nsresult rv = aSelection->ContainsNode(node, PR_TRUE, &visible);
   return NS_SUCCEEDED(rv) && visible;
 }
 
-/* virtual */ void
-nsBlockFrame::PaintTextDecorationLine(
-                gfxContext* aCtx, 
-                const nsPoint& aPt,
-                nsLineBox* aLine,
-                nscolor aColor, 
-                PRUint8 aStyle,
-                gfxFloat aOffset, 
-                gfxFloat aAscent, 
-                gfxFloat aSize,
-                const nsCharClipDisplayItem::ClipEdges& aClipEdges,
-                const PRUint8 aDecoration) 
-{
-  NS_ASSERTION(!aLine->IsBlock(), "Why did we ask for decorations on a block?");
-
-  nscoord start = aLine->mBounds.x;
-  nscoord width = aLine->mBounds.width;
-
-  AdjustForTextIndent(aLine, start, width);
-  nscoord x = start + aPt.x;
-  aClipEdges.Intersect(&x, &width);
-
-  // Only paint if we have a positive width
-  if (width > 0) {
-    gfxPoint pt(PresContext()->AppUnitsToGfxUnits(x),
-                PresContext()->AppUnitsToGfxUnits(aLine->mBounds.y + aPt.y));
-    gfxSize size(PresContext()->AppUnitsToGfxUnits(width), aSize);
-    nsCSSRendering::PaintDecorationLine(
-      aCtx, aColor, pt, size,
-      PresContext()->AppUnitsToGfxUnits(aLine->GetAscent()),
-      aOffset, aDecoration, aStyle);
-  }
-}
-
-/*virtual*/ void
-nsBlockFrame::AdjustForTextIndent(const nsLineBox* aLine,
-                                  nscoord& start,
-                                  nscoord& width)
-{
-  if (!GetPrevContinuation() && aLine == begin_lines().get() &&
-      (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR)) {
-    // Adjust for the text-indent.  See similar code in
-    // nsLineLayout::BeginLineReflow.
-    const nsStyleCoord &textIndent = GetStyleText()->mTextIndent;
-    nscoord pctBasis = 0;
-    if (textIndent.HasPercent()) {
-      // Only work out the percentage basis if we need to.
-      // It's a percentage of the containing block width.
-      nsIFrame* containingBlock =
-        nsHTMLReflowState::GetContainingBlockFor(this);
-      NS_ASSERTION(containingBlock, "Must have containing block!");
-      pctBasis = containingBlock->GetContentRect().width;
-    }
-    nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis);
-
-    // Adjust the start position and the width of the decoration by the
-    // value of the indent.  Note that indent can be negative; that's OK.
-    // It'll just increase the width (which can also happen to be
-    // negative!).
-    start += indent;
-    width -= indent;
-  }
-}
-
 #ifdef DEBUG
 static void DebugOutputDrawLine(PRInt32 aDepth, nsLineBox* aLine, PRBool aDrawn) {
   if (nsBlockFrame::gNoisyDamageRepair) {
     nsFrame::IndentBy(stdout, aDepth+1);
     nsRect lineArea = aLine->GetVisualOverflowArea();
     printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
            aDrawn ? "draw" : "skip",
            static_cast<void*>(aLine),
@@ -6190,23 +6145,16 @@ DisplayLine(nsDisplayListBuilder* aBuild
   PRBool lineMayHaveTextOverflow = aTextOverflow && lineInline;
   if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame) &&
       !lineMayHaveTextOverflow)
     return NS_OK;
 
   nsDisplayListCollection collection;
   nsresult rv;
   nsDisplayList aboveTextDecorations;
-  if (lineInline) {
-    // Display the text-decoration for the hypothetical anonymous inline box
-    // that wraps these inlines
-    rv = aFrame->DisplayTextDecorations(aBuilder, collection.Content(),
-                                        &aboveTextDecorations, aLine);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
 
   // Block-level child backgrounds go on the blockBorderBackgrounds list ...
   // Inline-level child backgrounds go on the regular child content list.
   nsDisplayListSet childLists(collection,
     lineInline ? collection.Content() : collection.BlockBorderBackgrounds());
   nsIFrame* kid = aLine->mFirstChild;
   PRInt32 n = aLine->GetChildCount();
   while (--n >= 0) {
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -356,35 +356,16 @@ protected:
     return aPresContext->StyleSet()->
       ProbePseudoElementStyle(mContent->AsElement(),
                               nsCSSPseudoElements::ePseudo_firstLetter,
                               mStyleContext);
   }
 #endif
 #endif
 
-  /*
-   * Overides member function of nsHTMLContainerFrame. Needed to handle the 
-   * lines in a nsBlockFrame properly.
-   */
-  virtual void PaintTextDecorationLine(gfxContext* aCtx,
-                                       const nsPoint& aPt,
-                                       nsLineBox* aLine,
-                                       nscolor aColor,
-                                       PRUint8 aStyle,
-                                       gfxFloat aOffset,
-                                       gfxFloat aAscent,
-                                       gfxFloat aSize,
-                                       const nsCharClipDisplayItem::ClipEdges& aClipEdges,
-                                       const PRUint8 aDecoration);
-
-  virtual void AdjustForTextIndent(const nsLineBox* aLine,
-                                   nscoord& start,
-                                   nscoord& width);
-
   void TryAllLines(nsLineList::iterator* aIterator,
                    nsLineList::iterator* aStartIterator,
                    nsLineList::iterator* aEndIterator,
                    PRBool* aInOverflowLines);
 
   void SetFlags(nsFrameState aFlags) {
     mState &= ~NS_BLOCK_FLAGS_MASK;
     mState |= aFlags;
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -221,39 +221,42 @@ nsFirstLetterFrame::Reflow(nsPresContext
     ll.SetInFirstLetter(PR_TRUE);
     ll.SetFirstLetterStyleOK(PR_TRUE);
 
     kid->WillReflow(aPresContext);
     kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus);
 
     ll.EndLineReflow();
     ll.SetInFirstLetter(PR_FALSE);
+
+    // In the floating first-letter case, we need to set this ourselves;
+    // nsLineLayout::BeginSpan will set it in the other case
+    mBaseline = aMetrics.ascent;
   }
   else {
     // Pretend we are a span and reflow the child frame
     nsLineLayout* ll = aReflowState.mLineLayout;
     PRBool        pushedFrame;
 
     ll->SetInFirstLetter(
       mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
-    ll->BeginSpan(this, &aReflowState, bp.left, availSize.width);
+    ll->BeginSpan(this, &aReflowState, bp.left, availSize.width, &mBaseline);
     ll->ReflowFrame(kid, aReflowStatus, &aMetrics, pushedFrame);
     ll->EndSpan(this);
     ll->SetInFirstLetter(PR_FALSE);
   }
 
   // Place and size the child and update the output metrics
   kid->SetRect(nsRect(bp.left, bp.top, aMetrics.width, aMetrics.height));
   kid->FinishAndStoreOverflow(&aMetrics);
   kid->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
 
   aMetrics.width += lr;
   aMetrics.height += tb;
   aMetrics.ascent += bp.top;
-  mBaseline = aMetrics.ascent;
 
   // Ensure that the overflow rect contains the child textframe's overflow rect.
   // Note that if this is floating, the overline/underline drawable area is in
   // the overflow rect of the child textframe.
   aMetrics.UnionOverflowAreasWithDesiredBounds();
   ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
 
   if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
--- a/layout/generic/nsHTMLContainerFrame.cpp
+++ b/layout/generic/nsHTMLContainerFrame.cpp
@@ -63,557 +63,28 @@
 #include "gfxFont.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsDisplayList.h"
 #include "nsBlockFrame.h"
 #include "nsLineBox.h"
 #include "nsDisplayList.h"
 #include "nsCSSRendering.h"
 
-class nsDisplayTextDecoration : public nsCharClipDisplayItem {
-public:
-  nsDisplayTextDecoration(nsDisplayListBuilder* aBuilder,
-                          nsHTMLContainerFrame* aFrame, PRUint8 aDecoration,
-                          nscolor aColor, PRUint8 aStyle, nsLineBox* aLine)
-    : nsCharClipDisplayItem(aBuilder, aFrame), mLine(aLine), mColor(aColor),
-      mDecoration(aDecoration), mStyle(aStyle) {
-    MOZ_COUNT_CTOR(nsDisplayTextDecoration);
-  }
-#ifdef NS_BUILD_REFCNT_LOGGING
-  virtual ~nsDisplayTextDecoration() {
-    MOZ_COUNT_DTOR(nsDisplayTextDecoration);
-  }
-#endif
-
-  virtual void Paint(nsDisplayListBuilder* aBuilder,
-                     nsRenderingContext* aCtx);
-  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
-  NS_DISPLAY_DECL_NAME("TextDecoration", TYPE_TEXT_DECORATION)
-
-  virtual PRUint32 GetPerFrameKey()
-  {
-    return TYPE_TEXT_DECORATION | (mDecoration << TYPE_BITS);
-  }
-
-private:
-  nsLineBox* mLine;
-  nscolor    mColor;
-  PRUint8    mDecoration;
-  PRUint8    mStyle;
-};
-
-void
-nsDisplayTextDecoration::Paint(nsDisplayListBuilder* aBuilder,
-                               nsRenderingContext* aCtx)
-{
-  nsRefPtr<nsFontMetrics> fm;
-  nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm));
-  gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
-  gfxFont* firstFont = fontGroup->GetFontAt(0);
-  if (!firstFont)
-    return; // OOM
-  const gfxFont::Metrics& metrics = firstFont->GetMetrics();
-
-  gfxFloat ascent;
-  // The ascent of first-letter frame's text may not be the same as the ascent
-  // of the font metrics. Because that may use the tight box of the actual
-  // glyph.
-  if (mFrame->GetType() == nsGkAtoms::letterFrame) {
-    // Note that nsFirstLetterFrame::GetFirstLetterBaseline() returns
-    // |border-top + padding-top + ascent|. But we only need the ascent value.
-    // Because they will be added in PaintTextDecorationLine.
-    nsFirstLetterFrame* letterFrame = static_cast<nsFirstLetterFrame*>(mFrame);
-    nscoord tmp = letterFrame->GetFirstLetterBaseline();
-    tmp -= letterFrame->GetUsedBorderAndPadding().top;
-    ascent = letterFrame->PresContext()->AppUnitsToGfxUnits(tmp);
-  } else {
-    ascent = metrics.maxAscent;
-  }
-
-  nsPoint pt = ToReferenceFrame();
-  nsHTMLContainerFrame* f = static_cast<nsHTMLContainerFrame*>(mFrame);
-  if (mDecoration == NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
-    gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
-    f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
-                               mStyle, underlineOffset, ascent,
-                               metrics.underlineSize, Edges(), mDecoration);
-  } else if (mDecoration == NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
-    f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
-                               mStyle, metrics.maxAscent, ascent,
-                               metrics.underlineSize, Edges(), mDecoration);
-  } else {
-    f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
-                               mStyle, metrics.strikeoutOffset, ascent,
-                               metrics.strikeoutSize, Edges(), mDecoration);
-  }
-}
-
-nsRect
-nsDisplayTextDecoration::GetBounds(nsDisplayListBuilder* aBuilder)
-{
-  return mFrame->GetVisualOverflowRect() + ToReferenceFrame();
-}
-
-class nsDisplayTextShadow : public nsCharClipDisplayItem {
-public:
-  nsDisplayTextShadow(nsDisplayListBuilder* aBuilder,
-                      nsHTMLContainerFrame* aFrame,
-                      const PRUint8 aDecoration, PRUint8 aUnderlineStyle,
-                      PRUint8 aOverlineStyle, PRUint8 aStrikeThroughStyle,
-                      nsLineBox* aLine)
-    : nsCharClipDisplayItem(aBuilder, aFrame), mLine(aLine),
-      mDecorationFlags(aDecoration), mUnderlineStyle(aUnderlineStyle),
-      mOverlineStyle(aOverlineStyle), mStrikeThroughStyle(aStrikeThroughStyle) {
-    MOZ_COUNT_CTOR(nsDisplayTextShadow);
-  }
-  virtual ~nsDisplayTextShadow() {
-    MOZ_COUNT_DTOR(nsDisplayTextShadow);
-  }
-
-  virtual void Paint(nsDisplayListBuilder* aBuilder,
-                     nsRenderingContext* aCtx);
-  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
-  NS_DISPLAY_DECL_NAME("TextShadowContainer", TYPE_TEXT_SHADOW)
-private:
-  nsLineBox*    mLine;
-  PRUint8       mDecorationFlags;
-  PRUint8       mUnderlineStyle;
-  PRUint8       mOverlineStyle;
-  PRUint8       mStrikeThroughStyle;
-};
-
-void
-nsDisplayTextShadow::Paint(nsDisplayListBuilder* aBuilder,
-                           nsRenderingContext* aCtx)
-{
-  nsRefPtr<nsFontMetrics> fm;
-  nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm));
-  gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
-  gfxFont* firstFont = fontGroup->GetFontAt(0);
-  if (!firstFont)
-    return; // OOM
-
-  const gfxFont::Metrics& metrics = firstFont->GetMetrics();
-  gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
-
-  nsHTMLContainerFrame* f = static_cast<nsHTMLContainerFrame*>(mFrame);
-  nsPresContext* presContext = mFrame->PresContext();
-  gfxContext* thebesCtx = aCtx->ThebesContext();
-
-  gfxFloat ascent;
-  gfxFloat lineWidth;
-  nscoord start;
-  if (mLine) {
-    // Block frames give us an nsLineBox, so we must use that
-    nscoord width = mLine->mBounds.width;
-    start = mLine->mBounds.x;
-    f->AdjustForTextIndent(mLine, start, width);
-    if (width <= 0)
-      return;
-
-    lineWidth = presContext->AppUnitsToGfxUnits(width);
-    ascent = presContext->AppUnitsToGfxUnits(mLine->GetAscent());
-  } else {
-    // For inline frames, we must use the frame's geometry
-    lineWidth = presContext->AppUnitsToGfxUnits(mFrame->GetContentRect().width);
-
-    // The ascent of :first-letter frame's text may not be the same as the ascent
-    // of the font metrics, because it may use the tight box of the actual
-    // glyph.
-    if (mFrame->GetType() == nsGkAtoms::letterFrame) {
-      // Note that nsFirstLetterFrame::GetFirstLetterBaseline() returns
-      // |border-top + padding-top + ascent|. But we only need the ascent value,
-      // because those will be added in PaintTextDecorationLine.
-      nsFirstLetterFrame* letterFrame = static_cast<nsFirstLetterFrame*>(mFrame);
-      nscoord tmp = letterFrame->GetFirstLetterBaseline();
-      tmp -= letterFrame->GetUsedBorderAndPadding().top;
-      ascent = presContext->AppUnitsToGfxUnits(tmp);
-    } else {
-      ascent = metrics.maxAscent;
-    }
-  }
-
-  nsCSSShadowArray* shadowList = mFrame->GetStyleText()->mTextShadow;
-  NS_ABORT_IF_FALSE(shadowList,
-                    "Why did we make a display list item if we have no shadows?");
-
-  // Get the rects for each text decoration line, so we know how big we
-  // can make each shadow's surface
-  nsRect underlineRect;
-  nsRect overlineRect;
-  nsRect lineThroughRect;
-  if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
-    gfxSize size(lineWidth, metrics.underlineSize);
-    underlineRect = nsCSSRendering::GetTextDecorationRect(presContext, size,
-                       ascent, underlineOffset,
-                       NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
-                       mUnderlineStyle);
-  }
-  if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
-    gfxSize size(lineWidth, metrics.underlineSize);
-    overlineRect = nsCSSRendering::GetTextDecorationRect(presContext, size,
-                       ascent, metrics.maxAscent,
-                       NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, mOverlineStyle);
-  }
-  if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
-    gfxSize size(lineWidth, metrics.strikeoutSize);
-    lineThroughRect = nsCSSRendering::GetTextDecorationRect(presContext, size,
-                       ascent, metrics.strikeoutOffset,
-                       NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
-                       mStrikeThroughStyle);
-  }
-
-  for (PRUint32 i = shadowList->Length(); i > 0; --i) {
-    nsCSSShadowItem* shadow = shadowList->ShadowAt(i - 1);
-
-    nscolor shadowColor =
-      shadow->mHasColor ? shadow->mColor : mFrame->GetStyleColor()->mColor;
-
-    nsPoint pt = ToReferenceFrame() +
-      nsPoint(shadow->mXOffset, shadow->mYOffset);
-    nsPoint linePt;
-    if (mLine) {
-      linePt = nsPoint(start + pt.x, mLine->mBounds.y + pt.y);
-    } else {
-      linePt = mFrame->GetContentRect().TopLeft() - mFrame->GetPosition() + pt;
-    }
-
-    nsRect shadowRect(0, 0, 0, 0);
-    if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
-      shadowRect.UnionRect(shadowRect, underlineRect + linePt);
-    }
-    if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
-      shadowRect.UnionRect(shadowRect, overlineRect + linePt);
-    }
-    if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
-      shadowRect.UnionRect(shadowRect, lineThroughRect + linePt);
-    }
-
-    gfxContextAutoSaveRestore save(thebesCtx);
-    thebesCtx->NewPath();
-    thebesCtx->SetColor(gfxRGBA(shadowColor));
-
-    // Create our shadow surface, then paint the text decorations onto it
-    nsContextBoxBlur contextBoxBlur;
-    gfxContext* shadowCtx = contextBoxBlur.Init(shadowRect, 0, shadow->mRadius,
-                                                presContext->AppUnitsPerDevPixel(),
-                                                thebesCtx, mVisibleRect, nsnull);
-    if (!shadowCtx) {
-      continue;
-    }
-
-    const nsCharClipDisplayItem::ClipEdges clipEdges = this->Edges();
-    if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
-      f->PaintTextDecorationLine(shadowCtx, pt, mLine, shadowColor,
-                                 mUnderlineStyle, underlineOffset, ascent,
-                                 metrics.underlineSize, clipEdges,
-                                 NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE);
-    }
-    if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
-      f->PaintTextDecorationLine(shadowCtx, pt, mLine, shadowColor,
-                                 mOverlineStyle, metrics.maxAscent, ascent,
-                                 metrics.underlineSize, clipEdges,
-                                 NS_STYLE_TEXT_DECORATION_LINE_OVERLINE);
-    }
-    if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
-      f->PaintTextDecorationLine(shadowCtx, pt, mLine, shadowColor,
-                                 mStrikeThroughStyle, metrics.strikeoutOffset,
-                                 ascent, metrics.strikeoutSize, clipEdges,
-                                 NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH);
-    }
-
-    contextBoxBlur.DoPaint();
-  }
-}
-
-nsRect
-nsDisplayTextShadow::GetBounds(nsDisplayListBuilder* aBuilder)
-{
-  // Shadows are always painted in the overflow rect
-  return mFrame->GetVisualOverflowRect() + ToReferenceFrame();
-}
-
-nsresult
-nsHTMLContainerFrame::DisplayTextDecorations(nsDisplayListBuilder* aBuilder,
-                                             nsDisplayList* aBelowTextDecorations,
-                                             nsDisplayList* aAboveTextDecorations,
-                                             nsLineBox* aLine)
-{
-  if (eCompatibility_NavQuirks == PresContext()->CompatibilityMode())
-    return NS_OK;
-  if (!IsVisibleForPainting(aBuilder))
-    return NS_OK;
-
-  // Hide text decorations if we're currently hiding @font-face fallback text
-  nsRefPtr<nsFontMetrics> fm;
-  nsresult rv = nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (fm->GetThebesFontGroup()->ShouldSkipDrawing())
-    return NS_OK;
-
-  // Do standards mode painting of 'text-decoration's: under+overline
-  // behind children, line-through in front.  For Quirks mode, see
-  // nsTextFrame::PaintTextDecorations.  (See bug 1777.)
-  nscolor underColor, overColor, strikeColor;
-  PRUint8 underStyle, overStyle, strikeStyle;
-  PRUint8 decorations = NS_STYLE_TEXT_DECORATION_LINE_NONE;
-  GetTextDecorations(PresContext(), aLine != nsnull, decorations, underColor, 
-                     overColor, strikeColor, underStyle, overStyle,
-                     strikeStyle);
-
-  if (decorations == NS_STYLE_TEXT_DECORATION_LINE_NONE) {
-    return NS_OK;
-  }
-
-  // The text-shadow spec says that any text decorations must also have a
-  // shadow applied to them. So draw the shadows as part of the display
-  // list, underneath the text and all decorations.
-  if (GetStyleText()->mTextShadow) {
-    rv = aBelowTextDecorations->AppendNewToTop(new (aBuilder)
-      nsDisplayTextShadow(aBuilder, this, decorations, underStyle, overStyle,
-                          strikeStyle, aLine));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) &&
-      underStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
-    rv = aBelowTextDecorations->AppendNewToTop(new (aBuilder)
-      nsDisplayTextDecoration(aBuilder, this,
-                              NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
-                              underColor, underStyle, aLine));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) &&
-      overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
-    rv = aBelowTextDecorations->AppendNewToTop(new (aBuilder)
-      nsDisplayTextDecoration(aBuilder, this,
-                              NS_STYLE_TEXT_DECORATION_LINE_OVERLINE,
-                              overColor, overStyle, aLine));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) &&
-      strikeStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
-    rv = aAboveTextDecorations->AppendNewToTop(new (aBuilder)
-      nsDisplayTextDecoration(aBuilder, this,
-                              NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
-                              strikeColor, strikeStyle, aLine));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  return NS_OK;
-}
-
-nsresult
-nsHTMLContainerFrame::DisplayTextDecorationsAndChildren(
-    nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect,
-    const nsDisplayListSet& aLists)
-{
-  nsDisplayList aboveChildrenDecorations;
-  nsresult rv = DisplayTextDecorations(aBuilder, aLists.Content(),
-      &aboveChildrenDecorations, nsnull);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  rv = BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists,
-                                           DISPLAY_CHILD_INLINE);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  aLists.Content()->AppendToTop(&aboveChildrenDecorations);
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 nsHTMLContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                        const nsRect&           aDirtyRect,
                                        const nsDisplayListSet& aLists) {
   nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return DisplayTextDecorationsAndChildren(aBuilder, aDirtyRect, aLists);
-}
-
-static PRBool 
-HasTextFrameDescendantOrInFlow(nsIFrame* aFrame);
-
-/*virtual*/ void
-nsHTMLContainerFrame::PaintTextDecorationLine(
-                   gfxContext* aCtx, 
-                   const nsPoint& aPt,
-                   nsLineBox* aLine,
-                   nscolor aColor, 
-                   PRUint8 aStyle,
-                   gfxFloat aOffset, 
-                   gfxFloat aAscent, 
-                   gfxFloat aSize,
-                   const nsCharClipDisplayItem::ClipEdges& aClipEdges,
-                   const PRUint8 aDecoration) 
-{
-  NS_ASSERTION(!aLine, "Should not have passed a linebox to a non-block frame");
-  nsMargin bp = GetUsedBorderAndPadding();
-  PRIntn skip = GetSkipSides();
-  NS_FOR_CSS_SIDES(side) {
-    if (skip & (1 << side)) {
-      bp.Side(side) = 0;
-    }
-  }
-  nscoord x = aPt.x + bp.left;
-  nscoord innerWidth = mRect.width - bp.left - bp.right;
-  aClipEdges.Intersect(&x, &innerWidth);
-  gfxPoint pt(PresContext()->AppUnitsToGfxUnits(x),
-              PresContext()->AppUnitsToGfxUnits(bp.top + aPt.y));
-  gfxSize size(PresContext()->AppUnitsToGfxUnits(innerWidth), aSize);
-  nsCSSRendering::PaintDecorationLine(aCtx, aColor, pt, size, aAscent, aOffset,
-                                      aDecoration, aStyle);
-}
-
-/*virtual*/ void
-nsHTMLContainerFrame::AdjustForTextIndent(const nsLineBox* aLine,
-                                          nscoord& start,
-                                          nscoord& width)
-{
-  // This function is not for us.
-  // It allows nsBlockFrame to adjust the width/X position of its
-  // shadowed decorations if a text-indent rule is in effect.
-}
-
-void
-nsHTMLContainerFrame::GetTextDecorations(nsPresContext* aPresContext, 
-                                         PRBool aIsBlock,
-                                         PRUint8& aDecorations,
-                                         nscolor& aUnderColor, 
-                                         nscolor& aOverColor, 
-                                         nscolor& aStrikeColor,
-                                         PRUint8& aUnderStyle,
-                                         PRUint8& aOverStyle,
-                                         PRUint8& aStrikeStyle)
-{
-  aDecorations = NS_STYLE_TEXT_DECORATION_LINE_NONE;
-  if (!mStyleContext->HasTextDecorationLines()) {
-    // This is a necessary, but not sufficient, condition for text
-    // decorations.
-    return; 
-  }
-
-  if (!aIsBlock) {
-    const nsStyleTextReset* styleTextReset = this->GetStyleTextReset();
-    aDecorations = styleTextReset->mTextDecorationLine &
-                   NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK;
-    if (aDecorations) {
-      nscolor color =
-        this->GetVisitedDependentColor(eCSSProperty_text_decoration_color);
-      aUnderColor = aOverColor = aStrikeColor = color;
-      aUnderStyle = aOverStyle = aStrikeStyle =
-        styleTextReset->GetDecorationStyle();
-    }
-  }
-  else {
-    // We want to ignore a text-decoration from an ancestor frame that
-    // is redundant with one from a descendant frame.  This isn't just
-    // an optimization; the descendant frame's color specification
-    // must win.  At any point in the loop below, this variable
-    // indicates which decorations we are still paying attention to;
-    // it starts set to all possible decorations.
-    PRUint8 decorMask = NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK;
+  rv = BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists,
+                                           DISPLAY_CHILD_INLINE);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    // walk tree
-    for (nsIFrame* frame = this; frame; frame = frame->GetParent()) {
-      const nsStyleTextReset* styleTextReset = frame->GetStyleTextReset();
-      PRUint8 decors = styleTextReset->mTextDecorationLine & decorMask;
-      if (decors) {
-        // A *new* text-decoration is found.
-        nscolor color = frame->GetVisitedDependentColor(
-                                 eCSSProperty_text_decoration_color);
-        PRUint8 style = styleTextReset->GetDecorationStyle();
-
-        if (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE & decors) {
-          aUnderColor = color;
-          aUnderStyle = style;
-          decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
-          aDecorations |= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
-        }
-        if (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE & decors) {
-          aOverColor = color;
-          aOverStyle = style;
-          decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
-          aDecorations |= NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
-        }
-        if (NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH & decors) {
-          aStrikeColor = color;
-          aStrikeStyle = style;
-          decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
-          aDecorations |= NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
-        }
-      }
-      // If all possible decorations have now been specified, no
-      // further ancestor frames can affect the rendering.
-      if (!decorMask) {
-        break;
-      }
-
-      // CSS2.1 16.3.1 specifies that this property is not always
-      // inherited from ancestor boxes (frames in our terminology):
-      //
-      //      When specified on an inline element, [the
-      //      text-decoration property] affects all the boxes
-      //      generated by that element; for all other elements, the
-      //      decorations are propagated to an anonymous inline box
-      //      that wraps all the in-flow inline children of the
-      //      element, and to any block-level in-flow descendants. It
-      //      is not, however, further propagated to floating and
-      //      absolutely positioned descendants, nor to the contents
-      //      of 'inline-table' and 'inline-block' descendants.
-      //
-      // So do not look at the ancestor frame if this frame is any of
-      // the above.  This check is at the bottom of the loop because
-      // even if it's true we still want to look at decorations on the
-      // frame itself.
-      const nsStyleDisplay* styleDisplay = frame->GetStyleDisplay();
-      if (styleDisplay->IsFloating() ||
-          styleDisplay->IsAbsolutelyPositioned() ||
-          styleDisplay->IsInlineOutside()) {
-        break;
-      }
-    }
-  }
-  
-  if (aDecorations) {
-    // If this frame contains no text, we're required to ignore this property
-    if (!HasTextFrameDescendantOrInFlow(this)) {
-      aDecorations = NS_STYLE_TEXT_DECORATION_LINE_NONE;
-    }
-  }
-}
-
-static PRBool 
-HasTextFrameDescendant(nsIFrame* aParent)
-{
-  for (nsIFrame* kid = aParent->GetFirstChild(nsnull); kid;
-       kid = kid->GetNextSibling())
-  {
-    if (kid->GetType() == nsGkAtoms::textFrame) {
-      // This is only a candidate. We need to determine if this text
-      // frame is empty, as in containing only (non-pre) whitespace.
-      // See bug 20163.
-      if (!kid->IsEmpty()) {
-        return PR_TRUE;
-      }
-    }
-    if (HasTextFrameDescendant(kid)) {
-      return PR_TRUE;
-    }
-  }
-  return PR_FALSE;
-}
-
-static PRBool 
-HasTextFrameDescendantOrInFlow(nsIFrame* aFrame)
-{
-  for (nsIFrame *f = aFrame->GetFirstInFlow(); f; f = f->GetNextInFlow()) {
-    if (HasTextFrameDescendant(f))
-      return PR_TRUE;
-  }
-  return PR_FALSE;
+  return NS_OK;
 }
 
 /*
  * Create a next-in-flow for aFrame. Will return the newly created
  * frame in aNextInFlowResult <b>if and only if</b> a new frame is
  * created; otherwise nsnull is returned in aNextInFlowResult.
  */
 nsresult
--- a/layout/generic/nsHTMLContainerFrame.h
+++ b/layout/generic/nsHTMLContainerFrame.h
@@ -61,18 +61,16 @@ class nsLineBox;
 #ifdef DEBUG
 #define CRAZY_W (1000000*60)
 #define CRAZY_H CRAZY_W
 
 #define CRAZY_WIDTH(_x) (((_x) < -CRAZY_W) || ((_x) > CRAZY_W))
 #define CRAZY_HEIGHT(_y) (((_y) < -CRAZY_H) || ((_y) > CRAZY_H))
 #endif
 
-class nsDisplayTextDecoration;
-
 // Base class for html container frames that provides common
 // functionality.
 class nsHTMLContainerFrame : public nsContainerFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   /**
    * Helper method to create next-in-flows if necessary. If aFrame
@@ -94,109 +92,14 @@ public:
   /**
    * Displays the standard border, background and outline for the frame
    * and calls DisplayTextDecorationsAndChildren. This is suitable for
    * inline frames or frames that behave like inlines.
    */
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists);
-                              
-  nsresult DisplayTextDecorations(nsDisplayListBuilder* aBuilder,
-                                  nsDisplayList* aBelowTextDecorations,
-                                  nsDisplayList* aAboveTextDecorations,
-                                  nsLineBox* aLine);
 
 protected:
   nsHTMLContainerFrame(nsStyleContext *aContext) : nsContainerFrame(aContext) {}
-
-  /**
-   * Displays the below-children decorations, then the children, then
-   * the above-children decorations, with the decorations going in the
-   * Content() list. This is suitable for inline elements and elements
-   * that behave like inline elements (e.g. MathML containers).
-   */
-  nsresult DisplayTextDecorationsAndChildren(nsDisplayListBuilder* aBuilder, 
-                                             const nsRect& aDirtyRect,
-                                             const nsDisplayListSet& aLists);
-
-  /**
-   * Fetch the text decorations for this frame. 
-   *  @param aIsBlock      whether |this| is a block frame or no.
-   *  @param aDecorations  mask with all decorations. 
-   *                         See bug 1777 and 20163 to understand how a
-   *                         frame can end up with several decorations.
-   *  @param aUnderColor   The color of underline if the appropriate bit 
-   *                         in aDecoration is set. It is undefined otherwise.
-   *  @param aOverColor    The color of overline if the appropriate bit 
-   *                         in aDecoration is set. It is undefined otherwise.
-   *  @param aStrikeColor  The color of strike-through if the appropriate bit 
-   *                         in aDecoration is set. It is undefined otherwise.
-   *  @param aUnderStyle   The style of underline if the appropriate bit
-   *                         in aDecoration is set. It is undefined otherwise.
-   *                         The style is one of
-   *                         NS_STYLE_TEXT_DECORATION_STYLE_* consts.
-   *  @param aOverStyle    The style of overline if the appropriate bit
-   *                         in aDecoration is set. It is undefined otherwise.
-   *                         The style is one of
-   *                         NS_STYLE_TEXT_DECORATION_STYLE_* consts.
-   *  @param aStrikeStyle  The style of strike-through if the appropriate bit
-   *                         in aDecoration is set. It is undefined otherwise.
-   *                         The style is one of
-   *                         NS_STYLE_TEXT_DECORATION_STYLE_* consts.
-   *  NOTE: This function assigns NS_STYLE_TEXT_DECORATION_LINE_NONE to
-   *        aDecorations for text-less frames.  See bug 20163 for
-   *        details.
-   *  NOTE: The results of color and style for each lines were not initialized
-   *        if the line wasn't included in aDecorations.
-   */
-  void GetTextDecorations(nsPresContext* aPresContext, 
-                          PRBool aIsBlock,
-                          PRUint8& aDecorations, 
-                          nscolor& aUnderColor, 
-                          nscolor& aOverColor, 
-                          nscolor& aStrikeColor,
-                          PRUint8& aUnderStyle,
-                          PRUint8& aOverStyle,
-                          PRUint8& aStrikeStyle);
-
-  /** 
-   * Function that does the actual drawing of the textdecoration. 
-   *   input:
-   *    @param aCtx               the Thebes graphics context to draw on
-   *    @param aLine              the line, or nsnull if this is an inline frame
-   *    @param aColor             the color of the text-decoration
-   *    @param aStyle             the style of the text-decoration, i.e., one of
-   *                                NS_STYLE_TEXT_DECORATION_STYLE_* consts.
-   *    @param aAscent            ascent of the font from which the
-   *                                text-decoration was derived. 
-   *    @param aOffset            distance *above* baseline where the
-   *                                text-decoration should be drawn,
-   *                                i.e. negative offsets draws *below*
-   *                                the baseline.
-   *    @param aSize              the thickness of the line
-   *    @param aClipEdges         clip edges from the display item
-   *    @param aDecoration        which line will be painted i.e.,
-   *                              NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or
-   *                              NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or
-   *                              NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH.
-   */
-  virtual void PaintTextDecorationLine(
-                 gfxContext* aCtx,
-                 const nsPoint& aPt,
-                 nsLineBox* aLine,
-                 nscolor aColor,
-                 PRUint8 aStyle,
-                 gfxFloat aOffset,
-                 gfxFloat aAscent,
-                 gfxFloat aSize,
-                 const nsCharClipDisplayItem::ClipEdges& aClipEdges,
-                 const PRUint8 aDecoration);
-
-  virtual void AdjustForTextIndent(const nsLineBox* aLine,
-                                   nscoord& start,
-                                   nscoord& width);
-
-  friend class nsDisplayTextDecoration;
-  friend class nsDisplayTextShadow;
 };
 
 #endif /* nsHTMLContainerFrame_h___ */
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -891,16 +891,18 @@ public:
   NS_DECLARE_FRAME_PROPERTY(PreTransformBBoxProperty, DestroyRect)
 
   NS_DECLARE_FRAME_PROPERTY(UsedMarginProperty, DestroyMargin)
   NS_DECLARE_FRAME_PROPERTY(UsedPaddingProperty, DestroyMargin)
   NS_DECLARE_FRAME_PROPERTY(UsedBorderProperty, DestroyMargin)
 
   NS_DECLARE_FRAME_PROPERTY(ScrollLayerCount, nsnull)
 
+  NS_DECLARE_FRAME_PROPERTY(LineBaselineOffset, nsnull)
+
   /**
    * Return the distance between the border edge of the frame and the
    * margin edge of the frame.  Like GetRect(), returns the dimensions
    * as of the most recent reflow.
    *
    * This doesn't include any margin collapsing that may have occurred.
    *
    * It also treats 'auto' margins as zero, and treats any margins that
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -469,17 +469,18 @@ nsInlineFrame::ReflowFrames(nsPresContex
   }
   nscoord availableWidth = aReflowState.availableWidth;
   NS_ASSERTION(availableWidth != NS_UNCONSTRAINEDSIZE,
                "should no longer use available widths");
   // Subtract off left and right border+padding from availableWidth
   availableWidth -= leftEdge;
   availableWidth -= ltr ? aReflowState.mComputedBorderPadding.right
                         : aReflowState.mComputedBorderPadding.left;
-  lineLayout->BeginSpan(this, &aReflowState, leftEdge, leftEdge + availableWidth);
+  lineLayout->BeginSpan(this, &aReflowState, leftEdge,
+                        leftEdge + availableWidth, &mBaseline);
 
   // First reflow our current children
   nsIFrame* frame = mFrames.FirstChild();
   PRBool done = PR_FALSE;
   while (nsnull != frame) {
     PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
 
     // Check if we should lazily set the child frame's parent pointer
@@ -908,23 +909,17 @@ nsInlineFrame::GetSkipSides() const
   }
 
   return skip;
 }
 
 nscoord
 nsInlineFrame::GetBaseline() const
 {
-  nscoord ascent = 0;
-  nsRefPtr<nsFontMetrics> fm;
-  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
-  if (fm) {
-    ascent = fm->MaxAscent();
-  }
-  return NS_MIN(mRect.height, ascent + GetUsedBorderAndPadding().top);
+  return mBaseline;
 }
 
 #ifdef ACCESSIBILITY
 already_AddRefed<nsAccessible>
 nsInlineFrame::CreateAccessible()
 {
   // Broken image accessibles are created here, because layout
   // replaces the image or image control frame with an inline frame
--- a/layout/generic/nsInlineFrame.h
+++ b/layout/generic/nsInlineFrame.h
@@ -189,16 +189,18 @@ protected:
   virtual nsIFrame* PullOneFrame(nsPresContext* aPresContext,
                                  InlineReflowState& rs,
                                  PRBool* aIsComplete);
 
   virtual void PushFrames(nsPresContext* aPresContext,
                           nsIFrame* aFromChild,
                           nsIFrame* aPrevSibling,
                           InlineReflowState& aState);
+
+  nscoord mBaseline;
 };
 
 //----------------------------------------------------------------------
 
 /**
  * Variation on inline-frame used to manage lines for line layout in
  * special situations (:first-line style in particular).
  */
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -397,17 +397,18 @@ nsLineLayout::NewPerSpanData(PerSpanData
   *aResult = psd;
   return NS_OK;
 }
 
 nsresult
 nsLineLayout::BeginSpan(nsIFrame* aFrame,
                         const nsHTMLReflowState* aSpanReflowState,
                         nscoord aLeftEdge,
-                        nscoord aRightEdge)
+                        nscoord aRightEdge,
+                        nscoord* aBaseline)
 {
   NS_ASSERTION(aRightEdge != NS_UNCONSTRAINEDSIZE,
                "should no longer be using unconstrained sizes");
 #ifdef NOISY_REFLOW
   nsFrame::IndentBy(stdout, mSpanDepth+1);
   nsFrame::ListTag(stdout, aFrame);
   printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aLeftEdge, aRightEdge);
 #endif
@@ -422,16 +423,17 @@ nsLineLayout::BeginSpan(nsIFrame* aFrame
 
     // Init new span
     psd->mFrame = pfd;
     psd->mParent = mCurrentSpan;
     psd->mReflowState = aSpanReflowState;
     psd->mLeftEdge = aLeftEdge;
     psd->mX = aLeftEdge;
     psd->mRightEdge = aRightEdge;
+    psd->mBaseline = aBaseline;
 
     psd->mNoWrap =
       !aSpanReflowState->frame->GetStyleText()->WhiteSpaceCanWrap();
     psd->mDirection = aSpanReflowState->mStyleVisibility->mDirection;
     psd->mChangedFrameDirection = PR_FALSE;
 
     // Switch to new span
     mCurrentSpan = psd;
@@ -1475,16 +1477,36 @@ nsLineLayout::VerticalAlignLine()
   for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
     if (pfd->mVerticalAlign == VALIGN_OTHER) {
       pfd->mBounds.y += baselineY;
       pfd->mFrame->SetRect(pfd->mBounds);
     }
   }
   PlaceTopBottomFrames(psd, -mTopEdge, lineHeight);
 
+  // If the frame being reflowed has text decorations, we simulate the
+  // propagation of those decorations to a line-level element by storing the
+  // offset in a frame property on any child frames that are vertically-aligned
+  // somewhere other than the baseline. This property is then used by
+  // nsTextFrame::GetTextDecorations when the same conditions are met.
+  if (rootPFD.mFrame->GetStyleContext()->HasTextDecorationLines()) {
+    for (const PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
+      const nsIFrame *const f = pfd->mFrame;
+      const nsStyleCoord& vAlign =
+          f->GetStyleContext()->GetStyleTextReset()->mVerticalAlign;
+
+      if (vAlign.GetUnit() != eStyleUnit_Enumerated ||
+          vAlign.GetIntValue() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
+        const nscoord offset = baselineY - (pfd->mBounds.y);
+        f->Properties().Set(nsIFrame::LineBaselineOffset(),
+                            NS_INT32_TO_PTR(offset));
+      }
+    }
+  }
+
   // Fill in returned line-box and max-element-width data
   mLineBox->mBounds.x = psd->mLeftEdge;
   mLineBox->mBounds.y = mTopEdge;
   mLineBox->mBounds.width = psd->mX - psd->mLeftEdge;
   mLineBox->mBounds.height = lineHeight;
   mFinalLineHeight = lineHeight;
   mLineBox->SetAscent(baselineY - mTopEdge);
 #ifdef NOISY_VERTICAL_ALIGN
@@ -1738,17 +1760,17 @@ nsLineLayout::VerticalAlignFrames(PerSpa
       // height that is outside this range.
       minY = spanFramePFD->mBorderPadding.top - psd->mTopLeading;
       maxY = minY + psd->mLogicalHeight;
     }
 
     // This is the distance from the top edge of the parents visual
     // box to the baseline. The span already computed this for us,
     // so just use it.
-    baselineY = spanFramePFD->mAscent;
+    *psd->mBaseline = baselineY = spanFramePFD->mAscent;
 
 
 #ifdef NOISY_VERTICAL_ALIGN
     printf("[%sSpan]", (psd == mRootSpan)?"Root":"");
     nsFrame::ListTag(stdout, spanFrame);
     printf(": baseLine=%d logicalHeight=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n",
            baselineY, psd->mLogicalHeight, psd->mTopLeading,
            spanFramePFD->mBounds.height,
@@ -2115,16 +2137,17 @@ nsLineLayout::VerticalAlignFrames(PerSpa
     if (minY > 0) {
 
       // shrink the content by moving its top down.  This is tricky, since
       // the top is the 0 for many coordinates, so what we do is
       // move everything else up.
       spanFramePFD->mAscent -= minY; // move the baseline up
       spanFramePFD->mBounds.height -= minY; // move the bottom up
       psd->mTopLeading += minY;
+      *psd->mBaseline -= minY;
 
       pfd = psd->mFirstFrame;
       while (nsnull != pfd) {
         pfd->mBounds.y -= minY; // move all the children back up
         pfd->mFrame->SetRect(pfd->mBounds);
         pfd = pfd->mNext;
       }
       maxY -= minY; // since minY is in the frame's own coordinate system
@@ -2594,17 +2617,22 @@ nsLineLayout::RelativePositionFrames(Per
     nsOverflowAreas r;
     if (pfd->mSpan) {
       // Compute a new combined area for the child span before
       // aggregating it into our combined area.
       RelativePositionFrames(pfd->mSpan, r);
     } else {
       r = pfd->mOverflowAreas;
       if (pfd->GetFlag(PFD_ISTEXTFRAME)) {
-        if (pfd->GetFlag(PFD_RECOMPUTEOVERFLOW)) {
+        // We need to recompute overflow areas in two cases:
+        // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
+        // (2) When there are text decorations, since we can't recompute the
+        //     overflow area until Reflow and VerticalAlignLine have finished
+        if (pfd->GetFlag(PFD_RECOMPUTEOVERFLOW) ||
+            frame->GetStyleContext()->HasTextDecorationLines()) {
           nsTextFrame* f = static_cast<nsTextFrame*>(frame);
           r = f->RecomputeOverflow();
         }
         frame->FinishAndStoreOverflow(r, frame->GetSize());
       }
 
       // If we have something that's not an inline but with a complex frame
       // hierarchy inside that contains views, they need to be
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -98,17 +98,18 @@ public:
    * @param aFloatFrame the float frame that was placed.
    */
   void UpdateBand(const nsRect& aNewAvailableSpace,
                   nsIFrame* aFloatFrame);
 
   nsresult BeginSpan(nsIFrame* aFrame,
                      const nsHTMLReflowState* aSpanReflowState,
                      nscoord aLeftEdge,
-                     nscoord aRightEdge);
+                     nscoord aRightEdge,
+                     nscoord* aBaseline);
 
   // Returns the width of the span
   nscoord EndSpan(nsIFrame* aFrame);
 
   PRInt32 GetCurrentSpanCount() const;
 
   void SplitLineTo(PRInt32 aNewCount);
 
@@ -501,16 +502,17 @@ protected:
 
     nscoord mLeftEdge;
     nscoord mX;
     nscoord mRightEdge;
 
     nscoord mTopLeading, mBottomLeading;
     nscoord mLogicalHeight;
     nscoord mMinY, mMaxY;
+    nscoord* mBaseline;
 
     void AppendFrame(PerFrameData* pfd) {
       if (nsnull == mLastFrame) {
         mFirstFrame = pfd;
       }
       else {
         mLastFrame->mNext = pfd;
         pfd->mPrev = mLastFrame;
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -294,24 +294,16 @@ public:
                               nscoord aLeftEdge, nscoord aRightEdge,
                               PRUint32* aStartOffset, PRUint32* aMaxLength,
                               nscoord* aSnappedLeftEdge,
                               nscoord* aSnappedRightEdge);
   // primary frame paint method called from nsDisplayText
   // The private DrawText() is what applies the text to a graphics context
   void PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
                  const nsRect& aDirtyRect, const nsCharClipDisplayItem& aItem);
-  // helper: paint quirks-mode CSS text decorations
-  void PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
-                            const gfxPoint& aFramePt,
-                            const gfxPoint& aTextBaselinePt,
-                            nsTextPaintStyle& aTextStyle,
-                            PropertyProvider& aProvider,
-                            const nsCharClipDisplayItem::ClipEdges& aClipEdges,
-                            const nscolor* aOverrideColor = nsnull);
   // helper: paint text frame when we're impacted by at least one selection.
   // Return false if the text was not painted and we should continue with
   // the fast path.
   bool PaintTextWithSelection(gfxContext* aCtx,
                               const gfxPoint& aFramePt,
                               const gfxPoint& aTextBaselinePt,
                               const gfxRect& aDirtyRect,
                               PropertyProvider& aProvider,
@@ -328,17 +320,18 @@ public:
                                     const gfxPoint& aFramePt,
                                     const gfxPoint& aTextBaselinePt,
                                     const gfxRect& aDirtyRect,
                                     PropertyProvider& aProvider,
                                     PRUint32 aContentOffset,
                                     PRUint32 aContentLength,
                                     nsTextPaintStyle& aTextPaintStyle,
                                     SelectionDetails* aDetails,
-                                    SelectionType* aAllTypes);
+                                    SelectionType* aAllTypes,
+                            const nsCharClipDisplayItem::ClipEdges& aClipEdges);
   // helper: paint text decorations for text selected by aSelectionType
   void PaintTextSelectionDecorations(gfxContext* aCtx,
                                      const gfxPoint& aFramePt,
                                      const gfxPoint& aTextBaselinePt,
                                      const gfxRect& aDirtyRect,
                                      PropertyProvider& aProvider,
                                      PRUint32 aContentOffset,
                                      PRUint32 aContentLength,
@@ -434,76 +427,121 @@ protected:
   PRInt32     mContentLengthHint;
   nscoord     mAscent;
   gfxTextRun* mTextRun;
 
   // The caller of this method must call DestroySelectionDetails() on the
   // return value, if that return value is not null.  Calling
   // DestroySelectionDetails() on a null value is still OK, just not necessary.
   SelectionDetails* GetSelectionDetails();
-  
-  void UnionTextDecorationOverflow(nsPresContext* aPresContext,
-                                   PropertyProvider& aProvider,
-                                   nsRect* aVisualOverflowRect);
 
-  void DrawText(gfxContext* aCtx,
-                const gfxPoint& aTextBaselinePt,
-                PRUint32 aOffset,
-                PRUint32 aLength,
-                const gfxRect* aDirtyRect,
-                PropertyProvider* aProvider,
-                gfxFloat& aAdvanceWidth,
-                PRBool aDrawSoftHyphen);
+  void UnionAdditionalOverflow(nsPresContext* aPresContext,
+                               PropertyProvider& aProvider,
+                               nsRect* aVisualOverflowRect,
+                               bool aIncludeTextDecorations);
 
   void PaintOneShadow(PRUint32 aOffset,
                       PRUint32 aLength,
                       nsCSSShadowItem* aShadowDetails,
                       PropertyProvider* aProvider,
                       const nsRect& aDirtyRect,
                       const gfxPoint& aFramePt,
                       const gfxPoint& aTextBaselinePt,
                       gfxContext* aCtx,
                       const nscolor& aForegroundColor,
                       const nsCharClipDisplayItem::ClipEdges& aClipEdges,
                       nscoord aLeftSideOffset);
 
-  struct TextDecorations {
-    PRUint8 mDecorations;
-    PRUint8 mOverStyle;
-    PRUint8 mUnderStyle;
-    PRUint8 mStrikeStyle;
-    nscolor mOverColor;
-    nscolor mUnderColor;
-    nscolor mStrikeColor;
+  struct LineDecoration {
+    nsIFrame* mFrame;
+
+    // This is represents the offset from our baseline to mFrame's baseline;
+    // positive offsets are *above* the baseline and negative offsets below
+    nscoord mBaselineOffset;
+
+    nscolor mColor;
+    PRUint8 mStyle;
+
+    LineDecoration(nsIFrame *const aFrame,
+                   const nscoord aOff,
+                   const nscolor aColor,
+                   const PRUint8 aStyle)
+      : mFrame(aFrame),
+        mBaselineOffset(aOff),
+        mColor(aColor),
+        mStyle(aStyle)
+    {}
 
-    TextDecorations() :
-      mDecorations(0), mOverStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID),
-      mUnderStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID),
-      mStrikeStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID),
-      mOverColor(NS_RGB(0, 0, 0)), mUnderColor(NS_RGB(0, 0, 0)),
-      mStrikeColor(NS_RGB(0, 0, 0))
-    { }
+    LineDecoration(const LineDecoration& aOther)
+      : mFrame(aOther.mFrame),
+        mBaselineOffset(aOther.mBaselineOffset),
+        mColor(aOther.mColor),
+        mStyle(aOther.mStyle)
+    {}
 
-    PRBool HasDecorationlines() {
+    bool operator==(const LineDecoration& aOther) const {
+      return mFrame == aOther.mFrame &&
+             mStyle == aOther.mStyle &&
+             mColor == aOther.mColor &&
+             mBaselineOffset == aOther.mBaselineOffset;
+    }
+  };
+  struct TextDecorations {
+    nsAutoTArray<LineDecoration, 1> mOverlines, mUnderlines, mStrikes;
+
+    TextDecorations() { }
+
+    PRBool HasDecorationLines() const {
       return HasUnderline() || HasOverline() || HasStrikeout();
     }
-    PRBool HasUnderline() {
-      return (mDecorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) &&
-             mUnderStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE;
+    PRBool HasUnderline() const {
+      return !mUnderlines.IsEmpty();
     }
-    PRBool HasOverline() {
-      return (mDecorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) &&
-             mOverStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE;
+    PRBool HasOverline() const {
+      return !mOverlines.IsEmpty();
     }
-    PRBool HasStrikeout() {
-      return (mDecorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) &&
-             mStrikeStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE;
+    PRBool HasStrikeout() const {
+      return !mStrikes.IsEmpty();
     }
   };
-  TextDecorations GetTextDecorations(nsPresContext* aPresContext);
+  void GetTextDecorations(nsPresContext* aPresContext,
+                          TextDecorations& aDecorations);
+
+  void DrawTextRun(gfxContext* const aCtx,
+                   const gfxPoint& aTextBaselinePt,
+                   PRUint32 aOffset,
+                   PRUint32 aLength,
+                   PropertyProvider& aProvider,
+                   gfxFloat& aAdvanceWidth,
+                   PRBool aDrawSoftHyphen);
+
+  void DrawTextRunAndDecorations(gfxContext* const aCtx,
+                                 const gfxPoint& aFramePt,
+                                 const gfxPoint& aTextBaselinePt,
+                                 PRUint32 aOffset,
+                                 PRUint32 aLength,
+                                 PropertyProvider& aProvider,
+                                 const nsTextPaintStyle& aTextStyle,
+                             const nsCharClipDisplayItem::ClipEdges& aClipEdges,
+                                 gfxFloat& aAdvanceWidth,
+                                 PRBool aDrawSoftHyphen,
+                                 const TextDecorations& aDecorations,
+                                 const nscolor* const aDecorationOverrideColor);
+
+  void DrawText(gfxContext* const aCtx,
+                const gfxPoint& aFramePt,
+                const gfxPoint& aTextBaselinePt,
+                PRUint32 aOffset,
+                PRUint32 aLength,
+                PropertyProvider& aProvider,
+                const nsTextPaintStyle& aTextStyle,
+                const nsCharClipDisplayItem::ClipEdges& aClipEdges,
+                gfxFloat& aAdvanceWidth,
+                PRBool aDrawSoftHyphen,
+                const nscolor* const aDecorationOverrideColor = nsnull);
 
   // Set non empty rect to aRect, it should be overflow rect or frame rect.
   // If the result rect is larger than the given rect, this returns PR_TRUE.
   PRBool CombineSelectionUnderlineRect(nsPresContext* aPresContext,
                                        nsRect& aRect);
 
   PRBool IsFloatingFirstLetterChild();
 
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -321,17 +321,17 @@ public:
 
   // if this returns PR_FALSE, we don't need to draw underline.
   static PRBool GetSelectionUnderline(nsPresContext* aPresContext,
                                       PRInt32 aIndex,
                                       nscolor* aLineColor,
                                       float* aRelativeSize,
                                       PRUint8* aStyle);
 
-  nsPresContext* PresContext() { return mPresContext; }
+  nsPresContext* PresContext() const { return mPresContext; }
 
   enum {
     eIndexRawInput = 0,
     eIndexSelRawText,
     eIndexConvText,
     eIndexSelConvText,
     eIndexSpellChecker
   };
@@ -4255,90 +4255,99 @@ FillClippedRect(gfxContext* aCtx, nsPres
   aCtx->NewPath();
   // pixel-snap
   aCtx->Rectangle(gfxRect(r.X() / app, r.Y() / app,
                           r.Width() / app, r.Height() / app), PR_TRUE);
   aCtx->SetColor(gfxRGBA(aColor));
   aCtx->Fill();
 }
 
-nsTextFrame::TextDecorations
-nsTextFrame::GetTextDecorations(nsPresContext* aPresContext)
-{
-  TextDecorations decorations;
-
-  // Quirks mode text decoration are rendered by children; see bug 1777
-  // In non-quirks mode, nsHTMLContainer::Paint and nsBlockFrame::Paint
-  // does the painting of text decorations.
-  // FIXME Bug 403524: We'd like to unify standards-mode and quirks-mode
-  // text-decoration drawing, using what's currently the quirks mode
-  // codepath.  But for now this code is only used for quirks mode.
+void
+nsTextFrame::GetTextDecorations(nsPresContext* aPresContext,
+                                nsTextFrame::TextDecorations& aDecorations)
+{
   const nsCompatibility compatMode = aPresContext->CompatibilityMode();
-  if (compatMode != eCompatibility_NavQuirks)
-    return decorations;
 
   PRBool useOverride = PR_FALSE;
   nscolor overrideColor;
 
-  // A mask of all possible decorations.
-  // FIXME: Per spec, we still need to draw all relevant decorations
-  // from ancestors, not just the nearest one from each.
-  PRUint8 decorMask = NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK;
-
-  PRBool isChild; // ignored
-  for (nsIFrame* f = this; decorMask && f;
-       NS_SUCCEEDED(f->GetParentStyleContextFrame(aPresContext, &f, &isChild))
-         || (f = nsnull)) {
-    nsStyleContext* context = f->GetStyleContext();
+  // frameTopOffset represents the offset to f's top from our baseline in our
+  // coordinate space
+  // baselineOffset represents the offset from our baseline to f's baseline or
+  // the nearest block's baseline, in our coordinate space, whichever is closest
+  // during the particular iteration
+  nscoord frameTopOffset = mAscent,
+          baselineOffset = 0;
+
+  bool nearestBlockFound = false;
+
+  for (nsIFrame* f = this, *fParent; f; f = fParent) {
+    nsStyleContext *const context = f->GetStyleContext();
     if (!context->HasTextDecorationLines()) {
       break;
     }
-    const nsStyleTextReset* styleText = context->GetStyleTextReset();
-    if (!useOverride && 
-        (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL &
-           styleText->mTextDecorationLine)) {
+
+    const nsStyleTextReset *const styleText = context->GetStyleTextReset();
+    const PRUint8 textDecorations = styleText->mTextDecorationLine;
+
+    if (!useOverride &&
+        (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL & textDecorations))
+    {
       // This handles the <a href="blah.html"><font color="green">La 
       // la la</font></a> case. The link underline should be green.
       useOverride = PR_TRUE;
       overrideColor = context->GetVisitedDependentColor(
                                  eCSSProperty_text_decoration_color);
     }
 
-    // FIXME: see above (remove this check)
-    PRUint8 useDecorations = decorMask & styleText->mTextDecorationLine;
-    if (useDecorations) {// a decoration defined here
-      nscolor color = context->GetVisitedDependentColor(
-                                 eCSSProperty_text_decoration_color);
-
-      // FIXME: We also need to record the thickness and position
-      // metrics appropriate to this element (at least in standards
-      // mode).  This will require adjusting the visual overflow region
-      // of this frame and maybe its ancestors.  The positions should
-      // probably be relative to the line's baseline (when text
-      // decorations are specified on inlines we should look for their
-      // containing line; otherwise use the element's font); when
-      // drawing it should always be relative to the line baseline.
-      // This way we move the decorations for relative positioning.
-      if (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE & useDecorations) {
-        decorations.mUnderColor = useOverride ? overrideColor : color;
-        decorations.mUnderStyle = styleText->GetDecorationStyle();
-        decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
-        decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+    fParent = nsLayoutUtils::GetParentOrPlaceholderFor(
+                aPresContext->FrameManager(), f);
+    const bool firstBlock = !nearestBlockFound &&
+                            nsLayoutUtils::GetAsBlock(fParent);
+
+    // Not updating positions once we hit a parent block is equivalent to
+    // the CSS 2.1 spec that blocks should propagate decorations down to their
+    // children (albeit the style should be preserved)
+    // However, if we're vertically aligned within a block, then we need to
+    // recover the right baseline from the line by querying the FrameProperty
+    // that should be set (see nsLineLayout::VerticalAlignLine).
+    if (firstBlock &&
+        (styleText->mVerticalAlign.GetUnit() != eStyleUnit_Enumerated ||
+         styleText->mVerticalAlign.GetIntValue() !=
+           NS_STYLE_VERTICAL_ALIGN_BASELINE)) {
+      baselineOffset = frameTopOffset -
+        NS_PTR_TO_INT32(f->Properties().Get(nsIFrame::LineBaselineOffset()));
+    }
+    else if (!nearestBlockFound) {
+      baselineOffset = frameTopOffset - f->GetBaseline();
+    }
+
+    nearestBlockFound = nearestBlockFound || firstBlock;
+    frameTopOffset += f->GetRect().Y() - f->GetRelativeOffset().y;
+
+    const PRUint8 style = styleText->GetDecorationStyle();
+    // Accumulate only elements that have decorations with a genuine style
+    if (textDecorations && style != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
+      const nscolor color = useOverride ? overrideColor
+        : context->GetVisitedDependentColor(eCSSProperty_text_decoration_color);
+
+      if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
+        aDecorations.mUnderlines.AppendElement(
+          nsTextFrame::LineDecoration(f, baselineOffset, color,
+                                      style));
       }
-      if (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE & useDecorations) {
-        decorations.mOverColor = useOverride ? overrideColor : color;
-        decorations.mOverStyle = styleText->GetDecorationStyle();
-        decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
-        decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+      if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
+        aDecorations.mOverlines.AppendElement(
+          nsTextFrame::LineDecoration(f, baselineOffset, color,
+                                      style));
       }
-      if (NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH & useDecorations) {
-        decorations.mStrikeColor = useOverride ? overrideColor : color;
-        decorations.mStrikeStyle = styleText->GetDecorationStyle();
-        decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
-        decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+      if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
+        aDecorations.mStrikes.AppendElement(
+          nsTextFrame::LineDecoration(f, baselineOffset, color,
+                                      style));
       }
     }
 
     // In all modes, if we're on an inline-block or inline-table (or
     // inline-stack, inline-box, inline-grid), we're done.
     const nsStyleDisplay *disp = context->GetStyleDisplay();
     if (disp->mDisplay != NS_STYLE_DISPLAY_INLINE &&
         disp->IsInlineOutside()) {
@@ -4353,108 +4362,113 @@ nsTextFrame::GetTextDecorations(nsPresCo
     } else {
       // In standards/almost-standards mode, if we're on an
       // absolutely-positioned element or a floating element, we're done.
       if (disp->IsFloating() || disp->IsAbsolutelyPositioned()) {
         break;
       }
     }
   }
-
-  return decorations;
 }
 
 void
-nsTextFrame::UnionTextDecorationOverflow(nsPresContext* aPresContext,
-                                         PropertyProvider& aProvider,
-                                         nsRect* aVisualOverflowRect)
+nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
+                                     PropertyProvider& aProvider,
+                                     nsRect* aVisualOverflowRect,
+                                     bool aIncludeTextDecorations)
 {
   // Text-shadow overflows
   nsRect shadowRect =
     nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
   aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
 
   if (IsFloatingFirstLetterChild()) {
     // The underline/overline drawable area must be contained in the overflow
     // rect when this is in floating first letter frame at *both* modes.
     nsFontMetrics* fm = aProvider.GetFontMetrics();
     nscoord fontAscent = fm->MaxAscent();
     nscoord fontHeight = fm->MaxHeight();
     nsRect fontRect(0, mAscent - fontAscent, GetSize().width, fontHeight);
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, fontRect);
   }
-
+  if (aIncludeTextDecorations) {
+    // Since CSS 2.1 requires that text-decoration defined on ancestors maintain
+    // style and position, they can be drawn at virtually any y-offset, so
+    // maxima and minima are required to reliably generate the rectangle for
+    // them
+    TextDecorations textDecs;
+    GetTextDecorations(aPresContext, textDecs);
+    if (textDecs.HasDecorationLines()) {
+      const nscoord width = GetSize().width;
+      const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(),
+                     gfxWidth = width / appUnitsPerDevUnit,
+                     ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
+      nscoord top(nscoord_MAX), bottom(nscoord_MIN);
+      // Below we loop through all text decorations and compute the rectangle
+      // containing all of them, in this frame's coordinate space
+      for (PRUint32 i = 0; i < textDecs.mUnderlines.Length(); ++i) {
+        const LineDecoration& dec = textDecs.mUnderlines[i];
+
+        const gfxFont::Metrics metrics =
+          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame));
+
+        const nsRect decorationRect =
+          nsCSSRendering::GetTextDecorationRect(aPresContext,
+            gfxSize(gfxWidth, metrics.underlineSize),
+            ascent, metrics.underlineOffset,
+            NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, dec.mStyle) +
+          nsPoint(0, -dec.mBaselineOffset);
+
+        top = NS_MIN(decorationRect.y, top);
+        bottom = NS_MAX(decorationRect.YMost(), bottom);
+      }
+      for (PRUint32 i = 0; i < textDecs.mOverlines.Length(); ++i) {
+        const LineDecoration& dec = textDecs.mOverlines[i];
+
+        const gfxFont::Metrics metrics =
+          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame));
+
+        const nsRect decorationRect =
+          nsCSSRendering::GetTextDecorationRect(aPresContext,
+            gfxSize(gfxWidth, metrics.underlineSize),
+            ascent, metrics.maxAscent,
+            NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle) +
+          nsPoint(0, -dec.mBaselineOffset);
+
+        top = NS_MIN(decorationRect.y, top);
+        bottom = NS_MAX(decorationRect.YMost(), bottom);
+      }
+      for (PRUint32 i = 0; i < textDecs.mStrikes.Length(); ++i) {
+        const LineDecoration& dec = textDecs.mStrikes[i];
+
+        const gfxFont::Metrics metrics =
+          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame));
+
+        const nsRect decorationRect =
+          nsCSSRendering::GetTextDecorationRect(aPresContext,
+            gfxSize(gfxWidth, metrics.strikeoutSize),
+            ascent, metrics.strikeoutOffset,
+            NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, dec.mStyle) +
+          nsPoint(0, -dec.mBaselineOffset);
+        top = NS_MIN(decorationRect.y, top);
+        bottom = NS_MAX(decorationRect.YMost(), bottom);
+      }
+
+      aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
+                                     nsRect(0, top, width, bottom - top));
+    }
+  }
   // When this frame is not selected, the text-decoration area must be in
   // frame bounds.
-  nsRect decorationRect;
   if (!(GetStateBits() & NS_FRAME_SELECTED_CONTENT) ||
       !CombineSelectionUnderlineRect(aPresContext, *aVisualOverflowRect))
     return;
   AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
 }
 
-void 
-nsTextFrame::PaintTextDecorations(
-               gfxContext* aCtx, const gfxRect& aDirtyRect,
-               const gfxPoint& aFramePt,
-               const gfxPoint& aTextBaselinePt,
-               nsTextPaintStyle& aTextPaintStyle,
-               PropertyProvider& aProvider,
-               const nsCharClipDisplayItem::ClipEdges& aClipEdges,
-               const nscolor* aOverrideColor)
-{
-  TextDecorations decorations =
-    GetTextDecorations(aTextPaintStyle.PresContext());
-  if (!decorations.HasDecorationlines())
-    return;
-
-  // Hide text decorations if we're currently hiding @font-face fallback text
-  if (aProvider.GetFontGroup()->ShouldSkipDrawing())
-    return;
-
-  gfxFont* firstFont = aProvider.GetFontGroup()->GetFontAt(0);
-  if (!firstFont)
-    return; // OOM
-  const gfxFont::Metrics& fontMetrics = firstFont->GetMetrics();
-  gfxFloat app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
-
-  // XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
-  nscoord x = NSToCoordRound(aFramePt.x);
-  nscoord width = GetRect().width;
-  aClipEdges.Intersect(&x, &width);
-  gfxPoint pt(x / app, (aTextBaselinePt.y - mAscent) / app);
-  gfxSize size(width / app, 0);
-  gfxFloat ascent = gfxFloat(mAscent) / app;
-
-  nscolor lineColor;
-  if (decorations.HasOverline()) {
-    lineColor = aOverrideColor ? *aOverrideColor : decorations.mOverColor;
-    size.height = fontMetrics.underlineSize;
-    nsCSSRendering::PaintDecorationLine(
-      aCtx, lineColor, pt, size, ascent, fontMetrics.maxAscent,
-      NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorations.mOverStyle);
-  }
-  if (decorations.HasUnderline()) {
-    lineColor = aOverrideColor ? *aOverrideColor : decorations.mUnderColor;
-    size.height = fontMetrics.underlineSize;
-    gfxFloat offset = aProvider.GetFontGroup()->GetUnderlineOffset();
-    nsCSSRendering::PaintDecorationLine(
-      aCtx, lineColor, pt, size, ascent, offset,
-      NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorations.mUnderStyle);
-  }
-  if (decorations.HasStrikeout()) {
-    lineColor = aOverrideColor ? *aOverrideColor : decorations.mStrikeColor;
-    size.height = fontMetrics.strikeoutSize;
-    gfxFloat offset = fontMetrics.strikeoutOffset;
-    nsCSSRendering::PaintDecorationLine(
-      aCtx, lineColor, pt, size, ascent, offset,
-      NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, decorations.mStrikeStyle);
-  }
-}
-
 static gfxFloat
 ComputeDescentLimitForSelectionUnderline(nsPresContext* aPresContext,
                                          nsTextFrame* aFrame,
                                          const gfxFont::Metrics& aFontMetrics)
 {
   gfxFloat app = aPresContext->AppUnitsPerDevPixel();
   nscoord lineHeightApp =
     nsHTMLReflowState::CalcLineHeight(aFrame->GetStyleContext(), NS_AUTOHEIGHT);
@@ -4805,57 +4819,53 @@ nsTextFrame::PaintOneShadow(PRUint32 aOf
   nsContextBoxBlur contextBoxBlur;
   gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
                                                   PresContext()->AppUnitsPerDevPixel(),
                                                   aCtx, aDirtyRect, nsnull);
   if (!shadowContext)
     return;
 
   nscolor shadowColor;
-  if (aShadowDetails->mHasColor)
+  const nscolor* decorationOverrideColor;
+  if (aShadowDetails->mHasColor) {
     shadowColor = aShadowDetails->mColor;
-  else
+    decorationOverrideColor = &shadowColor;
+  } else {
     shadowColor = aForegroundColor;
+    decorationOverrideColor = nsnull;
+  }
 
   aCtx->Save();
   aCtx->NewPath();
   aCtx->SetColor(gfxRGBA(shadowColor));
 
   // Draw the text onto our alpha-only surface to capture the alpha values.
   // Remember that the box blur context has a device offset on it, so we don't need to
   // translate any coordinates to fit on the surface.
-  gfxRect dirtyGfxRect(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
   gfxFloat advanceWidth;
-  DrawText(shadowContext,
-           aTextBaselinePt + shadowOffset,
-           aOffset, aLength, &dirtyGfxRect, aProvider, advanceWidth,
-           (GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
-
-  // This will only have an effect in quirks mode. Standards mode text-decoration shadow painting
-  // is handled in nsHTMLContainerFrame.cpp, so you must remember to consider that if you change
-  // any code behaviour here.
-  nsTextPaintStyle textPaintStyle(this);
-  PaintTextDecorations(shadowContext, dirtyGfxRect, aFramePt + shadowOffset,
-                       aTextBaselinePt + shadowOffset,
-                       textPaintStyle, *aProvider, aClipEdges, &shadowColor);
+  DrawText(shadowContext, aFramePt + shadowOffset,
+           aTextBaselinePt + shadowOffset, aOffset, aLength, *aProvider,
+           nsTextPaintStyle(this), aClipEdges, advanceWidth,
+           (GetStateBits() & TEXT_HYPHEN_BREAK) != 0, decorationOverrideColor);
 
   contextBoxBlur.DoPaint();
   aCtx->Restore();
 }
 
 // Paints selection backgrounds and text in the correct colors. Also computes
 // aAllTypes, the union of all selection types that are applying to this text.
 bool
 nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
-    const gfxPoint& aFramePt,
-    const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
+    const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
+    const gfxRect& aDirtyRect,
     PropertyProvider& aProvider,
     PRUint32 aContentOffset, PRUint32 aContentLength,
     nsTextPaintStyle& aTextPaintStyle, SelectionDetails* aDetails,
-    SelectionType* aAllTypes)
+    SelectionType* aAllTypes,
+    const nsCharClipDisplayItem::ClipEdges& aClipEdges)
 {
   // Figure out which selections control the colors to use for each character.
   nsAutoTArray<SelectionDetails*,BIG_TEXT_NODE_SIZE> prevailingSelectionsBuffer;
   if (!prevailingSelectionsBuffer.AppendElements(aContentLength))
     return false;
   SelectionDetails** prevailingSelections = prevailingSelectionsBuffer.Elements();
 
   SelectionType allTypes = 0;
@@ -4937,19 +4947,19 @@ nsTextFrame::PaintTextWithSelectionColor
                                  &type, &rangeStyle)) {
     nscolor foreground, background;
     GetSelectionTextColors(type, aTextPaintStyle, rangeStyle,
                            &foreground, &background);
     // Draw text segment
     aCtx->SetColor(gfxRGBA(foreground));
     gfxFloat advance;
 
-    DrawText(aCtx, gfxPoint(aFramePt.x + xOffset, aTextBaselinePt.y),
-             offset, length, &aDirtyRect, &aProvider,
-             advance, hyphenWidth > 0);
+    DrawText(aCtx, aFramePt, gfxPoint(aFramePt.x + xOffset, aTextBaselinePt.y),
+             offset, length, aProvider, aTextPaintStyle, aClipEdges, advance,
+             hyphenWidth > 0);
     if (hyphenWidth) {
       advance += hyphenWidth;
     }
     iterator.UpdateWithAdvance(advance);
   }
   return true;
 }
 
@@ -5020,17 +5030,18 @@ nsTextFrame::PaintTextSelectionDecoratio
     iterator.UpdateWithAdvance(advance);
   }
 }
 
 bool
 nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
     const gfxPoint& aFramePt,
     const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
-    PropertyProvider& aProvider, PRUint32 aContentOffset, PRUint32 aContentLength,
+    PropertyProvider& aProvider,
+    PRUint32 aContentOffset, PRUint32 aContentLength,
     nsTextPaintStyle& aTextPaintStyle,
     const nsCharClipDisplayItem::ClipEdges& aClipEdges)
 {
   SelectionDetails* details = GetSelectionDetails();
   if (!details) {
     if (aContentLength == aProvider.GetOriginalLength()) {
       // It's the full text range so we can remove the FRAME_SELECTED_CONTENT
       // bit to avoid going through this slow path until something is selected
@@ -5038,22 +5049,22 @@ nsTextFrame::PaintTextWithSelection(gfxC
       RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
     }
     return false;
   }
 
   SelectionType allTypes;
   if (!PaintTextWithSelectionColors(aCtx, aFramePt, aTextBaselinePt, aDirtyRect,
                                     aProvider, aContentOffset, aContentLength,
-                                    aTextPaintStyle, details, &allTypes)) {
+                                    aTextPaintStyle, details, &allTypes,
+                                    aClipEdges))
+  {
     DestroySelectionDetails(details);
     return false;
   }
-  PaintTextDecorations(aCtx, aDirtyRect, aFramePt, aTextBaselinePt,
-                       aTextPaintStyle, aProvider, aClipEdges);
   PRInt32 i;
   // Iterate through just the selection types that paint decorations and
   // paint decorations for any that actually occur in this frame. Paint
   // higher-numbered selection types below lower-numered ones on the
   // general principal that lower-numbered selections are higher priority.
   allTypes &= SelectionTypesWithDecorations;
   for (i = nsISelectionController::NUM_SELECTIONTYPES - 1; i >= 1; --i) {
     SelectionType type = 1 << (i - 1);
@@ -5287,47 +5298,153 @@ nsTextFrame::PaintText(nsRenderingContex
                                provider, contentOffset, contentLength,
                                textPaintStyle, clipEdges))
       return;
   }
 
   ctx->SetColor(gfxRGBA(foregroundColor));
 
   gfxFloat advanceWidth;
-  DrawText(ctx, textBaselinePt, startOffset, maxLength, &dirtyRect, &provider,
-           advanceWidth, (GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
-  PaintTextDecorations(ctx, dirtyRect, framePt, textBaselinePt,
-                       textPaintStyle, provider, clipEdges);
+  DrawText(ctx, framePt, textBaselinePt, startOffset, maxLength, provider,
+           textPaintStyle, clipEdges, advanceWidth,
+           (GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
 }
 
 void
-nsTextFrame::DrawText(gfxContext* aCtx, const gfxPoint& aTextBaselinePt,
-                      PRUint32 aOffset, PRUint32 aLength,
-                      const gfxRect* aDirtyRect, PropertyProvider* aProvider,
-                      gfxFloat& aAdvanceWidth, PRBool aDrawSoftHyphen)
-{
-  // Paint the text and soft-hyphen (if any) onto the given graphics context
+nsTextFrame::DrawTextRun(gfxContext* const aCtx,
+                         const gfxPoint& aTextBaselinePt,
+                         PRUint32 aOffset, PRUint32 aLength,
+                         PropertyProvider& aProvider,
+                         gfxFloat& aAdvanceWidth,
+                         PRBool aDrawSoftHyphen)
+{
   mTextRun->Draw(aCtx, aTextBaselinePt, aOffset, aLength,
-                 aProvider, &aAdvanceWidth);
+                 &aProvider, &aAdvanceWidth);
 
   if (aDrawSoftHyphen) {
     // Don't use ctx as the context, because we need a reference context here,
     // ctx may be transformed.
     gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, this));
     if (hyphenTextRun.get()) {
       // For right-to-left text runs, the soft-hyphen is positioned at the left
       // of the text, minus its own width
       gfxFloat hyphenBaselineX = aTextBaselinePt.x + mTextRun->GetDirection() * aAdvanceWidth -
         (mTextRun->IsRightToLeft() ? hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull) : 0);
       hyphenTextRun->Draw(aCtx, gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
                           0, hyphenTextRun->GetLength(), nsnull, nsnull);
     }