Merge m-c to f-t
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 26 Oct 2013 18:45:37 -0700
changeset 167159 26d92ad24d6702d42bf4a292428412ee5398be03
parent 167158 f80db02ac96dd4c72ae2a6f7d4d22e8b645ece7c (current diff)
parent 167130 a80dce1126db109fdbf19a3bde050c2bd580c834 (diff)
child 167160 a8af01ed5f521b02ef83863b09114caa68e25a55
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to f-t
--- a/accessible/src/base/AccEvent.cpp
+++ b/accessible/src/base/AccEvent.cpp
@@ -11,16 +11,20 @@
 #include "xpcAccEvents.h"
 #include "States.h"
 
 #include "nsEventStateManager.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
+static_assert(static_cast<bool>(eNoUserInput) == false &&
+              static_cast<bool>(eFromUserInput) == true,
+              "EIsFromUserInput cannot be casted to bool");
+
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent constructors
 
 AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible,
@@ -113,16 +117,28 @@ AccHideEvent::
 AccShowEvent::
   AccShowEvent(Accessible* aTarget, nsINode* aTargetNode) :
   AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode)
 {
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
+// AccTextSelChangeEvent
+////////////////////////////////////////////////////////////////////////////////
+
+AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget,
+                                             nsISelection* aSelection) :
+  AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget,
+           eAutoDetect, eCoalesceTextSelChange),
+  mSel(aSelection) {}
+
+AccTextSelChangeEvent::~AccTextSelChangeEvent() { }
+
+////////////////////////////////////////////////////////////////////////////////
 // AccSelChangeEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 AccSelChangeEvent::
   AccSelChangeEvent(Accessible* aWidget, Accessible* aItem,
                     SelChangeType aSelChangeType) :
     AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange),
     mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -5,16 +5,18 @@
 
 #ifndef _AccEvent_H_
 #define _AccEvent_H_
 
 #include "nsIAccessibleEvent.h"
 
 #include "mozilla/a11y/Accessible.h"
 
+class nsISelection;
+
 namespace mozilla {
 namespace a11y {
 
 class DocAccessible;
 
 // Constants used to point whether the event is from user input.
 enum EIsFromUserInput
 {
@@ -54,16 +56,19 @@ public:
     eCoalesceOfSameType,
 
     // eCoalesceSelectionChange: coalescence of selection change events.
     eCoalesceSelectionChange,
 
     // eCoalesceStateChange: coalesce state change events.
     eCoalesceStateChange,
 
+    // eCoalesceTextSelChange: coalescence of text selection change events.
+    eCoalesceTextSelChange,
+
      // eRemoveDupes : For repeat events, only the newest event in queue
      //    will be emitted.
     eRemoveDupes,
 
      // eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
     eDoNotEmit
   };
 
@@ -72,32 +77,35 @@ public:
            EIsFromUserInput aIsFromUserInput = eAutoDetect,
            EEventRule aEventRule = eRemoveDupes);
   virtual ~AccEvent() {}
 
   // AccEvent
   uint32_t GetEventType() const { return mEventType; }
   EEventRule GetEventRule() const { return mEventRule; }
   bool IsFromUserInput() const { return mIsFromUserInput; }
+  EIsFromUserInput FromUserInput() const
+    { return static_cast<EIsFromUserInput>(mIsFromUserInput); }
 
   Accessible* GetAccessible() const { return mAccessible; }
   DocAccessible* GetDocAccessible() const { return mAccessible->Document(); }
 
   /**
    * Down casting.
    */
   enum EventGroup {
     eGenericEvent,
     eStateChangeEvent,
     eTextChangeEvent,
     eMutationEvent,
     eReorderEvent,
     eHideEvent,
     eShowEvent,
     eCaretMoveEvent,
+    eTextSelChangeEvent,
     eSelectionChangeEvent,
     eTableChangeEvent,
     eVirtualCursorChangeEvent
   };
 
   static const EventGroup kEventGroup = eGenericEvent;
   virtual unsigned int GetEventGroups() const
   {
@@ -325,35 +333,59 @@ protected:
 
 
 /**
  * Accessible caret move event.
  */
 class AccCaretMoveEvent: public AccEvent
 {
 public:
-  AccCaretMoveEvent(Accessible* aAccessible) :
-    AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible),
-    mCaretOffset(-1) { }
+  AccCaretMoveEvent(Accessible* aAccessible, int32_t aCaretOffset,
+                    EIsFromUserInput aIsFromUserInput = eAutoDetect) :
+    AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible,
+             aIsFromUserInput),
+    mCaretOffset(aCaretOffset) { }
   virtual ~AccCaretMoveEvent() { }
 
   // AccEvent
   static const EventGroup kEventGroup = eCaretMoveEvent;
   virtual unsigned int GetEventGroups() const
   {
     return AccEvent::GetEventGroups() | (1U << eCaretMoveEvent);
   }
 
   // AccCaretMoveEvent
   int32_t GetCaretOffset() const { return mCaretOffset; }
 
 private:
   int32_t mCaretOffset;
+};
+
+
+/**
+ * Accessible text selection change event.
+ */
+class AccTextSelChangeEvent : public AccEvent
+{
+public:
+  AccTextSelChangeEvent(HyperTextAccessible* aTarget, nsISelection* aSelection);
+  virtual ~AccTextSelChangeEvent();
+
+  // AccEvent
+  static const EventGroup kEventGroup = eTextSelChangeEvent;
+  virtual unsigned int GetEventGroups() const
+  {
+    return AccEvent::GetEventGroups() | (1U << eTextSelChangeEvent);
+  }
+
+private:
+  nsCOMPtr<nsISelection> mSel;
 
   friend class EventQueue;
+  friend class SelectionManager;
 };
 
 
 /**
  * Accessible widget selection change event.
  */
 class AccSelChangeEvent : public AccEvent
 {
--- a/accessible/src/base/EventQueue.cpp
+++ b/accessible/src/base/EventQueue.cpp
@@ -147,16 +147,36 @@ EventQueue::CoalesceEvents()
             if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
               tailEvent->mEventRule = AccEvent::eDoNotEmit;
           }
         }
       }
       break; // eCoalesceStateChange
     }
 
+    case AccEvent::eCoalesceTextSelChange:
+    {
+      // Coalesce older event by newer event for the same selection or target.
+      // Events for same selection may have different targets and vice versa one
+      // target may be pointed by different selections (for latter see
+      // bug 927159).
+      for (uint32_t index = tail - 1; index < tail; index--) {
+        AccEvent* thisEvent = mEvents[index];
+        if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
+            thisEvent->mEventType == tailEvent->mEventType) {
+          AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent);
+          AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent);
+          if (thisTSCEvent->mSel == tailTSCEvent->mSel ||
+              thisEvent->mAccessible == tailEvent->mAccessible)
+            thisEvent->mEventRule = AccEvent::eDoNotEmit;
+        }
+
+      }
+    } break; // eCoalesceTextSelChange
+
     case AccEvent::eRemoveDupes:
     {
       // Check for repeat events, coalesce newly appended event by more older
       // event.
       for (uint32_t index = tail - 1; index < tail; index--) {
         AccEvent* accEvent = mEvents[index];
         if (accEvent->mEventType == tailEvent->mEventType &&
           accEvent->mEventRule == tailEvent->mEventRule &&
@@ -453,31 +473,18 @@ EventQueue::ProcessEventQueue()
 
       // Dispatch the focus event if target is still focused.
       if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
         FocusMgr()->ProcessFocusEvent(event);
         continue;
       }
 
       // Dispatch caret moved and text selection change events.
-      if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
-        AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(event);
-        HyperTextAccessible* hyperText = target->AsHyperText();
-        if (hyperText &&
-            NS_SUCCEEDED(hyperText->GetCaretOffset(&caretMoveEvent->mCaretOffset))) {
-
-          nsEventShell::FireEvent(caretMoveEvent);
-
-          // There's a selection so fire selection change as well.
-          int32_t selectionCount;
-          hyperText->GetSelectionCount(&selectionCount);
-          if (selectionCount)
-            nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
-                                    hyperText);
-        }
+      if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) {
+        SelectionMgr()->ProcessTextSelChangeEvent(event);
         continue;
       }
 
       // Fire selected state change events in support to selection events.
       if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) {
         nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
                                 true, event->mIsFromUserInput);
 
--- a/accessible/src/base/FocusManager.cpp
+++ b/accessible/src/base/FocusManager.cpp
@@ -260,19 +260,16 @@ FocusManager::ProcessDOMFocus(nsINode* a
 }
 
 void
 FocusManager::ProcessFocusEvent(AccEvent* aEvent)
 {
   NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS,
                   "Focus event is expected!");
 
-  EIsFromUserInput fromUserInputFlag = aEvent->IsFromUserInput() ?
-    eFromUserInput : eNoUserInput;
-
   // Emit focus event if event target is the active item. Otherwise then check
   // if it's still focused and then update active item and emit focus event.
   Accessible* target = aEvent->GetAccessible();
   if (target != mActiveItem) {
 
     // Check if still focused. Otherwise we can end up with storing the active
     // item for control that isn't focused anymore.
     DocAccessible* document = aEvent->GetDocAccessible();
@@ -294,60 +291,60 @@ FocusManager::ProcessFocusEvent(AccEvent
     Accessible* ARIAMenubar =
       nsAccUtils::GetAncestorWithRole(target, roles::MENUBAR);
 
     if (ARIAMenubar != mActiveARIAMenubar) {
       // Leaving ARIA menu. Fire menu_end event on current menubar.
       if (mActiveARIAMenubar) {
         nsRefPtr<AccEvent> menuEndEvent =
           new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
-                       fromUserInputFlag);
+                       aEvent->FromUserInput());
         nsEventShell::FireEvent(menuEndEvent);
       }
 
       mActiveARIAMenubar = ARIAMenubar;
 
       // Entering ARIA menu. Fire menu_start event.
       if (mActiveARIAMenubar) {
         nsRefPtr<AccEvent> menuStartEvent =
           new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
-                       mActiveARIAMenubar, fromUserInputFlag);
+                       mActiveARIAMenubar, aEvent->FromUserInput());
         nsEventShell::FireEvent(menuStartEvent);
       }
     }
   } else if (mActiveARIAMenubar) {
     // Focus left a menu. Fire menu_end event.
     nsRefPtr<AccEvent> menuEndEvent =
       new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
-                   fromUserInputFlag);
+                   aEvent->FromUserInput());
     nsEventShell::FireEvent(menuEndEvent);
 
     mActiveARIAMenubar = nullptr;
   }
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eFocus))
     logging::FocusNotificationTarget("fire focus event", "Target", target);
 #endif
 
   nsRefPtr<AccEvent> focusEvent =
-    new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag);
+    new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, aEvent->FromUserInput());
   nsEventShell::FireEvent(focusEvent);
 
   // Fire scrolling_start event when the document receives the focus if it has
   // an anchor jump. If an accessible within the document receive the focus
   // then null out the anchor jump because it no longer applies.
   DocAccessible* targetDocument = target->Document();
   Accessible* anchorJump = targetDocument->AnchorJump();
   if (anchorJump) {
     if (target == targetDocument) {
       // XXX: bug 625699, note in some cases the node could go away before we
       // we receive focus event, for example if the node is removed from DOM.
       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
-                              anchorJump, fromUserInputFlag);
+                              anchorJump, aEvent->FromUserInput());
     }
     targetDocument->SetAnchorJump(nullptr);
   }
 }
 
 nsINode*
 FocusManager::FocusedDOMNode() const
 {
--- a/accessible/src/base/SelectionManager.cpp
+++ b/accessible/src/base/SelectionManager.cpp
@@ -4,39 +4,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/a11y/SelectionManager.h"
 
 #include "DocAccessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
-#include "nsIAccessibleEvent.h"
+#include "nsEventShell.h"
 
-#include "nsCaret.h"
 #include "nsIAccessibleTypes.h"
 #include "nsIDOMDocument.h"
-#include "nsIFrame.h"
 #include "nsIPresShell.h"
 #include "nsISelectionPrivate.h"
 #include "mozilla/Selection.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 void
-SelectionManager::Shutdown()
-{
-  ClearControlSelectionListener();
-  mLastTextAccessible = nullptr;
-  mLastUsedSelection = nullptr;
-}
-
-void
 SelectionManager::ClearControlSelectionListener()
 {
   if (!mCurrCtrlFrame)
     return;
 
   const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
   NS_ASSERTION(frameSel, "No frame selection for the element!");
 
@@ -59,18 +49,16 @@ SelectionManager::ClearControlSelectionL
 void
 SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm)
 {
   // When focus moves such that the caret is part of a new frame selection
   // this removes the old selection listener and attaches a new one for
   // the current focus.
   ClearControlSelectionListener();
 
-  mLastTextAccessible = nullptr;
-
   mCurrCtrlFrame = aFocusedElm->GetPrimaryFrame();
   if (!mCurrCtrlFrame)
     return;
 
   const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
   NS_ASSERTION(frameSel, "No frame selection for focused element!");
   if (!frameSel)
     return;
@@ -114,16 +102,47 @@ SelectionManager::RemoveDocSelectionList
 
   // Remove 'this' registered as selection listener for the spellcheck
   // selection.
   Selection* spellSel =
     frameSel->GetSelection(nsISelectionController::SELECTION_SPELLCHECK);
   spellSel->RemoveSelectionListener(this);
 }
 
+void
+SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent)
+{
+  AccTextSelChangeEvent* event = downcast_accEvent(aEvent);
+  Selection* sel = static_cast<Selection*>(event->mSel.get());
+
+  // Fire selection change event if it's not pure caret-move selection change.
+  if (sel->GetRangeCount() != 1 || !sel->IsCollapsed())
+    nsEventShell::FireEvent(aEvent);
+
+  // Fire caret move event if there's a caret in the selection.
+  nsINode* caretCntrNode =
+    nsCoreUtils::GetDOMNodeFromDOMPoint(sel->GetFocusNode(),
+                                        sel->GetFocusOffset());
+  if (!caretCntrNode)
+    return;
+
+  HyperTextAccessible* caretCntr = nsAccUtils::GetTextContainer(caretCntrNode);
+  NS_ASSERTION(caretCntr,
+               "No text container for focus while there's one for common ancestor?!");
+  if (!caretCntr)
+    return;
+
+  int32_t caretOffset = -1;
+  if (NS_SUCCEEDED(caretCntr->GetCaretOffset(&caretOffset)) && caretOffset != -1) {
+    nsRefPtr<AccCaretMoveEvent> caretMoveEvent =
+      new AccCaretMoveEvent(caretCntr, caretOffset, aEvent->FromUserInput());
+    nsEventShell::FireEvent(caretMoveEvent);
+  }
+}
+
 NS_IMETHODIMP
 SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
                                          nsISelection* aSelection,
                                          int16_t aReason)
 {
   NS_ENSURE_ARG(aDOMDocument);
 
   nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument));
@@ -144,133 +163,39 @@ SelectionManager::NotifySelectionChanged
   }
 
   return NS_OK;
 }
 
 void
 SelectionManager::ProcessSelectionChanged(nsISelection* aSelection)
 {
-  nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(aSelection));
-
-  int16_t type = 0;
-  privSel->GetType(&type);
-
-  if (type == nsISelectionController::SELECTION_NORMAL)
-    NormalSelectionChanged(aSelection);
-
-  else if (type == nsISelectionController::SELECTION_SPELLCHECK)
-    SpellcheckSelectionChanged(aSelection);
-}
-
-void
-SelectionManager::NormalSelectionChanged(nsISelection* aSelection)
-{
-  mLastUsedSelection = do_GetWeakReference(aSelection);
-
-  int32_t rangeCount = 0;
-  aSelection->GetRangeCount(&rangeCount);
-  if (rangeCount == 0) {
-    mLastTextAccessible = nullptr;
-    return; // No selection
-  }
-
-  HyperTextAccessible* textAcc =
-    nsAccUtils::GetTextAccessibleFromSelection(aSelection);
-  if (!textAcc)
-    return;
-
-  int32_t caretOffset = -1;
-  nsresult rv = textAcc->GetCaretOffset(&caretOffset);
-  if (NS_FAILED(rv))
-    return;
-
-  if (textAcc == mLastTextAccessible && caretOffset == mLastCaretOffset) {
-    int32_t selectionCount = 0;
-    textAcc->GetSelectionCount(&selectionCount);   // Don't swallow similar events when selecting text
-    if (!selectionCount)
-      return;  // Swallow duplicate caret event
+  Selection* selection = static_cast<Selection*>(aSelection);
+  const nsRange* range = selection->GetAnchorFocusRange();
+  nsINode* cntrNode = nullptr;
+  if (range)
+    cntrNode = range->GetCommonAncestor();
+  if (!cntrNode) {
+    cntrNode = selection->GetFrameSelection()->GetAncestorLimiter();
+    if (!cntrNode) {
+      cntrNode = selection->GetPresShell()->GetDocument();
+      NS_ASSERTION(selection->GetPresShell()->ConstFrameSelection() == selection->GetFrameSelection(),
+                   "Wrong selection container was used!");
+    }
   }
 
-  mLastCaretOffset = caretOffset;
-  mLastTextAccessible = textAcc;
-
-  nsRefPtr<AccEvent> event = new AccCaretMoveEvent(mLastTextAccessible);
-  mLastTextAccessible->Document()->FireDelayedEvent(event);
-}
-
-void
-SelectionManager::SpellcheckSelectionChanged(nsISelection* aSelection)
-{
-  // XXX: fire an event for accessible of focus node of the selection. If
-  // spellchecking is enabled then we will fire the number of events for
-  // the same accessible for newly appended range of the selection (for every
-  // misspelled word). If spellchecking is disabled (for example,
-  // @spellcheck="false" on html:body) then we won't fire any event.
-
-  HyperTextAccessible* hyperText =
-    nsAccUtils::GetTextAccessibleFromSelection(aSelection);
-  if (hyperText) {
-    hyperText->Document()->
-      FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
-                       hyperText);
-  }
-}
-
-nsIntRect
-SelectionManager::GetCaretRect(nsIWidget** aWidget)
-{
-  nsIntRect caretRect;
-  NS_ENSURE_TRUE(aWidget, caretRect);
-  *aWidget = nullptr;
-
-  if (!mLastTextAccessible) {
-    return caretRect;    // Return empty rect
+  HyperTextAccessible* text = nsAccUtils::GetTextContainer(cntrNode);
+  if (!text) {
+    NS_NOTREACHED("We must reach document accessible implementing text interface!");
+    return;
   }
 
-  nsINode *lastNodeWithCaret = mLastTextAccessible->GetNode();
-  NS_ENSURE_TRUE(lastNodeWithCaret, caretRect);
-
-  nsIPresShell *presShell = nsCoreUtils::GetPresShellFor(lastNodeWithCaret);
-  NS_ENSURE_TRUE(presShell, caretRect);
-
-  nsRefPtr<nsCaret> caret = presShell->GetCaret();
-  NS_ENSURE_TRUE(caret, caretRect);
-
-  nsCOMPtr<nsISelection> caretSelection(do_QueryReferent(mLastUsedSelection));
-  NS_ENSURE_TRUE(caretSelection, caretRect);
-  
-  bool isVisible;
-  caret->GetCaretVisible(&isVisible);
-  if (!isVisible) {
-    return nsIntRect();  // Return empty rect
-  }
-
-  nsRect rect;
-  nsIFrame* frame = caret->GetGeometry(caretSelection, &rect);
-  if (!frame || rect.IsEmpty()) {
-    return nsIntRect(); // Return empty rect
-  }
+  if (selection->GetType() == nsISelectionController::SELECTION_NORMAL) {
+    nsRefPtr<AccEvent> event = new AccTextSelChangeEvent(text, aSelection);
+    text->Document()->FireDelayedEvent(event);
 
-  nsPoint offset;
-  // Offset from widget origin to the frame origin, which includes chrome
-  // on the widget.
-  *aWidget = frame->GetNearestWidget(offset);
-  NS_ENSURE_TRUE(*aWidget, nsIntRect());
-  rect.MoveBy(offset);
-
-  caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel());
-  // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
-  caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
-
-  // Correct for character size, so that caret always matches the size of the character
-  // This is important for font size transitions, and is necessary because the Gecko caret uses the
-  // previous character's size as the user moves forward in the text by character.
-  int32_t charX, charY, charWidth, charHeight;
-  if (NS_SUCCEEDED(mLastTextAccessible->GetCharacterExtents(mLastCaretOffset, &charX, &charY,
-                                                            &charWidth, &charHeight,
-                                                            nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE))) {
-    caretRect.height -= charY - caretRect.y;
-    caretRect.y = charY;
+  } else if (selection->GetType() == nsISelectionController::SELECTION_SPELLCHECK) {
+    // XXX: fire an event for container accessible of the focus/anchor range
+    // of the spelcheck selection.
+    text->Document()->FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
+                                       text);
   }
-
-  return caretRect;
 }
--- a/accessible/src/base/SelectionManager.h
+++ b/accessible/src/base/SelectionManager.h
@@ -1,35 +1,30 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_SelectionManager_h__
 #define mozilla_a11y_SelectionManager_h__
 
-#include "nsAutoPtr.h"
 #include "nsIFrame.h"
 #include "nsISelectionListener.h"
 
-class nsIContent;
-class nsIntRect;
 class nsIPresShell;
-class nsIWeakReference;
-class nsIWidget;
 
 namespace mozilla {
 
 namespace dom {
 class Element;
 }
 
 namespace a11y {
 
-class HyperTextAccessible;
+class AccEvent;
 
 /**
  * This special accessibility class is for the caret and selection management.
  * There is only 1 visible caret per top level window. However, there may be
  * several visible selections.
  *
  * The important selections are the one owned by each document, and the one in
  * the currently focused control.
@@ -48,17 +43,17 @@ class SelectionManager : public nsISelec
 public:
   // nsISupports
   // implemented by derived nsAccessibilityService
 
   // nsISelectionListener
   NS_DECL_NSISELECTIONLISTENER
 
   // SelectionManager
-  void Shutdown();
+  void Shutdown() { ClearControlSelectionListener(); }
 
   /**
    * Listen to selection events on the focused control.
    *
    * Note: only one control's selection events are listened to at a time. This
    * will remove the previous control's selection listener.
    */
   void SetControlSelectionListener(dom::Element* aFocusedElm);
@@ -74,45 +69,28 @@ public:
   void AddDocSelectionListener(nsIPresShell* aPresShell);
 
   /**
    * Stop listening to selection events for a given document
    */
   void RemoveDocSelectionListener(nsIPresShell* aShell);
 
   /**
-   * Return the caret rect and the widget containing the caret.
+   * Process delayed event, results in caret move and text selection change
+   * events.
    */
-  nsIntRect GetCaretRect(nsIWidget** aWidget);
+  void ProcessTextSelChangeEvent(AccEvent* aEvent);
 
 protected:
   /**
    * Process DOM selection change. Fire selection and caret move events.
    */
   void ProcessSelectionChanged(nsISelection* aSelection);
 
-  /**
-   * Process normal selection change and fire caret move event.
-   */
-  void NormalSelectionChanged(nsISelection* aSelection);
-
-  /**
-   * Process spellcheck selection change and fire text attribute changed event
-   * for invalid text attribute.
-   */
-  void SpellcheckSelectionChanged(nsISelection* aSelection);
-
 private:
   // Currently focused control.
   nsWeakFrame mCurrCtrlFrame;
-
-  // Info for the the last selection event.
-  // If it was on a control, then its control's selection. Otherwise, it's for
-  // a document where the selection changed.
-  nsCOMPtr<nsIWeakReference> mLastUsedSelection; // Weak ref to nsISelection
-  nsRefPtr<HyperTextAccessible> mLastTextAccessible;
-  int32_t mLastCaretOffset;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -247,52 +247,34 @@ bool
 nsAccUtils::IsARIASelected(Accessible* aAccessible)
 {
   return aAccessible->GetContent()->
     AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected,
                 nsGkAtoms::_true, eCaseMatters);
 }
 
 HyperTextAccessible*
-nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection)
+nsAccUtils::GetTextContainer(nsINode* aNode)
 {
-  // Get accessible from selection's focus DOM point (the DOM point where
-  // selection is ended).
-
-  nsCOMPtr<nsIDOMNode> focusDOMNode;
-  aSelection->GetFocusNode(getter_AddRefs(focusDOMNode));
-  if (!focusDOMNode)
+  // Get text accessible containing the result node.
+  DocAccessible* doc =
+    GetAccService()->GetDocAccessible(aNode->OwnerDoc());
+  Accessible* accessible =
+    doc ? doc->GetAccessibleOrContainer(aNode) : nullptr;
+  if (!accessible)
     return nullptr;
 
-  int32_t focusOffset = 0;
-  aSelection->GetFocusOffset(&focusOffset);
-
-  nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode));
-  nsCOMPtr<nsINode> resultNode =
-    nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
-
-  // Get text accessible containing the result node.
-  DocAccessible* doc = 
-    GetAccService()->GetDocAccessible(resultNode->OwnerDoc());
-  Accessible* accessible = doc ? 
-    doc->GetAccessibleOrContainer(resultNode) : nullptr;
-  if (!accessible) {
-    NS_NOTREACHED("No nsIAccessibleText for selection change event!");
-    return nullptr;
-  }
-
   do {
     HyperTextAccessible* textAcc = accessible->AsHyperText();
     if (textAcc)
       return textAcc;
 
     accessible = accessible->Parent();
   } while (accessible);
 
-  NS_NOTREACHED("We must reach document accessible implementing nsIAccessibleText!");
   return nullptr;
 }
 
 nsIntPoint
 nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY,
                                   uint32_t aCoordinateType,
                                   Accessible* aAccessible)
 {
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -132,32 +132,27 @@ public:
    *
    * @param  aAccessible  [in] the item accessible
    * @param  aState       [in] the state of the item accessible
    */
   static Accessible* GetSelectableContainer(Accessible* aAccessible,
                                             uint64_t aState);
 
   /**
+   * Return a text container accessible for the given node.
+   */
+  static HyperTextAccessible* GetTextContainer(nsINode* aNode);
+
+  /**
    * Return true if the DOM node of given accessible has aria-selected="true"
    * attribute.
    */
   static bool IsARIASelected(Accessible* aAccessible);
 
   /**
-   * Return text accessible containing focus point of the given selection.
-   * Used for normal and misspelling selection changes processing.
-   *
-   * @param aSelection  [in] the given selection
-   * @return            text accessible
-   */
-  static HyperTextAccessible*
-    GetTextAccessibleFromSelection(nsISelection* aSelection);
-
-  /**
    * Converts the given coordinates to coordinates relative screen.
    *
    * @param aX               [in] the given x coord
    * @param aY               [in] the given y coord
    * @param aCoordinateType  [in] specifies coordinates origin (refer to
    *                         nsIAccessibleCoordinateType)
    * @param aAccessible      [in] the accessible if coordinates are given
    *                         relative it.
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -4,22 +4,24 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HyperTextAccessible.h"
 
 #include "Accessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
+#include "nsIAccessibleTypes.h"
 #include "DocAccessible.h"
 #include "Role.h"
 #include "States.h"
 #include "TextAttrs.h"
 #include "TreeWalker.h"
 
+#include "nsCaret.h"
 #include "nsIClipboard.h"
 #include "nsContentUtils.h"
 #include "nsFocusManager.h"
 #include "nsIDOMRange.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
 #include "nsFrameSelection.h"
@@ -1737,16 +1739,67 @@ HyperTextAccessible::CaretLineNumber()
 
     caretFrame = parentFrame;
   }
 
   NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
   return lineNumber;
 }
 
+nsIntRect
+HyperTextAccessible::GetCaretRect(nsIWidget** aWidget)
+{
+  *aWidget = nullptr;
+
+  nsRefPtr<nsCaret> caret = mDoc->PresShell()->GetCaret();
+  NS_ENSURE_TRUE(caret, nsIntRect());
+
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  NS_ENSURE_TRUE(caretSelection, nsIntRect());
+
+  bool isVisible = false;
+  caret->GetCaretVisible(&isVisible);
+  if (!isVisible)
+    return nsIntRect();
+
+  nsRect rect;
+  nsIFrame* frame = caret->GetGeometry(caretSelection, &rect);
+  if (!frame || rect.IsEmpty())
+    return nsIntRect();
+
+  nsPoint offset;
+  // Offset from widget origin to the frame origin, which includes chrome
+  // on the widget.
+  *aWidget = frame->GetNearestWidget(offset);
+  NS_ENSURE_TRUE(*aWidget, nsIntRect());
+  rect.MoveBy(offset);
+
+  nsIntRect caretRect;
+  caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel());
+  // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
+  caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
+
+  int32_t caretOffset = -1;
+  GetCaretOffset(&caretOffset);
+
+  // Correct for character size, so that caret always matches the size of
+  // the character. This is important for font size transitions, and is
+  // necessary because the Gecko caret uses the previous character's size as
+  // the user moves forward in the text by character.
+  int32_t charX = 0, charY = 0, charWidth = 0, charHeight = 0;
+  if (NS_SUCCEEDED(GetCharacterExtents(caretOffset, &charX, &charY,
+                                       &charWidth, &charHeight,
+                                       nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE))) {
+    caretRect.height -= charY - caretRect.y;
+    caretRect.y = charY;
+  }
+
+  return caretRect;
+}
+
 already_AddRefed<nsFrameSelection>
 HyperTextAccessible::FrameSelection()
 {
   nsIFrame* frame = GetFrame();
   return frame ? frame->GetFrameSelection() : nullptr;
 }
 
 void
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -257,16 +257,25 @@ public:
   }
 
   /**
    * Provide the line number for the caret.
    * @return 1-based index for the line number with the caret
    */
   int32_t CaretLineNumber();
 
+  /**
+   * Return the caret rect and the widget containing the caret within this
+   * text accessible.
+   *
+   * @param [out] the widget containing the caret
+   * @return      the caret rect
+   */
+  nsIntRect GetCaretRect(nsIWidget** aWidget);
+
   //////////////////////////////////////////////////////////////////////////////
   // EditableTextAccessible
 
   /**
    * Return the editor associated with the accessible.
    */
   virtual already_AddRefed<nsIEditor> GetEditor() const;
 
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -528,19 +528,17 @@ HTMLFileInputAccessible::HandleAccEvent(
       (event->GetState() == states::BUSY ||
        event->GetState() == states::REQUIRED ||
        event->GetState() == states::HASPOPUP ||
        event->GetState() == states::INVALID)) {
     Accessible* button = GetChildAt(0);
     if (button && button->Role() == roles::PUSHBUTTON) {
       nsRefPtr<AccStateChangeEvent> childEvent =
         new AccStateChangeEvent(button, event->GetState(),
-                                event->IsStateEnabled(),
-                                (event->IsFromUserInput() ? eFromUserInput
-                                                          : eNoUserInput));
+                                event->IsStateEnabled(), event->FromUserInput());
       nsEventShell::FireEvent(childEvent);
     }
   }
 
   return NS_OK;
 }
 
 
--- a/accessible/src/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/src/windows/msaa/AccessibleWrap.cpp
@@ -1127,17 +1127,17 @@ AccessibleWrap::HandleAccEvent(AccEvent*
   NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE);
 
   Accessible* accessible = aEvent->GetAccessible();
   if (!accessible)
     return NS_OK;
 
   if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
       eventType == nsIAccessibleEvent::EVENT_FOCUS) {
-    UpdateSystemCaret();
+    UpdateSystemCaretFor(accessible);
   }
 
   int32_t childID = GetChildIDFor(accessible); // get the id for the accessible
   if (!childID)
     return NS_OK; // Can't fire an event without a child ID
 
   HWND hWnd = GetHWNDFor(accessible);
   NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
@@ -1281,30 +1281,29 @@ AccessibleWrap::GetXPAccessibleFor(const
     return nullptr;
   }
 
   // Gecko child indices are 0-based in contrast to indices used in MSAA.
   return GetChildAt(aVarChild.lVal - 1);
 }
 
 void
-AccessibleWrap::UpdateSystemCaret()
+AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible)
 {
   // Move the system caret so that Windows Tablet Edition and tradional ATs with 
   // off-screen model can follow the caret
   ::DestroyCaret();
 
-  a11y::RootAccessible* rootAccessible = RootAccessible();
-  if (!rootAccessible) {
+  HyperTextAccessible* text = aAccessible->AsHyperText();
+  if (!text)
     return;
-  }
 
   nsIWidget* widget = nullptr;
-  nsIntRect caretRect = SelectionMgr()->GetCaretRect(&widget);
-  HWND caretWnd; 
+  nsIntRect caretRect = text->GetCaretRect(&widget);
+  HWND caretWnd;
   if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
     return;
   }
 
   // Create invisible bitmap for caret, otherwise its appearance interferes
   // with Gecko caret
   HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, nullptr);
   if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) {  // Also destroys the last caret
--- a/accessible/src/windows/msaa/AccessibleWrap.h
+++ b/accessible/src/windows/msaa/AccessibleWrap.h
@@ -159,17 +159,17 @@ public: // construction, destruction
 
   /**
    * System caret support: update the Windows caret position. 
    * The system caret works more universally than the MSAA caret
    * For example, Window-Eyes, JAWS, ZoomText and Windows Tablet Edition use it
    * We will use an invisible system caret.
    * Gecko is still responsible for drawing its own caret
    */
-  void UpdateSystemCaret();
+  void UpdateSystemCaretFor(Accessible* aAccessible);
 
   /**
    * Find an accessible by the given child ID in cached documents.
    */
   Accessible* GetXPAccessibleFor(const VARIANT& aVarChild);
 
   NS_IMETHOD GetNativeInterface(void **aOutAccessible);
 
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -1205,16 +1205,24 @@ function synthDownKey(aNodeOrID, aChecke
  */
 function synthUpKey(aNodeOrID, aCheckerOrEventSeq, aArgs)
 {
   this.__proto__ = new synthKey(aNodeOrID, "VK_UP", aArgs,
                                 aCheckerOrEventSeq);
 }
 
 /**
+ * Left arrow key invoker.
+ */
+function synthLeftKey(aNodeOrID, aCheckerOrEventSeq)
+{
+  this.__proto__ = new synthKey(aNodeOrID, "VK_LEFT", null, aCheckerOrEventSeq);
+}
+
+/**
  * Right arrow key invoker.
  */
 function synthRightKey(aNodeOrID, aCheckerOrEventSeq)
 {
   this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq);
 }
 
 /**
@@ -1653,16 +1661,27 @@ function invokerChecker(aEventType, aTar
     return prettyName(this.mTarget);
   }
 
   this.mTarget = aTargetOrFunc;
   this.mTargetFuncArg = aTargetFuncArg;
 }
 
 /**
+ * Generic invoker checker for unexpected events.
+ */
+function unexpectedInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
+{
+  this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
+                                      aTargetFuncArg, true);
+
+  this.unexpected = true;
+}
+
+/**
  * Common invoker checker for async events.
  */
 function asyncInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
 {
   this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
                                       aTargetFuncArg, true);
 }
 
@@ -1732,16 +1751,29 @@ function caretMoveChecker(aCaretOffset, 
   {
     is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset,
        aCaretOffset,
        "Wrong caret offset for " + prettyName(aEvent.accessible));
   }
 }
 
 /**
+ * Text selection change checker.
+ */
+function textSelectionChecker(aID, aStartOffset, aEndOffset)
+{
+  this.__proto__ = new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID);
+
+  this.check = function textSelectionChecker_check(aEvent)
+  {
+    testTextGetSelection(aID, aStartOffset, aEndOffset, 0);
+  }
+}
+
+/**
  * State change checker.
  */
 function stateChangeChecker(aState, aIsExtraState, aIsEnabled,
                             aTargetOrFunc, aTargetFuncArg, aIsAsync,
                             aSkipCurrentStateCheck)
 {
   this.__proto__ = new invokerChecker(EVENT_STATE_CHANGE, aTargetOrFunc,
                                       aTargetFuncArg, aIsAsync);
--- a/accessible/tests/mochitest/events/a11y.ini
+++ b/accessible/tests/mochitest/events/a11y.ini
@@ -41,10 +41,11 @@ support-files =
 [test_scroll.xul]
 [test_selection.html]
 [test_selection.xul]
 [test_selection_aria.html]
 [test_statechange.html]
 [test_text.html]
 [test_text_alg.html]
 [test_textattrchange.html]
+[test_textselchange.html]
 [test_tree.xul]
 [test_valuechange.html]
--- a/accessible/tests/mochitest/events/test_caretmove.html
+++ b/accessible/tests/mochitest/events/test_caretmove.html
@@ -57,16 +57,20 @@
       gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
       gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
 
       id = "textarea";
       gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
       gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
       gQueue.push(new synthDownKey(id, new caretMoveChecker(12, id)));
 
+      id = "textarea_wrapped";
+      gQueue.push(new setCaretOffset(id, 4, id));
+      gQueue.push(new synthLeftKey(id, new caretMoveChecker(4, id)));
+
       id = "p";
       gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
       gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
       gQueue.push(new synthDownKey(id, new caretMoveChecker(6, id)));
 
       id = "p1_in_div";
       gQueue.push(new synthClick(id, new clickChecker(0, id, "p2_in_div", -1)));
 
@@ -100,16 +104,21 @@
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=454377"
      title="Accessible caret move events testing">
     Bug 454377
   </a>
   <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=567571"
+     title="caret-moved events missing at the end of a wrapped line of text">
+    Bug 567571
+  </a>
+  <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=824901"
      title="HyperTextAccessible::DOMPointToHypertextOffset fails for node and offset equal to node child count">
     Bug 824901
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
@@ -119,11 +128,13 @@
   <p id="p" contentEditable="true"><span>text</span><br/>text</p>
   <div id="div" contentEditable="true"><p id="p1_in_div">text</p><p id="p2_in_div">text</p></div>
 
   <p contentEditable="true" id="test1"><span id="test1_span">text</span>ohoho</p>
   <p contentEditable="true" id="test2"><span><span id="test2_span">text</span></span>ohoho</p>
   <p contentEditable="true" id="test3"></p>
   <p contentEditable="true" id="test4"><span id="test4_span"></span></p>
 
+  <textarea id="textarea_wrapped" cols="5">hey friend</textarea>
+
   <div id="eventdump"></div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_textselchange.html
@@ -0,0 +1,67 @@
+<html>
+
+<head>
+  <title>Accessible text selection change events testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../text.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    var gQueue = null;
+
+    // gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
+
+    function doTests()
+    {
+      // test caret move events and caret offsets
+      gQueue = new eventQueue();
+
+      var onclickSeq = [
+        new caretMoveChecker(0, "c1_p1"), 
+        new unexpectedInvokerChecker(EVENT_TEXT_SELECTION_CHANGED, "c1_p1")
+      ];
+      gQueue.push(new synthClick("c1_p1", onclickSeq));
+      gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 1), { shiftKey: true }));
+      gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 2), { shiftKey: true }));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=762934"
+     title="Text selection change event has a wrong target when selection is spanned through several objects">
+    Bug 762934
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="c1" contentEditable="true">
+    <p id="c1_p1">paragraph</p>
+    <p id="c1_p2">paragraph</p>
+  </div>
+
+  <div id="eventdump"></div>
+</body>
+</html>
--- a/accessible/tests/mochitest/hittest/test_browser.html
+++ b/accessible/tests/mochitest/hittest/test_browser.html
@@ -11,16 +11,18 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../layout.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       // Hit testing. See bug #726097
+      getNode("hittest").scrollIntoView(true);
+
       var hititem = getAccessible("hititem");
       var hittest = getAccessible("hittest");
 
       var [hitX, hitY, hitWidth, hitHeight] = getBounds(hititem);
       var tgtX = hitX + hitWidth / 2;
       var tgtY = hitY + hitHeight / 2;
 
       var rootAcc = getRootAccessible();
--- a/accessible/tests/mochitest/hittest/test_menu.xul
+++ b/accessible/tests/mochitest/hittest/test_menu.xul
@@ -76,16 +76,18 @@
     function doTest()
     {
       if (LINUX) {
         ok(true, "No tests is running on Linux");
         SimpleTest.finish();
         return;
       }
 
+      getNode("mi_file1").scrollIntoView(true);
+
       gQueue = new eventQueue();
       gQueue.push(new openMenu("mi_file1", "mp_file1", "mi_file1.1"));
       gQueue.push(new openMenu("mi_file1.2", "mp_file1.2", "mi_file1.2.1"));
       gQueue.push(new closeMenu("mi_file1", "mi_file1.1", "mi_file1.2.1"));
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
--- a/accessible/tests/mochitest/textselection/test_general.html
+++ b/accessible/tests/mochitest/textselection/test_general.html
@@ -84,17 +84,17 @@
       }
     }
 
     function removeSelection(aID)
     {
       this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
 
       this.eventSeq = [
-        new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
+        new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, document)
       ];
 
       this.invoke = function removeSelection_invoke()
       {
         this.hyperText.removeSelection(0);
       }
 
       this.finalCheck = function removeSelection_finalCheck()
@@ -128,18 +128,17 @@
 
     var gQueue = null;
     function doTests()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new addSelection("paragraph", 1, 3));
       gQueue.push(new changeSelection("paragraph", 2, 4));
-      //gQueue.push(new removeSelection("paragraph"));
-      todo(false, "removeSelection doesn't fire text selection changed events, see bug bug 688124.");
+      gQueue.push(new removeSelection("paragraph"));
 
       gQueue.push(new synthFocus("textbox", onfocusEventSeq("textbox")));
       gQueue.push(new changeSelection("textbox", 1, 3));
 
       gQueue.push(new synthFocus("textarea", onfocusEventSeq("textarea")));
       gQueue.push(new changeSelection("textarea", 1, 3));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
@@ -150,17 +149,22 @@
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=688126"
      title="nsIAccessibleText::setSelectionBounds doesn't fire text selection changed events in some cases">
-    Mozilla Bug 688126
+    Bug 688126
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=688124"
+     title="no text selection changed event when selection is removed">
+    Bug 688124
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <p id="paragraph">hello</p>
   <input id="textbox" value="hello"/>
--- a/browser/devtools/shadereditor/panel.js
+++ b/browser/devtools/shadereditor/panel.js
@@ -28,16 +28,17 @@ ShaderEditorPanel.prototype = {
     if (!this.target.isRemote) {
       targetPromise = this.target.makeRemote();
     } else {
       targetPromise = promise.resolve(this.target);
     }
 
     return targetPromise
       .then(() => {
+        this.panelWin.gToolbox = this._toolbox;
         this.panelWin.gTarget = this.target;
         this.panelWin.gFront = new WebGLFront(this.target.client, this.target.form);
         return this.panelWin.startupShaderEditor();
       })
       .then(() => {
         this.isReady = true;
         this.emit("ready");
         return this;
--- a/browser/devtools/shadereditor/shadereditor.js
+++ b/browser/devtools/shadereditor/shadereditor.js
@@ -34,17 +34,17 @@ const DEFAULT_EDITOR_CONFIG = {
   mode: Editor.modes.text,
   lineNumbers: true,
   showAnnotationRuler: true
 };
 
 /**
  * The current target and the WebGL Editor front, set by this tool's host.
  */
-let gTarget, gFront;
+let gToolbox, gTarget, gFront;
 
 /**
  * Initializes the shader editor controller and views.
  */
 function startupShaderEditor() {
   return promise.all([
     EventsHandler.initialize(),
     ShadersListView.initialize(),
@@ -66,32 +66,44 @@ function shutdownShaderEditor() {
 /**
  * Functions handling target-related lifetime events.
  */
 let EventsHandler = {
   /**
    * Listen for events emitted by the current tab target.
    */
   initialize: function() {
+    this._onHostChanged = this._onHostChanged.bind(this);
     this._onWillNavigate = this._onWillNavigate.bind(this);
     this._onProgramLinked = this._onProgramLinked.bind(this);
+    gToolbox.on("host-changed", this._onHostChanged);
     gTarget.on("will-navigate", this._onWillNavigate);
     gFront.on("program-linked", this._onProgramLinked);
 
   },
 
   /**
    * Remove events emitted by the current tab target.
    */
   destroy: function() {
+    gToolbox.off("host-changed", this._onHostChanged);
     gTarget.off("will-navigate", this._onWillNavigate);
     gFront.off("program-linked", this._onProgramLinked);
   },
 
   /**
+   * Handles a host change event on the parent toolbox.
+   */
+  _onHostChanged: function() {
+    if (gToolbox.hostType == "side") {
+      $("#shaders-pane").removeAttribute("height");
+    }
+  },
+
+  /**
    * Called for each location change in the debugged tab.
    */
   _onWillNavigate: function() {
     gFront.setup();
 
     ShadersListView.empty();
     ShadersEditorsView.setText({ vs: "", fs: "" });
     $("#reload-notice").hidden = true;
@@ -167,16 +179,22 @@ let ShadersListView = Heritage.extend(Wi
         checkboxTooltip: L10N.getStr("shadersList.blackboxLabel")
       }
     });
 
     // Make sure there's always a selected item available.
     if (!this.selectedItem) {
       this.selectedIndex = 0;
     }
+
+    // Prevent this container from growing indefinitely in height when the
+    // toolbox is docked to the side.
+    if (gToolbox.hostType == "side" && this.itemCount == SHADERS_AUTOGROW_ITEMS) {
+      this._pane.setAttribute("height", this._pane.getBoundingClientRect().height);
+    }
   },
 
   /**
    * The select listener for the sources container.
    */
   _onShaderSelect: function({ detail: sourceItem }) {
     if (!sourceItem) {
       return;
--- a/browser/devtools/shadereditor/shadereditor.xul
+++ b/browser/devtools/shadereditor/shadereditor.xul
@@ -35,30 +35,30 @@
           pack="center"
           flex="1"
           hidden="true">
       <label id="requests-menu-waiting-notice-label"
              class="plain"
              value="&shaderEditorUI.emptyNotice;"/>
     </hbox>
 
-    <hbox id="content" flex="1" hidden="true">
+    <box id="content" class="devtools-responsive-container" flex="1" hidden="true">
       <vbox id="shaders-pane"/>
       <splitter class="devtools-side-splitter"/>
-      <hbox id="shaders-editors" flex="1">
+      <box id="shaders-editors" class="devtools-responsive-container" flex="1">
         <vbox flex="1">
           <vbox id="vs-editor" flex="1"/>
           <label id="vs-editor-label"
                  class="plain editor-label"
                  value="&shaderEditorUI.vertexShader;"/>
         </vbox>
         <splitter id="editors-splitter" class="devtools-side-splitter"/>
         <vbox flex="1">
           <vbox id="fs-editor" flex="1"/>
           <label id="fs-editor-label"
                  class="plain editor-label"
                  value="&shaderEditorUI.fragmentShader;"/>
         </vbox>
-      </hbox>
-    </hbox>
+      </box>
+    </box>
   </vbox>
 
 </window>
--- a/browser/themes/shared/devtools/shadereditor.inc.css
+++ b/browser/themes/shared/devtools/shadereditor.inc.css
@@ -100,8 +100,37 @@
   color: #fff;
 }
 
 .editor-label[selected] {
   background: linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)) repeat-x top left;
   box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15),
               inset 0 -1px 0 hsla(210,40%,83%,.05);
 }
+
+/* Responsive sidebar */
+
+@media (max-width: 700px) {
+  #shaders-pane {
+    max-height: 60vh;
+  }
+
+  .side-menu-widget-container {
+    box-shadow: none !important;
+  }
+
+  .side-menu-widget-item-arrow {
+    background-image: none !important;
+  }
+
+  .devtools-side-splitter {
+    border-top-color: transparent !important;
+  }
+
+  .editor-label {
+    -moz-box-ordinal-group: 0;
+  }
+
+  .editor-label:not([selected]) {
+    border-top: 1px solid hsla(210,8%,5%,.25);
+    box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15);
+  }
+}
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -179,39 +179,39 @@ nsDOMFileReader::GetReadyState(uint16_t 
 {
   *aReadyState = ReadyState();
   return NS_OK;
 }
 
 JS::Value
 nsDOMFileReader::GetResult(JSContext* aCx, ErrorResult& aRv)
 {
-  JS::Rooted<JS::Value> result(aCx, JS::UndefinedValue());
+  JS::Rooted<JS::Value> result(aCx);
   aRv = GetResult(aCx, result.address());
   return result;
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::GetResult(JSContext* aCx, JS::Value* aResult)
 {
+  JS::Rooted<JS::Value> result(aCx);
   if (mDataFormat == FILE_AS_ARRAYBUFFER) {
     if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) {
-      JSObject* tmp = mResultArrayBuffer;
-      *aResult = OBJECT_TO_JSVAL(tmp);
+      result.setObject(*mResultArrayBuffer);
     } else {
-      *aResult = JSVAL_NULL;
+      result.setNull();
     }
-    if (!JS_WrapValue(aCx, aResult)) {
+    if (!JS_WrapValue(aCx, &result)) {
       return NS_ERROR_FAILURE;
     }
+    *aResult = result;
     return NS_OK;
   }
- 
+
   nsString tmpResult = mResult;
-  JS::Rooted<JS::Value> result(aCx);
   if (!xpc::StringToJsval(aCx, tmpResult, &result)) {
     return NS_ERROR_FAILURE;
   }
   *aResult = result;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -949,17 +949,17 @@ nsFrameMessageManager::ReceiveMessage(ns
 
       JS::Rooted<JS::Value> rval(ctx, JSVAL_VOID);
       JS::Rooted<JS::Value> argv(ctx, JS::ObjectValue(*param));
 
       {
         JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull());
 
         JSAutoCompartment tac(ctx, thisObject);
-        if (!JS_WrapValue(ctx, argv.address())) {
+        if (!JS_WrapValue(ctx, &argv)) {
           return NS_ERROR_UNEXPECTED;
         }
 
         JS_CallFunctionValue(ctx, thisObject,
                              funval, 1, argv.address(), rval.address());
         if (aJSONRetVal) {
           nsString json;
           if (JS_Stringify(ctx, &rval, JS::NullPtr(), JS::NullHandleValue,
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -3141,27 +3141,25 @@ nsObjectLoadingContent::LegacyCall(JSCon
     return JS::UndefinedValue();
   }
 
   obj = thisContent->GetWrapper();
   // Now wrap things up into the compartment of "obj"
   JSAutoCompartment ac(aCx, obj);
   nsTArray<JS::Value> args(aArguments);
   JS::AutoArrayRooter rooter(aCx, args.Length(), args.Elements());
-  for (JS::Value *arg = args.Elements(), *arg_end = arg + args.Length();
-       arg != arg_end;
-       ++arg) {
-    if (!JS_WrapValue(aCx, arg)) {
+  for (size_t i = 0; i < args.Length(); i++) {
+    if (!JS_WrapValue(aCx, rooter.handleAt(i))) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return JS::UndefinedValue();
     }
   }
 
   JS::Rooted<JS::Value> thisVal(aCx, aThisVal);
-  if (!JS_WrapValue(aCx, thisVal.address())) {
+  if (!JS_WrapValue(aCx, &thisVal)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return JS::UndefinedValue();
   }
 
   nsRefPtr<nsNPAPIPluginInstance> pi;
   nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
@@ -3184,17 +3182,17 @@ nsObjectLoadingContent::LegacyCall(JSCon
   }
 
   if (!pi_obj) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return JS::UndefinedValue();
   }
 
   JS::Rooted<JS::Value> retval(aCx);
-  bool ok = JS::Call(aCx, thisVal, pi_obj, args.Length(), args.Elements(),
+  bool ok = JS::Call(aCx, thisVal, pi_obj, args.Length(), rooter.array,
                      &retval);
   if (!ok) {
     aRv.Throw(NS_ERROR_FAILURE);
     return JS::UndefinedValue();
   }
 
   Telemetry::Accumulate(Telemetry::PLUGIN_CALLED_DIRECTLY, true);
   return retval;
--- a/content/events/src/nsDOMMessageEvent.cpp
+++ b/content/events/src/nsDOMMessageEvent.cpp
@@ -62,17 +62,17 @@ nsDOMMessageEvent::GetData(JSContext* aC
   *aData = GetData(aCx, rv);
   return rv.ErrorCode();
 }
 
 JS::Value
 nsDOMMessageEvent::GetData(JSContext* aCx, ErrorResult& aRv)
 {
   JS::Rooted<JS::Value> data(aCx, mData);
-  if (!JS_WrapValue(aCx, data.address())) {
+  if (!JS_WrapValue(aCx, &data)) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
   return data;
 }
 
 NS_IMETHODIMP
 nsDOMMessageEvent::GetOrigin(nsAString& aOrigin)
 {
--- a/content/html/content/src/UndoManager.cpp
+++ b/content/html/content/src/UndoManager.cpp
@@ -1151,17 +1151,17 @@ UndoManager::DispatchTransactionEvent(JS
   nsCOMPtr<nsIWritableVariant> transactions = new nsVariant();
 
   // Unwrap the DOMTransactions into jsvals, then convert
   // to nsIVariant then put into a nsIVariant array. Arrays in XPIDL suck.
   nsCOMArray<nsIVariant> keepAlive;
   nsTArray<nsIVariant*> transactionItems;
   for (uint32_t i = 0; i < items.Length(); i++) {
     JS::Rooted<JS::Value> txVal(aCx, JS::ObjectValue(*items[i]->Callback()));
-    if (!JS_WrapValue(aCx, txVal.address())) {
+    if (!JS_WrapValue(aCx, &txVal)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     nsCOMPtr<nsIVariant> txVariant;
     nsresult rv =
       nsContentUtils::XPConnect()->JSToVariant(aCx, txVal,
                                                getter_AddRefs(txVariant));
     if (NS_SUCCEEDED(rv)) {
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -340,17 +340,17 @@ nsXBLProtoImplField::InstallAccessors(JS
     return NS_OK;
 
   // FieldGetter and FieldSetter need to run in the XBL scope so that they can
   // see through any SOWs on their targets.
 
   // First, enter the XBL scope, and compile the functions there.
   JSAutoCompartment ac(aCx, scopeObject);
   JS::Rooted<JS::Value> wrappedClassObj(aCx, JS::ObjectValue(*aTargetClassObject));
-  if (!JS_WrapValue(aCx, wrappedClassObj.address()) || !JS_WrapId(aCx, id.address()))
+  if (!JS_WrapValue(aCx, &wrappedClassObj) || !JS_WrapId(aCx, id.address()))
     return NS_ERROR_OUT_OF_MEMORY;
 
   JS::Rooted<JSObject*> get(aCx,
     JS_GetFunctionObject(js::NewFunctionByIdWithReserved(aCx, FieldGetter,
                                                          0, 0, scopeObject, id)));
   if (!get) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
@@ -440,19 +440,19 @@ nsXBLProtoImplField::InstallField(nsIScr
     return rv;
   }
 
 
   // Now, enter the node's compartment, wrap the eval result, and define it on
   // the bound node.
   JSAutoCompartment ac2(cx, aBoundNode);
   nsDependentString name(mName);
-  if (!JS_WrapValue(cx, result.address()) ||
+  if (!JS_WrapValue(cx, &result) ||
       !::JS_DefineUCProperty(cx, aBoundNode,
-                             reinterpret_cast<const jschar*>(mName), 
+                             reinterpret_cast<const jschar*>(mName),
                              name.Length(), result, nullptr, nullptr,
                              mJSAttributes)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   *aDidInstall = true;
   return NS_OK;
 }
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1586,17 +1586,17 @@ Navigator::DoNewResolve(JSContext* aCx, 
     rv = nsContentUtils::WrapNative(aCx, aObject, native, &prop_val,
                                     getter_AddRefs(holder), true);
 
     if (NS_FAILED(rv)) {
       return Throw(aCx, rv);
     }
   }
 
-  if (!JS_WrapValue(aCx, prop_val.address())) {
+  if (!JS_WrapValue(aCx, &prop_val)) {
     return Throw(aCx, NS_ERROR_UNEXPECTED);
   }
 
   aValue.set(prop_val);
   return true;
 }
 
 static PLDHashOperator
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -2224,22 +2224,22 @@ BaseStubConstructor(nsIWeakReference* aW
         unsigned argc = args.length() + 1;
         JS::AutoValueVector argv(cx);
         argv.resize(argc);
 
         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
         nsCOMPtr<nsIDOMWindow> currentWin(do_GetInterface(currentInner));
         rv = WrapNative(cx, obj, currentWin, &NS_GET_IID(nsIDOMWindow),
                         true, argv.handleAt(0), getter_AddRefs(holder));
-        if (!JS_WrapValue(cx, &argv[0]))
+        if (!JS_WrapValue(cx, argv.handleAt(0)))
           return NS_ERROR_FAILURE;
 
         for (size_t i = 1; i < argc; ++i) {
           argv[i] = args[i - 1];
-          if (!JS_WrapValue(cx, &argv[i]))
+          if (!JS_WrapValue(cx, argv.handleAt(i)))
             return NS_ERROR_FAILURE;
         }
 
         JS::Rooted<JS::Value> frval(cx);
         bool ret = JS_CallFunctionValue(cx, thisObject, funval,
                                         argc, argv.begin(),
                                         frval.address());
 
@@ -2366,17 +2366,17 @@ public:
                        JS::Handle<JSObject*> obj, const jsval &val, bool *bp,
                        bool *_retval);
 
   nsresult Install(JSContext *cx, JS::Handle<JSObject*> target,
                    JS::Handle<JS::Value> aThisAsVal)
   {
     JS::Rooted<JS::Value> thisAsVal(cx, aThisAsVal);
     // The 'attrs' argument used to be JSPROP_PERMANENT. See bug 628612.
-    bool ok = JS_WrapValue(cx, thisAsVal.address()) &&
+    bool ok = JS_WrapValue(cx, &thisAsVal) &&
       ::JS_DefineUCProperty(cx, target,
                             reinterpret_cast<const jschar *>(mClassName),
                             NS_strlen(mClassName), thisAsVal, JS_PropertyStub,
                             JS_StrictPropertyStub, 0);
 
     return ok ? NS_OK : NS_ERROR_UNEXPECTED;
   }
 
@@ -2940,17 +2940,17 @@ ResolvePrototype(nsIXPConnect *aXPConnec
     }
   }
 
   v = OBJECT_TO_JSVAL(dot_prototype);
 
   JSAutoCompartment ac(cx, class_obj);
 
   // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly}
-  if (!JS_WrapValue(cx, v.address()) ||
+  if (!JS_WrapValue(cx, &v) ||
       !JS_DefineProperty(cx, class_obj, "prototype", v,
                          JS_PropertyStub, JS_StrictPropertyStub,
                          JSPROP_PERMANENT | JSPROP_READONLY)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   *did_resolve = true;
 
@@ -3252,17 +3252,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
       }
 
       rv = WrapNative(cx, scope, native, true, &prop_val,
                       getter_AddRefs(holder));
     }
 
     NS_ENSURE_SUCCESS(rv, rv);
 
-    if (!JS_WrapValue(cx, prop_val.address())) {
+    if (!JS_WrapValue(cx, &prop_val)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     bool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val,
                                       JS_PropertyStub, JS_StrictPropertyStub,
                                       JSPROP_ENUMERATE);
 
     *did_resolve = true;
@@ -3563,17 +3563,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     JS::Rooted<JSObject*> scope(cx, wrapper->GetJSObject());
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     JS::Rooted<JS::Value> v(cx);
     rv = WrapNative(cx, scope, location, &NS_GET_IID(nsIDOMLocation), true,
                     &v, getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    bool ok = JS_WrapValue(cx, v.address()) &&
+    bool ok = JS_WrapValue(cx, &v) &&
                 JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub,
                                       LocationSetterUnwrapper,
                                       JSPROP_PERMANENT | JSPROP_ENUMERATE);
 
     if (!ok) {
       return NS_ERROR_FAILURE;
     }
 
@@ -3687,17 +3687,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
       // property
       *objp = obj;
 
       // NB: We need to do this for any Xray wrapper.
       if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
         // Unless our object is a native wrapper, in which case we have to
         // define it ourselves.
 
-        *_retval = JS_WrapValue(cx, v.address()) &&
+        *_retval = JS_WrapValue(cx, &v) &&
                    JS_DefineProperty(cx, obj, "document", v,
                                      JS_PropertyStub, JS_StrictPropertyStub,
                                      JSPROP_READONLY | JSPROP_ENUMERATE);
         if (!*_retval) {
           return NS_ERROR_UNEXPECTED;
         }
       }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -617,17 +617,18 @@ protected:
     return nsGlobalWindow::FromSupports(
       static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
   }
 
   // False return value means we threw an exception.  True return value
   // but false "found" means we didn't have a subframe at that index.
   bool GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy,
                          JS::Handle<jsid> id,
-                         JS::Value *vp, bool &found);
+                         JS::MutableHandle<JS::Value> vp,
+                         bool &found);
 
   // Returns a non-null window only if id is an index and we have a
   // window at that index.
   already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx,
                                                    JS::Handle<JSObject*> proxy,
                                                    JS::Handle<jsid> id);
 
   bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
@@ -703,17 +704,17 @@ nsOuterWindowProxy::getPropertyDescripto
 bool
 nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx,
                                              JS::Handle<JSObject*> proxy,
                                              JS::Handle<jsid> id,
                                              JS::MutableHandle<JSPropertyDescriptor> desc,
                                              unsigned flags)
 {
   bool found;
-  if (!GetSubframeWindow(cx, proxy, id, desc.value().address(), found)) {
+  if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) {
     return false;
   }
   if (found) {
     FillPropertyDescriptor(desc, proxy, true);
     return true;
   }
   // else fall through to js::Wrapper
 
@@ -824,17 +825,17 @@ nsOuterWindowProxy::get(JSContext *cx, J
 {
   if (id == nsDOMClassInfo::sWrappedJSObject_id &&
       xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
     vp.set(JS::ObjectValue(*proxy));
     return true;
   }
 
   bool found;
-  if (!GetSubframeWindow(cx, proxy, id, vp.address(), found)) {
+  if (!GetSubframeWindow(cx, proxy, id, vp, found)) {
     return false;
   }
   if (found) {
     return true;
   }
   // Else fall through to js::Wrapper
 
   return js::Wrapper::get(cx, proxy, receiver, id, vp);
@@ -875,17 +876,18 @@ nsOuterWindowProxy::iterate(JSContext *c
   // BaseProxyHandler::iterate seems to do what we want here: fall
   // back on the property names returned from keys() and enumerate().
   return js::BaseProxyHandler::iterate(cx, proxy, flags, vp);
 }
 
 bool
 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
                                       JS::Handle<JSObject*> proxy,
-                                      JS::Handle<jsid> id, JS::Value* vp,
+                                      JS::Handle<jsid> id,
+                                      JS::MutableHandle<JS::Value> vp,
                                       bool& found)
 {
   nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id);
   if (!frame) {
     found = false;
     return true;
   }
 
@@ -895,17 +897,18 @@ nsOuterWindowProxy::GetSubframeWindow(JS
   global->EnsureInnerWindow();
   JSObject* obj = global->FastGetGlobalJSObject();
   // This null check fixes a hard-to-reproduce crash that occurs when we
   // get here when we're mid-call to nsDocShell::Destroy. See bug 640904
   // comment 105.
   if (MOZ_UNLIKELY(!obj)) {
     return xpc::Throw(cx, NS_ERROR_FAILURE);
   }
-  *vp = JS::ObjectValue(*obj);
+
+  vp.setObject(*obj);
   return JS_WrapValue(cx, vp);
 }
 
 already_AddRefed<nsIDOMWindow>
 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
                                       JS::Handle<JSObject*> proxy,
                                       JS::Handle<jsid> id)
 {
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -126,17 +126,17 @@ nsHistory::GetState(JSContext* aCx, Erro
   if (variant) {
     JS::Rooted<JS::Value> jsData(aCx);
     aRv = variant->GetAsJSVal(jsData.address());
 
     if (aRv.Failed()) {
       return JS::UndefinedValue();
     }
 
-    if (!JS_WrapValue(aCx, jsData.address())) {
+    if (!JS_WrapValue(aCx, &jsData)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return JS::UndefinedValue();
     }
 
     return jsData;
   }
 
   return JS::UndefinedValue();
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1201,25 +1201,30 @@ nsJSContext::SetProperty(JS::Handle<JSOb
 
   Maybe<nsRootedJSValueArray> tempStorage;
 
   JS::Rooted<JSObject*> global(mContext, GetWindowProxy());
   nsresult rv =
     ConvertSupportsTojsvals(aArgs, global, &argc, &argv, tempStorage);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  JS::AutoArrayRooter array(mContext, argc, argv);
+
   // got the arguments, now attach them.
 
   for (uint32_t i = 0; i < argc; ++i) {
-    if (!JS_WrapValue(mContext, &argv[i])) {
+    if (!JS_WrapValue(mContext, array.handleAt(i))) {
       return NS_ERROR_FAILURE;
     }
   }
 
-  JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
+  JSObject *args = ::JS_NewArrayObject(mContext, argc, array.array);
+  if (!args) {
+    return NS_ERROR_FAILURE;
+  }
   JS::Value vargs = OBJECT_TO_JSVAL(args);
 
   return JS_DefineProperty(mContext, aTarget, aPropName, vargs, NULL, NULL, 0)
     ? NS_OK
     : NS_ERROR_FAILURE;
 }
 
 nsresult
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -301,18 +301,23 @@ nsJSUtils::EvaluateString(JSContext* aCx
       if (aRetValue) {
         *aRetValue = exn;
       }
       JS_ClearPendingException(aCx);
     }
   }
 
   // Wrap the return value into whatever compartment aCx was in.
-  if (aRetValue && !JS_WrapValue(aCx, aRetValue))
-    return NS_ERROR_OUT_OF_MEMORY;
+  if (aRetValue) {
+    JS::Rooted<JS::Value> v(aCx, *aRetValue);
+    if (!JS_WrapValue(aCx, &v)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    *aRetValue = v;
+  }
   return rv;
 }
 
 //
 // nsDOMJSUtils.h
 //
 
 JSObject* GetDefaultScopeFromJSContext(JSContext *cx)
--- a/dom/base/nsStructuredCloneContainer.cpp
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -43,22 +43,23 @@ nsStructuredCloneContainer::InitFromJSVa
                                           JSContext* aCx)
 {
   NS_ENSURE_STATE(!mData);
   NS_ENSURE_ARG_POINTER(aCx);
 
   // Make sure that we serialize in the right context.
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   JS::Rooted<JS::Value> jsData(aCx, aData);
-  JS_WrapValue(aCx, jsData.address());
+  bool success = JS_WrapValue(aCx, &jsData);
+  NS_ENSURE_STATE(success);
 
   uint64_t* jsBytes = nullptr;
-  bool success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize,
-                                         nullptr, nullptr,
-                                         JS::UndefinedHandleValue);
+  success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize,
+                                    nullptr, nullptr,
+                                    JS::UndefinedHandleValue);
   NS_ENSURE_STATE(success);
   NS_ENSURE_STATE(jsBytes);
 
   // Copy jsBytes into our own buffer.
   mData = (uint64_t*) malloc(mSize);
   if (!mData) {
     mSize = 0;
     mVersion = 0;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -176,20 +176,22 @@ ErrorResult::ThrowJSException(JSContext*
   }
 }
 
 void
 ErrorResult::ReportJSException(JSContext* cx)
 {
   MOZ_ASSERT(!mMightHaveUnreportedJSException,
              "Why didn't you tell us you planned to handle JS exceptions?");
-  if (JS_WrapValue(cx, &mJSException)) {
-    JS::RootedValue exception(cx, mJSException);
+
+  JS::Rooted<JS::Value> exception(cx, mJSException);
+  if (JS_WrapValue(cx, &exception)) {
     JS_SetPendingException(cx, exception);
   }
+  mJSException = exception;
   // If JS_WrapValue failed, not much we can do about it...  No matter
   // what, go ahead and unroot mJSException.
   JS_RemoveValueRoot(cx, &mJSException);
 }
 
 void
 ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
 {
@@ -1558,17 +1560,18 @@ ConcatJSString(JSContext* cx, const char
   }
 
   return JS_ConcatStrings(cx, preString, postString);
 }
 
 bool
 NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
                JS::Handle<JSObject*> obj, const char* pre,
-               const char* post, JS::Value* v)
+               const char* post,
+               JS::MutableHandle<JS::Value> v)
 {
   JS::Rooted<JSPropertyDescriptor> toStringDesc(cx);
   toStringDesc.object().set(nullptr);
   toStringDesc.setAttributes(0);
   toStringDesc.setShortId(0);
   toStringDesc.setGetter(nullptr);
   toStringDesc.setSetter(nullptr);
   toStringDesc.value().set(JS::UndefinedValue());
@@ -1578,17 +1581,17 @@ NativeToString(JSContext* cx, JS::Handle
     return false;
   }
 
   JS::Rooted<JSString*> str(cx);
   {
     JSAutoCompartment ac(cx, obj);
     if (toStringDesc.object()) {
       JS::Rooted<JS::Value> toString(cx, toStringDesc.value());
-      if (!JS_WrapValue(cx, toString.address())) {
+      if (!JS_WrapValue(cx, &toString)) {
         return false;
       }
       MOZ_ASSERT(JS_ObjectIsCallable(cx, &toString.toObject()));
       JS::Rooted<JS::Value> toStringResult(cx);
       if (JS_CallFunctionValue(cx, obj, toString, 0, nullptr,
                                toStringResult.address())) {
         str = toStringResult.toString();
       } else {
@@ -1614,17 +1617,17 @@ NativeToString(JSContext* cx, JS::Handle
       str = ConcatJSString(cx, pre, str, post);
     }
   }
 
   if (!str) {
     return false;
   }
 
-  v->setString(str);
+  v.setString(str);
   return JS_WrapValue(cx, v);
 }
 
 // Dynamically ensure that two objects don't end up with the same reserved slot.
 class MOZ_STACK_CLASS AutoCloneDOMObjectSlotGuard
 {
 public:
   AutoCloneDOMObjectSlotGuard(JSContext* aCx, JSObject* aOld, JSObject* aNew)
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -506,45 +506,45 @@ SetSystemOnlyWrapper(JSObject* obj, nsWr
 // needed.
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isString());
   JSString* str = rval.toString();
   if (JS::GetGCThingZone(str) != js::GetContextZone(cx)) {
-    return JS_WrapValue(cx, rval.address());
+    return JS_WrapValue(cx, rval);
   }
   return true;
 }
 
 // Make sure to wrap the given object value into the right compartment as
 // needed.  This will work correctly, but possibly slowly, on all objects.
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isObject());
 
   JSObject* obj = &rval.toObject();
   if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) {
-    return JS_WrapValue(cx, rval.address());
+    return JS_WrapValue(cx, rval);
   }
 
   // We're same-compartment, but even then we might need to wrap
   // objects specially.  Check for that.
   if (GetSameCompartmentWrapperForDOMBinding(obj)) {
     // We're a new-binding object, and "obj" now points to the right thing
     rval.set(JS::ObjectValue(*obj));
     return true;
   }
 
   // It's not a WebIDL object.  But it might be an XPConnect one, in which case
   // we may need to outerize here, so make sure to call JS_WrapValue.
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Like MaybeWrapObjectValue, but also allows null
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isObjectOrNull());
@@ -563,17 +563,17 @@ MaybeWrapNonDOMObjectValue(JSContext* cx
   MOZ_ASSERT(!GetDOMClass(&rval.toObject()));
   MOZ_ASSERT(!(js::GetObjectClass(&rval.toObject())->flags &
                JSCLASS_PRIVATE_IS_NSISUPPORTS));
 
   JSObject* obj = &rval.toObject();
   if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
     return true;
   }
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Like MaybeWrapNonDOMObjectValue but allows null
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapNonDOMObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isObjectOrNull());
@@ -677,17 +677,17 @@ WrapNewBindingObject(JSContext* cx, JS::
   bool sameCompartment =
     js::GetObjectCompartment(obj) == js::GetContextCompartment(cx);
   if (sameCompartment && couldBeDOMBinding) {
     WrapNewBindingForSameCompartment(cx, obj, value, rval);
     return true;
   }
 
   rval.set(JS::ObjectValue(*obj));
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Create a JSObject wrapping "value", for cases when "value" is a
 // non-wrapper-cached object using WebIDL bindings.  "value" must implement a
 // WrapObject() method taking a JSContext and a scope.
 template <class T>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx,
@@ -718,17 +718,17 @@ WrapNewBindingNonWrapperCachedObject(JSC
 
   if (!obj) {
     return false;
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Create a JSObject wrapping "value", for cases when "value" is a
 // non-wrapper-cached owned object using WebIDL bindings.  "value" must implement a
 // WrapObject() method taking a JSContext, a scope, and a boolean outparam that
 // is true if the JSObject took ownership
 template <class T>
 inline bool
@@ -769,17 +769,17 @@ WrapNewBindingNonWrapperCachedOwnedObjec
 
   if (!obj) {
     return false;
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
 template <template <typename> class SmartPtr, typename T>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
                                      const SmartPtr<T>& value,
                                      JS::MutableHandle<JS::Value> rval)
@@ -1938,17 +1938,18 @@ void SetXrayExpandoChain(JSObject *obj, 
  *     interface or interface prototype object.
  * pre is a string that should be prefixed to the value.
  * post is a string that should be prefixed to the value.
  * v contains the JSString for the value if the function returns true.
  */
 bool
 NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
                JS::Handle<JSObject*> obj, const char* pre,
-               const char* post, JS::Value* v);
+               const char* post,
+               JS::MutableHandle<JS::Value> v);
 
 HAS_MEMBER(JSBindingFinalized)
 
 template<class T, bool hasCallback=HasJSBindingFinalizedMember<T>::Value>
 struct JSBindingFinalized
 {
   static void Finalized(T* self)
   {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -4600,19 +4600,19 @@ def wrapTypeIntoCurrentCompartment(type,
     """
     Take the thing named by "value" and if it contains "any",
     "object", or spidermonkey-interface types inside return a CGThing
     that will wrap them into the current compartment.
     """
     if type.isAny():
         assert not type.nullable()
         if isMember:
+            value = "JS::MutableHandleValue::fromMarkedLocation(&%s)" % value
+        else:
             value = "&" + value
-        else:
-            value = value + ".address()"
         return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n"
                          "  return false;\n"
                          "}" % value)
 
     if type.isObject():
         if isMember:
             value = "JS::MutableHandleObject::fromMarkedLocation(&%s)" % value
         else:
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -75,17 +75,17 @@ ThrowExceptionObject(JSContext* aCx, Exc
 
   // If we stored the original thrown JS value in the exception
   // (see XPCConvert::ConstructException) and we are in a web context
   // (i.e., not chrome), rethrow the original value. This only applies to JS
   // implemented components so we only need to check for this on the main
   // thread.
   if (NS_IsMainThread() && !IsCallerChrome() &&
       aException->StealJSVal(thrown.address())) {
-    if (!JS_WrapValue(aCx, thrown.address())) {
+    if (!JS_WrapValue(aCx, &thrown)) {
       return false;
     }
     JS_SetPendingException(aCx, thrown);
     return true;
   }
 
   JS::Rooted<JSObject*> glob(aCx, JS::CurrentGlobalOrNull(aCx));
   if (!glob) {
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -324,17 +324,17 @@ nsresult nsJSThunk::EvaluateScript(nsICh
         // lose the error), or it might be JS that then proceeds to
         // cause an error of its own (which will also make us lose
         // this error).
         ::JS_ReportPendingException(cx);
     }
 
     // If we took the sandbox path above, v might be in the sandbox
     // compartment.
-    if (!JS_WrapValue(cx, v.address())) {
+    if (!JS_WrapValue(cx, &v)) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
         return NS_ERROR_MALFORMED_URI;
     } else if (v.isUndefined()) {
         return NS_ERROR_DOM_RETVAL_UNDEFINED;
     } else {
--- a/dom/tests/mochitest/general/test_offsets.html
+++ b/dom/tests/mochitest/general/test_offsets.html
@@ -11,51 +11,51 @@
     -moz-box-sizing: content-box;
   }
 </style>
 </head>
 <body id="body" onload="setTimeout(testElements, 0, 'testelements', SimpleTest.finish);"
       style="margin: 1px; border: 2px solid black; padding: 4px;">
 
 <div id="testelements" style="margin: 0; border: 0; padding: 0;">
-  <input id="input1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px"
+  <div id="div1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px"
          _offsetLeft="13" _offsetTop="9" _offsetWidth="64" _offsetHeight="34"
          _scrollWidth="62" _scrollHeight="32"
-         _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32">
+         _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"></div>
   <div id="noscroll" style="margin: 2px; border: 1px solid blue; padding: 3px;"
        _offsetLeft="10" _offsetTop="12" _offsetWidth="64" _offsetHeight="34"
        _scrollWidth="62" _scrollHeight="32"
        _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32">
     <div id="inner">Inner Text</div>
   </div>
 
   <div id="absolute" style="position: absolute; margin: 5px; border: 2px solid blue; padding: 0;">
     <div id="absolute-block" _offsetParent="absolute">
-      <input id="absolute-replaced" _offsetParent="absolute" style="margin: 1px; border: 0; padding: 3px;">
+      <div id="absolute-replaced" _offsetParent="absolute" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="absolutelr" style="position: absolute; margin: 5px; border: 2px solid blue; padding: 0; left: 90px; top: 130px;">
     This is some absolute positioned text.
     <div id="absolutelr-block" _offsetParent="absolutelr">
-      <input id="absolutelr-replaced" _offsetParent="absolutelr" style="margin: 1px; border: 0; padding: 3px;">
+      <div id="absolutelr-replaced" _offsetParent="absolutelr" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="relative" style="position: relative; margin: 2px; border: 1px solid orange; padding: 7px; left: 10px; top: 5px;">
     This is some relative positioned text.
     <div id="relative-block" _offsetParent="relative">
-      <input id="relative-replaced" _offsetParent="relative" style="margin: 1px; border: 0; padding: 3px;">
+      <div id="relative-replaced" _offsetParent="relative" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="fixed" style="position: fixed; margin: 2px; border: 1px solid orange; padding: 7px; left: 87px; top: 12px;">
     This is some fixed positioned text.
     <div id="fixed-block" _offsetParent="fixed">
-      <input id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;">
+      <div id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="scrollbox"
        style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px;"
        _scrollWidth="62" _scrollHeight="32"
        _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"><p id="p1" style="margin: 0; padding: 0;">One</p>
     <p id="p2">Two</p>
@@ -65,23 +65,23 @@
                                value="This button is much longer than the others">
   </p></div>
 
   <div id="overflow-visible" style="width:100px; height:100px;">
     <div id="overflow-visible-1" style="width:200px; height:1px; background:yellow;"></div>
     <div id="overflow-visible-2" style="height:200px; background:lime;"></div>
   </div>
 
-  <input id="input-displaynone" style="display: none; border: 0; padding: 0;"
-         _offsetParent="null">
+  <div id="div-displaynone" style="display: none; border: 0; padding: 0;"
+         _offsetParent="null"></div>
   <p id="p3" style="margin: 2px; border: 0; padding: 1px;"
          _offsetLeft="9" _offsetTop="9" _offsetWidth="64" _offsetHeight="34"
          _scrollWidth="62" _scrollHeight="32"
          _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32">
-    <input id="input-nosize" style="width: 0; height: 0; margin: 0; border: 0; padding: 0;">
+    <div id="div-nosize" style="width: 0; height: 0; margin: 0; border: 0; padding: 0;"></div>
   </p>
 
 </div>
 
 <div id="scrollbox-test" style="float: left; overflow: scroll; margin: 0; border: 0; padding: 0"></div>
 
 <script type="application/javascript">
 SimpleTest.waitForExplicitFinish();
--- a/dom/tests/mochitest/general/test_offsets.js
+++ b/dom/tests/mochitest/general/test_offsets.js
@@ -105,17 +105,17 @@ function testElement(element)
   isEqualAppunits(boundingrect.height, borderTop + paddingTop + height + paddingBottom + borderBottom,
      element.id + " bounding rect height");
   isEqualAppunits(boundingrect.right - boundingrect.left, boundingrect.width,
      element.id + " bounding rect right");
   isEqualAppunits(boundingrect.bottom - boundingrect.top, boundingrect.height,
      element.id + " bounding rect bottom");
 
   var rects = element.getClientRects();
-  if (element.id == "input-displaynone" || element.id == "nonappended") {
+  if (element.id == "div-displaynone" || element.id == "nonappended") {
     is(rects.length, 0, element.id + " getClientRects empty");
   }
   else {
     is(rects[0].left, boundingrect.left, element.id + " getClientRects left");
     is(rects[0].top, boundingrect.top, element.id + " getClientRects top");
     is(rects[0].right, boundingrect.right, element.id + " getClientRects right");
     is(rects[0].bottom, boundingrect.bottom, element.id + " getClientRects bottom");
   }
@@ -189,22 +189,19 @@ function checkCoords(element, type, left
   checkCoord(element, type + "Height", height, testname);
 
   if (element instanceof SVGElement)
     return;
 
   if (element.id == "outerpopup" && !element.parentNode.open) // closed popup
     return;
 
-  if (element.id == "input-displaynone" || element.id == "nonappended") // hidden elements
+  if (element.id == "div-displaynone" || element.id == "nonappended") // hidden elements
     ok(element[type + "Width"] == 0 && element[type + "Height"] == 0,
        element.id + " has zero " + type + " width and height");
-  else if (element.id != "input-nosize") // for some reason, this element has a width of 2
-    ok(element[type + "Width"] > 0 && element[type + "Height"] > 0,
-       element.id + " has non-zero " + type + " width and height");
 }
 
 function gcs(element, prop)
 {
   var propVal = (element instanceof SVGElement && (prop == "width" || prop == "height")) ?
                    element.getAttribute(prop) : getComputedStyle(element, "")[prop];
   if (propVal == "auto")
     return 0;
--- a/js/jsd/jsd_val.cpp
+++ b/js/jsd/jsd_val.cpp
@@ -191,17 +191,17 @@ jsd_GetValueString(JSDContext* jsdc, JSD
         AutoSaveExceptionState as(cx);
         string = JS_ValueToString(cx, jsdval->val);
     }
 
     JSAutoCompartment ac2(cx, jsdc->glob);
     if(string) {
         stringval = STRING_TO_JSVAL(string);
     }
-    if(!string || !JS_WrapValue(cx, stringval.address())) {
+    if(!string || !JS_WrapValue(cx, &stringval)) {
         return nullptr;
     }
 
     jsdval->string = JSVAL_TO_STRING(stringval);
     if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString"))
         jsdval->string = nullptr;
 
     return jsdval->string;
@@ -248,17 +248,17 @@ jsd_NewValue(JSDContext* jsdc, jsval val
 
     if(JSVAL_IS_GCTHING(val))
     {
         bool ok;
         JSAutoCompartment ac(cx, jsdc->glob);
 
         ok = JS_AddNamedValueRoot(cx, &jsdval->val, "JSDValue");
         if(ok && JSVAL_IS_STRING(val)) {
-            if(!JS_WrapValue(cx, val.address())) {
+            if(!JS_WrapValue(cx, &val)) {
                 ok = false;
             }
         }
 
         if(!ok)
         {
             free(jsdval);
             return nullptr;
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -2315,21 +2315,22 @@ jsdValue::Refresh()
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdValue::GetWrappedValue(JSContext* aCx, JS::Value* aRetval)
 {
     ASSERT_VALID_EPHEMERAL;
 
-    *aRetval = JSD_GetValueWrappedJSVal(mCx, mValue);
-    if (!JS_WrapValue(aCx, aRetval)) {
+    JS::RootedValue value(aCx, JSD_GetValueWrappedJSVal(mCx, mValue));
+    if (!JS_WrapValue(aCx, &value)) {
         return NS_ERROR_FAILURE;
     }
 
+    *aRetval = value;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdValue::GetScript(jsdIScript **_rval)
 {
     ASSERT_VALID_EPHEMERAL;
     JSDScript *script = JSD_GetScriptForValue(mCx, mValue);
--- a/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
+++ b/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
@@ -1,8 +1,9 @@
+// |jit-test| slow
 // This test is too slow to run with ASan in a debug configuration
 if (getBuildConfiguration()['asan'] && getBuildConfiguration()['debug']) quit(0);
 
 function fatty() {
     try {
         fatty();
     } catch (e) {
         foo();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/BaselineIC.h"
 
+#include "mozilla/DebugOnly.h"
 #include "mozilla/TemplateLib.h"
 
 #include "jsautooplen.h"
 #include "jslibmath.h"
 #include "jstypes.h"
 
 #include "builtin/Eval.h"
 #include "jit/BaselineHelpers.h"
@@ -26,16 +27,18 @@
 #include "jsboolinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/IonFrames-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/StringObject-inl.h"
 
+using mozilla::DebugOnly;
+
 namespace js {
 namespace jit {
 
 #ifdef DEBUG
 void
 FallbackICSpew(JSContext *cx, ICFallbackStub *stub, const char *fmt, ...)
 {
     if (IonSpewEnabled(IonSpew_BaselineICFallback)) {
@@ -4133,18 +4136,18 @@ ICGetElemNativeCompiler::generateStubCod
         JS_ASSERT(R0 == JSReturnOperand);
         leaveStubFrame(masm);
         masm.moveValue(JSReturnOperand, R1);
 
         // Unstow R0
         EmitUnstowICValues(masm, 1);
 
         // Extract string from R1 again.
-        Register strExtract2 = masm.extractString(R1, ExtractTemp1);
-        JS_ASSERT(strExtract2 == strExtract);
+        DebugOnly<Register> strExtract2 = masm.extractString(R1, ExtractTemp1);
+        JS_ASSERT(Register(strExtract2) == strExtract);
 
         masm.bind(&skipAtomize);
     }
 
     // Since this stub sometimes enter a stub frame, we manually set this to true (lie).
     entersStubFrame_ = true;
 
     // Key has been atomized if necessary.  Do identity check on string pointer.
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1727,27 +1727,20 @@ LIRGenerator::visitToInt32(MToInt32 *con
 
       case MIRType_Double:
       {
         LDoubleToInt32 *lir = new LDoubleToInt32(useRegister(opd));
         return assignSnapshot(lir) && define(lir, convert);
       }
 
       case MIRType_String:
-        // Strings are complicated - we don't handle them yet.
-        IonSpew(IonSpew_Abort, "String to Int32 not supported yet.");
-        return false;
-
       case MIRType_Object:
-        // Objects might be effectful.
-        IonSpew(IonSpew_Abort, "Object to Int32 not supported yet.");
-        return false;
-
       case MIRType_Undefined:
-        IonSpew(IonSpew_Abort, "Undefined coerces to NaN, not int32_t.");
+        // Objects might be effectful. Undefined coerces to NaN, not int32.
+        MOZ_ASSUME_UNREACHABLE("ToInt32 invalid input type");
         return false;
 
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 bool
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2914,17 +2914,19 @@ class MAsmJSUnsignedToFloat32
     }
 
     bool canProduceFloat32() const { return true; }
 };
 
 // Converts a primitive (either typed or untyped) to an int32. If the input is
 // not primitive at runtime, a bailout occurs. If the input cannot be converted
 // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
-class MToInt32 : public MUnaryInstruction
+class MToInt32
+  : public MUnaryInstruction,
+    public ToInt32Policy
 {
     bool canBeNegativeZero_;
 
     MToInt32(MDefinition *def)
       : MUnaryInstruction(def),
         canBeNegativeZero_(true)
     {
         setResultType(MIRType_Int32);
@@ -2945,16 +2947,20 @@ class MToInt32 : public MUnaryInstructio
 
     bool canBeNegativeZero() {
         return canBeNegativeZero_;
     }
     void setCanBeNegativeZero(bool negativeZero) {
         canBeNegativeZero_ = negativeZero;
     }
 
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
     bool congruentTo(MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     void computeRange();
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -531,16 +531,37 @@ ToDoublePolicy::staticAdjustInputs(MInst
     if (in->type() != MIRType_Object && in->type() != MIRType_String)
         return true;
 
     in = boxAt(ins, in);
     ins->replaceOperand(0, in);
     return true;
 }
 
+bool
+ToInt32Policy::staticAdjustInputs(MInstruction *ins)
+{
+    JS_ASSERT(ins->isToInt32());
+
+    MDefinition *in = ins->getOperand(0);
+    switch (in->type()) {
+      case MIRType_Object:
+      case MIRType_String:
+      case MIRType_Undefined:
+        // Objects might be effectful. Undefined coerces to NaN, not int32.
+        in = boxAt(ins, in);
+        ins->replaceOperand(0, in);
+        break;
+      default:
+        break;
+    }
+
+    return true;
+}
+
 template <unsigned Op>
 bool
 ObjectPolicy<Op>::staticAdjustInputs(MInstruction *ins)
 {
     MDefinition *in = ins->getOperand(Op);
     if (in->type() == MIRType_Object || in->type() == MIRType_Slots ||
         in->type() == MIRType_Elements)
     {
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -189,16 +189,26 @@ class ToDoublePolicy : public BoxInputsP
 {
   public:
     static bool staticAdjustInputs(MInstruction *def);
     bool adjustInputs(MInstruction *def) {
         return staticAdjustInputs(def);
     }
 };
 
+// Box objects, strings and undefined as input to a ToInt32 instruction.
+class ToInt32Policy : public BoxInputsPolicy
+{
+  public:
+    static bool staticAdjustInputs(MInstruction *def);
+    bool adjustInputs(MInstruction *def) {
+        return staticAdjustInputs(def);
+    }
+};
+
 template <unsigned Op>
 class ObjectPolicy : public BoxInputsPolicy
 {
   public:
     static bool staticAdjustInputs(MInstruction *ins);
     bool adjustInputs(MInstruction *ins) {
         return staticAdjustInputs(ins);
     }
--- a/js/src/jsapi-tests/testArgumentsObject.cpp
+++ b/js/src/jsapi-tests/testArgumentsObject.cpp
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsapi-tests/tests.h"
 
+#include "jsobjinlines.h"
+
 #include "vm/ArgumentsObject-inl.h"
 
 using namespace js;
 
 static const char NORMAL_ZERO[] =
     "function f() { return arguments; }";
 static const char NORMAL_ONE[] =
     "function f(a) { return arguments; }";
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -68,17 +68,17 @@ BEGIN_TEST(testChromeBuffer)
                                                         "trusted", 1, &paramName, bytes, strlen(bytes),
                                                         "", 0));
             trusted_fun = JS_GetFunctionObject(fun);
             if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
                 return false;
         }
 
         JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
-        CHECK(JS_WrapValue(cx, v.address()));
+        CHECK(JS_WrapValue(cx, &v));
 
         const char *paramName = "trusted";
         const char *bytes = "try {                                      "
                             "    return untrusted(trusted);             "
                             "} catch (e) {                              "
                             "    try {                                  "
                             "        return trusted(100);               "
                             "    } catch(e) {                           "
@@ -109,17 +109,17 @@ BEGIN_TEST(testChromeBuffer)
             JS::HandleObject global = JS::HandleObject::fromMarkedLocation(&trusted_glob);
             CHECK(fun = JS_CompileFunctionForPrincipals(cx, global, &system_principals,
                                                         "trusted", 1, &paramName, bytes, strlen(bytes),
                                                         "", 0));
             trusted_fun = JS_GetFunctionObject(fun);
         }
 
         JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
-        CHECK(JS_WrapValue(cx, v.address()));
+        CHECK(JS_WrapValue(cx, &v));
 
         const char *paramName = "trusted";
         const char *bytes = "try {                                      "
                             "  return untrusted(trusted);               "
                             "} catch (e) {                              "
                             "  return trusted(untrusted);               "
                             "}                                          ";
         CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1150,26 +1150,22 @@ JS_WrapObject(JSContext *cx, MutableHand
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     if (objp)
         JS::ExposeGCThingToActiveJS(objp, JSTRACE_OBJECT);
     return cx->compartment()->wrap(cx, objp);
 }
 
 JS_PUBLIC_API(bool)
-JS_WrapValue(JSContext *cx, jsval *vp)
+JS_WrapValue(JSContext *cx, MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    if (vp)
-        JS::ExposeValueToActiveJS(*vp);
-    RootedValue value(cx, *vp);
-    bool ok = cx->compartment()->wrap(cx, &value);
-    *vp = value.get();
-    return ok;
+    JS::ExposeValueToActiveJS(vp);
+    return cx->compartment()->wrap(cx, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_WrapId(JSContext *cx, jsid *idp)
 {
   AssertHeapIsIdle(cx);
   CHECK_REQUEST(cx);
   if (idp) {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1756,17 +1756,17 @@ JS_SetCompartmentPrivate(JSCompartment *
 
 extern JS_PUBLIC_API(void *)
 JS_GetCompartmentPrivate(JSCompartment *compartment);
 
 extern JS_PUBLIC_API(bool)
 JS_WrapObject(JSContext *cx, JS::MutableHandleObject objp);
 
 extern JS_PUBLIC_API(bool)
-JS_WrapValue(JSContext *cx, jsval *vp);
+JS_WrapValue(JSContext *cx, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_WrapId(JSContext *cx, jsid *idp);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_TransplantObject(JSContext *cx, JS::Handle<JSObject*> origobj, JS::Handle<JSObject*> target);
 
 extern JS_PUBLIC_API(bool)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1099,17 +1099,17 @@ Evaluate(JSContext *cx, unsigned argc, j
                     return false;
                 args.rval().setString(str);
                 return true;
             }
             return false;
         }
     }
 
-    return JS_WrapValue(cx, vp);
+    return JS_WrapValue(cx, args.rval());
 }
 
 static JSString *
 FileAsString(JSContext *cx, const char *pathname)
 {
     FILE *file;
     RootedString str(cx);
     size_t len, cc;
@@ -3772,17 +3772,17 @@ DecompileThisScript(JSContext *cx, unsig
     {
         JSAutoCompartment ac(cx, script);
         JSString *result = JS_DecompileScript(cx, script, "test", 0);
         if (!result)
             return false;
         args.rval().setString(result);
     }
 
-    return JS_WrapValue(cx, vp);
+    return JS_WrapValue(cx, args.rval());
 }
 
 static bool
 ThisFilename(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedScript script (cx);
     if (!JS_DescribeScriptedCaller(cx, &script, nullptr) || !script->filename()) {
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -1024,16 +1024,21 @@ class AutoPropertyDescArray
 };
 
 } /* anonymous namespace */
 
 static const char *
 FormatValue(JSContext *cx, const Value &vArg, JSAutoByteString &bytes)
 {
     RootedValue v(cx, vArg);
+
+    mozilla::Maybe<AutoCompartment> ac;
+    if (v.isObject())
+        ac.construct(cx, &v.toObject());
+
     JSString *str = ToString<CanGC>(cx, v);
     if (!str)
         return nullptr;
     const char *buf = bytes.encodeLatin1(cx, str);
     if (!buf)
         return nullptr;
     const char *found = strstr(buf, "function ");
     if (found && (found - buf <= 2))
@@ -1054,26 +1059,16 @@ FormatFrame(JSContext *cx, const NonBuil
 
     const char *filename = script->filename();
     unsigned lineno = PCToLineNumber(script, pc);
     RootedFunction fun(cx, iter.maybeCallee());
     RootedString funname(cx);
     if (fun)
         funname = fun->atom();
 
-    RootedObject callObj(cx);
-    AutoPropertyDescArray callProps(cx);
-
-    if (!iter.isJit() && (showArgs || showLocals)) {
-        JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr()));
-        callObj = frame.callObject(cx);
-        if (callObj)
-            callProps.fetch(callObj);
-    }
-
     RootedValue thisVal(cx);
     AutoPropertyDescArray thisProps(cx);
     if (iter.computeThis(cx)) {
         thisVal = iter.thisv();
         if (showThisProps && !thisVal.isPrimitive())
             thisProps.fetch(&thisVal.toObject());
     }
 
@@ -1084,118 +1079,85 @@ FormatFrame(JSContext *cx, const NonBuil
     } else if (fun) {
         buf = JS_sprintf_append(buf, "%d anonymous(", num);
     } else {
         buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
     }
     if (!buf)
         return buf;
 
-    // print the function arguments
-    if (showArgs && callObj) {
-        uint32_t namedArgCount = 0;
-        for (uint32_t i = 0; i < callProps->length; i++) {
-            JSPropertyDesc* desc = &callProps->array[i];
+    if (showArgs && iter.hasArgs()) {
+        BindingVector bindings(cx);
+        if (fun && fun->isInterpreted()) {
+            if (!FillBindingVector(script, &bindings))
+                return buf;
+        }
+
+
+        bool first = true;
+        for (unsigned i = 0; i < iter.numActualArgs(); i++) {
+            RootedValue arg(cx);
+            if (i < iter.numFormalArgs() && script->formalIsAliased(i)) {
+                for (AliasedFormalIter fi(script); ; fi++) {
+                    if (fi.frameIndex() == i) {
+                        arg = iter.callObj().aliasedVar(fi);
+                        break;
+                    }
+                }
+            } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) {
+                arg = iter.argsObj().arg(i);
+            } else {
+                arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
+            }
+
+            JSAutoByteString valueBytes;
+            const char *value = FormatValue(cx, arg, valueBytes);
+
             JSAutoByteString nameBytes;
             const char *name = nullptr;
-            bool hasName = JSVAL_IS_STRING(desc->id);
-            if (hasName)
-                name = FormatValue(cx, desc->id, nameBytes);
-            JSAutoByteString valueBytes;
-            const char *value = FormatValue(cx, desc->value, valueBytes);
 
-            if (value && (name || !hasName)) {
+            if (i < bindings.length()) {
+                name = nameBytes.encodeLatin1(cx, bindings[i].name());
+                if (!buf)
+                    return NULL;
+            }
+
+            if (value) {
                 buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
-                                        namedArgCount ? ", " : "",
+                                        !first ? ", " : "",
                                         name ? name :"",
                                         name ? " = " : "",
-                                        desc->value.isString() ? "\"" : "",
+                                        arg.isString() ? "\"" : "",
                                         value ? value : "?unknown?",
-                                        desc->value.isString() ? "\"" : "");
+                                        arg.isString() ? "\"" : "");
                 if (!buf)
                     return buf;
+
+                first = false;
             } else {
-                buf = JS_sprintf_append(buf, "    <Failed to get named argument while inspecting stack frame>\n");
+                buf = JS_sprintf_append(buf, "    <Failed to get argument while inspecting stack frame>\n");
+                if (!buf)
+                    return buf;
                 cx->clearPendingException();
 
             }
-            namedArgCount++;
-        }
-
-        // print any unnamed trailing args (found in 'arguments' object)
-        RootedValue val(cx);
-        if (JS_GetProperty(cx, callObj, "arguments", &val) && val.isObject()) {
-            uint32_t argCount;
-            RootedObject argsObj(cx, &val.toObject());
-            if (JS_GetProperty(cx, argsObj, "length", &val) &&
-                ToUint32(cx, val, &argCount) &&
-                argCount > namedArgCount)
-            {
-                for (uint32_t k = namedArgCount; k < argCount; k++) {
-                    char number[8];
-                    JS_snprintf(number, 8, "%d", (int) k);
-
-                    JSAutoByteString valueBytes;
-                    const char *value = nullptr;
-                    if (JS_GetProperty(cx, argsObj, number, &val) &&
-                        (value = FormatValue(cx, val, valueBytes)))
-                    {
-                        buf = JS_sprintf_append(buf, "%s%s%s%s",
-                                                k ? ", " : "",
-                                                val.isString() ? "\"" : "",
-                                                value ? value : "?unknown?",
-                                                val.isString() ? "\"" : "");
-                        if (!buf)
-                            return buf;
-                    } else {
-                        buf = JS_sprintf_append(buf, "    <Failed to get argument while inspecting stack frame>\n");
-                        cx->clearPendingException();
-                    }
-                }
-            } else {
-                buf = JS_sprintf_append(buf, "    <Failed to get 'length' while inspecting stack frame>\n");
-                cx->clearPendingException();
-            }
-        } else {
-            buf = JS_sprintf_append(buf, "    <Failed to get 'arguments' while inspecting stack frame>\n");
-            cx->clearPendingException();
         }
     }
 
     // print filename and line number
     buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
                             fun ? ")" : "",
                             filename ? filename : "<unknown>",
                             lineno);
     if (!buf)
         return buf;
 
-    // print local variables
-    if (showLocals && callProps->array) {
-        for (uint32_t i = 0; i < callProps->length; i++) {
-            JSPropertyDesc* desc = &callProps->array[i];
-            JSAutoByteString nameBytes;
-            JSAutoByteString valueBytes;
-            const char *name = FormatValue(cx, desc->id, nameBytes);
-            const char *value = FormatValue(cx, desc->value, valueBytes);
 
-            if (name && value) {
-                buf = JS_sprintf_append(buf, "    %s = %s%s%s\n",
-                                        name,
-                                        desc->value.isString() ? "\"" : "",
-                                        value,
-                                        desc->value.isString() ? "\"" : "");
-                if (!buf)
-                    return buf;
-            } else {
-                buf = JS_sprintf_append(buf, "    <Failed to get local while inspecting stack frame>\n");
-                cx->clearPendingException();
-            }
-        }
-    }
+    // Note: Right now we don't dump the local variables anymore, because
+    // that is hard to support across all the JITs etc.
 
     // print the value of 'this'
     if (showLocals) {
         if (!thisVal.isUndefined()) {
             JSAutoByteString thisValBytes;
             RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
             const char *str = nullptr;
             if (thisValStr &&
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1614,17 +1614,17 @@ NewObjectCache::invalidateEntriesForShap
 /* static */ void
 EmptyShape::insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto)
 {
     InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto),
                                      shape->getObjectParent(), shape->getObjectMetadata(),
                                      shape->numFixedSlots(), shape->getObjectFlags());
 
     /* Bug 929547 - we do not rekey based on metadata object moves */
-    JSObject *metadata = shape->getObjectMetadata();
+    DebugOnly<JSObject *> metadata = shape->getObjectMetadata();
     JS_ASSERT_IF(metadata, !gc::IsInsideNursery(cx->compartment()->runtimeFromAnyThread(), metadata));
 
     InitialShapeSet::Ptr p = cx->compartment()->initialShapes.lookup(lookup);
     JS_ASSERT(p);
 
     InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p);
 
     /* The new shape had better be rooted at the old one. */
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -1069,17 +1069,17 @@ mozJSComponentLoader::Import(const nsACS
         if (targetVal.isObject()) {
             // If we're passing in something like a content DOM window, chances
             // are the caller expects the properties to end up on the object
             // proper and not on the Xray holder. This is dubious, but can be used
             // during testing. Given that dumb callers can already leak JSMs into
             // content by passing a raw content JS object (where Xrays aren't
             // possible), we aim for consistency here. Waive xray.
             if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
-                !WrapperFactory::WaiveXrayAndWrap(cx, targetVal.address()))
+                !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal))
             {
                 return NS_ERROR_FAILURE;
             }
             targetObject = &targetVal.toObject();
         } else if (!targetVal.isNull()) {
             // If targetVal isNull(), we actually want to leave targetObject null.
             // Not doing so breaks |make package|.
             return ReportOnCaller(cx, ERROR_SCOPE_OBJ,
@@ -1202,17 +1202,17 @@ mozJSComponentLoader::ImportInto(const n
                                &newEntry->location, true, &exception);
 
         mInProgressImports.Remove(key);
 
         if (NS_FAILED(rv)) {
             if (!exception.isUndefined()) {
                 // An exception was thrown during compilation. Propagate it
                 // out to our caller so they can report it.
-                if (!JS_WrapValue(callercx, exception.address()))
+                if (!JS_WrapValue(callercx, &exception))
                     return NS_ERROR_OUT_OF_MEMORY;
                 JS_SetPendingException(callercx, exception);
                 return NS_OK;
             }
 
             // Something failed, but we don't know what it is, guess.
             return NS_ERROR_FILE_NOT_FOUND;
         }
@@ -1276,17 +1276,17 @@ mozJSComponentLoader::ImportInto(const n
                     return NS_ERROR_FAILURE;
                 return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL,
                                       PromiseFlatCString(aLocation).get(),
                                       bytes.ptr());
             }
 
             JSAutoCompartment target_ac(mContext, targetObj);
 
-            if (!JS_WrapValue(mContext, value.address()) ||
+            if (!JS_WrapValue(mContext, &value) ||
                 !JS_SetPropertyById(mContext, targetObj, symbolId, value)) {
                 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
                 if (!bytes)
                     return NS_ERROR_FAILURE;
                 return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL,
                                       PromiseFlatCString(aLocation).get(),
                                       bytes.ptr());
             }
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -305,27 +305,29 @@ mozJSSubScriptLoader::LoadSubScript(cons
         return rv;
 
     if (function) {
         script = JS_GetFunctionScript(cx, function);
     }
 
     loader->NoteSubScript(script, targetObj);
 
+    RootedValue rval(cx);
     bool ok = false;
     if (function) {
-        ok = JS_CallFunction(cx, targetObj, function, 0, nullptr, retval);
+        ok = JS_CallFunction(cx, targetObj, function, 0, nullptr, rval.address());
     } else {
-        ok = JS_ExecuteScriptVersion(cx, targetObj, script, retval, version);
+        ok = JS_ExecuteScriptVersion(cx, targetObj, script, rval.address(), version);
     }
 
     if (ok) {
         JSAutoCompartment rac(cx, result_obj);
-        if (!JS_WrapValue(cx, retval))
+        if (!JS_WrapValue(cx, &rval))
             return NS_ERROR_UNEXPECTED;
+        *retval = rval;
     }
 
     if (cache && ok && writeScript) {
         WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script);
     }
 
     return NS_OK;
 }
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -300,17 +300,17 @@ ExportFunction(JSContext *cx, unsigned a
         // we have to add it to the target scope as a property.
         if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(),
                                    JS_PropertyStub, JS_StrictPropertyStub,
                                    JSPROP_ENUMERATE))
             return false;
     }
 
     // Finally we have to re-wrap the exported function back to the caller compartment.
-    if (!JS_WrapValue(cx, args.rval().address()))
+    if (!JS_WrapValue(cx, args.rval()))
         return false;
 
     return true;
 }
 
 static bool
 GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno)
 {
@@ -977,17 +977,18 @@ xpc::GlobalProperties::Define(JSContext 
     if (btoa &&
         !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
         return false;
 
     return true;
 }
 
 nsresult
-xpc::CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options)
+xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prinOrSop,
+                         SandboxOptions& options)
 {
     // Create the sandbox global object
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
     if (NS_FAILED(rv))
         return NS_ERROR_XPC_UNEXPECTED;
 
     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
@@ -1037,17 +1038,17 @@ xpc::CreateSandboxObject(JSContext *cx, 
 
         if (options.proto) {
             bool ok = JS_WrapObject(cx, &options.proto);
             if (!ok)
                 return NS_ERROR_XPC_UNEXPECTED;
 
             if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) {
                 RootedValue v(cx, ObjectValue(*options.proto));
-                if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()))
+                if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v))
                     return NS_ERROR_FAILURE;
                 options.proto = &v.toObject();
             }
 
             // Now check what sort of thing we've got in |proto|
             JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false);
             const js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto);
             if (IS_WN_CLASS(unwrappedClass) ||
@@ -1089,26 +1090,25 @@ xpc::CreateSandboxObject(JSContext *cx, 
              !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0) ||
              !JS_DefineFunction(cx, sandbox, "createObjectIn", CreateObjectIn, 2, 0)))
             return NS_ERROR_XPC_UNEXPECTED;
 
         if (!options.globalProperties.Define(cx, sandbox))
             return NS_ERROR_XPC_UNEXPECTED;
     }
 
-    if (vp) {
-        // We have this crazy behavior where wantXrays=false also implies that the
-        // returned sandbox is implicitly waived. We've stopped advertising it, but
-        // keep supporting it for now.
-        *vp = OBJECT_TO_JSVAL(sandbox);
-        if (options.wantXrays && !JS_WrapValue(cx, vp))
-            return NS_ERROR_UNEXPECTED;
-        if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp))
-            return NS_ERROR_UNEXPECTED;
-    }
+
+    // We have this crazy behavior where wantXrays=false also implies that the
+    // returned sandbox is implicitly waived. We've stopped advertising it, but
+    // keep supporting it for now.
+    vp.setObject(*sandbox);
+    if (options.wantXrays && !JS_WrapValue(cx, vp))
+        return NS_ERROR_UNEXPECTED;
+    if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp))
+        return NS_ERROR_UNEXPECTED;
 
     // Set the location information for the new global, so that tools like
     // about:memory may use that information
     xpc::SetLocationForGlobal(sandbox, options.sandboxName);
 
     xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
 
     JS_FireOnNewGlobalObject(cx, sandbox);
@@ -1504,17 +1504,17 @@ nsXPCComponents_utils_Sandbox::CallOrCon
 
     SandboxOptions options(cx, optionsObject);
     if (calledWithOptions && !options.Parse())
         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
 
     if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
 
-    rv = CreateSandboxObject(cx, args.rval().address(), prinOrSop, options);
+    rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
 
     if (NS_FAILED(rv))
         return ThrowAndFail(rv, cx, _retval);
 
     *_retval = true;
     return NS_OK;
 }
 
@@ -1642,29 +1642,29 @@ xpc::EvalInSandbox(JSContext *cx, Handle
     //
     // Alright, we're back on the caller's cx. If an error occured, try to
     // wrap and set the exception. Otherwise, wrap the return value.
     //
 
     if (!ok) {
         // If we end up without an exception, it was probably due to OOM along
         // the way, in which case we thow. Otherwise, wrap it.
-        if (exn.isUndefined() || !JS_WrapValue(cx, exn.address()))
+        if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
             return NS_ERROR_OUT_OF_MEMORY;
 
         // Set the exception on our caller's cx.
         JS_SetPendingException(cx, exn);
         return NS_ERROR_FAILURE;
     }
 
     // Transitively apply Xray waivers if |sb| was waived.
     if (waiveXray) {
-        ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address());
+        ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
     } else {
-        ok = JS_WrapValue(cx, v.address());
+        ok = JS_WrapValue(cx, &v);
     }
     NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
     // Whew!
     rval.set(v);
     return NS_OK;
 }
 
@@ -1705,22 +1705,23 @@ CloningFunctionForwarder(JSContext *cx, 
             }
         }
 
         // JS API does not support any JSObject to JSFunction conversion,
         // so let's use JS_CallFunctionValue instead.
         RootedValue functionVal(cx);
         functionVal.setObject(*origFunObj);
 
-        if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp))
+        if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(),
+                                  args.rval().address()))
             return false;
     }
 
     // Return value must be wrapped.
-    return JS_WrapValue(cx, vp);
+    return JS_WrapValue(cx, args.rval());
 }
 
 bool
 xpc::NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone,
                           MutableHandleValue vp)
 {
     JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder :
                                                                     NonCloningFunctionForwarder,
@@ -1743,17 +1744,17 @@ xpc::GetSandboxMetadata(JSContext *cx, H
     MOZ_ASSERT(IsSandbox(sandbox));
 
     RootedValue metadata(cx);
     {
       JSAutoCompartment ac(cx, sandbox);
       metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
     }
 
-    if (!JS_WrapValue(cx, metadata.address()))
+    if (!JS_WrapValue(cx, &metadata))
         return NS_ERROR_UNEXPECTED;
 
     rval.set(metadata);
     return NS_OK;
 }
 
 nsresult
 xpc::SetSandboxMetadata(JSContext *cx, HandleObject sandbox, HandleValue metadataArg)
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3208,38 +3208,38 @@ nsXPCComponents_Utils::SetWantXrays(cons
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetComponentsForScope(const jsval &vscope, JSContext *cx,
                                              jsval *rval)
 {
     if (!vscope.isObject())
         return NS_ERROR_INVALID_ARG;
     JSObject *scopeObj = js::UncheckedUnwrap(&vscope.toObject());
     XPCWrappedNativeScope *scope = GetObjectScope(scopeObj);
-    JSObject *components = scope->GetComponentsJSObject();
+    RootedObject components(cx, scope->GetComponentsJSObject());
     if (!components)
         return NS_ERROR_FAILURE;
+    if (!JS_WrapObject(cx, &components))
+        return NS_ERROR_FAILURE;
     *rval = ObjectValue(*components);
-    if (!JS_WrapValue(cx, rval))
-        return NS_ERROR_FAILURE;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::Dispatch(const jsval &runnableArg, const jsval &scope,
                                 JSContext *cx)
 {
     // Enter the given compartment, if any, and rewrap runnable.
     Maybe<JSAutoCompartment> ac;
     RootedValue runnable(cx, runnableArg);
     if (scope.isObject()) {
         JSObject *scopeObj = js::UncheckedUnwrap(&scope.toObject());
         if (!scopeObj)
             return NS_ERROR_FAILURE;
         ac.construct(cx, scopeObj);
-        if (!JS_WrapValue(cx, runnable.address()))
+        if (!JS_WrapValue(cx, &runnable))
             return NS_ERROR_FAILURE;
     }
 
     // Get an XPCWrappedJS for |runnable|.
     if (!runnable.isObject())
         return NS_ERROR_INVALID_ARG;
 
     nsCOMPtr<nsIRunnable> run;
@@ -3338,33 +3338,35 @@ nsXPCComponents_Utils::IsXrayWrapper(con
     *aRetval =
         obj.isObject() && xpc::WrapperFactory::IsXrayWrapper(&obj.toObject());
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::WaiveXrays(const Value &aVal, JSContext *aCx, jsval *aRetval)
 {
-    *aRetval = aVal;
-    if (!xpc::WrapperFactory::WaiveXrayAndWrap(aCx, aRetval))
+    RootedValue value(aCx, aVal);
+    if (!xpc::WrapperFactory::WaiveXrayAndWrap(aCx, &value))
         return NS_ERROR_FAILURE;
+    *aRetval = value;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::UnwaiveXrays(const Value &aVal, JSContext *aCx, jsval *aRetval)
 {
     if (!aVal.isObject()) {
         *aRetval = aVal;
         return NS_OK;
     }
 
-    *aRetval = ObjectValue(*js::UncheckedUnwrap(&aVal.toObject()));
-    if (!JS_WrapValue(aCx, aRetval))
+    RootedObject obj(aCx, js::UncheckedUnwrap(&aVal.toObject()));
+    if (!JS_WrapObject(aCx, &obj))
         return NS_ERROR_FAILURE;
+    *aRetval = ObjectValue(*obj);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetClassName(const Value &aObj, bool aUnwrap, JSContext *aCx, char **aRv)
 {
     if (!aObj.isObject())
         return NS_ERROR_INVALID_ARG;
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -150,17 +150,17 @@ XPCConvert::NativeData2JS(MutableHandleV
                 return false;
             d.setString(str);
             break;
         }
 
     case nsXPTType::T_JSVAL :
         {
             d.set(*((Value*)s));
-            if (!JS_WrapValue(cx, d.address()))
+            if (!JS_WrapValue(cx, d))
                 return false;
             break;
         }
 
     default:
 
         // set the default result
         d.setNull();
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -3373,20 +3373,17 @@ XPCJSRuntime::RemoveContextCallback(xpcC
 JSObject *
 XPCJSRuntime::GetJunkScope()
 {
     if (!mJunkScope) {
         AutoSafeJSContext cx;
         SandboxOptions options;
         options.sandboxName.AssignASCII("XPConnect Junk Compartment");
         RootedValue v(cx);
-        nsresult rv = CreateSandboxObject(cx, v.address(),
-                                          nsContentUtils::GetSystemPrincipal(),
-                                          options);
-
+        nsresult rv = CreateSandboxObject(cx, &v, nsContentUtils::GetSystemPrincipal(), options);
         NS_ENSURE_SUCCESS(rv, nullptr);
 
         mJunkScope = js::UncheckedUnwrap(&v.toObject());
         JS_AddNamedObjectRoot(cx, &mJunkScope, "XPConnect Junk Compartment");
     }
     return mJunkScope;
 }
 
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -386,29 +386,29 @@ XPCVariant::VariantDataToJS(nsIVariant* 
     RootedValue realVal(cx);
     nsresult rv = variant->GetAsJSVal(realVal.address());
 
     if (NS_SUCCEEDED(rv) &&
         (JSVAL_IS_PRIMITIVE(realVal) ||
          type == nsIDataType::VTYPE_ARRAY ||
          type == nsIDataType::VTYPE_EMPTY_ARRAY ||
          type == nsIDataType::VTYPE_ID)) {
-        if (!JS_WrapValue(cx, realVal.address()))
+        if (!JS_WrapValue(cx, &realVal))
             return false;
         pJSVal.set(realVal);
         return true;
     }
 
     nsCOMPtr<XPCVariant> xpcvariant = do_QueryInterface(variant);
     if (xpcvariant && xpcvariant->mReturnRawObject) {
         MOZ_ASSERT(type == nsIDataType::VTYPE_INTERFACE ||
                    type == nsIDataType::VTYPE_INTERFACE_IS,
                    "Weird variant");
 
-        if (!JS_WrapValue(cx, realVal.address()))
+        if (!JS_WrapValue(cx, &realVal))
             return false;
         pJSVal.set(realVal);
         return true;
     }
 
     // else, it's an object and we really need to double wrap it if we've
     // already decided that its 'natural' type is as some sort of interface.
 
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -129,32 +129,34 @@ GetDoubleWrappedJSObject(XPCCallContext&
 }
 
 // This is the getter native function we use to handle 'wrappedJSObject' for
 // double wrapped JSObjects.
 
 static bool
 XPC_WN_DoubleWrappedGetter(JSContext *cx, unsigned argc, jsval *vp)
 {
+    CallArgs args = CallArgsFromVp(argc, vp);
+
     RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
     if (!obj)
         return false;
 
     XPCCallContext ccx(JS_CALLER, cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     MOZ_ASSERT(JS_TypeOfValue(cx, JS_CALLEE(cx, vp)) == JSTYPE_FUNCTION, "bad function");
 
     RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper));
     if (!realObject) {
         // This is pretty unexpected at this point. The object originally
         // responded to this get property call and now gives no object.
         // XXX Should this throw something at the caller?
-        *vp = JSVAL_NULL;
+        args.rval().setNull();
         return true;
     }
 
     // It is a double wrapped object. Figure out if the caller
     // is allowed to see it.
 
     nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager();
     if (sm) {
@@ -173,18 +175,19 @@ XPC_WN_DoubleWrappedGetter(JSContext *cx
                                     wrapper->GetIdentityObject(),
                                     wrapper->GetClassInfo(), id,
                                     wrapper->GetSecurityInfoAddr()))) {
                 // The SecurityManager should have set an exception.
                 return false;
             }
         }
     }
-    *vp = OBJECT_TO_JSVAL(realObject);
-    return JS_WrapValue(cx, vp);
+
+    args.rval().setObject(*realObject);
+    return JS_WrapValue(cx, args.rval());
 }
 
 /***************************************************************************/
 
 // This is our shared function to define properties on our JSObjects.
 
 /*
  * NOTE:
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -255,18 +255,18 @@ XPCWrappedNativeScope::EnsureXBLScope(JS
     nsIPrincipal *principal = GetPrincipal();
     nsCOMPtr<nsIExpandedPrincipal> ep;
     MOZ_ASSERT(!(ep = do_QueryInterface(principal)));
     nsTArray< nsCOMPtr<nsIPrincipal> > principalAsArray(1);
     principalAsArray.AppendElement(principal);
     ep = new nsExpandedPrincipal(principalAsArray);
 
     // Create the sandbox.
-    JS::RootedValue v(cx, JS::UndefinedValue());
-    nsresult rv = CreateSandboxObject(cx, v.address(), ep, options);
+    RootedValue v(cx);
+    nsresult rv = CreateSandboxObject(cx, &v, ep, options);
     NS_ENSURE_SUCCESS(rv, nullptr);
     mXBLScope = &v.toObject();
 
     // Tag it.
     EnsureCompartmentPrivate(js::UncheckedUnwrap(mXBLScope))->scope->mIsXBLScope = true;
 
     // Good to go!
     return mXBLScope;
--- a/js/xpconnect/src/XPCWrapper.cpp
+++ b/js/xpconnect/src/XPCWrapper.cpp
@@ -32,39 +32,39 @@ UnwrapNW(JSContext *cx, unsigned argc, j
 
   JS::RootedValue v(cx, JS_ARGV(cx, vp)[0]);
   if (!v.isObject() || !js::IsWrapper(&v.toObject())) {
     JS_SET_RVAL(cx, vp, v);
     return true;
   }
 
   if (AccessCheck::wrapperSubsumes(&v.toObject())) {
-    bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address());
+    bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
     NS_ENSURE_TRUE(ok, false);
   }
 
   JS_SET_RVAL(cx, vp, v);
   return true;
 }
 
 static bool
 XrayWrapperConstructor(JSContext *cx, unsigned argc, jsval *vp)
 {
-  if (argc == 0) {
+  JS::CallArgs args = CallArgsFromVp(argc, vp);
+  if (args.length() == 0) {
     return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx);
   }
 
-  JS::RootedValue v(cx, JS_ARGV(cx, vp)[0]);
-  if (!v.isObject()) {
-    JS_SET_RVAL(cx, vp, v);
+  if (!args[0].isObject()) {
+    args.rval().set(args[0]);
     return true;
   }
 
-  *vp = JS::ObjectValue(*js::UncheckedUnwrap(&v.toObject()));
-  return JS_WrapValue(cx, vp);
+  args.rval().setObject(*js::UncheckedUnwrap(&args[0].toObject()));
+  return JS_WrapValue(cx, args.rval());
 }
 // static
 bool
 AttachNewConstructorObject(JSContext *aCx, JS::HandleObject aGlobalObject)
 {
   // Pushing a JSContext calls ActivateDebugger which calls this function, so
   // we can't use an AutoJSContext here until JSD is gone.
   JSAutoCompartment ac(aCx, aGlobalObject);
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -908,20 +908,19 @@ nsXPConnect::ClearAllWrappedNativeSecuri
 }
 
 NS_IMETHODIMP
 nsXPConnect::CreateSandbox(JSContext *cx, nsIPrincipal *principal,
                            nsIXPConnectJSObjectHolder **_retval)
 {
     *_retval = nullptr;
 
-    RootedValue rval(cx, JSVAL_VOID);
-
+    RootedValue rval(cx);
     SandboxOptions options;
-    nsresult rv = CreateSandboxObject(cx, rval.address(), principal, options);
+    nsresult rv = CreateSandboxObject(cx, &rval, principal, options);
     MOZ_ASSERT(NS_FAILED(rv) || !JSVAL_IS_PRIMITIVE(rval),
                "Bad return value from xpc_CreateSandboxObject()!");
 
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(rval)) {
         *_retval = XPCJSObjectHolder::newHolder(JSVAL_TO_OBJECT(rval));
         NS_ENSURE_TRUE(*_retval, NS_ERROR_OUT_OF_MEMORY);
 
         NS_ADDREF(*_retval);
--- a/js/xpconnect/src/qsgen.py
+++ b/js/xpconnect/src/qsgen.py
@@ -674,17 +674,17 @@ resultConvTemplates = {
     '[astring]':
         "    return xpc::StringToJsval(cx, result, ${jsvalPtr});\n",
 
     '[domstring]':
         "    return xpc::StringToJsval(cx, result, ${jsvalPtr});\n",
 
     '[jsval]':
         "    ${jsvalPtr}.set(result);\n"
-        "    return JS_WrapValue(cx, ${jsvalPtr}.address());\n"
+        "    return JS_WrapValue(cx, ${jsvalPtr});\n"
     }
 
 def isVariantType(t):
     return isSpecificInterfaceType(t, 'nsIVariant')
 
 def writeResultConv(f, type, jsvalPtr, jsvalRef):
     """ Emit code to convert the C++ variable `result` to a jsval.
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3687,17 +3687,17 @@ CreateGlobalObject(JSContext *cx, const 
 // system using EvalInSandbox(). Takes the JSContext on which to
 // do setup etc on, puts the sandbox object in *vp (which must be
 // rooted by the caller), and uses the principal that's either
 // directly passed in prinOrSop or indirectly as an
 // nsIScriptObjectPrincipal holding the principal. If no principal is
 // reachable through prinOrSop, a new null principal will be created
 // and used.
 nsresult
-CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop,
+CreateSandboxObject(JSContext *cx, JS::MutableHandleValue vp, nsISupports *prinOrSop,
                     xpc::SandboxOptions& options);
 // Helper for evaluating scripts in a sandbox object created with
 // CreateSandboxObject(). The caller is responsible of ensuring
 // that *rval doesn't get collected during the call or usage after the
 // call. This helper will use filename and lineNo for error reporting,
 // and if no filename is provided it will use the codebase from the
 // principal and line number 1 as a fallback. if returnStringOnly is
 // true, then the result in *rval, or the exception in cx->exception
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp
+++ b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp
@@ -13,24 +13,24 @@ using namespace JS;
 
 namespace xpc {
 
 static bool
 WaiveAccessors(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc)
 {
     if (desc.hasGetterObject() && desc.getterObject()) {
         RootedValue v(cx, JS::ObjectValue(*desc.getterObject()));
-        if (!WrapperFactory::WaiveXrayAndWrap(cx, v.address()))
+        if (!WrapperFactory::WaiveXrayAndWrap(cx, &v))
             return false;
         desc.setGetterObject(&v.toObject());
     }
 
     if (desc.hasSetterObject() && desc.setterObject()) {
         RootedValue v(cx, JS::ObjectValue(*desc.setterObject()));
-        if (!WrapperFactory::WaiveXrayAndWrap(cx, v.address()))
+        if (!WrapperFactory::WaiveXrayAndWrap(cx, &v))
             return false;
         desc.setSetterObject(&v.toObject());
     }
     return true;
 }
 
 WaiveXrayWrapper::WaiveXrayWrapper(unsigned flags) : js::CrossCompartmentWrapper(flags)
 {
@@ -41,54 +41,54 @@ WaiveXrayWrapper::~WaiveXrayWrapper()
 }
 
 bool
 WaiveXrayWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper,
                                         HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc,
                                         unsigned flags)
 {
     return CrossCompartmentWrapper::getPropertyDescriptor(cx, wrapper, id, desc, flags) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, desc.value().address()) && WaiveAccessors(cx, desc);
+           WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc);
 }
 
 bool
 WaiveXrayWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper,
                                            HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc,
                                            unsigned flags)
 {
     return CrossCompartmentWrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, desc.value().address()) && WaiveAccessors(cx, desc);
+           WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc);
 }
 
 bool
 WaiveXrayWrapper::get(JSContext *cx, HandleObject wrapper,
                       HandleObject receiver, HandleId id,
                       MutableHandleValue vp)
 {
     return CrossCompartmentWrapper::get(cx, wrapper, receiver, id, vp) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, vp.address());
+           WrapperFactory::WaiveXrayAndWrap(cx, vp);
 }
 
 bool
 WaiveXrayWrapper::call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args)
 {
     return CrossCompartmentWrapper::call(cx, wrapper, args) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address());
+           WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
 }
 
 bool
 WaiveXrayWrapper::construct(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args)
 {
     return CrossCompartmentWrapper::construct(cx, wrapper, args) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address());
+           WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
 }
 
 // NB: This is important as the other side of a handshake with FieldGetter. See
 // nsXBLProtoImplField.cpp.
 bool
 WaiveXrayWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test,
                              JS::NativeImpl impl, JS::CallArgs args)
 {
     return CrossCompartmentWrapper::nativeCall(cx, test, impl, args) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address());
+           WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
 }
 
 }
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -539,25 +539,25 @@ WrapperFactory::WrapForSameCompartment(J
     return wrapper;
 }
 
 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
 // using the returned object. If the object to be wrapped is already in the
 // correct compartment, then this returns the unwrapped object.
 bool
-WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp)
+WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp)
 {
-    if (JSVAL_IS_PRIMITIVE(*vp))
+    if (vp.isPrimitive())
         return JS_WrapValue(cx, vp);
 
-    JSObject *obj = js::UncheckedUnwrap(JSVAL_TO_OBJECT(*vp));
+    JSObject *obj = js::UncheckedUnwrap(&vp.toObject());
     MOZ_ASSERT(!js::IsInnerObject(obj));
     if (js::IsObjectInContextCompartment(obj, cx)) {
-        *vp = OBJECT_TO_JSVAL(obj);
+        vp.setObject(*obj);
         return true;
     }
 
     // Even though waivers have no effect on access by scopes that don't subsume
     // the underlying object, good defense-in-depth dictates that we should avoid
     // handing out waivers to callers that can't use them. The transitive waiving
     // machinery unconditionally calls WaiveXrayAndWrap on return values from
     // waived functions, even though the return value might be not be same-origin
@@ -565,17 +565,17 @@ WrapperFactory::WaiveXrayAndWrap(JSConte
     // |cx|, we should check whether the caller has any business with waivers
     // to things in |obj|'s compartment.
     JSCompartment *target = js::GetContextCompartment(cx);
     JSCompartment *origin = js::GetObjectCompartment(obj);
     obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj;
     if (!obj)
         return false;
 
-    *vp = OBJECT_TO_JSVAL(obj);
+    vp.setObject(*obj);
     return JS_WrapValue(cx, vp);
 }
 
 JSObject *
 WrapperFactory::WrapSOWObject(JSContext *cx, JSObject *objArg)
 {
     RootedObject obj(cx, objArg);
     RootedObject proto(cx);
--- a/js/xpconnect/wrappers/WrapperFactory.h
+++ b/js/xpconnect/wrappers/WrapperFactory.h
@@ -58,17 +58,17 @@ class WrapperFactory {
                             JS::HandleObject parent,
                             unsigned flags);
 
     // Wrap an object for same-compartment access.
     static JSObject *WrapForSameCompartment(JSContext *cx,
                                             JS::HandleObject obj);
 
     // Wrap wrapped object into a waiver wrapper and then re-wrap it.
-    static bool WaiveXrayAndWrap(JSContext *cx, jsval *vp);
+    static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleValue vp);
 
     // Wrap a (same compartment) object in a SOW.
     static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj);
 
     // Return true if this is a Components object.
     static bool IsComponentsObject(JSObject *obj);
 
     // Wrap a (same compartment) Components object.
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -765,17 +765,17 @@ XPCWrappedNativeXrayTraits::resolveNativ
         // Without a wrapper the function would live on the prototype. Since we
         // don't have one, we have to avoid calling the scriptable helper's
         // GetProperty method for this property, so stub out the getter and
         // setter here explicitly.
         desc.setGetter(JS_PropertyStub);
         desc.setSetter(JS_StrictPropertyStub);
     }
 
-    if (!JS_WrapValue(cx, desc.value().address()) || !JS_WrapValue(cx, fval.address()))
+    if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval))
         return false;
 
     if (desc.hasGetterObject())
         desc.setGetterObject(&fval.toObject());
     if (desc.hasSetterObject())
         desc.setSetterObject(&fval.toObject());
 
     // Define the property.
@@ -788,17 +788,17 @@ wrappedJSObject_getter(JSContext *cx, Ha
 {
     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
         JS_ReportError(cx, "Unexpected object");
         return false;
     }
 
     vp.set(OBJECT_TO_JSVAL(wrapper));
 
-    return WrapperFactory::WaiveXrayAndWrap(cx, vp.address());
+    return WrapperFactory::WaiveXrayAndWrap(cx, vp);
 }
 
 bool
 XrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper,
                                HandleObject wrapper, HandleObject holder, HandleId id,
                                MutableHandle<JSPropertyDescriptor> desc, unsigned flags)
 {
     desc.object().set(nullptr);
@@ -1100,17 +1100,17 @@ DOMXrayTraits::call(JSContext *cx, Handl
         if (!clasp->call(cx, args.length(), args.base()))
             return false;
     } else {
         // This is only reached for WebIDL instance objects, and in practice
         // only for plugins.  Just call them on the content compartment.
         if (!baseInstance.call(cx, wrapper, args))
             return false;
     }
-    return JS_WrapValue(cx, args.rval().address());
+    return JS_WrapValue(cx, args.rval());
 }
 
 bool
 DOMXrayTraits::construct(JSContext *cx, HandleObject wrapper,
                          const JS::CallArgs &args, js::Wrapper& baseInstance)
 {
     RootedObject obj(cx, getTargetObject(wrapper));
     MOZ_ASSERT(mozilla::dom::HasConstructor(obj));
@@ -1122,17 +1122,17 @@ DOMXrayTraits::construct(JSContext *cx, 
             return false;
         }
         if (!clasp->construct(cx, args.length(), args.base()))
             return false;
     } else {
         if (!baseInstance.construct(cx, wrapper, args))
             return false;
     }
-    if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval().address()))
+    if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval()))
         return false;
     return true;
 }
 
 void
 DOMXrayTraits::preserveWrapper(JSObject *target)
 {
     nsISupports *identity = mozilla::dom::UnwrapDOMObjectToISupports(target);
@@ -1225,36 +1225,43 @@ HasNativeProperty(JSContext *cx, HandleO
         return false;
     *hasProp = !!desc.object();
     return true;
 }
 
 } // namespace XrayUtils
 
 static bool
-XrayToString(JSContext *cx, unsigned argc, jsval *vp)
+XrayToString(JSContext *cx, unsigned argc, Value *vp)
 {
-    RootedObject  wrapper(cx, JS_THIS_OBJECT(cx, vp));
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!args.thisv().isObject()) {
+        JS_ReportError(cx, "XrayToString called on an incompatible object");
+        return false;
+    }
+
+    RootedObject wrapper(cx, &args.thisv().toObject());
     if (!wrapper)
         return false;
     if (IsWrapper(wrapper) &&
         GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) {
         wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper);
     }
     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
         JS_ReportError(cx, "XrayToString called on an incompatible object");
         return false;
     }
 
     RootedObject obj(cx, XrayTraits::getTargetObject(wrapper));
 
     static const char start[] = "[object XrayWrapper ";
     static const char end[] = "]";
     if (UseDOMXray(obj))
-        return NativeToString(cx, wrapper, obj, start, end, vp);
+        return NativeToString(cx, wrapper, obj, start, end, args.rval());
 
     nsAutoString result;
     result.AppendASCII(start);
 
     XPCCallContext ccx(JS_CALLER, cx, obj);
     XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper);
     char *wrapperStr = wn->ToString();
     if (!wrapperStr) {
@@ -1266,17 +1273,17 @@ XrayToString(JSContext *cx, unsigned arg
 
     result.AppendASCII(end);
 
     JSString *str = JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar *>(result.get()),
                                         result.Length());
     if (!str)
         return false;
 
-    *vp = STRING_TO_JSVAL(str);
+    args.rval().setString(str);
     return true;
 }
 
 #ifdef DEBUG
 
 static void
 DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj)
 {
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -1236,22 +1236,16 @@ nsTextControlFrame::SetInitialChildList(
       statefulFrame->RestoreState(&fakePresState);
       Properties().Remove(ContentScrollPos());
       delete contentScrollPos;
     }
   }
   return rv;
 }
 
-bool
-nsTextControlFrame::IsScrollable() const
-{
-  return !IsSingleLineTextControl();
-}
-
 void
 nsTextControlFrame::SetValueChanged(bool aValueChanged)
 {
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
 
   if (mUsePlaceholder) {
     nsWeakFrame weakFrame(this);
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -34,18 +34,16 @@ public:
   NS_DECLARE_FRAME_PROPERTY(ContentScrollPos, DestroyPoint)
 
   nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext);
   virtual ~nsTextControlFrame();
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
 
   virtual nsIScrollableFrame* GetScrollTargetFrame() MOZ_OVERRIDE {
-    if (!IsScrollable())
-      return nullptr;
     return do_QueryFrame(GetFirstPrincipalChild());
   }
 
   virtual nscoord GetMinWidth(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE;
   virtual nscoord GetPrefWidth(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE;
 
   virtual nsSize ComputeAutoSize(nsRenderingContext *aRenderingContext,
                                  nsSize aCBSize, nscoord aAvailableWidth,
@@ -230,23 +228,16 @@ protected:
 
   private:
     nsTextControlFrame* mFrame;
   };
 
   nsresult OffsetToDOMPoint(int32_t aOffset, nsIDOMNode** aResult, int32_t* aPosition);
 
   /**
-   * Find out whether this control is scrollable (i.e. if it is not a single
-   * line text control)
-   * @return whether this control is scrollable
-   */
-  bool IsScrollable() const;
-
-  /**
    * Update the textnode under our anonymous div to show the new
    * value. This should only be called when we have no editor yet.
    * @throws NS_ERROR_UNEXPECTED if the div has no text content
    */
   nsresult UpdateValueDisplay(bool aNotify,
                               bool aBeforeEditorInit = false,
                               const nsAString *aValue = nullptr);
 
--- a/layout/forms/test/mochitest.ini
+++ b/layout/forms/test/mochitest.ini
@@ -27,13 +27,14 @@ support-files =
 [test_bug571352.html]
 [test_bug572406.html]
 [test_bug572649.html]
 [test_bug595310.html]
 [test_bug620936.html]
 [test_bug644542.html]
 [test_bug672810.html]
 [test_bug704049.html]
+[test_bug717878_input_scroll.html]
 [test_bug869314.html]
 [test_bug903715.html]
 [test_listcontrol_search.html]
 [test_select_prevent_default.html]
 [test_textarea_resize.html]
new file mode 100644
--- /dev/null
+++ b/layout/forms/test/test_bug717878_input_scroll.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717878
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 717878</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717878">Mozilla Bug 717878</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<!-- size=10 and monospace font ensure there's no overflow in either direction -->
+<input id="no-overflow" type="text"
+  size="10"
+  style="
+    font-family: monospace;
+    font-size: 1em;"
+  value="Short">
+<!-- size=10, monospace font, and height=0.5em ensure overflow in both directions -->
+<input id="overflow" type="text"
+  size="10"
+  style="
+    font-family: monospace;
+    font-size: 1em;
+    height: 0.5em;"
+  value="This is a long string">
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 717878 **/
+
+/**
+ * Test an element's scroll properties for correctness
+ *
+ * @param element Element to test
+ * @param prop Specify the property to test,
+ *             i.e. "scrollLeft" or "scrollTop"
+ * @param propMax Specify the scrollMax property to test,
+ *                i.e. "scrollLeftMax" or "scrollTopMax"
+ * @param is_overflow Specify whether the element is
+ *                    scrollable in the above direction
+ */
+function test_scroll(element, scroll, scrollMax, is_overflow) {
+
+  is(element[scroll], 0, element.id + " initial " + scroll + " != 0");
+  if (is_overflow) {
+    isnot(element[scrollMax], 0, element.id + " " + scrollMax + " == 0");
+  } else {
+    is(element[scrollMax], 0, element.id + " " + scrollMax + " != 0");
+  }
+
+  element[scroll] = 10;
+  if (is_overflow) {
+    isnot(element[scroll], 0, element.id + " unable to scroll " + scroll);
+  } else {
+    is(element[scroll], 0, element.id + " able to scroll " + scroll);
+  }
+
+  element[scroll] = element[scrollMax];
+  is(element[scroll], element[scrollMax], element.id + " did not scroll to " + scrollMax);
+
+  element[scroll] = element[scrollMax] + 10;
+  is(element[scroll], element[scrollMax], element.id + " scrolled past " + scrollMax);
+}
+
+var no_overflow = document.getElementById("no-overflow");
+test_scroll(no_overflow, "scrollLeft", "scrollLeftMax", /* is_overflow */ false);
+test_scroll(no_overflow, "scrollTop", "scrollTopMax", /* is_overflow */ false);
+
+var overflow = document.getElementById("overflow");
+test_scroll(overflow, "scrollLeft", "scrollLeftMax", /* is_overflow */ true);
+test_scroll(overflow, "scrollTop", "scrollTopMax", /* is_overflow */ true);
+
+</script>
+</pre>
+</body>
+</html>
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5237,18 +5237,18 @@ nsFrame::UpdateOverflow()
 {
   MOZ_ASSERT(!(mState & NS_FRAME_SVG_LAYOUT) ||
              !(mState & NS_FRAME_IS_NONDISPLAY),
              "Non-display SVG do not maintain visual overflow rects");
 
   nsRect rect(nsPoint(0, 0), GetSize());
   nsOverflowAreas overflowAreas(rect, rect);
 
-  bool isBox = IsBoxFrame() || IsBoxWrapped();
-  if (!isBox || (!IsCollapsed() && !DoesClipChildren())) {
+  if (!DoesClipChildren() &&
+      !(IsCollapsed() && (IsBoxFrame() || IsBoxWrapped()))) {
     nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
   }
 
   if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
     nsView* view = GetView();
     if (view) {
       uint32_t flags = 0;
       GetLayoutFlags(flags);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/928607-1-ref.html
@@ -0,0 +1,8 @@
+<DOCTYPE HTML>
+<html>
+<body>
+<div style="overflow:hidden; border:1px solid black;">
+  <div style="position:relative; left:50px; height:10px; background:blue;"></div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/928607-1.html
@@ -0,0 +1,15 @@
+<DOCTYPE HTML>
+<html class="reftest-wait">
+<body>
+<div style="overflow:hidden; border:1px solid black;">
+  <div id="d" style="position:relative; left:10px; height:10px; background:blue;"></div>
+</div>
+<script>
+function doTest() {
+  d.style.left = "50px";
+  document.documentElement.removeAttribute("class");
+}
+window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1770,8 +1770,9 @@ test-pref(layout.css.flexbox.enabled,tru
 == 883987-1f.html 883987-1-ref.html
 == 890495-1.html 890495-1-ref.html
 == 894931-1.html 894931-1-ref.html
 == 897491-1.html 897491-1-ref.html
 == 897491-2.html 897491-2-ref.html
 fuzzy(1,10000) fuzzy-if(Android&&AndroidVersion>=15,5,10000) == 902330-1.html 902330-1-ref.html
 fuzzy-if(Android,8,400) == 906199-1.html 906199-1-ref.html
 == 921716-1.html 921716-1-ref.html
+fuzzy-if(cocoaWidget,1,40) == 928607-1.html 928607-1-ref.html
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -723,16 +723,19 @@ pref("layers.force-tiles", true);
 // Enable the dynamic toolbar
 pref("browser.chrome.dynamictoolbar", true);
 
 // The mode of browser titlebar
 // 0: Show a current page title.
 // 1: Show a current page url.
 pref("browser.chrome.titlebarMode", 0);
 
+// Hide common parts of URLs like "www." or "http://"
+pref("browser.urlbar.trimURLs", true);
+
 #ifdef MOZ_PKG_SPECIAL
 // Disable webgl on ARMv6 because running the reftests takes
 // too long for some reason (bug 843738)
 pref("webgl.disabled", true);
 #endif
 
 // initial web feed readers list
 pref("browser.contentHandlers.types.0.title", "chrome://browser/locale/region.properties");
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -84,16 +84,17 @@ public class BrowserToolbar extends Geck
                             implements TextWatcher,
                                        AutocompleteHandler,
                                        Tabs.OnTabsChangedListener,
                                        GeckoMenu.ActionItemBarPresenter,
                                        Animation.AnimationListener,
                                        GeckoEventListener {
     private static final String LOGTAG = "GeckoToolbar";
     public static final String PREF_TITLEBAR_MODE = "browser.chrome.titlebarMode";
+    public static final String PREF_TRIM_URLS = "browser.urlbar.trimURLs";
 
     public interface OnActivateListener {
         public void onActivate();
     }
 
     public interface OnCommitListener {
         public void onCommit();
     }
@@ -183,16 +184,17 @@ public class BrowserToolbar extends Geck
 
     private static final int FORWARD_ANIMATION_DURATION = 450;
     private final ForegroundColorSpan mUrlColor;
     private final ForegroundColorSpan mBlockedColor;
     private final ForegroundColorSpan mDomainColor;
     private final ForegroundColorSpan mPrivateDomainColor;
 
     private boolean mShowUrl;
+    private boolean mTrimURLs;
 
     private Integer mPrefObserverId;
 
     public BrowserToolbar(Context context) {
         this(context, null);
     }
 
     public BrowserToolbar(Context context, AttributeSet attrs) {
@@ -205,43 +207,64 @@ public class BrowserToolbar extends Geck
         LayoutInflater.from(context).inflate(R.layout.browser_toolbar, this);
 
         Tabs.registerOnTabsChangedListener(this);
         mSwitchingTabs = true;
 
         mIsEditing = false;
         mAnimatingEntry = false;
         mShowUrl = false;
+        mTrimURLs = true;
 
+        final String[] prefs = {
+            PREF_TITLEBAR_MODE,
+            PREF_TRIM_URLS
+        };
         // listen to the title bar pref.
-        mPrefObserverId = PrefsHelper.getPref(PREF_TITLEBAR_MODE, new PrefsHelper.PrefHandlerBase() {
+        mPrefObserverId = PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
             @Override
             public void prefValue(String pref, String str) {
+                // Handles PREF_TITLEBAR_MODE, which is always a string.
                 int value = Integer.parseInt(str);
                 boolean shouldShowUrl = (value == 1);
 
                 if (shouldShowUrl == mShowUrl) {
                     return;
                 }
                 mShowUrl = shouldShowUrl;
 
-                ThreadUtils.postToUiThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        updateTitle();
-                    }
-                });
+                triggerTitleUpdate();
+            }
+
+            @Override
+            public void prefValue(String pref, boolean value) {
+                // Handles PREF_TRIM_URLS, which should usually be a boolean.
+                if (value == mTrimURLs) {
+                    return;
+                }
+                mTrimURLs = value;
+
+                triggerTitleUpdate();
             }
 
             @Override
             public boolean isObserver() {
                 // We want to be notified of changes to be able to switch mode
                 // without restarting.
                 return true;
             }
+
+            private void triggerTitleUpdate() {
+                ThreadUtils.postToUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        updateTitle();
+                    }
+                });
+            }
         });
 
         Resources res = getResources();
         mUrlColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_urltext));
         mBlockedColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_blockedtext));
         mDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext));
         mPrivateDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext_private));
 
@@ -1092,17 +1115,20 @@ public class BrowserToolbar extends Geck
         }
 
         // If the pref to show the URL isn't set, just use the tab's display title.
         if (!mShowUrl || url == null) {
             setTitle(tab.getDisplayTitle());
             return;
         }
 
-        CharSequence title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url));
+        CharSequence title = url;
+        if (mTrimURLs) {
+            title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url));
+        }
 
         String baseDomain = tab.getBaseDomain();
         if (!TextUtils.isEmpty(baseDomain)) {
             SpannableStringBuilder builder = new SpannableStringBuilder(title);
             int index = title.toString().indexOf(baseDomain);
             if (index > -1) {
                 builder.setSpan(mUrlColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                 builder.setSpan(tab.isPrivate() ? mPrivateDomainColor : mDomainColor, index, index+baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4640,22 +4640,24 @@ var BrowserEventHandler = {
   _findScrollableElement: function(elem, checkElem) {
     // Walk the DOM tree until we find a scrollable element
     let scrollable = false;
     while (elem) {
       /* Element is scrollable if its scroll-size exceeds its client size, and:
        * - It has overflow 'auto' or 'scroll'
        * - It's a textarea
        * - It's an HTML/BODY node
+       * - It's a text input
        * - It's a select element showing multiple rows
        */
       if (checkElem) {
         if ((elem.scrollTopMax > 0 || elem.scrollLeftMax > 0) &&
             (this._hasScrollableOverflow(elem) ||
              elem.mozMatchesSelector("html, body, textarea")) ||
+            (elem instanceof HTMLInputElement && elem.mozIsTextField(false)) ||
             (elem instanceof HTMLSelectElement && (elem.size > 1 || elem.multiple))) {
           scrollable = true;
           break;
         }
       } else {
         checkElem = true;
       }
 
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -1159,28 +1159,25 @@ AuthCertificateHook(void *arg, PRFileDes
   NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
 
   // PSM never causes libssl to call this function with PR_TRUE for isServer,
   // and many things in PSM assume that we are a client.
   NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
 
   nsNSSSocketInfo *socketInfo = static_cast<nsNSSSocketInfo*>(arg);
   
-  if (socketInfo) {
-    // This is the first callback during full handshakes.
-    socketInfo->SetFirstServerHelloReceived();
-  }
-
   ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd));
 
   if (!checkSig || isServer || !socketInfo || !serverCert) {
       PR_SetError(PR_INVALID_STATE_ERROR, 0);
       return SECFailure;
   }
 
+  socketInfo->SetFullHandshake();
+
   if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess)
     return SECFailure;
 
   bool onSTSThread;
   nsresult nrv;
   nsCOMPtr<nsIEventTarget> sts
     = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
   if (NS_SUCCEEDED(nrv)) {
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/src/nsNSSCallbacks.cpp
@@ -849,17 +849,16 @@ PreliminaryHandshakeDone(PRFileDesc* fd)
   nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
   if (!infoObject)
     return;
 
   if (infoObject->IsPreliminaryHandshakeDone())
     return;
 
   infoObject->SetPreliminaryHandshakeDone();
-  infoObject->SetFirstServerHelloReceived();
 
   // Get the NPN value.
   SSLNextProtoState state;
   unsigned char npnbuf[256];
   unsigned int npnlen;
 
   if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess) {
     if (state == SSL_NEXT_PROTO_NEGOTIATED) {
@@ -1023,20 +1022,21 @@ CanFalseStartCallback(PRFileDesc* fd, vo
 }
 
 void HandshakeCallback(PRFileDesc* fd, void* client_data) {
   nsNSSShutDownPreventionLock locker;
   SECStatus rv;
 
   nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
 
-  // certificate validation sets FirstServerHelloReceived, so if that flag
+  // certificate validation sets IsFullHandshake, so if that flag
   // is absent at handshake time we have a resumed session. Check this before
   // PreliminaryHandshakeDone() because that function also sets that flag.
-  bool isResumedSession = !(infoObject->GetFirstServerHelloReceived());
+  bool isResumedSession = !infoObject->IsFullHandshake();
+
   // Do the bookkeeping that needs to be done after the
   // server's ServerHello...ServerHelloDone have been processed, but that doesn't
   // need the handshake to be completed.
   PreliminaryHandshakeDone(fd);
 
   nsSSLIOLayerHelpers& ioLayerHelpers
     = infoObject->SharedState().IOLayerHelpers();
 
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -84,23 +84,20 @@ extern PRLogModuleInfo* gPIPNSSLog;
 
 nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
   : mFd(nullptr),
     mCertVerificationState(before_cert_verification),
     mSharedState(aState),
     mForSTARTTLS(false),
     mHandshakePending(true),
     mHasCleartextPhase(false),
-    mHandshakeInProgress(false),
-    mAllowTLSIntoleranceTimeout(true),
     mRememberClientAuthCertificate(false),
     mPreliminaryHandshakeDone(false),
-    mHandshakeStartTime(0),
-    mFirstServerHelloReceived(false),
     mNPNCompleted(false),
+    mIsFullHandshake(false),
     mHandshakeCompleted(false),
     mJoined(false),
     mSentClientCert(false),
     mNotedTimeUntilReady(false),
     mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
     mKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
     mSymmetricCipherUsed(nsISSLSocketControl::SYMMETRIC_CIPHER_UNKNOWN),
     mSymmetricCipherExpected(nsISSLSocketControl::SYMMETRIC_CIPHER_UNKNOWN),
@@ -160,30 +157,16 @@ nsNSSSocketInfo::GetSymmetricCipherExpec
 
 NS_IMETHODIMP
 nsNSSSocketInfo::SetSymmetricCipherExpected(int16_t aSymmetricCipher)
 {
   mSymmetricCipherExpected = aSymmetricCipher;
   return NS_OK;
 }
 
-nsresult
-nsNSSSocketInfo::GetHandshakePending(bool *aHandshakePending)
-{
-  *aHandshakePending = mHandshakePending;
-  return NS_OK;
-}
-
-nsresult
-nsNSSSocketInfo::SetHandshakePending(bool aHandshakePending)
-{
-  mHandshakePending = aHandshakePending;
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsNSSSocketInfo::GetRememberClientAuthCertificate(bool *aRememberClientAuthCertificate)
 {
   NS_ENSURE_ARG_POINTER(aRememberClientAuthCertificate);
   *aRememberClientAuthCertificate = mRememberClientAuthCertificate;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsNSSSocketInfo::SetRememberClientAuthCertificate(bool aRememberClientAuthCertificate)
@@ -292,16 +275,18 @@ nsNSSSocketInfo::SetHandshakeCompleted(b
       PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
       poppedPlaintext->dtor(poppedPlaintext);
     }
 
     mHandshakeCompleted = true;
 
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
            ("[%p] nsNSSSocketInfo::SetHandshakeCompleted\n", (void*)mFd));
+
+    mIsFullHandshake = false; // reset for next handshake on this connection
   }
 }
 
 void
 nsNSSSocketInfo::SetNegotiatedNPN(const char *value, uint32_t length)
 {
   if (!value)
     mNegotiatedNPN.Truncate();
@@ -550,62 +535,21 @@ nsNSSSocketInfo::SetCertVerificationResu
   if (mPlaintextBytesRead && !errorCode) {
     Telemetry::Accumulate(Telemetry::SSL_BYTES_BEFORE_CERT_CALLBACK,
                           SafeCast<uint32_t>(mPlaintextBytesRead));
   }
 
   mCertVerificationState = after_cert_verification;
 }
 
-void nsNSSSocketInfo::SetHandshakeInProgress(bool aIsIn)
-{
-  mHandshakeInProgress = aIsIn;
-
-  if (mHandshakeInProgress && !mHandshakeStartTime)
-  {
-    mHandshakeStartTime = PR_IntervalNow();
-  }
-}
-
-void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(bool aAllow)
-{
-  mAllowTLSIntoleranceTimeout = aAllow;
-}
-
 SharedSSLState& nsNSSSocketInfo::SharedState()
 {
   return mSharedState;
 }
 
-bool nsNSSSocketInfo::HandshakeTimeout()
-{
-  if (!mAllowTLSIntoleranceTimeout)
-    return false;
-
-  if (!mHandshakeInProgress)
-    return false; // have not even sent client hello yet
-
-  if (mFirstServerHelloReceived)
-    return false;
-
-  // Now we know we are in the first handshake, and haven't received the
-  // ServerHello+Certificate sequence or the
-  // ServerHello+ChangeCipherSpec+Finished sequence.
-  //
-  // XXX: Bug 754356 - waiting to receive the Certificate or Finished messages
-  // may cause us to time out in cases where we shouldn't.
-
-  static const PRIntervalTime handshakeTimeoutInterval
-    = PR_SecondsToInterval(25);
-
-  PRIntervalTime now = PR_IntervalNow();
-  bool result = (now - mHandshakeStartTime) > handshakeTimeoutInterval;
-  return result;
-}
-
 void nsSSLIOLayerHelpers::Cleanup()
 {
   mTLSIntoleranceInfo.Clear();
 
   if (mRenegoUnrestrictedSites) {
     delete mRenegoUnrestrictedSites;
     mRenegoUnrestrictedSites = nullptr;
   }
@@ -1035,44 +979,30 @@ int32_t checkHandshake(int32_t bytesTran
   // to send back the version of the protocol we requested (ie v3.1).  At
   // this point many servers's implementations are broken and they shut
   // down the connection when they don't see the version they sent back.
   // This is supposed to prevent a man in the middle from forcing one
   // side to dumb down to a lower level of the protocol.  Unfortunately,
   // there are enough broken servers out there that such a gross work-around
   // is necessary.  :(
 
-  // Additional comment added in August 2006:
-  // When we begun to use TLS hello extensions, we encountered a new class of
-  // broken server, which simply stall for a very long time.
-  // We would like to shorten the timeout, but limit this shorter timeout 
-  // to the handshake phase.
-  // When we arrive here for the first time (for a given socket),
-  // we know the connection is established, and the application code
-  // tried the first read or write. This triggers the beginning of the
-  // SSL handshake phase at the SSL FD level.
-  // We'll make a note of the current time,
-  // and use this to measure the elapsed time since handshake begin.
-
   // Do NOT assume TLS intolerance on a closed connection after bad cert ui was shown.
   // Simply retry.
   // This depends on the fact that Cert UI will not be shown again,
   // should the user override the bad cert.
 
-  bool handleHandshakeResultNow;
-  socketInfo->GetHandshakePending(&handleHandshakeResultNow);
+  bool handleHandshakeResultNow = socketInfo->IsHandshakePending();
 
   bool wantRetry = false;
 
   if (0 > bytesTransfered) {
     int32_t err = PR_GetError();
 
     if (handleHandshakeResultNow) {
       if (PR_WOULD_BLOCK_ERROR == err) {
-        socketInfo->SetHandshakeInProgress(true);
         return bytesTransfered;
       }
 
       if (!wantRetry // no decision yet
           && isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
       {
         SSLVersionRange range = socketInfo->GetTLSVersionRange();
         wantRetry = socketInfo->SharedState().IOLayerHelpers()
@@ -1124,18 +1054,17 @@ int32_t checkHandshake(int32_t bytesTran
     if (wasReading)
       bytesTransfered = -1;
   }
 
   // TLS intolerant servers only cause the first transfer to fail, so let's 
   // set the HandshakePending attribute to false so that we don't try the logic
   // above again in a subsequent transfer.
   if (handleHandshakeResultNow) {
-    socketInfo->SetHandshakePending(false);
-    socketInfo->SetHandshakeInProgress(false);
+    socketInfo->SetHandshakeNotPending();
   }
   
   return bytesTransfered;
 }
 
 }
 
 static int16_t
@@ -1172,27 +1101,16 @@ nsSSLIOLayerPoll(PRFileDesc * fd, int16_
   }
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
          (socketInfo->IsWaitingForCertVerification()
             ?  "[%p] polling SSL socket during certificate verification using lower %d\n"
             :  "[%p] poll SSL socket using lower %d\n",
          fd, (int) in_flags));
 
-  // See comments in HandshakeTimeout before moving and/or changing this block
-  if (socketInfo->HandshakeTimeout()) {
-    NS_WARNING("SSL handshake timed out");
-    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] handshake timed out\n", fd));
-    NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
-                 "caller did not poll for EXCEPT (handshake timeout)");
-    *out_flags = in_flags | PR_POLL_EXCEPT;
-    socketInfo->SetCanceled(PR_CONNECT_RESET_ERROR, PlainErrorMessage);
-    return in_flags;
-  }
-
   // We want the handshake to continue during certificate validation, so we
   // don't need to do anything special here. libssl automatically blocks when
   // it reaches any point that would be unsafe to send/receive something before
   // cert validation is complete.
   int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] poll SSL socket returned %d\n",
                                     (void*)fd, (int) result));
   return result;
@@ -2646,22 +2564,16 @@ nsSSLIOLayerSetOptions(PRFileDesc *fd, b
           fd, static_cast<unsigned int>(range.min),
               static_cast<unsigned int>(range.max)));
 
   if (SSL_VersionRangeSet(fd, &range) != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
   infoObject->SetTLSVersionRange(range);
 
-  // If min == max, then we don't need the intolerance timeout since we have no
-  // lower version to fall back to.
-  if (range.min == range.max) {
-    infoObject->SetAllowTLSIntoleranceTimeout(false);
-  }
-
   bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled();
   if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_OCSP_STAPLING, enabled)) {
     return NS_ERROR_FAILURE;
   }
 
   if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, true)) {
     return NS_ERROR_FAILURE;
   }
@@ -2765,17 +2677,17 @@ nsSSLIOLayerAddToSocket(int32_t family,
   
   nsNSSShutDownList::trackSSLSocketCreate();
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Socket set up\n", (void*)sslSock));
   infoObject->QueryInterface(NS_GET_IID(nsISupports), (void**) (info));
 
   // We are going use a clear connection first //
   if (forSTARTTLS || proxyHost) {
-    infoObject->SetHandshakePending(false);
+    infoObject->SetHandshakeNotPending();
   }
 
   infoObject->SharedState().NoteSocketCreated();
 
   return NS_OK;
  loser:
   NS_IF_RELEASE(infoObject);
   if (layer) {
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -36,42 +36,39 @@ public:
   NS_DECL_NSICLIENTAUTHUSERDECISION
  
   nsresult SetForSTARTTLS(bool aForSTARTTLS);
   nsresult GetForSTARTTLS(bool *aForSTARTTLS);
 
   nsresult GetFileDescPtr(PRFileDesc** aFilePtr);
   nsresult SetFileDescPtr(PRFileDesc* aFilePtr);
 
-  nsresult GetHandshakePending(bool *aHandshakePending);
-  nsresult SetHandshakePending(bool aHandshakePending);
+  bool IsHandshakePending() const { return mHandshakePending; }
+  void SetHandshakeNotPending() { mHandshakePending = false; }
 
   void GetPreviousCert(nsIX509Cert** _result);
   
   void SetHasCleartextPhase(bool aHasCleartextPhase);
   bool GetHasCleartextPhase();
   
-  void SetHandshakeInProgress(bool aIsIn);
-  bool GetHandshakeInProgress() { return mHandshakeInProgress; }
-  void SetFirstServerHelloReceived() { mFirstServerHelloReceived = true; }
-  bool GetFirstServerHelloReceived() { return mFirstServerHelloReceived; }
-  bool HandshakeTimeout();
-
-  void SetAllowTLSIntoleranceTimeout(bool aAllow);
-
   void SetTLSVersionRange(SSLVersionRange range) { mTLSVersionRange = range; }
   SSLVersionRange GetTLSVersionRange() const { return mTLSVersionRange; };
 
   PRStatus CloseSocketAndDestroy(
                 const nsNSSShutDownPreventionLock & proofOfLock);
   
   void SetNegotiatedNPN(const char *value, uint32_t length);
   void SetHandshakeCompleted(bool aResumedSession);
   void NoteTimeUntilReady();
 
+  // Note that this is only valid *during* a handshake; at the end of the handshake,
+  // it gets reset back to false.
+  void SetFullHandshake() { mIsFullHandshake = true; }
+  bool IsFullHandshake() const { return mIsFullHandshake; }
+
   bool GetJoined() { return mJoined; }
   void SetSentClientCert() { mSentClientCert = true; }
 
   uint32_t GetProviderFlags() const { return mProviderFlags; }
 
   mozilla::psm::SharedSSLState& SharedState();
 
   // XXX: These are only used on for diagnostic purposes
@@ -121,27 +118,26 @@ private:
 
   CertVerificationState mCertVerificationState;
 
   mozilla::psm::SharedSSLState& mSharedState;
   bool mForSTARTTLS;
   SSLVersionRange mTLSVersionRange;
   bool mHandshakePending;
   bool mHasCleartextPhase;
-  bool mHandshakeInProgress;
-  bool mAllowTLSIntoleranceTimeout;
   bool mRememberClientAuthCertificate;
   bool mPreliminaryHandshakeDone; // after false start items are complete
   PRIntervalTime mHandshakeStartTime;
   bool mFirstServerHelloReceived;
 
   nsresult ActivateSSL();
 
   nsCString mNegotiatedNPN;
   bool      mNPNCompleted;
+  bool      mIsFullHandshake;
   bool      mHandshakeCompleted;
   bool      mJoined;
   bool      mSentClientCert;
   bool      mNotedTimeUntilReady;
 
   // mKEA* and mSymmetricCipher* are used in false start detetermination
   // values are from nsISSLSocketControl
   int16_t mKEAUsed;