Merge latest green birch changeset and mozilla-central
authorEd Morley <emorley@mozilla.com>
Mon, 29 Jul 2013 15:15:48 +0100
changeset 152617 ba0f15bf8b67729b35c4133ce4afa52d46145ea3
parent 152616 7246ac13cd4bce8a4b145fbb82d80f6dc0c0e61e (current diff)
parent 152596 8c89fe2a5c92c6cda691263a46266d999f4e59a9 (diff)
child 152624 b8c7acba4b40fb752418dce29f5e9835bdcc4c0d
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.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 latest green birch changeset and mozilla-central
--- a/accessible/src/base/EventQueue.cpp
+++ b/accessible/src/base/EventQueue.cpp
@@ -298,17 +298,17 @@ EventQueue::CoalesceSelChangeEvents(AccS
       aTailEvent->mPackedEvent = aThisEvent;
       return;
     }
 
     if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
         aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
       aTailEvent->mEventRule = AccEvent::eDoNotEmit;
       aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
-      aThisEvent->mPackedEvent = aThisEvent;
+      aThisEvent->mPackedEvent = aTailEvent;
       return;
     }
   }
 
   // Unpack the packed selection change event because we've got one
   // more selection add/remove.
   if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
     if (aThisEvent->mPackedEvent) {
@@ -467,16 +467,39 @@ EventQueue::ProcessEventQueue()
           hyperText->GetSelectionCount(&selectionCount);
           if (selectionCount)
             nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
                                     hyperText);
         }
         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);
+
+      } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) {
+        nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
+                                false, event->mIsFromUserInput);
+
+      } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
+        AccSelChangeEvent* selChangeEvent = downcast_accEvent(event);
+        nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
+                                (selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
+                                event->mIsFromUserInput);
+
+        if (selChangeEvent->mPackedEvent) {
+          nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible,
+                                  states::SELECTED,
+                                  (selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
+                                  selChangeEvent->mPackedEvent->mIsFromUserInput);
+        }
+      }
+
       nsEventShell::FireEvent(event);
 
       // Fire text change events.
       AccMutationEvent* mutationEvent = downcast_accEvent(event);
       if (mutationEvent) {
         if (mutationEvent->mTextChangeEvent)
           nsEventShell::FireEvent(mutationEvent->mTextChangeEvent);
       }
--- a/accessible/src/base/nsEventShell.h
+++ b/accessible/src/base/nsEventShell.h
@@ -31,16 +31,30 @@ public:
    * @param  aEventType   [in] the event type
    * @param  aAccessible  [in] the event target
    */
   static void FireEvent(uint32_t aEventType,
                         mozilla::a11y::Accessible* aAccessible,
                         mozilla::a11y::EIsFromUserInput aIsFromUserInput = mozilla::a11y::eAutoDetect);
 
   /**
+   * Fire state change event.
+   */
+  static void FireEvent(mozilla::a11y::Accessible* aTarget, uint64_t aState,
+                        bool aIsEnabled, bool aIsFromUserInput)
+  {
+    nsRefPtr<mozilla::a11y::AccStateChangeEvent> stateChangeEvent =
+      new mozilla::a11y::AccStateChangeEvent(aTarget, aState, aIsEnabled,
+                                             (aIsFromUserInput ?
+                                               mozilla::a11y::eFromUserInput :
+                                               mozilla::a11y::eNoUserInput));
+    FireEvent(stateChangeEvent);
+  }
+
+  /**
    * Append 'event-from-input' object attribute if the accessible event has
    * been fired just now for the given node.
    *
    * @param  aNode        [in] the DOM node
    * @param  aAttributes  [in, out] the attributes
    */
   static void GetEventAttributes(nsINode *aNode,
                                  nsIPersistentProperties *aAttributes);
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -503,69 +503,74 @@ function eventQueue(aEventType)
             ok(false,
                "Unique type " +
                eventQueue.getEventTypeAsString(checker) + " event was handled.");
           }
         }
       }
     }
 
-    var matchedChecker = null;
+    var hasMatchedCheckers = false;
     for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
       var eventSeq = this.mScenarios[scnIdx];
 
       // Check if handled event matches expected sync event.
       var nextChecker = this.getNextExpectedEvent(eventSeq);
       if (nextChecker) {
         if (eventQueue.compareEvents(nextChecker, aEvent)) {
-          matchedChecker = nextChecker;
-          matchedChecker.wasCaught++;
-          break;
+          this.processMatchedChecker(aEvent, nextChecker, scnIdx, eventSeq.idx);
+          hasMatchedCheckers = true;
+          continue;
         }
       }
 
       // Check if handled event matches any expected async events.
       for (idx = 0; idx < eventSeq.length; idx++) {
         if (!eventSeq[idx].unexpected && eventSeq[idx].async) {
           if (eventQueue.compareEvents(eventSeq[idx], aEvent)) {
-            matchedChecker = eventSeq[idx];
-            matchedChecker.wasCaught++;
+            this.processMatchedChecker(aEvent, eventSeq[idx], scnIdx, idx);
+            hasMatchedCheckers = true;
             break;
           }
         }
       }
     }
 
-    // Call 'check' functions on invoker's side.
-    if (matchedChecker) {
-      if ("check" in matchedChecker)
-        matchedChecker.check(aEvent);
-
+    if (hasMatchedCheckers) {
       var invoker = this.getInvoker();
       if ("check" in invoker)
         invoker.check(aEvent);
     }
 
-    // Dump handled event.
-    eventQueue.logEvent(aEvent, matchedChecker, this.areExpectedEventsLeft(),
-                        this.mNextInvokerStatus);
-
     // If we don't have more events to wait then schedule next invoker.
-    if (!this.areExpectedEventsLeft() &&
+    if (this.hasMatchedScenario() &&
         (this.mNextInvokerStatus == kInvokerNotScheduled)) {
       this.processNextInvokerInTimeout();
       return;
     }
 
     // If we have scheduled a next invoker then cancel in case of match.
-    if ((this.mNextInvokerStatus == kInvokerPending) && matchedChecker)
+    if ((this.mNextInvokerStatus == kInvokerPending) && hasMatchedCheckers)
       this.mNextInvokerStatus = kInvokerCanceled;
   }
 
   // Helpers
+  this.processMatchedChecker =
+    function eventQueue_function(aEvent, aMatchedChecker, aScenarioIdx, aEventIdx)
+  {
+    aMatchedChecker.wasCaught++;
+
+    if ("check" in aMatchedChecker)
+      aMatchedChecker.check(aEvent);
+
+    eventQueue.logEvent(aEvent, aMatchedChecker, aScenarioIdx, aEventIdx,
+                        this.areExpectedEventsLeft(),
+                        this.mNextInvokerStatus);
+  }
+
   this.getNextExpectedEvent =
     function eventQueue_getNextExpectedEvent(aEventSeq)
   {
     if (!("idx" in aEventSeq))
       aEventSeq.idx = 0;
 
     while (aEventSeq.idx < aEventSeq.length &&
            (aEventSeq[aEventSeq.idx].unexpected ||
@@ -630,16 +635,26 @@ function eventQueue(aEventType)
           break;
       }
       if (idx == eventSeq.length)
         return true;
     }
 
     return false;
   }
+  
+  this.hasMatchedScenario =
+    function eventQueue_hasMatchedScenario()
+  {
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      if (!this.areExpectedEventsLeft(this.mScenarios[scnIdx]))
+        return true;
+    }
+    return false;
+  }
 
   this.getInvoker = function eventQueue_getInvoker()
   {
     return this.mInvokers[this.mIndex];
   }
 
   this.getNextInvoker = function eventQueue_getNextInvoker()
   {
@@ -853,16 +868,17 @@ eventQueue.isSameEvent = function eventQ
   // target, thus we should filter text change and state change events since
   // they may occur on the same element because of complex changes.
   return this.compareEvents(aChecker, aEvent) &&
     !(aEvent instanceof nsIAccessibleTextChangeEvent) &&
     !(aEvent instanceof nsIAccessibleStateChangeEvent);
 }
 
 eventQueue.logEvent = function eventQueue_logEvent(aOrigEvent, aMatchedChecker,
+                                                   aScenarioIdx, aEventIdx,
                                                    aAreExpectedEventsLeft,
                                                    aInvokerStatus)
 {
   if (!gLogger.isEnabled()) // debug stuff
     return;
 
   // Dump DOM event information. Skip a11y event since it is dumped by
   // gA11yEventObserver.
@@ -892,17 +908,18 @@ eventQueue.logEvent = function eventQueu
   if (!aMatchedChecker)
     return;
 
   var msg = "EQ: ";
   var emphText = "matched ";
 
   var currType = eventQueue.getEventTypeAsString(aMatchedChecker);
   var currTargetDescr = eventQueue.getEventTargetDescr(aMatchedChecker);
-  var consoleMsg = "*****\nEQ matched: " + currType + "\n*****";
+  var consoleMsg = "*****\nScenario " + aScenarioIdx + 
+    ", event " + aEventIdx + " matched: " + currType + "\n*****";
   gLogger.logToConsole(consoleMsg);
 
   msg += " event, type: " + currType + ", target: " + currTargetDescr;
 
   gLogger.logToDOM(msg, true, emphText);
 }
 
 
@@ -1722,17 +1739,18 @@ function stateChangeChecker(aState, aIsE
     var unxpdExtraState = aIsEnabled ? 0 : (aIsExtraState ? aState : 0);
     testStates(event.accessible, state, extraState, unxpdState, unxpdExtraState);
   }
 
   this.match = function stateChangeChecker_match(aEvent)
   {
     if (aEvent instanceof nsIAccessibleStateChangeEvent) {
       var scEvent = aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
-      return aEvent.accessible = this.target && scEvent.state == aState;
+      return (aEvent.accessible == getAccessible(this.target)) &&
+        (scEvent.state == aState);
     }
     return false;
   }
 }
 
 function asyncStateChangeChecker(aState, aIsExtraState, aIsEnabled,
                                  aTargetOrFunc, aTargetFuncArg)
 {
@@ -1767,16 +1785,69 @@ function expandedStateChecker(aIsEnabled
       "Wrong state of statechange event state");
 
     testStates(event.accessible,
                (aIsEnabled ? STATE_EXPANDED : STATE_COLLAPSED));
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// Event sequances (array of predefined checkers)
+
+/**
+ * Event seq for single selection change.
+ */
+function selChangeSeq(aUnselectedID, aSelectedID)
+{
+  if (!aUnselectedID) {
+    return [
+      new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID),
+      new invokerChecker(EVENT_SELECTION, aSelectedID)
+    ];
+  }
+
+  // Return two possible scenarios: depending on widget type when selection is
+  // moved the the order of items that get selected and unselected may vary. 
+  return [
+    [
+      new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID),
+      new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID),
+      new invokerChecker(EVENT_SELECTION, aSelectedID)
+    ],
+    [
+      new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID),
+      new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID),
+      new invokerChecker(EVENT_SELECTION, aSelectedID)
+    ]
+  ];
+}
+
+/**
+ * Event seq for item removed form the selection.
+ */
+function selRemoveSeq(aUnselectedID)
+{
+  return [
+    new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID),
+    new invokerChecker(EVENT_SELECTION_REMOVE, aUnselectedID)
+  ];
+}
+
+/**
+ * Event seq for item added to the selection.
+ */
+function selAddSeq(aSelectedID)
+{
+  return [
+    new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID),
+    new invokerChecker(EVENT_SELECTION_ADD, aSelectedID)
+  ];
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // Private implementation details.
 ////////////////////////////////////////////////////////////////////////////////
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // General
 
 var gA11yEventListeners = {};
@@ -2038,23 +2109,30 @@ function sequenceItem(aProcessor, aEvent
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Event queue invokers
 
 /**
  * Invoker base class for prepare an action.
  */
-function synthAction(aNodeOrID, aCheckerOrEventSeq)
+function synthAction(aNodeOrID, aEventsObj)
 {
   this.DOMNode = getNode(aNodeOrID);
 
-  if (aCheckerOrEventSeq) {
-    if (aCheckerOrEventSeq instanceof Array) {
-      this.eventSeq = aCheckerOrEventSeq;
+  if (aEventsObj) {
+    var scenarios = null;
+    if (aEventsObj instanceof Array) {
+      if (aEventsObj[0] instanceof Array)
+        scenarios = aEventsObj; // scenarios
+      else
+        scenarios = [ aEventsObj ]; // event sequance
     } else {
-      this.eventSeq = [ aCheckerOrEventSeq ];
+      scenarios = [ [ aEventsObj ] ]; // a single checker object
     }
+
+    for (var i = 0; i < scenarios.length; i++)
+      defineScenario(this, scenarios[i]);
   }
 
   this.getID = function synthAction_getID()
     { return prettyName(aNodeOrID) + " action"; }
 }
--- a/accessible/tests/mochitest/events/test_selection.html
+++ b/accessible/tests/mochitest/events/test_selection.html
@@ -33,56 +33,61 @@
     function doTests()
     {
       gQueue = new eventQueue();
 
       // open combobox
       gQueue.push(new synthClick("combobox",
                                  new invokerChecker(EVENT_FOCUS, "cb1_item1")));
       gQueue.push(new synthDownKey("cb1_item1",
-                                   new invokerChecker(EVENT_SELECTION, "cb1_item2")));
+                                   selChangeSeq("cb1_item1", "cb1_item2")));
 
       // closed combobox
       gQueue.push(new synthEscapeKey("combobox",
                                      new invokerChecker(EVENT_FOCUS, "combobox")));
       gQueue.push(new synthDownKey("cb1_item2",
-                                   new invokerChecker(EVENT_SELECTION, "cb1_item3")));
+                                   selChangeSeq("cb1_item2", "cb1_item3")));
 
       // listbox
       gQueue.push(new synthClick("lb1_item1",
                                  new invokerChecker(EVENT_SELECTION, "lb1_item1")));
       gQueue.push(new synthDownKey("lb1_item1",
-                                   new invokerChecker(EVENT_SELECTION, "lb1_item2")));
+                                   selChangeSeq("lb1_item1", "lb1_item2")));
 
       // multiselectable listbox
       gQueue.push(new synthClick("lb2_item1",
-                                 new invokerChecker(EVENT_SELECTION, "lb2_item1")));
+                                 selChangeSeq(null, "lb2_item1")));
       gQueue.push(new synthDownKey("lb2_item1",
-                                   new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
+                                   selAddSeq("lb2_item2"),
                                    { shiftKey: true }));
       gQueue.push(new synthUpKey("lb2_item2",
-                                 new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
+                                 selRemoveSeq("lb2_item2"),
                                  { shiftKey: true }));
       gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
-                               new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
+                               selRemoveSeq("lb2_item1")));
 
       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=414302"
      title="Incorrect selection events in HTML, XUL and ARIA">
-    Mozilla Bug 414302
+    Bug 414302
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=810268"
+     title="There's no way to know unselected item when selection in single selection was changed">
+    Bug 810268
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <select id="combobox">
--- a/accessible/tests/mochitest/events/test_selection_aria.html
+++ b/accessible/tests/mochitest/events/test_selection_aria.html
@@ -31,17 +31,17 @@
 
       this.eventSeq = [
         new invokerChecker(EVENT_SELECTION, aItemID)
       ];
 
       this.invoke = function selectItem_invoke() {
         var itemNode = this.selectNode.querySelector("*[aria-selected='true']");
         if (itemNode)
-          itemNode.removeAttribute("aria-selected", "true");
+          itemNode.removeAttribute("aria-selected");
 
         this.itemNode.setAttribute("aria-selected", "true");
       }
 
       this.getID = function selectItem_getID()
       {
         return "select item " + prettyName(aItemID);
       }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3151,18 +3151,19 @@ nsDocument::ElementFromPointHelper(float
   }
   nsIFrame *rootFrame = ps->GetRootFrame();
 
   // XUL docs, unlike HTML, have no frame tree until everything's done loading
   if (!rootFrame) {
     return nullptr; // return null to premature XUL callers as a reminder to wait
   }
 
-  nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, true,
-                                                      aIgnoreRootScrollFrame);
+  nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
+    nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
+    (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
   if (!ptFrame) {
     return nullptr;
   }
 
   nsIContent* elem = GetContentInThisDocument(ptFrame);
   if (elem && !elem->IsElement()) {
     elem = elem->GetParent();
   }
@@ -3206,17 +3207,18 @@ nsDocument::NodesFromRectHelper(float aX
   nsIFrame *rootFrame = ps->GetRootFrame();
 
   // XUL docs, unlike HTML, have no frame tree until everything's done loading
   if (!rootFrame)
     return NS_OK; // return nothing to premature XUL callers as a reminder to wait
 
   nsAutoTArray<nsIFrame*,8> outFrames;
   nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames,
-                                  true, aIgnoreRootScrollFrame);
+    nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
+    (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
 
   // Used to filter out repeated elements in sequence.
   nsIContent* lastAdded = nullptr;
 
   for (uint32_t i = 0; i < outFrames.Length(); i++) {
     nsIContent* node = GetContentInThisDocument(outFrames[i]);
 
     if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) {
@@ -9345,18 +9347,18 @@ nsIDocument::CaretPositionFromPoint(floa
 
   nsIFrame *rootFrame = ps->GetRootFrame();
 
   // XUL docs, unlike HTML, have no frame tree until everything's done loading
   if (!rootFrame) {
     return nullptr;
   }
 
-  nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, true,
-                                                      false);
+  nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
+      nsLayoutUtils::IGNORE_PAINT_SUPPRESSION);
   if (!ptFrame) {
     return nullptr;
   }
 
   // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
   // to adjust to frame-relative coordinates before we can perform this call.
   // It should also not take into account the padding of the frame.
   nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
--- a/content/media/ogg/OggReader.cpp
+++ b/content/media/ogg/OggReader.cpp
@@ -87,17 +87,16 @@ OggReader::OggReader(AbstractMediaDecode
     mVorbisState(nullptr),
     mOpusState(nullptr),
     mOpusEnabled(MediaDecoder::IsOpusEnabled()),
     mSkeletonState(nullptr),
     mVorbisSerial(0),
     mOpusSerial(0),
     mTheoraSerial(0),
     mOpusPreSkip(0),
-    mPageOffset(0),
     mIsChained(false),
     mDecodedAudioFrames(0)
 {
   MOZ_COUNT_CTOR(OggReader);
   memset(&mTheoraInfo, 0, sizeof(mTheoraInfo));
 }
 
 OggReader::~OggReader()
@@ -179,18 +178,17 @@ nsresult OggReader::ReadMetadata(VideoIn
 
   NS_ASSERTION(aTags, "Called with null MetadataTags**.");
   *aTags = nullptr;
 
   ogg_page page;
   nsAutoTArray<OggCodecState*,4> bitstreams;
   bool readAllBOS = false;
   while (!readAllBOS) {
-    int64_t pageOffset = ReadOggPage(&page);
-    if (pageOffset == -1) {
+    if (!ReadOggPage(&page)) {
       // Some kind of error...
       break;
     }
 
     int serial = ogg_page_serialno(&page);
     OggCodecState* codecState = 0;
 
     if (!ogg_page_bos(&page)) {
@@ -410,17 +408,17 @@ nsresult OggReader::DecodeVorbis(ogg_pac
         // No channel mapping for more than 8 channels.
         return NS_ERROR_FAILURE;
       }
       DownmixToStereo(buffer, channels, frames);
     }
 
     int64_t duration = mVorbisState->Time((int64_t)frames);
     int64_t startTime = mVorbisState->Time(endFrame - frames);
-    mAudioQueue.Push(new AudioData(mPageOffset,
+    mAudioQueue.Push(new AudioData(mDecoder->GetResource()->Tell(),
                                    startTime,
                                    duration,
                                    frames,
                                    buffer.forget(),
                                    channels));
 
     mDecodedAudioFrames += frames;
 
@@ -530,17 +528,17 @@ nsresult OggReader::DecodeOpus(ogg_packe
     if (channels > 8)
       return NS_ERROR_FAILURE;
     DownmixToStereo(buffer, channels, frames);
   }
 
   LOG(PR_LOG_DEBUG, ("Opus decoder pushing %d frames", frames));
   int64_t startTime = mOpusState->Time(startFrame);
   int64_t endTime = mOpusState->Time(endFrame);
-  mAudioQueue.Push(new AudioData(mPageOffset,
+  mAudioQueue.Push(new AudioData(mDecoder->GetResource()->Tell(),
                                  startTime,
                                  endTime - startTime,
                                  frames,
                                  buffer.forget(),
                                  channels));
 
   mDecodedAudioFrames += frames;
 
@@ -670,18 +668,17 @@ bool OggReader::ReadOggChain()
   long rate = 0;
   MetadataTags* tags = nullptr;
 
   if (HasVideo() || HasSkeleton() || !HasAudio()) {
     return false;
   }
 
   ogg_page page;
-  int64_t pageOffset = ReadOggPage(&page);
-  if ((pageOffset == -1) || (!ogg_page_bos(&page))) {
+  if (!ReadOggPage(&page) || !ogg_page_bos(&page)) {
     return false;
   }
 
   int serial = ogg_page_serialno(&page);
   if (mCodecStore.Contains(serial)) {
     return false;
   }
 
@@ -779,17 +776,17 @@ nsresult OggReader::DecodeTheora(ogg_pac
   int64_t endTime = mTheoraState->Time(aPacket->granulepos);
   if (endTime < aTimeThreshold) {
     // The end time of this frame is already before the current playback
     // position. It will never be displayed, don't bother enqueing it.
     return NS_OK;
   }
 
   if (ret == TH_DUPFRAME) {
-    VideoData* v = VideoData::CreateDuplicate(mPageOffset,
+    VideoData* v = VideoData::CreateDuplicate(mDecoder->GetResource()->Tell(),
                                               time,
                                               endTime,
                                               aPacket->granulepos);
     mVideoQueue.Push(v);
   } else if (ret == 0) {
     th_ycbcr_buffer buffer;
     ret = th_decode_ycbcr_out(mTheoraState->mCtx, buffer);
     NS_ASSERTION(ret == 0, "th_decode_ycbcr_out failed");
@@ -800,17 +797,17 @@ nsresult OggReader::DecodeTheora(ogg_pac
       b.mPlanes[i].mHeight = buffer[i].height;
       b.mPlanes[i].mWidth = buffer[i].width;
       b.mPlanes[i].mStride = buffer[i].stride;
       b.mPlanes[i].mOffset = b.mPlanes[i].mSkip = 0;
     }
 
     VideoData *v = VideoData::Create(mInfo,
                                      mDecoder->GetImageContainer(),
-                                     mPageOffset,
+                                     mDecoder->GetResource()->Tell(),
                                      time,
                                      endTime,
                                      b,
                                      isKeyframe,
                                      aPacket->granulepos,
                                      mPicture);
     if (!v) {
       // There may be other reasons for this error, but for
@@ -868,68 +865,65 @@ bool OggReader::DecodeVideoFrame(bool &a
     // there will be no more frames.
     mVideoQueue.Finish();
     return false;
   }
 
   return true;
 }
 
-int64_t OggReader::ReadOggPage(ogg_page* aPage)
+bool OggReader::ReadOggPage(ogg_page* aPage)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   int ret = 0;
   while((ret = ogg_sync_pageseek(&mOggState, aPage)) <= 0) {
     if (ret < 0) {
       // Lost page sync, have to skip up to next page.
-      mPageOffset += -ret;
       continue;
     }
     // Returns a buffer that can be written too
     // with the given size. This buffer is stored
     // in the ogg synchronisation structure.
     char* buffer = ogg_sync_buffer(&mOggState, 4096);
     NS_ASSERTION(buffer, "ogg_sync_buffer failed");
 
     // Read from the resource into the buffer
     uint32_t bytesRead = 0;
 
     nsresult rv = mDecoder->GetResource()->Read(buffer, 4096, &bytesRead);
     if (NS_FAILED(rv) || (bytesRead == 0 && ret == 0)) {
       // End of file.
-      return -1;
+      return false;
     }
 
     mDecoder->NotifyBytesConsumed(bytesRead);
     // Update the synchronisation layer with the number
     // of bytes written to the buffer
     ret = ogg_sync_wrote(&mOggState, bytesRead);
-    NS_ENSURE_TRUE(ret == 0, -1);    
+    NS_ENSURE_TRUE(ret == 0, false);
   }
-  int64_t offset = mPageOffset;
-  mPageOffset += aPage->header_len + aPage->body_len;
-  
-  return offset;
+
+  return true;
 }
 
 ogg_packet* OggReader::NextOggPacket(OggCodecState* aCodecState)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   if (!aCodecState || !aCodecState->mActive) {
     return nullptr;
   }
 
   ogg_packet* packet;
   while ((packet = aCodecState->PacketOut()) == nullptr) {
     // The codec state does not have any buffered pages, so try to read another
     // page from the channel.
     ogg_page page;
-    if (ReadOggPage(&page) == -1) {
+    if (!ReadOggPage(&page)) {
       return nullptr;
     }
 
     uint32_t serial = ogg_page_serialno(&page);
     OggCodecState* codecState = nullptr;
     codecState = mCodecStore.Get(serial);
     if (codecState && NS_FAILED(codecState->PageIn(&page))) {
       return nullptr;
@@ -943,17 +937,17 @@ ogg_packet* OggReader::NextOggPacket(Ogg
 static ogg_uint32_t
 GetChecksum(ogg_page* page)
 {
   if (page == 0 || page->header == 0 || page->header_len < 25) {
     return 0;
   }
   const unsigned char* p = page->header + 22;
   uint32_t c =  p[0] +
-               (p[1] << 8) + 
+               (p[1] << 8) +
                (p[2] << 16) +
                (p[3] << 24);
   return c;
 }
 
 int64_t OggReader::RangeStartTime(int64_t aOffset)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
@@ -1008,17 +1002,17 @@ int64_t OggReader::RangeEndTime(int64_t 
   int64_t readStartOffset = aEndOffset;
   int64_t readLimitOffset = aEndOffset;
   int64_t readHead = aEndOffset;
   int64_t endTime = -1;
   uint32_t checksumAfterSeek = 0;
   uint32_t prevChecksumAfterSeek = 0;
   bool mustBackOff = false;
   while (true) {
-    ogg_page page;    
+    ogg_page page;
     int ret = ogg_sync_pageseek(&sync.mState, &page);
     if (ret == 0) {
       // We need more data if we've not encountered a page we've seen before,
       // or we've read to the end of file.
       if (mustBackOff || readHead == aEndOffset || readHead == aStartOffset) {
         if (endTime != -1 || readStartOffset == 0) {
           // We have encountered a page before, or we're at the end of file.
           break;
@@ -1191,17 +1185,17 @@ OggReader::IndexedSeekResult OggReader::
 {
   mSkeletonState->Deactivate();
   MediaResource* resource = mDecoder->GetResource();
   NS_ENSURE_TRUE(resource != nullptr, SEEK_FATAL_ERROR);
   nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
   return SEEK_INDEX_FAIL;
 }
- 
+
 OggReader::IndexedSeekResult OggReader::SeekToKeyframeUsingIndex(int64_t aTarget)
 {
   MediaResource* resource = mDecoder->GetResource();
   NS_ENSURE_TRUE(resource != nullptr, SEEK_FATAL_ERROR);
   if (!HasSkeleton() || !mSkeletonState->HasIndex()) {
     return SEEK_INDEX_FAIL;
   }
   // We have an index from the Skeleton track, try to use it to seek.
@@ -1226,30 +1220,29 @@ OggReader::IndexedSeekResult OggReader::
     // Index must be invalid.
     return RollbackIndexedSeek(tell);
   }
   LOG(PR_LOG_DEBUG, ("Seeking using index to keyframe at offset %lld\n",
                      keyframe.mKeyPoint.mOffset));
   nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET,
                               keyframe.mKeyPoint.mOffset);
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
-  mPageOffset = keyframe.mKeyPoint.mOffset;
 
   // We've moved the read set, so reset decode.
   res = ResetDecode();
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
 
   // Check that the page the index thinks is exactly here is actually exactly
   // here. If not, the index is invalid.
   ogg_page page;
   int skippedBytes = 0;
   PageSyncResult syncres = PageSync(resource,
                                     &mOggState,
                                     false,
-                                    mPageOffset,
+                                    keyframe.mKeyPoint.mOffset,
                                     resource->GetLength(),
                                     &page,
                                     skippedBytes);
   NS_ENSURE_TRUE(syncres != PAGE_SYNC_ERROR, SEEK_FATAL_ERROR);
   if (syncres != PAGE_SYNC_OK || skippedBytes != 0) {
     LOG(PR_LOG_DEBUG, ("Indexed-seek failure: Ogg Skeleton Index is invalid "
                        "or sync error after seek"));
     return RollbackIndexedSeek(tell);
@@ -1264,17 +1257,16 @@ OggReader::IndexedSeekResult OggReader::
   if (codecState &&
       codecState->mActive &&
       ogg_stream_pagein(&codecState->mState, &page) != 0)
   {
     // Couldn't insert page into the ogg resource, or somehow the resource
     // is no longer active.
     return RollbackIndexedSeek(tell);
   }
-  mPageOffset = keyframe.mKeyPoint.mOffset + page.header_len + page.body_len;
   return SEEK_OK;
 }
 
 nsresult OggReader::SeekInBufferedRange(int64_t aTarget,
                                           int64_t aAdjustedTarget,
                                           int64_t aStartTime,
                                           int64_t aEndTime,
                                           const nsTArray<SeekRange>& aRanges,
@@ -1330,17 +1322,17 @@ nsresult OggReader::SeekInBufferedRange(
 }
 
 nsresult OggReader::SeekInUnbuffered(int64_t aTarget,
                                        int64_t aStartTime,
                                        int64_t aEndTime,
                                        const nsTArray<SeekRange>& aRanges)
 {
   LOG(PR_LOG_DEBUG, ("%p Seeking in unbuffered data to %lld using bisection search", mDecoder, aTarget));
-  
+
   // If we've got an active Theora bitstream, determine the maximum possible
   // time in usecs which a keyframe could be before a given interframe. We
   // subtract this from our seek target, seek to the new target, and then
   // will decode forward to the original seek target. We should encounter a
   // keyframe in that interval. This prevents us from needing to run two
   // bisections; one for the seek target frame, and another to find its
   // keyframe. It's usually faster to just download this extra data, rather
   // tham perform two bisections to find the seek target's keyframe. We
@@ -1381,17 +1373,16 @@ nsresult OggReader::Seek(int64_t aTarget
   }
 
   if (adjustedTarget == aStartTime) {
     // We've seeked to the media start. Just seek to the offset of the first
     // content page.
     res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
 
-    mPageOffset = 0;
     res = ResetDecode(true);
     NS_ENSURE_SUCCESS(res,res);
 
     NS_ASSERTION(aStartTime != -1, "mStartTime should be known");
     {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
       mDecoder->UpdatePlaybackPosition(aStartTime);
     }
@@ -1479,28 +1470,28 @@ PageSync(MediaResource* aResource,
         // End of file.
         return PAGE_SYNC_END_OF_RANGE;
       }
       readHead += bytesRead;
 
       // Update the synchronisation layer with the number
       // of bytes written to the buffer
       ret = ogg_sync_wrote(aState, bytesRead);
-      NS_ENSURE_TRUE(ret == 0, PAGE_SYNC_ERROR);    
+      NS_ENSURE_TRUE(ret == 0, PAGE_SYNC_ERROR);
       continue;
     }
 
     if (ret < 0) {
       NS_ASSERTION(aSkippedBytes >= 0, "Offset >= 0");
       aSkippedBytes += -ret;
       NS_ASSERTION(aSkippedBytes >= 0, "Offset >= 0");
       continue;
     }
   }
-  
+
   return PAGE_SYNC_OK;
 }
 
 nsresult OggReader::SeekBisection(int64_t aTarget,
                                     const SeekRange& aRange,
                                     uint32_t aFuzz)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
@@ -1508,17 +1499,16 @@ nsresult OggReader::SeekBisection(int64_
   MediaResource* resource = mDecoder->GetResource();
 
   if (aTarget == aRange.mTimeStart) {
     if (NS_FAILED(ResetDecode())) {
       return NS_ERROR_FAILURE;
     }
     res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
-    mPageOffset = 0;
     return NS_OK;
   }
 
   // Bisection search, find start offset of last page with end time less than
   // the seek target.
   ogg_int64_t startOffset = aRange.mOffsetStart;
   ogg_int64_t startTime = aRange.mTimeStart;
   ogg_int64_t startLength = 0; // Length of the page at startOffset.
@@ -1548,17 +1538,17 @@ nsresult OggReader::SeekBisection(int64_
     ogg_int64_t pageLength = 0;
     ogg_int64_t granuleTime = -1;
     bool mustBackoff = false;
 
     // Guess where we should bisect to, based on the bit rate and the time
     // remaining in the interval. Loop until we can determine the time at
     // the guess offset.
     while (true) {
-  
+
       // Discard any previously buffered packets/pages.
       if (NS_FAILED(ResetDecode())) {
         return NS_ERROR_FAILURE;
       }
 
       interval = endOffset - startOffset - startLength;
       if (interval == 0) {
         // Our interval is empty, we've found the optimal seek point, as the
@@ -1609,45 +1599,44 @@ nsresult OggReader::SeekBisection(int64_
                               endOffset, endTime, interval, target, guess));
 
       NS_ASSERTION(guess >= startOffset + startLength, "Guess must be after range start");
       NS_ASSERTION(guess < endOffset, "Guess must be before range end");
       NS_ASSERTION(guess != previousGuess, "Guess should be different to previous");
       previousGuess = guess;
 
       hops++;
-    
+
       // Locate the next page after our seek guess, and then figure out the
       // granule time of the audio and video bitstreams there. We can then
       // make a bisection decision based on our location in the media.
       PageSyncResult res = PageSync(resource,
                                     &mOggState,
                                     false,
                                     guess,
                                     endOffset,
                                     &page,
                                     skippedBytes);
       NS_ENSURE_TRUE(res != PAGE_SYNC_ERROR, NS_ERROR_FAILURE);
 
-      // We've located a page of length |ret| at |guess + skippedBytes|.
-      // Remember where the page is located.
-      pageOffset = guess + skippedBytes;
-      pageLength = page.header_len + page.body_len;
-      mPageOffset = pageOffset + pageLength;
-
       if (res == PAGE_SYNC_END_OF_RANGE) {
         // Our guess was too close to the end, we've ended up reading the end
         // page. Backoff exponentially from the end point, in case the last
         // page/frame/sample is huge.
         mustBackoff = true;
         SEEK_LOG(PR_LOG_DEBUG, ("Hit the end of range, backing off"));
         continue;
       }
 
-      // Read pages until we can determine the granule time of the audio and 
+      // We've located a page of length |ret| at |guess + skippedBytes|.
+      // Remember where the page is located.
+      pageOffset = guess + skippedBytes;
+      pageLength = page.header_len + page.body_len;
+
+      // Read pages until we can determine the granule time of the audio and
       // video bitstream.
       ogg_int64_t audioTime = -1;
       ogg_int64_t videoTime = -1;
       do {
         // Add the page to its codec state, determine its granule time.
         uint32_t serial = ogg_page_serialno(&page);
         OggCodecState* codecState = mCodecStore.Get(serial);
         if (codecState && codecState->mActive) {
@@ -1661,43 +1650,43 @@ nsresult OggReader::SeekBisection(int64_
           if (mVorbisState && serial == mVorbisState->mSerial) {
             audioTime = mVorbisState->Time(granulepos);
 #ifdef MOZ_OPUS
           } else if (mOpusState && serial == mOpusState->mSerial) {
             audioTime = mOpusState->Time(granulepos);
 #endif
           }
         }
-        
+
         if (HasVideo() &&
             granulepos > 0 &&
             serial == mTheoraState->mSerial &&
             videoTime == -1) {
-          videoTime = mTheoraState->StartTime(granulepos);
+          videoTime = mTheoraState->Time(granulepos);
         }
 
-        if (mPageOffset == endOffset) {
+        if (pageOffset + pageLength >= endOffset) {
           // Hit end of readable data.
           break;
         }
 
-        if (ReadOggPage(&page) == -1) {
+        if (!ReadOggPage(&page)) {
           break;
         }
-        
+
       } while ((HasAudio() && audioTime == -1) ||
                (HasVideo() && videoTime == -1));
 
-      NS_ASSERTION(mPageOffset <= endOffset, "Page read cursor should be inside range");
 
       if ((HasAudio() && audioTime == -1) ||
-          (HasVideo() && videoTime == -1)) 
+          (HasVideo() && videoTime == -1))
       {
         // We don't have timestamps for all active tracks...
-        if (pageOffset == startOffset + startLength && mPageOffset == endOffset) {
+        if (pageOffset == startOffset + startLength &&
+            pageOffset + pageLength >= endOffset) {
           // We read the entire interval without finding timestamps for all
           // active tracks. We know the interval start offset is before the seek
           // target, and the interval end is after the seek target, and we can't
           // terminate inside the interval, so we terminate the seek at the
           // start of the interval.
           interval = 0;
           break;
         }
@@ -1717,33 +1706,31 @@ nsresult OggReader::SeekBisection(int64_
 
     if (interval == 0) {
       // Seek termination condition; we've found the page boundary of the
       // last page before the target, and the first page after the target.
       SEEK_LOG(PR_LOG_DEBUG, ("Terminating seek at offset=%lld", startOffset));
       NS_ASSERTION(startTime < aTarget, "Start time must always be less than target");
       res = resource->Seek(nsISeekableStream::NS_SEEK_SET, startOffset);
       NS_ENSURE_SUCCESS(res,res);
-      mPageOffset = startOffset;
       if (NS_FAILED(ResetDecode())) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
 
     SEEK_LOG(PR_LOG_DEBUG, ("Time at offset %lld is %lld", guess, granuleTime));
     if (granuleTime < seekTarget && granuleTime > seekLowerBound) {
       // We're within the fuzzy region in which we want to terminate the search.
       res = resource->Seek(nsISeekableStream::NS_SEEK_SET, pageOffset);
       NS_ENSURE_SUCCESS(res,res);
-      mPageOffset = pageOffset;
       if (NS_FAILED(ResetDecode())) {
         return NS_ERROR_FAILURE;
       }
-      SEEK_LOG(PR_LOG_DEBUG, ("Terminating seek at offset=%lld", mPageOffset));
+      SEEK_LOG(PR_LOG_DEBUG, ("Terminating seek at offset=%lld", pageOffset));
       break;
     }
 
     if (granuleTime >= seekTarget) {
       // We've landed after the seek target.
       NS_ASSERTION(pageOffset < endOffset, "offset_end must decrease");
       endOffset = pageOffset;
       endTime = granuleTime;
@@ -1774,17 +1761,17 @@ nsresult OggReader::GetBuffered(TimeRang
 #ifdef OGG_ESTIMATE_BUFFERED
   MediaResource* stream = mDecoder->GetResource();
   int64_t durationUs = 0;
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     durationUs = mDecoder->GetMediaDuration();
   }
   GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
-  
+
   return NS_OK;
 #else
   // HasAudio and HasVideo are not used here as they take a lock and cause
   // a deadlock. Accessing mInfo doesn't require a lock - it doesn't change
   // after metadata is read.
   if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
     // No need to search through the file if there are no audio or video tracks
     return NS_OK;
--- a/content/media/ogg/OggReader.h
+++ b/content/media/ogg/OggReader.h
@@ -207,17 +207,17 @@ private:
   // bisection's search space when the target isn't in a known buffered range.
   SeekRange SelectSeekRange(const nsTArray<SeekRange>& aRanges,
                             int64_t aTarget,
                             int64_t aStartTime,
                             int64_t aEndTime,
                             bool aExact);
 private:
 
-  // Decodes a packet of Vorbis data, and inserts its samples into the 
+  // Decodes a packet of Vorbis data, and inserts its samples into the
   // audio queue.
   nsresult DecodeVorbis(ogg_packet* aPacket);
 
   // Decodes a packet of Opus data, and inserts its samples into the
   // audio queue.
   nsresult DecodeOpus(ogg_packet* aPacket);
 
   // Downmix multichannel Audio samples to Stereo.
@@ -229,19 +229,19 @@ private:
 
   // Decodes a packet of Theora data, and inserts its frame into the
   // video queue. May return NS_ERROR_OUT_OF_MEMORY. Caller must have obtained
   // the reader's monitor. aTimeThreshold is the current playback position
   // in media time in microseconds. Frames with an end time before this will
   // not be enqueued.
   nsresult DecodeTheora(ogg_packet* aPacket, int64_t aTimeThreshold);
 
-  // Read a page of data from the Ogg file. Returns the offset of the start
-  // of the page, or -1 if the page read failed.
-  int64_t ReadOggPage(ogg_page* aPage);
+  // Read a page of data from the Ogg file. Returns true if a page has been
+  // read, false if the page read failed or end of file reached.
+  bool ReadOggPage(ogg_page* aPage);
 
   // Reads and decodes header packets for aState, until either header decode
   // fails, or is complete. Initializes the codec state before returning.
   // Returns true if reading headers and initializtion of the stream
   // succeeds.
   bool ReadHeaders(OggCodecState* aState);
 
   // Reads the next link in the chain.
@@ -290,20 +290,16 @@ private:
   // using this codec data.
   uint32_t mVorbisSerial;
   uint32_t mOpusSerial;
   uint32_t mTheoraSerial;
   vorbis_info mVorbisInfo;
   int mOpusPreSkip;
   th_info mTheoraInfo;
 
-  // The offset of the end of the last page we've read, or the start of
-  // the page we're about to read.
-  int64_t mPageOffset;
-
   // The picture region inside Theora frame to be displayed, if we have
   // a Theora video track.
   nsIntRect mPicture;
 
   // True if we are decoding a chained ogg. Reading or writing to this member
   // should be done with |mMonitor| acquired.
   bool mIsChained;
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -9627,17 +9627,17 @@ class CGJSImplClass(CGBindingImplClass):
             "// GlobalObject will go through wrappers as needed for us, and\n"
             "// is simpler than the right UnwrapArg incantation.\n"
             "GlobalObject global(cx, &args[0].toObject());\n"
             "if (global.Failed()) {\n"
             "  return false;\n"
             "}\n"
             "nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.Get());\n"
             "if (!window) {\n"
-            '  return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "Argument 1 of ${ifaceName}._create");\n'
+            '  return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "Argument 1 of ${ifaceName}._create", "Window");\n'
             "}\n"
             "JS::Rooted<JSObject*> arg(cx, &args[1].toObject());\n"
             "nsRefPtr<${implName}> impl = new ${implName}(arg, window);\n"
             "return WrapNewBindingObject(cx, arg, impl, args.rval());").substitute({
                 "ifaceName": self.descriptor.interface.identifier.name,
                 "implName": self.descriptor.name
                 })
 
--- a/dom/contacts/moz.build
+++ b/dom/contacts/moz.build
@@ -1,16 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 # Android only supports the Contacts API on the Nightly channel.
-if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' or CONFIG['NIGHTLY_BUILD']:
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     TEST_DIRS += ['tests']
 
 EXTRA_COMPONENTS += [
     'ContactManager.js',
     'ContactManager.manifest',
 ]
 
 EXTRA_JS_MODULES += [
--- a/editor/libeditor/html/nsTableEditor.cpp
+++ b/editor/libeditor/html/nsTableEditor.cpp
@@ -2747,16 +2747,26 @@ nsHTMLEditor::GetCellDataAt(nsIDOMElemen
 
 // When all you want is the cell
 NS_IMETHODIMP 
 nsHTMLEditor::GetCellAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, nsIDOMElement **aCell)
 {
   NS_ENSURE_ARG_POINTER(aCell);
   *aCell = nullptr;
 
+  if (!aTable)
+  {
+    // Get the selected table or the table enclosing the selection anchor
+    nsCOMPtr<nsIDOMElement> table;
+    nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table));
+    NS_ENSURE_SUCCESS(res, res);
+    NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
+    aTable = table;
+  }
+
   nsTableOuterFrame* tableFrame = GetTableFrame(aTable);
   if (!tableFrame)
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIDOMElement> domCell =
     do_QueryInterface(tableFrame->GetCellAt(aRowIndex, aColIndex));
   domCell.forget(aCell);
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -115,21 +115,36 @@ DeprecatedTextureClientShmem::GetSurface
                     ? OPEN_READ_WRITE
                     : OPEN_READ_ONLY;
     mSurface = ShadowLayerForwarder::OpenDescriptor(mode, mDescriptor);
   }
 
   return mSurface.get();
 }
 
+
+gfx::DrawTarget*
+DeprecatedTextureClientShmem::LockDrawTarget()
+{
+  if (mDrawTarget) {
+    return mDrawTarget;
+  }
+
+  gfxASurface* surface = GetSurface();
+  mDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surface, mSize);
+
+  return mDrawTarget;
+}
+
 void
 DeprecatedTextureClientShmem::Unlock()
 {
   mSurface = nullptr;
   mSurfaceAsImage = nullptr;
+  mDrawTarget = nullptr;
 
   ShadowLayerForwarder::CloseDescriptor(mDescriptor);
 }
 
 gfxImageSurface*
 DeprecatedTextureClientShmem::LockImageSurface()
 {
   if (!mSurfaceAsImage) {
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -160,27 +160,29 @@ public:
   ~DeprecatedTextureClientShmem() { ReleaseResources(); }
 
   virtual bool SupportsType(DeprecatedTextureClientType aType) MOZ_OVERRIDE
   {
     return aType == TEXTURE_SHMEM || aType == TEXTURE_CONTENT;
   }
   virtual gfxImageSurface* LockImageSurface() MOZ_OVERRIDE;
   virtual gfxASurface* LockSurface() MOZ_OVERRIDE { return GetSurface(); }
+  virtual gfx::DrawTarget* LockDrawTarget();
   virtual void Unlock() MOZ_OVERRIDE;
   virtual void EnsureAllocated(gfx::IntSize aSize, gfxASurface::gfxContentType aType) MOZ_OVERRIDE;
 
   virtual void ReleaseResources() MOZ_OVERRIDE;
   virtual void SetDescriptor(const SurfaceDescriptor& aDescriptor) MOZ_OVERRIDE;
   virtual gfxASurface::gfxContentType GetContentType() MOZ_OVERRIDE { return mContentType; }
 private:
   gfxASurface* GetSurface();
 
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<gfxImageSurface> mSurfaceAsImage;
+  RefPtr<gfx::DrawTarget> mDrawTarget;
 
   gfxASurface::gfxContentType mContentType;
   gfx::IntSize mSize;
 
   friend class CompositingFactory;
 };
 
 class DeprecatedTextureClientShmemYCbCr : public DeprecatedTextureClient
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -629,17 +629,18 @@ gfxPlatform::GetSourceSurfaceForSurface(
     format = FORMAT_B8G8R8X8;
   } else {
     format = FORMAT_B8G8R8A8;
   }
 
   RefPtr<SourceSurface> srcBuffer;
 
 #ifdef XP_WIN
-  if (aSurface->GetType() == gfxASurface::SurfaceTypeD2D) {
+  if (aSurface->GetType() == gfxASurface::SurfaceTypeD2D &&
+      format != FORMAT_A8) {
     NativeSurface surf;
     surf.mFormat = format;
     surf.mType = NATIVE_SURFACE_D3D10_TEXTURE;
     surf.mSurface = static_cast<gfxD2DSurface*>(aSurface)->GetTexture();
     mozilla::gfx::DrawTarget *dt = static_cast<mozilla::gfx::DrawTarget*>(aSurface->GetData(&kDrawTarget));
     if (dt) {
       dt->Flush();
     }
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -67,20 +67,16 @@ VPATH		+= \
 
 ###############################################
 # BEGIN enable non-releasable features
 #
 ifdef NIGHTLY_BUILD
 DEFINES += -DENABLE_PARALLEL_JS
 endif
 
-ifdef NIGHTLY_BUILD
-DEFINES += -DENABLE_BINARYDATA
-endif
-
 # Ion
 ifdef ENABLE_ION
 VPATH +=	$(srcdir)/ion
 VPATH +=	$(srcdir)/ion/shared
 
 ifeq (86, $(findstring 86,$(TARGET_CPU)))
 ifeq (x86_64, $(TARGET_CPU))
 VPATH +=	$(srcdir)/ion/x64
--- a/js/src/builtin/BinaryData.cpp
+++ b/js/src/builtin/BinaryData.cpp
@@ -1,29 +1,30 @@
 /* -*- 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 "builtin/BinaryData.h"
 
-#include <vector>
+#include "mozilla/FloatingPoint.h"
 
-#include "mozilla/FloatingPoint.h"
+#include <vector>
 
 #include "jscompartment.h"
 #include "jsfun.h"
 #include "jsobj.h"
 #include "jsutil.h"
 
-#include "vm/TypedArrayObject.h"
+#include "gc/Marking.h"
+#include "vm/GlobalObject.h"
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
-#include "vm/GlobalObject.h"
+#include "vm/TypedArrayObject.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
 /*
  * Reify() takes a complex binary data object `owner` and an offset and tries to
@@ -176,19 +177,26 @@ GetAlign(JSContext *cx, HandleObject typ
     JS_ASSERT(&NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() &&
               type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]);
     JSObject::getProperty(cx, typeObj, typeObj, cx->names().bytes, &val);
     return val.toInt32();
 }
 
 struct FieldInfo
 {
-    jsid name;
-    JSObject *type;
+    HeapId name;
+    HeapPtrObject type;
     size_t offset;
+
+    FieldInfo() : offset(0) {}
+
+    FieldInfo(const FieldInfo &o)
+        : name(o.name.get()), type(o.type), offset(o.offset)
+    {
+    }
 };
 
 Class js::DataClass = {
     "Data",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Data),
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
@@ -257,17 +265,17 @@ IsSameStructType(JSContext *cx, HandleOb
     if (fieldList1->size() != fieldList2->size())
         return false;
 
     // Names and layout should be the same.
     for (uint32_t i = 0; i < fieldList1->size(); ++i) {
         FieldInfo fieldInfo1 = fieldList1->at(i);
         FieldInfo fieldInfo2 = fieldList2->at(i);
 
-        if (fieldInfo1.name != fieldInfo2.name)
+        if (fieldInfo1.name.get() != fieldInfo2.name.get())
             return false;
 
         if (fieldInfo1.offset != fieldInfo2.offset)
             return false;
 
         RootedObject fieldType1(cx, fieldInfo1.type);
         RootedObject fieldType2(cx, fieldInfo2.type);
         if (!IsSameBinaryDataType(cx, fieldType1, fieldType2))
@@ -1422,21 +1430,21 @@ Class StructType::class_ = {
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     StructType::finalize,
-    NULL,
-    NULL,
-    NULL,
+    NULL, /* checkAccess */
+    NULL, /* call */
+    NULL, /* hasInstance */
     BinaryStruct::construct,
-    NULL
+    StructType::trace
 };
 
 Class BinaryStruct::class_ = {
     "BinaryStruct",
     Class::NON_NATIVE |
     JSCLASS_HAS_RESERVED_SLOTS(BLOCK_RESERVED_SLOTS) |
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_StructType),
@@ -1445,18 +1453,18 @@ Class BinaryStruct::class_ = {
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     BinaryStruct::finalize,
     NULL,           /* checkAccess */
     NULL,           /* call        */
+    NULL,           /* hasInstance */
     NULL,           /* construct   */
-    NULL,           /* hasInstance */
     BinaryStruct::obj_trace,
     JS_NULL_CLASS_EXT,
     {
         NULL, /* lookupGeneric */
         NULL, /* lookupProperty */
         NULL, /* lookupElement */
         NULL, /* lookupSpecial */
         NULL, /* defineGeneric */
@@ -1685,16 +1693,27 @@ StructType::construct(JSContext *cx, uns
 
 void
 StructType::finalize(FreeOp *op, JSObject *obj)
 {
     FieldList *list = static_cast<FieldList *>(obj->getPrivate());
     delete list;
 }
 
+void
+StructType::trace(JSTracer *tracer, JSObject *obj)
+{
+    FieldList *fieldList = static_cast<FieldList *>(obj->getPrivate());
+    JS_ASSERT(fieldList);
+    for (FieldList::iterator it = fieldList->begin(); it != fieldList->end(); ++it) {
+        gc::MarkId(tracer, &(it->name), "structtype.field.name");
+        MarkObject(tracer, &(it->type), "structtype.field.type");
+    }
+}
+
 JSBool
 StructType::toString(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject thisObj(cx, args.thisv().toObjectOrNull());
 
     if (!IsStructType(thisObj))
--- a/js/src/builtin/BinaryData.h
+++ b/js/src/builtin/BinaryData.h
@@ -3,18 +3,19 @@
  * 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 builtin_BinaryData_h
 #define builtin_BinaryData_h
 
 #include "jsapi.h"
+#include "jsfriendapi.h"
 #include "jsobj.h"
-#include "jsfriendapi.h"
+
 #include "gc/Heap.h"
 
 namespace js {
 typedef float float32_t;
 typedef double float64_t;
 
 enum {
     NUMERICTYPE_UINT8 = 0,
@@ -272,16 +273,17 @@ class StructType : public JSObject
 
     static bool convertAndCopyTo(JSContext *cx, HandleObject exemplar,
                                  HandleValue from, uint8_t *mem);
 
     static bool reify(JSContext *cx, HandleObject type, HandleObject owner,
                       size_t offset, MutableHandleValue to);
 
     static void finalize(js::FreeOp *op, JSObject *obj);
+    static void trace(JSTracer *tracer, JSObject *obj);
 };
 
 class BinaryStruct : public JSObject
 {
   private:
     static JSObject *createEmpty(JSContext *cx, HandleObject type);
     static JSObject *create(JSContext *cx, HandleObject type);
 
--- a/js/src/ion/AsmJSSignalHandlers.cpp
+++ b/js/src/ion/AsmJSSignalHandlers.cpp
@@ -991,19 +991,8 @@ js::TriggerOperationCallbackForAsmJSCode
     DWORD oldProtect;
     if (!VirtualProtect(module.functionCode(), module.functionBytes(), PAGE_NOACCESS, &oldProtect))
         MOZ_CRASH();
 #else  // assume Unix
     if (mprotect(module.functionCode(), module.functionBytes(), PROT_NONE))
         MOZ_CRASH();
 #endif
 }
-
-#ifdef MOZ_ASAN
-// When running with asm.js under AddressSanitizer, we need to explicitely
-// tell AddressSanitizer to allow custom signal handlers because it will 
-// otherwise trigger ASan's SIGSEGV handler for the internal SIGSEGVs that 
-// asm.js would otherwise handle.
-extern "C" MOZ_ASAN_BLACKLIST
-const char* __asan_default_options() {
-    return "allow_user_segv_handler=1";
-}
-#endif
--- a/js/src/ion/BaselineCompiler.h
+++ b/js/src/ion/BaselineCompiler.h
@@ -8,18 +8,18 @@
 #define ion_BaselineCompiler_h
 
 #ifdef JS_ION
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsinfer.h"
 
+#include "ion/BaselineIC.h"
 #include "ion/BaselineJIT.h"
-#include "ion/BaselineIC.h"
 #include "ion/BytecodeAnalysis.h"
 #include "ion/FixedList.h"
 #include "ion/IonAllocPolicy.h"
 #include "ion/IonCode.h"
 #if defined(JS_CPU_X86)
 # include "ion/x86/BaselineCompiler-x86.h"
 #elif defined(JS_CPU_X64)
 # include "ion/x64/BaselineCompiler-x64.h"
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -4661,16 +4661,17 @@ IonBuilder::jsop_funapplyarguments(uint3
     RootedFunction target(cx, getSingleCallTarget(funTypes));
 
     // When this script isn't inlined, use MApplyArgs,
     // to copy the arguments from the stack and call the function
     if (inliningDepth_ == 0) {
 
         // Vp
         MPassArg *passVp = current->pop()->toPassArg();
+        passVp->getArgument()->setFoldedUnchecked();
         passVp->replaceAllUsesWith(passVp->getArgument());
         passVp->block()->discard(passVp);
 
         // This
         MPassArg *passThis = current->pop()->toPassArg();
         MDefinition *argThis = passThis->getArgument();
         passThis->replaceAllUsesWith(argThis);
         passThis->block()->discard(passThis);
@@ -4700,16 +4701,17 @@ IonBuilder::jsop_funapplyarguments(uint3
     // When inlining we have the arguments the function gets called with
     // and can optimize even more, by just calling the functions with the args.
     JS_ASSERT(inliningDepth_ > 0);
 
     CallInfo callInfo(cx, false);
 
     // Vp
     MPassArg *passVp = current->pop()->toPassArg();
+    passVp->getArgument()->setFoldedUnchecked();
     passVp->replaceAllUsesWith(passVp->getArgument());
     passVp->block()->discard(passVp);
 
     // Arguments
     Vector<MDefinition *> args(cx);
     if (!args.append(inlineCallInfo_->argv().begin(), inlineCallInfo_->argv().end()))
         return false;
     callInfo.setArgs(&args);
--- a/js/src/ion/arm/Architecture-arm.cpp
+++ b/js/src/ion/arm/Architecture-arm.cpp
@@ -1,39 +1,40 @@
 /* -*- 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/. */
 
-#define HWCAP_ARMv7 (1 << 31)
+#include "ion/arm/Architecture-arm.h"
+
 #include "mozilla/StandardInteger.h"
 
-#include <sys/types.h>
+#include <elf.h>
+#include <fcntl.h>
 #include <sys/stat.h>
-#include <fcntl.h>
+#include <sys/types.h>
 #include <unistd.h>
-#include <elf.h>
+
+#include "ion/arm/Assembler-arm.h"
 
-// lame check for kernel version
-// see bug 586550
 #if !(defined(ANDROID) || defined(MOZ_B2G))
+#define HWCAP_ARMv7 (1 << 31)
 #include <asm/hwcap.h>
 #else
 #define HWCAP_VFP      (1<<0)
 #define HWCAP_VFPv3    (1<<1)
 #define HWCAP_VFPv3D16 (1<<2)
 #define HWCAP_VFPv4    (1<<3)
 #define HWCAP_IDIVA    (1<<4)
 #define HWCAP_IDIVT    (1<<5)
 #define HWCAP_NEON     (1<<6)
 #define HWCAP_ARMv7    (1<<7)
 #endif
-#include "ion/arm/Architecture-arm.h"
-#include "ion/arm/Assembler-arm.h"
+
 namespace js {
 namespace ion {
 
 uint32_t getFlags()
 {
     static bool isSet = false;
     static uint32_t flags = 0;
     if (isSet)
--- a/js/src/ion/arm/Architecture-arm.h
+++ b/js/src/ion/arm/Architecture-arm.h
@@ -2,17 +2,20 @@
  * 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/. */
 
 #ifndef ion_arm_Architecture_arm_h
 #define ion_arm_Architecture_arm_h
 
+#include "mozilla/StandardInteger.h"
+
 #include <limits.h>
+
 // gcc appears to use __ARM_PCS_VFP to denote that the target is a hard-float target.
 #ifdef __ARM_PCS_VFP
 #define JS_CPU_ARM_HARDFP
 #endif
 namespace js {
 namespace ion {
 
 static const uint32_t STACK_SLOT_SIZE       = 4;
--- a/js/src/ion/arm/Assembler-arm.cpp
+++ b/js/src/ion/arm/Assembler-arm.cpp
@@ -1,24 +1,26 @@
 /* -*- 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 "ion/arm/Assembler-arm.h"
+
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 
-#include "ion/arm/Assembler-arm.h"
-#include "ion/arm/MacroAssembler-arm.h"
-#include "gc/Marking.h"
+#include "jscompartment.h"
 #include "jsutil.h"
+
 #include "assembler/jit/ExecutableAllocator.h"
-#include "jscompartment.h"
+#include "gc/Marking.h"
 #include "ion/IonCompartment.h"
+#include "ion/arm/MacroAssembler-arm.h"
 
 using namespace js;
 using namespace js::ion;
 
 using mozilla::CountLeadingZeroes32;
 
 ABIArgGenerator::ABIArgGenerator() :
 #if defined(JS_CPU_ARM_HARDFP)
--- a/js/src/ion/arm/Assembler-arm.h
+++ b/js/src/ion/arm/Assembler-arm.h
@@ -6,21 +6,21 @@
 
 #ifndef ion_arm_Assembler_arm_h
 #define ion_arm_Assembler_arm_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Util.h"
 
-#include "ion/shared/Assembler-shared.h"
 #include "assembler/assembler/AssemblerBufferWithConstantPool.h"
+#include "ion/arm/Architecture-arm.h"
 #include "ion/CompactBuffer.h"
 #include "ion/IonCode.h"
-#include "ion/arm/Architecture-arm.h"
+#include "ion/shared/Assembler-shared.h"
 #include "ion/shared/IonAssemblerBufferWithConstantPools.h"
 
 namespace js {
 namespace ion {
 
 //NOTE: there are duplicates in this list!
 // sometimes we want to specifically refer to the
 // link register as a link register (bl lr is much
--- a/js/src/ion/arm/Bailouts-arm.cpp
+++ b/js/src/ion/arm/Bailouts-arm.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 "jscntxt.h"
 #include "jscompartment.h"
+
 #include "ion/Bailouts.h"
 #include "ion/IonCompartment.h"
 
 using namespace js;
 using namespace js::ion;
 
 #if 0
 // no clue what these asserts should be.
--- a/js/src/ion/arm/BaselineHelpers-arm.h
+++ b/js/src/ion/arm/BaselineHelpers-arm.h
@@ -3,21 +3,20 @@
  * 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 ion_arm_BaselineHelpers_arm_h
 #define ion_arm_BaselineHelpers_arm_h
 
 #ifdef JS_ION
-
+#include "ion/BaselineFrame.h"
+#include "ion/BaselineIC.h"
+#include "ion/BaselineRegisters.h"
 #include "ion/IonMacroAssembler.h"
-#include "ion/BaselineFrame.h"
-#include "ion/BaselineRegisters.h"
-#include "ion/BaselineIC.h"
 
 namespace js {
 namespace ion {
 
 // Distance from sp to the top Value inside an IC stub (no return address on the stack on ARM).
 static const size_t ICStackValueOffset = 0;
 
 inline void
--- a/js/src/ion/arm/BaselineIC-arm.cpp
+++ b/js/src/ion/arm/BaselineIC-arm.cpp
@@ -1,18 +1,18 @@
 /* -*- 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 "ion/BaselineJIT.h"
-#include "ion/BaselineIC.h"
 #include "ion/BaselineCompiler.h"
 #include "ion/BaselineHelpers.h"
+#include "ion/BaselineIC.h"
+#include "ion/BaselineJIT.h"
 #include "ion/IonLinker.h"
 
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -1,32 +1,33 @@
 /* -*- 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 "ion/arm/CodeGenerator-arm.h"
+
 #include "mozilla/MathAlgorithms.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsnum.h"
 
-#include "ion/arm/CodeGenerator-arm.h"
-#include "ion/PerfSpewer.h"
 #include "ion/CodeGenerator.h"
 #include "ion/IonCompartment.h"
 #include "ion/IonFrames.h"
 #include "ion/MIR.h"
 #include "ion/MIRGraph.h"
-#include "ion/shared/CodeGenerator-shared-inl.h"
+#include "ion/PerfSpewer.h"
 #include "vm/Shape.h"
 
 #include "jsscriptinlines.h"
 
+#include "ion/shared/CodeGenerator-shared-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 using mozilla::FloorLog2;
 
 // shared
--- a/js/src/ion/arm/IonFrames-arm.h
+++ b/js/src/ion/arm/IonFrames-arm.h
@@ -3,17 +3,16 @@
  * 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 ion_arm_IonFrames_arm_h
 #define ion_arm_IonFrames_arm_h
 
 #include "ion/shared/IonFrames-shared.h"
-//#include "ion/arm/Assembler-arm.h"
 
 namespace js {
 namespace ion {
 
 class IonFramePrefix;
 // Layout of the frame prefix. This assumes the stack architecture grows down.
 // If this is ever not the case, we'll have to refactor.
 class IonCommonFrameLayout
--- a/js/src/ion/arm/Lowering-arm.cpp
+++ b/js/src/ion/arm/Lowering-arm.cpp
@@ -1,19 +1,20 @@
 /* -*- 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 "mozilla/MathAlgorithms.h"
 
-#include "ion/MIR.h"
+#include "ion/arm/Assembler-arm.h"
 #include "ion/Lowering.h"
-#include "ion/arm/Assembler-arm.h"
+#include "ion/MIR.h"
+
 #include "ion/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 using mozilla::FloorLog2;
 
 bool
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -1,18 +1,19 @@
 /* -*- 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 "ion/arm/MacroAssembler-arm.h"
+
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 
-#include "ion/arm/MacroAssembler-arm.h"
 #include "ion/BaselineFrame.h"
 #include "ion/MoveEmitter.h"
 
 using namespace js;
 using namespace ion;
 
 using mozilla::Abs;
 
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -4,21 +4,22 @@
  * 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 ion_arm_MacroAssembler_arm_h
 #define ion_arm_MacroAssembler_arm_h
 
 #include "mozilla/DebugOnly.h"
 
+#include "jsopcode.h"
+
 #include "ion/arm/Assembler-arm.h"
 #include "ion/IonCaches.h"
 #include "ion/IonFrames.h"
 #include "ion/MoveResolver.h"
-#include "jsopcode.h"
 
 using mozilla::DebugOnly;
 
 namespace js {
 namespace ion {
 
 static Register CallReg = ip;
 static const int defaultShift = 3;
--- a/js/src/ion/arm/MoveEmitter-arm.h
+++ b/js/src/ion/arm/MoveEmitter-arm.h
@@ -2,18 +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/. */
 
 #ifndef ion_arm_MoveEmitter_arm_h
 #define ion_arm_MoveEmitter_arm_h
 
+#include "ion/IonMacroAssembler.h"
 #include "ion/MoveResolver.h"
-#include "ion/IonMacroAssembler.h"
 
 namespace js {
 namespace ion {
 
 class CodeGenerator;
 
 class MoveEmitterARM
 {
--- a/js/src/ion/arm/Trampoline-arm.cpp
+++ b/js/src/ion/arm/Trampoline-arm.cpp
@@ -1,24 +1,25 @@
 /* -*- 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 "jscompartment.h"
+
 #include "assembler/assembler/MacroAssembler.h"
+#include "ion/arm/BaselineHelpers-arm.h"
+#include "ion/Bailouts.h"
+#include "ion/ExecutionModeInlines.h"
 #include "ion/IonCompartment.h"
+#include "ion/IonFrames.h"
 #include "ion/IonLinker.h"
-#include "ion/IonFrames.h"
 #include "ion/IonSpewer.h"
-#include "ion/Bailouts.h"
 #include "ion/VMFunctions.h"
-#include "ion/arm/BaselineHelpers-arm.h"
-#include "ion/ExecutionModeInlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 static void
 GenerateReturn(MacroAssembler &masm, int returnCode)
 {
     // Restore non-volatile registers
--- a/js/src/ion/shared/MacroAssembler-x86-shared.h
+++ b/js/src/ion/shared/MacroAssembler-x86-shared.h
@@ -9,19 +9,19 @@
 
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 
 #include "jsopcode.h"
 
 #include "ion/IonCaches.h"
 #include "ion/IonFrames.h"
-#ifdef JS_CPU_X86
+#if defined(JS_CPU_X86)
 # include "ion/x86/Assembler-x86.h"
-#elif JS_CPU_X64
+#elif defined(JS_CPU_X64)
 # include "ion/x64/Assembler-x64.h"
 #endif
 
 namespace js {
 namespace ion {
 
 class MacroAssemblerX86Shared : public Assembler
 {
--- a/js/src/ion/x64/Lowering-x64.cpp
+++ b/js/src/ion/x64/Lowering-x64.cpp
@@ -2,18 +2,19 @@
  * 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 "ion/x64/Lowering-x64.h"
 
 #include "ion/MIR.h"
+#include "ion/x64/Assembler-x64.h"
+
 #include "ion/shared/Lowering-shared-inl.h"
-#include "ion/x64/Assembler-x64.h"
 
 using namespace js;
 using namespace js::ion;
 
 bool
 LIRGeneratorX64::useBox(LInstruction *lir, size_t n, MDefinition *mir,
                         LUse::Policy policy, bool useAtStart)
 {
--- a/js/src/ion/x86/Lowering-x86.cpp
+++ b/js/src/ion/x86/Lowering-x86.cpp
@@ -2,18 +2,19 @@
  * 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 "ion/x86/Lowering-x86.h"
 
 #include "ion/MIR.h"
+#include "ion/x86/Assembler-x86.h"
+
 #include "ion/shared/Lowering-shared-inl.h"
-#include "ion/x86/Assembler-x86.h"
 
 using namespace js;
 using namespace js::ion;
 
 bool
 LIRGeneratorX86::useBox(LInstruction *lir, size_t n, MDefinition *mir,
                         LUse::Policy policy, bool useAtStart)
 {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug898047.js
@@ -0,0 +1,23 @@
+function g(aa) {
+    assertEq(aa, 123);
+}
+function f(x, yy) {
+    if (yy < 0) {
+	for (var j=0; j<100; j++) {}
+    }
+    var o = yy < 2000 ? o1 : o2;
+    o.fun.apply(22, arguments);
+}
+
+function test() {
+    o1 = {};
+    o1.fun = g;
+
+    o2 = {};
+    o2.x = 3;
+    o2.fun = g;
+
+    for (var i=0; i<3000; i++)
+	f(123, i);
+}
+test();
--- a/js/src/jsapi-tests/tests.cpp
+++ b/js/src/jsapi-tests/tests.cpp
@@ -72,16 +72,21 @@ JSObject * JSAPITest::createGlobal(JSPri
 }
 
 int main(int argc, char *argv[])
 {
     int total = 0;
     int failures = 0;
     const char *filter = (argc == 2) ? argv[1] : NULL;
 
+    if (!JS_Init()) {
+        printf("TEST-UNEXPECTED-FAIL | jsapi-tests | JS_Init() failed.\n");
+        return 1;
+    }
+
     for (JSAPITest *test = JSAPITest::list; test; test = test->next) {
         const char *name = test->name();
         if (filter && strstr(name, filter) == NULL)
             continue;
 
         total += 1;
 
         printf("%s\n", name);
@@ -100,15 +105,17 @@ int main(int argc, char *argv[])
                    (test->knownFail ? "TEST-KNOWN-FAIL" : "TEST-UNEXPECTED-FAIL"),
                    name, (int) messages.length(), messages.begin());
             if (!test->knownFail)
                 failures++;
         }
         test->uninit();
     }
 
+    JS_ShutDown();
+
     if (failures) {
         printf("\n%d unexpected failure%s.\n", failures, (failures == 1 ? "" : "s"));
         return 1;
     }
     printf("\nPassed: ran %d tests.\n", total);
     return 0;
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -7,38 +7,35 @@
 /*
  * JavaScript API.
  */
 
 #include "jsapi.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/PodOperations.h"
-#include "mozilla/ThreadLocal.h"
 
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jsclone.h"
 #include "jscntxt.h"
 #include "jsdate.h"
-#include "jsdtoa.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
-#include "jsnativestack.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "json.h"
 #include "jsprf.h"
 #include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
@@ -60,30 +57,27 @@
 #include "builtin/ParallelArray.h"
 #include "builtin/RegExp.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #include "ion/AsmJS.h"
-#ifdef JS_ION
-#include "ion/Ion.h"
-#endif
-#include "ion/PcScriptCache.h"
 #include "js/CharacterEncoding.h"
 #if ENABLE_INTL_API
 #include "unicode/uclean.h"
 #include "unicode/utypes.h"
-#endif
+#endif // ENABLE_INTL_API
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/ErrorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
+#include "vm/Runtime.h"
 #include "vm/Shape.h"
 #include "vm/StopIterationObject.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WeakMapObject.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 #include "yarr/BumpPointerAllocator.h"
@@ -627,547 +621,128 @@ JS_PUBLIC_API(JSBool)
 JS_IsBuiltinFunctionConstructor(JSFunction *fun)
 {
     return fun->isBuiltinFunctionConstructor();
 }
 
 /************************************************************************/
 
 /*
- * Has a new runtime ever been created?  This flag is used to control things
- * that should happen only once across all runtimes.
+ * SpiderMonkey's initialization status is tracked here, and it controls things
+ * that should happen only once across all runtimes.  It's an API requirement
+ * that JS_Init (and JS_ShutDown, if called) be called in a thread-aware
+ * manner, so this variable doesn't need to be atomic.
+ *
+ * The only reason at present for the restriction that you can't call
+ * JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ NowInit
+ * initialization code, which uses PR_CallOnce to initialize the PRMJ_Now
+ * subsystem.  (For reinitialization to be permitted, we'd need to "reset" the
+ * called-once status -- doable, but more trouble than it's worth now.)
+ * Initializing that subsystem from JS_Init eliminates the problem, but
+ * initialization can take a comparatively long time (15ms or so), so we
+ * really don't want to do it in JS_Init, and we really do want to do it only
+ * when PRMJ_Now is eventually called.
  */
-static JSBool js_NewRuntimeWasCalled = JS_FALSE;
-
-/*
- * Thread Local Storage slot for storing the runtime for a thread.
- */
-mozilla::ThreadLocal<PerThreadData *> js::TlsPerThreadData;
+enum InitState { Uninitialized, Running, ShutDown };
+static InitState jsInitState = Uninitialized;
+
+JS_PUBLIC_API(JSBool)
+JS_Init(void)
+{
+    MOZ_ASSERT(jsInitState == Uninitialized,
+               "must call JS_Init once before any JSAPI operation except "
+               "JS_SetICUMemoryFunctions");
+    MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
+               "how do we have live runtimes before JS_Init?");
+
+#ifdef DEBUG
+    // Assert that the numbers associated with the error names in js.msg are
+    // monotonically increasing.  It's not a compile-time check, but it's
+    // better than nothing.
+    int errorNumber = 0;
+#define MSG_DEF(name, number, count, exception, format)                       \
+    JS_ASSERT(name == errorNumber++);
+#include "js.msg"
+#undef MSG_DEF
+
+    // Assert that each message format has the correct number of braced
+    // parameters.
+#define MSG_DEF(name, number, count, exception, format)                       \
+    JS_BEGIN_MACRO                                                            \
+        unsigned numfmtspecs = 0;                                             \
+        for (const char *fmt = format; *fmt != '\0'; fmt++) {                 \
+            if (*fmt == '{' && isdigit(fmt[1]))                               \
+                ++numfmtspecs;                                                \
+        }                                                                     \
+        JS_ASSERT(count == numfmtspecs);                                      \
+    JS_END_MACRO;
+#include "js.msg"
+#undef MSG_DEF
+#endif /* DEBUG */
+
+    using js::TlsPerThreadData;
+    if (!TlsPerThreadData.initialized() && !TlsPerThreadData.init())
+        return false;
+
+#if defined(JS_ION)
+    if (!ion::InitializeIon())
+        return false;
+#endif
+
+    if (!ForkJoinSlice::InitializeTLS())
+        return false;
+
+#if ENABLE_INTL_API
+    UErrorCode err = U_ZERO_ERROR;
+    u_init(&err);
+    if (U_FAILURE(err))
+        return false;
+#endif // ENABLE_INTL_API
+
+    jsInitState = Running;
+    return true;
+}
+
+JS_PUBLIC_API(void)
+JS_ShutDown(void)
+{
+    MOZ_ASSERT(jsInitState == Running,
+               "JS_ShutDown must only be called after JS_Init and can't race with it");
+    MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
+               "forgot to destroy a runtime before shutting down");
+
+    PRMJ_NowShutdown();
+
+#if ENABLE_INTL_API
+    u_cleanup();
+#endif // ENABLE_INTL_API
+
+    jsInitState = ShutDown;
+}
 
 #ifdef DEBUG
 JS_FRIEND_API(bool)
 JS::isGCEnabled()
 {
     return !TlsPerThreadData.get()->suppressGC;
 }
 #else
 JS_FRIEND_API(bool) JS::isGCEnabled() { return true; }
 #endif
 
-static const JSSecurityCallbacks NullSecurityCallbacks = { };
-
-static bool
-JitSupportsFloatingPoint()
-{
-#if defined(JS_ION)
-    if (!JSC::MacroAssembler::supportsFloatingPoint())
-        return false;
-
-#if defined(JS_ION) && WTF_ARM_ARCH_VERSION == 6
-    if (!js::ion::hasVFP())
-        return false;
-#endif
-
-    return true;
-#else
-    return false;
-#endif
-}
-
-PerThreadData::PerThreadData(JSRuntime *runtime)
-  : PerThreadDataFriendFields(),
-    runtime_(runtime),
-    ionTop(NULL),
-    ionJSContext(NULL),
-    ionStackLimit(0),
-    activation_(NULL),
-    asmJSActivationStack_(NULL),
-    dtoaState(NULL),
-    suppressGC(0),
-    gcKeepAtoms(0),
-    activeCompilations(0)
-{}
-
-PerThreadData::~PerThreadData()
-{
-    if (dtoaState)
-        js_DestroyDtoaState(dtoaState);
-
-    if (isInList())
-        removeFromThreadList();
-}
-
-bool
-PerThreadData::init()
-{
-    dtoaState = js_NewDtoaState();
-    if (!dtoaState)
-        return false;
-
-    return true;
-}
-
-void
-PerThreadData::addToThreadList()
-{
-    // PerThreadData which are created/destroyed off the main thread do not
-    // show up in the runtime's thread list.
-    runtime_->assertValidThread();
-    runtime_->threadList.insertBack(this);
-}
-
-void
-PerThreadData::removeFromThreadList()
-{
-    runtime_->assertValidThread();
-    removeFrom(runtime_->threadList);
-}
-
-JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
-  : mainThread(this),
-    interrupt(0),
-#ifdef JS_THREADSAFE
-    operationCallbackLock(NULL),
-#ifdef DEBUG
-    operationCallbackOwner(NULL),
-#endif
-    exclusiveAccessLock(NULL),
-    exclusiveAccessOwner(NULL),
-    mainThreadHasExclusiveAccess(false),
-    numExclusiveThreads(0),
-#endif
-    atomsCompartment(NULL),
-    systemZone(NULL),
-    numCompartments(0),
-    localeCallbacks(NULL),
-    defaultLocale(NULL),
-    defaultVersion_(JSVERSION_DEFAULT),
-#ifdef JS_THREADSAFE
-    ownerThread_(NULL),
-#endif
-    tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    freeLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    execAlloc_(NULL),
-    bumpAlloc_(NULL),
-    ionRuntime_(NULL),
-    selfHostingGlobal_(NULL),
-    nativeStackBase(0),
-    nativeStackQuota(0),
-    interpreterFrames(NULL),
-    cxCallback(NULL),
-    destroyCompartmentCallback(NULL),
-    compartmentNameCallback(NULL),
-    activityCallback(NULL),
-    activityCallbackArg(NULL),
-#ifdef JS_THREADSAFE
-    requestDepth(0),
-# ifdef DEBUG
-    checkRequestDepth(0),
-# endif
-#endif
-    gcSystemAvailableChunkListHead(NULL),
-    gcUserAvailableChunkListHead(NULL),
-    gcBytes(0),
-    gcMaxBytes(0),
-    gcMaxMallocBytes(0),
-    gcNumArenasFreeCommitted(0),
-    gcMarker(this),
-    gcVerifyPreData(NULL),
-    gcVerifyPostData(NULL),
-    gcChunkAllocationSinceLastGC(false),
-    gcNextFullGCTime(0),
-    gcLastGCTime(0),
-    gcJitReleaseTime(0),
-    gcMode(JSGC_MODE_GLOBAL),
-    gcAllocationThreshold(30 * 1024 * 1024),
-    gcHighFrequencyGC(false),
-    gcHighFrequencyTimeThreshold(1000),
-    gcHighFrequencyLowLimitBytes(100 * 1024 * 1024),
-    gcHighFrequencyHighLimitBytes(500 * 1024 * 1024),
-    gcHighFrequencyHeapGrowthMax(3.0),
-    gcHighFrequencyHeapGrowthMin(1.5),
-    gcLowFrequencyHeapGrowth(1.5),
-    gcDynamicHeapGrowth(false),
-    gcDynamicMarkSlice(false),
-    gcDecommitThreshold(32 * 1024 * 1024),
-    gcShouldCleanUpEverything(false),
-    gcGrayBitsValid(false),
-    gcIsNeeded(0),
-    gcStats(thisFromCtor()),
-    gcNumber(0),
-    gcStartNumber(0),
-    gcIsFull(false),
-    gcTriggerReason(JS::gcreason::NO_REASON),
-    gcStrictCompartmentChecking(false),
-#ifdef DEBUG
-    gcDisableStrictProxyCheckingCount(0),
-#endif
-    gcIncrementalState(gc::NO_INCREMENTAL),
-    gcLastMarkSlice(false),
-    gcSweepOnBackgroundThread(false),
-    gcFoundBlackGrayEdges(false),
-    gcSweepingZones(NULL),
-    gcZoneGroupIndex(0),
-    gcZoneGroups(NULL),
-    gcCurrentZoneGroup(NULL),
-    gcSweepPhase(0),
-    gcSweepZone(NULL),
-    gcSweepKindIndex(0),
-    gcAbortSweepAfterCurrentGroup(false),
-    gcArenasAllocatedDuringSweep(NULL),
-#ifdef DEBUG
-    gcMarkingValidator(NULL),
-#endif
-    gcInterFrameGC(0),
-    gcSliceBudget(SliceBudget::Unlimited),
-    gcIncrementalEnabled(true),
-    gcGenerationalEnabled(true),
-    gcManipulatingDeadZones(false),
-    gcObjectsMarkedInDeadZones(0),
-    gcPoke(false),
-    heapState(Idle),
-#ifdef JSGC_GENERATIONAL
-    gcNursery(thisFromCtor()),
-    gcStoreBuffer(thisFromCtor(), gcNursery),
-#endif
-#ifdef JS_GC_ZEAL
-    gcZeal_(0),
-    gcZealFrequency(0),
-    gcNextScheduled(0),
-    gcDeterministicOnly(false),
-    gcIncrementalLimit(0),
-#endif
-    gcValidate(true),
-    gcFullCompartmentChecks(false),
-    gcCallback(NULL),
-    gcSliceCallback(NULL),
-    gcFinalizeCallback(NULL),
-    analysisPurgeCallback(NULL),
-    analysisPurgeTriggerBytes(0),
-    gcMallocBytes(0),
-    scriptAndCountsVector(NULL),
-    NaNValue(UndefinedValue()),
-    negativeInfinityValue(UndefinedValue()),
-    positiveInfinityValue(UndefinedValue()),
-    emptyString(NULL),
-    sourceHook(NULL),
-    debugMode(false),
-    spsProfiler(thisFromCtor()),
-    profilingScripts(false),
-    alwaysPreserveCode(false),
-    hadOutOfMemory(false),
-    data(NULL),
-    gcLock(NULL),
-    gcHelperThread(thisFromCtor()),
-#ifdef JS_THREADSAFE
-#ifdef JS_ION
-    workerThreadState(NULL),
-#endif
-    sourceCompressorThread(),
-#endif
-    defaultFreeOp_(thisFromCtor(), false),
-    debuggerMutations(0),
-    securityCallbacks(const_cast<JSSecurityCallbacks *>(&NullSecurityCallbacks)),
-    DOMcallbacks(NULL),
-    destroyPrincipals(NULL),
-    structuredCloneCallbacks(NULL),
-    telemetryCallback(NULL),
-    propertyRemovals(0),
-#if !ENABLE_INTL_API
-    thousandsSeparator(0),
-    decimalSeparator(0),
-    numGrouping(0),
-#endif
-    mathCache_(NULL),
-    trustedPrincipals_(NULL),
-    wrapObjectCallback(TransparentObjectWrapper),
-    sameCompartmentWrapObjectCallback(NULL),
-    preWrapObjectCallback(NULL),
-    preserveWrapperCallback(NULL),
-#ifdef DEBUG
-    noGCOrAllocationCheck(0),
-#endif
-    jitHardening(false),
-    jitSupportsFloatingPoint(false),
-    ionPcScriptCache(NULL),
-    threadPool(this),
-    ctypesActivityCallback(NULL),
-    parallelWarmup(0),
-    ionReturnOverride_(MagicValue(JS_ARG_POISON)),
-    useHelperThreads_(useHelperThreads),
-    requestedHelperThreadCount(-1)
-#ifdef DEBUG
-    , enteredPolicy(NULL)
-#endif
-{
-    /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
-    JS_INIT_CLIST(&onNewGlobalObjectWatchers);
-
-    PodZero(&debugHooks);
-    PodZero(&atomState);
-
-#if JS_STACK_GROWTH_DIRECTION > 0
-    nativeStackLimit = UINTPTR_MAX;
-#endif
-}
-
-bool
-JSRuntime::init(uint32_t maxbytes)
-{
-#ifdef JS_THREADSAFE
-    ownerThread_ = PR_GetCurrentThread();
-
-    operationCallbackLock = PR_NewLock();
-    if (!operationCallbackLock)
-        return false;
-
-    exclusiveAccessLock = PR_NewLock();
-    if (!exclusiveAccessLock)
-        return false;
-#endif
-
-    if (!mainThread.init())
-        return false;
-
-    js::TlsPerThreadData.set(&mainThread);
-    mainThread.addToThreadList();
-
-    if (!js_InitGC(this, maxbytes))
-        return false;
-
-    if (!gcMarker.init())
-        return false;
-
-    const char *size = getenv("JSGC_MARK_STACK_LIMIT");
-    if (size)
-        SetMarkStackLimit(this, atoi(size));
-
-    ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this));
-    if (!atomsZone)
-        return false;
-
-    JS::CompartmentOptions options;
-    ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options));
-    if (!atomsCompartment || !atomsCompartment->init(NULL))
-        return false;
-
-    zones.append(atomsZone.get());
-    atomsZone->compartments.append(atomsCompartment.get());
-
-    atomsCompartment->isSystem = true;
-    atomsZone->isSystem = true;
-    atomsZone->setGCLastBytes(8192, GC_NORMAL);
-
-    atomsZone.forget();
-    this->atomsCompartment = atomsCompartment.forget();
-
-    if (!InitAtoms(this))
-        return false;
-
-    if (!InitRuntimeNumberState(this))
-        return false;
-
-    dateTimeInfo.updateTimeZoneAdjustment();
-
-    if (!scriptDataTable.init())
-        return false;
-
-    if (!threadPool.init())
-        return false;
-
-#ifdef JS_THREADSAFE
-    if (useHelperThreads() && !sourceCompressorThread.init())
-        return false;
-#endif
-
-    if (!evalCache.init())
-        return false;
-
-    nativeStackBase = GetNativeStackBase();
-
-    jitSupportsFloatingPoint = JitSupportsFloatingPoint();
-    return true;
-}
-
-JSRuntime::~JSRuntime()
-{
-    mainThread.removeFromThreadList();
-
-#ifdef JS_THREADSAFE
-# ifdef JS_ION
-    if (workerThreadState)
-        js_delete(workerThreadState);
-# endif
-    sourceCompressorThread.finish();
-
-    clearOwnerThread();
-
-    JS_ASSERT(!operationCallbackOwner);
-    if (operationCallbackLock)
-        PR_DestroyLock(operationCallbackLock);
-
-    JS_ASSERT(!exclusiveAccessOwner);
-    if (exclusiveAccessLock)
-        PR_DestroyLock(exclusiveAccessLock);
-#endif
-
-    /*
-     * Even though all objects in the compartment are dead, we may have keep
-     * some filenames around because of gcKeepAtoms.
-     */
-    FreeScriptData(this);
-
-#ifdef DEBUG
-    /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
-    if (hasContexts()) {
-        unsigned cxcount = 0;
-        for (ContextIter acx(this); !acx.done(); acx.next()) {
-            fprintf(stderr,
-"JS API usage error: found live context at %p\n",
-                    (void *) acx.get());
-            cxcount++;
-        }
-        fprintf(stderr,
-"JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n",
-                cxcount, (cxcount == 1) ? "" : "s");
-    }
-#endif
-
-#if !ENABLE_INTL_API
-    FinishRuntimeNumberState(this);
-#endif
-    FinishAtoms(this);
-
-    js_FinishGC(this);
-#ifdef JS_THREADSAFE
-    if (gcLock)
-        PR_DestroyLock(gcLock);
-#endif
-
-    js_delete(bumpAlloc_);
-    js_delete(mathCache_);
-#ifdef JS_ION
-    js_delete(ionRuntime_);
-#endif
-    js_delete(execAlloc_);  /* Delete after ionRuntime_. */
-
-    if (ionPcScriptCache)
-        js_delete(ionPcScriptCache);
-
-#ifdef JSGC_GENERATIONAL
-    gcStoreBuffer.disable();
-    gcNursery.disable();
-#endif
-}
-
-#ifdef JS_THREADSAFE
-void
-JSRuntime::setOwnerThread()
-{
-    JS_ASSERT(ownerThread_ == (void *)0xc1ea12);  /* "clear" */
-    JS_ASSERT(requestDepth == 0);
-    JS_ASSERT(js_NewRuntimeWasCalled);
-    JS_ASSERT(js::TlsPerThreadData.get() == NULL);
-    ownerThread_ = PR_GetCurrentThread();
-    js::TlsPerThreadData.set(&mainThread);
-    nativeStackBase = GetNativeStackBase();
-    if (nativeStackQuota)
-        JS_SetNativeStackQuota(this, nativeStackQuota);
-#ifdef XP_MACOSX
-    asmJSMachExceptionHandler.setCurrentThread();
-#endif
-}
-
-void
-JSRuntime::clearOwnerThread()
-{
-    assertValidThread();
-    JS_ASSERT(requestDepth == 0);
-    JS_ASSERT(js_NewRuntimeWasCalled);
-    ownerThread_ = (void *)0xc1ea12;  /* "clear" */
-    js::TlsPerThreadData.set(NULL);
-    nativeStackBase = 0;
-#if JS_STACK_GROWTH_DIRECTION > 0
-    mainThread.nativeStackLimit = UINTPTR_MAX;
-#else
-    mainThread.nativeStackLimit = 0;
-#endif
-#ifdef XP_MACOSX
-    asmJSMachExceptionHandler.clearCurrentThread();
-#endif
-}
-
-JS_FRIEND_API(void)
-JSRuntime::abortIfWrongThread() const
-{
-    if (ownerThread_ != PR_GetCurrentThread())
-        MOZ_CRASH();
-    if (!js::TlsPerThreadData.get()->associatedWith(this))
-        MOZ_CRASH();
-}
-
-#ifdef DEBUG
-JS_FRIEND_API(void)
-JSRuntime::assertValidThread() const
-{
-    JS_ASSERT(ownerThread_ == PR_GetCurrentThread());
-    JS_ASSERT(js::TlsPerThreadData.get()->associatedWith(this));
-}
-#endif  /* DEBUG */
-#endif  /* JS_THREADSAFE */
-
 JS_PUBLIC_API(JSRuntime *)
 JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads)
 {
-    if (!js_NewRuntimeWasCalled) {
-#ifdef DEBUG
-        /*
-         * This code asserts that the numbers associated with the error names
-         * in jsmsg.def are monotonically increasing.  It uses values for the
-         * error names enumerated in jscntxt.c.  It's not a compile-time check
-         * but it's better than nothing.
-         */
-        int errorNumber = 0;
-#define MSG_DEF(name, number, count, exception, format)                       \
-    JS_ASSERT(name == errorNumber++);
-#include "js.msg"
-#undef MSG_DEF
-
-#define MSG_DEF(name, number, count, exception, format)                       \
-    JS_BEGIN_MACRO                                                            \
-        unsigned numfmtspecs = 0;                                                \
-        const char *fmt;                                                      \
-        for (fmt = format; *fmt != '\0'; fmt++) {                             \
-            if (*fmt == '{' && isdigit(fmt[1]))                               \
-                ++numfmtspecs;                                                \
-        }                                                                     \
-        JS_ASSERT(count == numfmtspecs);                                      \
-    JS_END_MACRO;
-#include "js.msg"
-#undef MSG_DEF
-#endif /* DEBUG */
-
-        if (!js::TlsPerThreadData.init())
-            return NULL;
-
-        js_NewRuntimeWasCalled = JS_TRUE;
-    }
+    MOZ_ASSERT(jsInitState == Running,
+               "must call JS_Init prior to creating any JSRuntimes");
 
     JSRuntime *rt = js_new<JSRuntime>(useHelperThreads);
     if (!rt)
         return NULL;
 
-#if defined(JS_ION)
-    if (!ion::InitializeIon())
-        return NULL;
-#endif
-
-    if (!ForkJoinSlice::InitializeTLS())
-        return NULL;
-
     if (!rt->init(maxbytes)) {
         JS_DestroyRuntime(rt);
         return NULL;
     }
 
     return rt;
 }
 
@@ -1176,31 +751,29 @@ JS_DestroyRuntime(JSRuntime *rt)
 {
     js_free(rt->defaultLocale);
     js_delete(rt);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn)
 {
+    MOZ_ASSERT(jsInitState == Uninitialized,
+               "must call JS_SetICUMemoryFunctions before any other JSAPI "
+               "operation (including JS_Init)");
+
 #if ENABLE_INTL_API
     UErrorCode status = U_ZERO_ERROR;
     u_setMemoryFunctions(/* context = */ NULL, allocFn, reallocFn, freeFn, &status);
     return U_SUCCESS(status);
 #else
     return true;
 #endif
 }
 
-JS_PUBLIC_API(void)
-JS_ShutDown(void)
-{
-    PRMJ_NowShutdown();
-}
-
 JS_PUBLIC_API(void *)
 JS_GetRuntimePrivate(JSRuntime *rt)
 {
     return rt->data;
 }
 
 JS_PUBLIC_API(void)
 JS_SetRuntimePrivate(JSRuntime *rt, void *data)
@@ -7006,25 +6579,27 @@ JS_GetCurrentThread()
     return 0;
 #endif
 }
 
 extern JS_PUBLIC_API(void)
 JS_ClearRuntimeThread(JSRuntime *rt)
 {
     AssertHeapIsIdle(rt);
+    JS_ASSERT(jsInitState == Running);
 #ifdef JS_THREADSAFE
     rt->clearOwnerThread();
 #endif
 }
 
 extern JS_PUBLIC_API(void)
 JS_SetRuntimeThread(JSRuntime *rt)
 {
     AssertHeapIsIdle(rt);
+    JS_ASSERT(jsInitState == Running);
 #ifdef JS_THREADSAFE
     rt->setOwnerThread();
 #endif
 }
 
 extern JS_NEVER_INLINE JS_PUBLIC_API(void)
 JS_AbortIfWrongThread(JSRuntime *rt)
 {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1765,16 +1765,52 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
  */
 
 typedef enum JSUseHelperThreads
 {
     JS_NO_HELPER_THREADS,
     JS_USE_HELPER_THREADS
 } JSUseHelperThreads;
 
+/**
+ * Initialize SpiderMonkey, returning true only if initialization succeeded.
+ * Once this method has succeeded, it is safe to call JS_NewRuntime and other
+ * JSAPI methods.
+ *
+ * This method must be called before any other JSAPI method is used on any
+ * thread.  Once it has been used, it is safe to call any JSAPI method, and it
+ * remains safe to do so until JS_ShutDown is correctly called.
+ *
+ * It is currently not possible to initialize SpiderMonkey multiple times (that
+ * is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
+ * again).  This restriction may eventually be lifted.
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_Init(void);
+
+/**
+ * Destroy free-standing resources allocated by SpiderMonkey, not associated
+ * with any runtime, context, or other structure.
+ *
+ * This method should be called after all other JSAPI data has been properly
+ * cleaned up: every new runtime must have been destroyed, every new context
+ * must have been destroyed, and so on.  Calling this method before all other
+ * resources have been destroyed has undefined behavior.
+ *
+ * Failure to call this method, at present, has no adverse effects other than
+ * leaking memory.  This may not always be the case; it's recommended that all
+ * embedders call this method when all other JSAPI operations have completed.
+ *
+ * It is currently not possible to initialize SpiderMonkey multiple times (that
+ * is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
+ * again).  This restriction may eventually be lifted.
+ */
+extern JS_PUBLIC_API(void)
+JS_ShutDown(void);
+
 extern JS_PUBLIC_API(JSRuntime *)
 JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads);
 
 extern JS_PUBLIC_API(void)
 JS_DestroyRuntime(JSRuntime *rt);
 
 // These are equivalent to ICU's |UMemAllocFn|, |UMemReallocFn|, and
 // |UMemFreeFn| types.  The first argument (called |context| in the ICU docs)
@@ -1783,19 +1819,16 @@ typedef void *(*JS_ICUAllocFn)(const voi
 typedef void *(*JS_ICUReallocFn)(const void *, void *p, size_t size);
 typedef void (*JS_ICUFreeFn)(const void *, void *p);
 
 // This function can be used to track memory used by ICU.
 // Do not use it unless you know what you are doing!
 extern JS_PUBLIC_API(bool)
 JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn);
 
-extern JS_PUBLIC_API(void)
-JS_ShutDown(void);
-
 JS_PUBLIC_API(void *)
 JS_GetRuntimePrivate(JSRuntime *rt);
 
 extern JS_PUBLIC_API(JSRuntime *)
 JS_GetRuntime(JSContext *cx);
 
 JS_PUBLIC_API(void)
 JS_SetRuntimePrivate(JSRuntime *rt, void *data);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -199,18 +199,16 @@ JS_DefineFunctionsWithHelp(JSContext *cx
 typedef bool (* JS_SourceHook)(JSContext *cx, JS::Handle<JSScript*> script,
                                jschar **src, uint32_t *length);
 
 extern JS_FRIEND_API(void)
 JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook);
 
 namespace js {
 
-extern mozilla::ThreadLocal<PerThreadData *> TlsPerThreadData;
-
 inline JSRuntime *
 GetRuntime(const JSContext *cx)
 {
     return ContextFriendFields::get(cx)->runtime_;
 }
 
 inline JSCompartment *
 GetContextCompartment(const JSContext *cx)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5417,16 +5417,20 @@ main(int argc, char **argv, char **envp)
         OOM_printAllocationCount = true;
 
 #if defined(JS_CPU_X86)
     if (op.getBoolOption("no-fpu"))
         JSC::MacroAssembler::SetFloatingPointDisabled();
 #endif
 #endif
 
+    // Start the engine.
+    if (!JS_Init())
+        return 1;
+
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
     rt = JS_NewRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS);
     if (!rt)
         return 1;
     gTimeoutFunc = NullValue();
     if (!JS_AddNamedValueRootRT(rt, &gTimeoutFunc, "gTimeoutFunc"))
         return 1;
 
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -1707,19 +1707,19 @@ ForkJoinSlice::check()
     else
         return true;
 }
 
 bool
 ForkJoinSlice::InitializeTLS()
 {
     if (!TLSInitialized) {
+        if (PR_NewThreadPrivateIndex(&ThreadPrivateIndex, NULL) != PR_SUCCESS)
+            return false;
         TLSInitialized = true;
-        PRStatus status = PR_NewThreadPrivateIndex(&ThreadPrivateIndex, NULL);
-        return status == PR_SUCCESS;
     }
     return true;
 }
 
 void
 ForkJoinSlice::requestGC(JS::gcreason::Reason reason)
 {
     shared->requestGC(reason);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -1,38 +1,518 @@
 /* -*- 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 "vm/Runtime-inl.h"
 
+#include "mozilla/Atomics.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/ThreadLocal.h"
 #include "mozilla/Util.h"
 
 #include <locale.h>
 #include <string.h>
 
 #include "jsatom.h"
+#include "jsdtoa.h"
 #include "jsgc.h"
 #include "jsmath.h"
+#include "jsnativestack.h"
 #include "jsobj.h"
 #include "jsscript.h"
+#include "jswrapper.h"
 
 #include "js/MemoryMetrics.h"
+#include "ion/IonCompartment.h"
+#include "ion/PcScriptCache.h"
 #include "yarr/BumpPointerAllocator.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
+using mozilla::Atomic;
+using mozilla::DebugOnly;
 using mozilla::PodZero;
+using mozilla::ThreadLocal;
+
+/* static */ ThreadLocal<PerThreadData*> js::TlsPerThreadData;
+
+/* static */ Atomic<size_t> JSRuntime::liveRuntimesCount;
+
+const JSSecurityCallbacks js::NullSecurityCallbacks = { };
+
+PerThreadData::PerThreadData(JSRuntime *runtime)
+  : PerThreadDataFriendFields(),
+    runtime_(runtime),
+    ionTop(NULL),
+    ionJSContext(NULL),
+    ionStackLimit(0),
+    activation_(NULL),
+    asmJSActivationStack_(NULL),
+    dtoaState(NULL),
+    suppressGC(0),
+    gcKeepAtoms(0),
+    activeCompilations(0)
+{}
+
+PerThreadData::~PerThreadData()
+{
+    if (dtoaState)
+        js_DestroyDtoaState(dtoaState);
+
+    if (isInList())
+        removeFromThreadList();
+}
+
+bool
+PerThreadData::init()
+{
+    dtoaState = js_NewDtoaState();
+    if (!dtoaState)
+        return false;
+
+    return true;
+}
+
+void
+PerThreadData::addToThreadList()
+{
+    // PerThreadData which are created/destroyed off the main thread do not
+    // show up in the runtime's thread list.
+    runtime_->assertValidThread();
+    runtime_->threadList.insertBack(this);
+}
+
+void
+PerThreadData::removeFromThreadList()
+{
+    runtime_->assertValidThread();
+    removeFrom(runtime_->threadList);
+}
+
+JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
+  : mainThread(this),
+    interrupt(0),
+#ifdef JS_THREADSAFE
+    operationCallbackLock(NULL),
+#ifdef DEBUG
+    operationCallbackOwner(NULL),
+#endif
+    exclusiveAccessLock(NULL),
+    exclusiveAccessOwner(NULL),
+    mainThreadHasExclusiveAccess(false),
+    numExclusiveThreads(0),
+#endif
+    atomsCompartment(NULL),
+    systemZone(NULL),
+    numCompartments(0),
+    localeCallbacks(NULL),
+    defaultLocale(NULL),
+    defaultVersion_(JSVERSION_DEFAULT),
+#ifdef JS_THREADSAFE
+    ownerThread_(NULL),
+#endif
+    tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    freeLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    execAlloc_(NULL),
+    bumpAlloc_(NULL),
+    ionRuntime_(NULL),
+    selfHostingGlobal_(NULL),
+    nativeStackBase(0),
+    nativeStackQuota(0),
+    interpreterFrames(NULL),
+    cxCallback(NULL),
+    destroyCompartmentCallback(NULL),
+    compartmentNameCallback(NULL),
+    activityCallback(NULL),
+    activityCallbackArg(NULL),
+#ifdef JS_THREADSAFE
+    requestDepth(0),
+# ifdef DEBUG
+    checkRequestDepth(0),
+# endif
+#endif
+    gcSystemAvailableChunkListHead(NULL),
+    gcUserAvailableChunkListHead(NULL),
+    gcBytes(0),
+    gcMaxBytes(0),
+    gcMaxMallocBytes(0),
+    gcNumArenasFreeCommitted(0),
+    gcMarker(this),
+    gcVerifyPreData(NULL),
+    gcVerifyPostData(NULL),
+    gcChunkAllocationSinceLastGC(false),
+    gcNextFullGCTime(0),
+    gcLastGCTime(0),
+    gcJitReleaseTime(0),
+    gcMode(JSGC_MODE_GLOBAL),
+    gcAllocationThreshold(30 * 1024 * 1024),
+    gcHighFrequencyGC(false),
+    gcHighFrequencyTimeThreshold(1000),
+    gcHighFrequencyLowLimitBytes(100 * 1024 * 1024),
+    gcHighFrequencyHighLimitBytes(500 * 1024 * 1024),
+    gcHighFrequencyHeapGrowthMax(3.0),
+    gcHighFrequencyHeapGrowthMin(1.5),
+    gcLowFrequencyHeapGrowth(1.5),
+    gcDynamicHeapGrowth(false),
+    gcDynamicMarkSlice(false),
+    gcDecommitThreshold(32 * 1024 * 1024),
+    gcShouldCleanUpEverything(false),
+    gcGrayBitsValid(false),
+    gcIsNeeded(0),
+    gcStats(thisFromCtor()),
+    gcNumber(0),
+    gcStartNumber(0),
+    gcIsFull(false),
+    gcTriggerReason(JS::gcreason::NO_REASON),
+    gcStrictCompartmentChecking(false),
+#ifdef DEBUG
+    gcDisableStrictProxyCheckingCount(0),
+#endif
+    gcIncrementalState(gc::NO_INCREMENTAL),
+    gcLastMarkSlice(false),
+    gcSweepOnBackgroundThread(false),
+    gcFoundBlackGrayEdges(false),
+    gcSweepingZones(NULL),
+    gcZoneGroupIndex(0),
+    gcZoneGroups(NULL),
+    gcCurrentZoneGroup(NULL),
+    gcSweepPhase(0),
+    gcSweepZone(NULL),
+    gcSweepKindIndex(0),
+    gcAbortSweepAfterCurrentGroup(false),
+    gcArenasAllocatedDuringSweep(NULL),
+#ifdef DEBUG
+    gcMarkingValidator(NULL),
+#endif
+    gcInterFrameGC(0),
+    gcSliceBudget(SliceBudget::Unlimited),
+    gcIncrementalEnabled(true),
+    gcGenerationalEnabled(true),
+    gcManipulatingDeadZones(false),
+    gcObjectsMarkedInDeadZones(0),
+    gcPoke(false),
+    heapState(Idle),
+#ifdef JSGC_GENERATIONAL
+    gcNursery(thisFromCtor()),
+    gcStoreBuffer(thisFromCtor(), gcNursery),
+#endif
+#ifdef JS_GC_ZEAL
+    gcZeal_(0),
+    gcZealFrequency(0),
+    gcNextScheduled(0),
+    gcDeterministicOnly(false),
+    gcIncrementalLimit(0),
+#endif
+    gcValidate(true),
+    gcFullCompartmentChecks(false),
+    gcCallback(NULL),
+    gcSliceCallback(NULL),
+    gcFinalizeCallback(NULL),
+    analysisPurgeCallback(NULL),
+    analysisPurgeTriggerBytes(0),
+    gcMallocBytes(0),
+    scriptAndCountsVector(NULL),
+    NaNValue(UndefinedValue()),
+    negativeInfinityValue(UndefinedValue()),
+    positiveInfinityValue(UndefinedValue()),
+    emptyString(NULL),
+    sourceHook(NULL),
+    debugMode(false),
+    spsProfiler(thisFromCtor()),
+    profilingScripts(false),
+    alwaysPreserveCode(false),
+    hadOutOfMemory(false),
+    data(NULL),
+    gcLock(NULL),
+    gcHelperThread(thisFromCtor()),
+#ifdef JS_THREADSAFE
+#ifdef JS_ION
+    workerThreadState(NULL),
+#endif
+    sourceCompressorThread(),
+#endif
+    defaultFreeOp_(thisFromCtor(), false),
+    debuggerMutations(0),
+    securityCallbacks(const_cast<JSSecurityCallbacks *>(&NullSecurityCallbacks)),
+    DOMcallbacks(NULL),
+    destroyPrincipals(NULL),
+    structuredCloneCallbacks(NULL),
+    telemetryCallback(NULL),
+    propertyRemovals(0),
+#if !ENABLE_INTL_API
+    thousandsSeparator(0),
+    decimalSeparator(0),
+    numGrouping(0),
+#endif
+    mathCache_(NULL),
+    trustedPrincipals_(NULL),
+    wrapObjectCallback(TransparentObjectWrapper),
+    sameCompartmentWrapObjectCallback(NULL),
+    preWrapObjectCallback(NULL),
+    preserveWrapperCallback(NULL),
+#ifdef DEBUG
+    noGCOrAllocationCheck(0),
+#endif
+    jitHardening(false),
+    jitSupportsFloatingPoint(false),
+    ionPcScriptCache(NULL),
+    threadPool(this),
+    ctypesActivityCallback(NULL),
+    parallelWarmup(0),
+    ionReturnOverride_(MagicValue(JS_ARG_POISON)),
+    useHelperThreads_(useHelperThreads),
+    requestedHelperThreadCount(-1)
+#ifdef DEBUG
+    , enteredPolicy(NULL)
+#endif
+{
+    liveRuntimesCount++;
+
+    /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
+    JS_INIT_CLIST(&onNewGlobalObjectWatchers);
+
+    PodZero(&debugHooks);
+    PodZero(&atomState);
+
+#if JS_STACK_GROWTH_DIRECTION > 0
+    nativeStackLimit = UINTPTR_MAX;
+#endif
+}
+
+static bool
+JitSupportsFloatingPoint()
+{
+#if defined(JS_ION)
+    if (!JSC::MacroAssembler::supportsFloatingPoint())
+        return false;
+
+#if defined(JS_ION) && WTF_ARM_ARCH_VERSION == 6
+    if (!js::ion::hasVFP())
+        return false;
+#endif
+
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool
+JSRuntime::init(uint32_t maxbytes)
+{
+#ifdef JS_THREADSAFE
+    ownerThread_ = PR_GetCurrentThread();
+
+    operationCallbackLock = PR_NewLock();
+    if (!operationCallbackLock)
+        return false;
+
+    exclusiveAccessLock = PR_NewLock();
+    if (!exclusiveAccessLock)
+        return false;
+#endif
+
+    if (!mainThread.init())
+        return false;
+
+    js::TlsPerThreadData.set(&mainThread);
+    mainThread.addToThreadList();
+
+    if (!js_InitGC(this, maxbytes))
+        return false;
+
+    if (!gcMarker.init())
+        return false;
+
+    const char *size = getenv("JSGC_MARK_STACK_LIMIT");
+    if (size)
+        SetMarkStackLimit(this, atoi(size));
+
+    ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this));
+    if (!atomsZone)
+        return false;
+
+    JS::CompartmentOptions options;
+    ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options));
+    if (!atomsCompartment || !atomsCompartment->init(NULL))
+        return false;
+
+    zones.append(atomsZone.get());
+    atomsZone->compartments.append(atomsCompartment.get());
+
+    atomsCompartment->isSystem = true;
+    atomsZone->isSystem = true;
+    atomsZone->setGCLastBytes(8192, GC_NORMAL);
+
+    atomsZone.forget();
+    this->atomsCompartment = atomsCompartment.forget();
+
+    if (!InitAtoms(this))
+        return false;
+
+    if (!InitRuntimeNumberState(this))
+        return false;
+
+    dateTimeInfo.updateTimeZoneAdjustment();
+
+    if (!scriptDataTable.init())
+        return false;
+
+    if (!threadPool.init())
+        return false;
+
+#ifdef JS_THREADSAFE
+    if (useHelperThreads() && !sourceCompressorThread.init())
+        return false;
+#endif
+
+    if (!evalCache.init())
+        return false;
+
+    nativeStackBase = GetNativeStackBase();
+
+    jitSupportsFloatingPoint = JitSupportsFloatingPoint();
+    return true;
+}
+
+JSRuntime::~JSRuntime()
+{
+    mainThread.removeFromThreadList();
+
+#ifdef JS_THREADSAFE
+# ifdef JS_ION
+    if (workerThreadState)
+        js_delete(workerThreadState);
+# endif
+    sourceCompressorThread.finish();
+
+    clearOwnerThread();
+
+    JS_ASSERT(!operationCallbackOwner);
+    if (operationCallbackLock)
+        PR_DestroyLock(operationCallbackLock);
+
+    JS_ASSERT(!exclusiveAccessOwner);
+    if (exclusiveAccessLock)
+        PR_DestroyLock(exclusiveAccessLock);
+#endif
+
+    /*
+     * Even though all objects in the compartment are dead, we may have keep
+     * some filenames around because of gcKeepAtoms.
+     */
+    FreeScriptData(this);
+
+#ifdef DEBUG
+    /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
+    if (hasContexts()) {
+        unsigned cxcount = 0;
+        for (ContextIter acx(this); !acx.done(); acx.next()) {
+            fprintf(stderr,
+"JS API usage error: found live context at %p\n",
+                    (void *) acx.get());
+            cxcount++;
+        }
+        fprintf(stderr,
+"JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n",
+                cxcount, (cxcount == 1) ? "" : "s");
+    }
+#endif
+
+#if !ENABLE_INTL_API
+    FinishRuntimeNumberState(this);
+#endif
+    FinishAtoms(this);
+
+    js_FinishGC(this);
+#ifdef JS_THREADSAFE
+    if (gcLock)
+        PR_DestroyLock(gcLock);
+#endif
+
+    js_delete(bumpAlloc_);
+    js_delete(mathCache_);
+#ifdef JS_ION
+    js_delete(ionRuntime_);
+#endif
+    js_delete(execAlloc_);  /* Delete after ionRuntime_. */
+
+    if (ionPcScriptCache)
+        js_delete(ionPcScriptCache);
+
+#ifdef JSGC_GENERATIONAL
+    gcStoreBuffer.disable();
+    gcNursery.disable();
+#endif
+
+    DebugOnly<size_t> oldCount = liveRuntimesCount--;
+    JS_ASSERT(oldCount > 0);
+}
+
+#ifdef JS_THREADSAFE
+void
+JSRuntime::setOwnerThread()
+{
+    JS_ASSERT(ownerThread_ == (void *)0xc1ea12);  /* "clear" */
+    JS_ASSERT(requestDepth == 0);
+    JS_ASSERT(js::TlsPerThreadData.get() == NULL);
+    ownerThread_ = PR_GetCurrentThread();
+    js::TlsPerThreadData.set(&mainThread);
+    nativeStackBase = GetNativeStackBase();
+    if (nativeStackQuota)
+        JS_SetNativeStackQuota(this, nativeStackQuota);
+#ifdef XP_MACOSX
+    asmJSMachExceptionHandler.setCurrentThread();
+#endif
+}
+
+void
+JSRuntime::clearOwnerThread()
+{
+    assertValidThread();
+    JS_ASSERT(requestDepth == 0);
+    ownerThread_ = (void *)0xc1ea12;  /* "clear" */
+    js::TlsPerThreadData.set(NULL);
+    nativeStackBase = 0;
+#if JS_STACK_GROWTH_DIRECTION > 0
+    mainThread.nativeStackLimit = UINTPTR_MAX;
+#else
+    mainThread.nativeStackLimit = 0;
+#endif
+#ifdef XP_MACOSX
+    asmJSMachExceptionHandler.clearCurrentThread();
+#endif
+}
+
+JS_FRIEND_API(void)
+JSRuntime::abortIfWrongThread() const
+{
+    if (ownerThread_ != PR_GetCurrentThread())
+        MOZ_CRASH();
+    if (!js::TlsPerThreadData.get()->associatedWith(this))
+        MOZ_CRASH();
+}
+
+#ifdef DEBUG
+JS_FRIEND_API(void)
+JSRuntime::assertValidThread() const
+{
+    JS_ASSERT(ownerThread_ == PR_GetCurrentThread());
+    JS_ASSERT(js::TlsPerThreadData.get()->associatedWith(this));
+}
+#endif  /* DEBUG */
+#endif  /* JS_THREADSAFE */
 
 void
 NewObjectCache::clearNurseryObjects(JSRuntime *rt)
 {
     for (unsigned i = 0; i < mozilla::ArrayLength(entries); ++i) {
         Entry &e = entries[i];
         JSObject *obj = reinterpret_cast<JSObject *>(&e.templateObject);
         if (IsInsideNursery(rt, e.key) ||
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -2,20 +2,22 @@
  * 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/. */
 
 #ifndef vm_Runtime_h
 #define vm_Runtime_h
 
+#include "mozilla/Atomics.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TemplateLib.h"
+#include "mozilla/ThreadLocal.h"
 
 #include <setjmp.h>
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jsclist.h"
 #include "jsfriendapi.h"
@@ -37,16 +39,23 @@
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #pragma warning(push)
 #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */
 #endif
 
+namespace js {
+
+/* Thread Local Storage slot for storing the runtime for a thread. */
+extern mozilla::ThreadLocal<PerThreadData*> TlsPerThreadData;
+
+} // namespace js
+
 struct DtoaState;
 
 extern void
 js_ReportOutOfMemory(js::ThreadSafeContext *cx);
 
 extern void
 js_ReportAllocationOverflow(js::ThreadSafeContext *cx);
 
@@ -1416,17 +1425,23 @@ struct JSRuntime : public JS::shadow::Ru
     //
     // To allow the GetPropertyCacheT optimization, we allow the ability for
     // GetPropertyCache to override the return value at the top of the stack - the
     // value that will be temporarily corrupt. This special override value is set
     // only in callVM() targets that are about to return *and* have invalidated
     // their callee.
     js::Value            ionReturnOverride_;
 
+    static mozilla::Atomic<size_t> liveRuntimesCount;
+
   public:
+    static bool hasLiveRuntimes() {
+        return liveRuntimesCount > 0;
+    }
+
     bool hasIonReturnOverride() const {
         return !ionReturnOverride_.isMagic();
     }
     js::Value takeIonReturnOverride() {
         js::Value v = ionReturnOverride_;
         ionReturnOverride_ = js::MagicValue(JS_ARG_POISON);
         return v;
     }
@@ -1783,16 +1798,18 @@ public:
         return get();
     }
 
     PerThreadData *operator ->() const {
         return get();
     }
 };
 
+extern const JSSecurityCallbacks NullSecurityCallbacks;
+
 } /* namespace js */
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
 
 #endif /* vm_Runtime_h */
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1414,17 +1414,16 @@ XPCJSRuntime::~XPCJSRuntime()
 #ifdef XPC_DUMP_AT_SHUTDOWN
         uint32_t count = mDetachedWrappedNativeProtoMap->Count();
         if (count)
             printf("deleting XPCJSRuntime with %d live detached XPCWrappedNativeProto\n", (int)count);
 #endif
         delete mDetachedWrappedNativeProtoMap;
     }
 
-    JS_ShutDown();
 #ifdef MOZ_ENABLE_PROFILER_SPS
     // Tell the profiler that the runtime is gone
     if (PseudoStack *stack = mozilla_get_pseudo_stack())
         stack->sampleRuntime(nullptr);
 #endif
 
 #ifdef DEBUG
     for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
@@ -2681,66 +2680,16 @@ SourceHook(JSContext *cx, JS::Handle<JSS
   if (NS_FAILED(rv)) {
     xpc::Throw(cx, rv);
     return false;
   }
 
   return true;
 }
 
-// |sSizeOfICU| can be accessed by multiple JSRuntimes, so it must be
-// thread-safe.
-static Atomic<size_t> sSizeOfICU;
-
-static int64_t
-GetICUSize()
-{
-    return sSizeOfICU;
-}
-
-NS_MEMORY_REPORTER_IMPLEMENT(ICU,
-    "explicit/icu",
-    KIND_HEAP,
-    UNITS_BYTES,
-    GetICUSize,
-    "Memory used by ICU, a Unicode and globalization support library."
-)
-
-NS_MEMORY_REPORTER_MALLOC_SIZEOF_ON_ALLOC_FUN(ICUMallocSizeOfOnAlloc)
-NS_MEMORY_REPORTER_MALLOC_SIZEOF_ON_FREE_FUN(ICUMallocSizeOfOnFree)
-
-static void *
-ICUAlloc(const void *, size_t size)
-{
-    void *p = malloc(size);
-    sSizeOfICU += ICUMallocSizeOfOnAlloc(p);
-    return p;
-}
-
-static void *
-ICURealloc(const void *, void *p, size_t size)
-{
-    sSizeOfICU -= ICUMallocSizeOfOnFree(p);
-    void *pnew = realloc(p, size);
-    if (pnew) {
-        sSizeOfICU += ICUMallocSizeOfOnAlloc(pnew);
-    } else {
-        // realloc failed;  undo the decrement from above
-        sSizeOfICU += ICUMallocSizeOfOnAlloc(p);
-    }
-    return pnew;
-}
-
-static void
-ICUFree(const void *, void *p)
-{
-    sSizeOfICU -= ICUMallocSizeOfOnFree(p);
-    free(p);
-}
-
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
    : CycleCollectedJSRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS, true),
    mJSContextStack(new XPCJSContextStack()),
    mCallContext(nullptr),
    mAutoRoots(nullptr),
    mResolveName(JSID_VOID),
    mResolvingWrapper(nullptr),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
@@ -2833,30 +2782,26 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
     ///
     // Note we do have to retain the source code in memory for scripts compiled in
     // compileAndGo mode and compiled function bodies (from
     // JS_CompileFunction*). In practice, this means content scripts and event
     // handlers.
     JS_SetSourceHook(runtime, SourceHook);
 
-    if (!JS_SetICUMemoryFunctions(ICUAlloc, ICURealloc, ICUFree))
-        NS_RUNTIMEABORT("JS_SetICUMemoryFunctions failed.");
-
     // Set up locale information and callbacks for the newly-created runtime so
     // that the various toLocaleString() methods, localeCompare(), and other
     // internationalization APIs work as desired.
     if (!xpc_LocalizeRuntime(runtime))
         NS_RUNTIMEABORT("xpc_LocalizeRuntime failed.");
 
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(JSMainRuntimeTemporaryPeak));
-    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(ICU));
     NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter);
 
     // Install a JavaScript 'debugger' keyword handler in debug builds only
 #ifdef DEBUG
     if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler)
         xpc_InstallJSDebuggerKeywordHandler(runtime);
 #endif
 }
--- a/layout/base/PositionedEventTargeting.cpp
+++ b/layout/base/PositionedEventTargeting.cpp
@@ -244,20 +244,20 @@ GetClosest(nsIFrame* aRoot, const nsPoin
 }
 
 nsIFrame*
 FindFrameTargetedByInputEvent(const nsGUIEvent *aEvent,
                               nsIFrame* aRootFrame,
                               const nsPoint& aPointRelativeToRootFrame,
                               uint32_t aFlags)
 {
-  bool ignoreRootScrollFrame = (aFlags & INPUT_IGNORE_ROOT_SCROLL_FRAME) != 0;
+  uint32_t flags = (aFlags & INPUT_IGNORE_ROOT_SCROLL_FRAME) ?
+     nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0;
   nsIFrame* target =
-    nsLayoutUtils::GetFrameForPoint(aRootFrame, aPointRelativeToRootFrame,
-                                    false, ignoreRootScrollFrame);
+    nsLayoutUtils::GetFrameForPoint(aRootFrame, aPointRelativeToRootFrame, flags);
 
   const EventRadiusPrefs* prefs = GetPrefsFor(aEvent->eventStructType);
   if (!prefs || !prefs->mEnabled || (target && IsElementClickable(target))) {
     return target;
   }
 
   // Do not modify targeting for actual mouse hardware; only for mouse
   // events generated by touch-screen hardware.
@@ -265,18 +265,17 @@ FindFrameTargetedByInputEvent(const nsGU
       prefs->mTouchOnly &&
       static_cast<const nsMouseEvent*>(aEvent)->inputSource !=
           nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
       return target;
   }
 
   nsRect targetRect = GetTargetRect(aRootFrame, aPointRelativeToRootFrame, prefs);
   nsAutoTArray<nsIFrame*,8> candidates;
-  nsresult rv = nsLayoutUtils::GetFramesForArea(aRootFrame, targetRect, candidates,
-                                                false, ignoreRootScrollFrame);
+  nsresult rv = nsLayoutUtils::GetFramesForArea(aRootFrame, targetRect, candidates, flags);
   if (NS_FAILED(rv)) {
     return target;
   }
 
   // If the exact target is non-null, only consider candidate targets in the same
   // document as the exact target. Otherwise, if an ancestor document has
   // a mouse event handler for example, targets that are !IsElementClickable can
   // never be targeted --- something nsSubDocumentFrame in an ancestor document
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1797,46 +1797,42 @@ nsLayoutUtils::GetRemoteContentIds(nsIFr
   nsDisplayItem::HitTestState hitTestState(&aOutIDs);
   list.HitTest(&builder, aTarget, &hitTestState, &outFrames);
   list.DeleteAll();
 
   return NS_OK;
 }
 
 nsIFrame*
-nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt,
-                                bool aShouldIgnoreSuppression,
-                                bool aIgnoreRootScrollFrame)
+nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt, uint32_t aFlags)
 {
   PROFILER_LABEL("nsLayoutUtils", "GetFrameForPoint");
   nsresult rv;
   nsAutoTArray<nsIFrame*,8> outFrames;
-  rv = GetFramesForArea(aFrame, nsRect(aPt, nsSize(1, 1)), outFrames,
-                        aShouldIgnoreSuppression, aIgnoreRootScrollFrame);
+  rv = GetFramesForArea(aFrame, nsRect(aPt, nsSize(1, 1)), outFrames, aFlags);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return outFrames.Length() ? outFrames.ElementAt(0) : nullptr;
 }
 
 nsresult
 nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
                                 nsTArray<nsIFrame*> &aOutFrames,
-                                bool aShouldIgnoreSuppression,
-                                bool aIgnoreRootScrollFrame)
+                                uint32_t aFlags)
 {
   PROFILER_LABEL("nsLayoutUtils","GetFramesForArea");
   nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY,
-		                       false);
+                               false);
   nsDisplayList list;
   nsRect target(aRect);
 
-  if (aShouldIgnoreSuppression) {
+  if (aFlags & IGNORE_PAINT_SUPPRESSION) {
     builder.IgnorePaintSuppression();
   }
 
-  if (aIgnoreRootScrollFrame) {
+  if (aFlags & IGNORE_ROOT_SCROLL_FRAME) {
     nsIFrame* rootScrollFrame =
       aFrame->PresContext()->PresShell()->GetRootScrollFrame();
     if (rootScrollFrame) {
       builder.SetIgnoreScrollFrame(rootScrollFrame);
     }
   }
 
   builder.EnterPresShell(aFrame, target);
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -521,45 +521,50 @@ public:
    * @param aIgnoreRootScrollFrame a boolean to control if the display list
    *        builder should ignore the root scroll frame
    */
   static nsresult GetRemoteContentIds(nsIFrame* aFrame,
                                      const nsRect& aTarget,
                                      nsTArray<ViewID> &aOutIDs,
                                      bool aIgnoreRootScrollFrame);
 
+  enum FrameForPointFlags {
+    /**
+     * When set, paint suppression is ignored, so we'll return non-root page
+     * elements even if paint suppression is stopping them from painting.
+     */
+    IGNORE_PAINT_SUPPRESSION = 0x01,
+    /**
+     * When set, clipping due to the root scroll frame (and any other viewport-
+     * related clipping) is ignored.
+     */
+    IGNORE_ROOT_SCROLL_FRAME = 0x02
+  };
+
   /**
    * Given aFrame, the root frame of a stacking context, find its descendant
    * frame under the point aPt that receives a mouse event at that location,
    * or nullptr if there is no such frame.
    * @param aPt the point, relative to the frame origin
-   * @param aShouldIgnoreSuppression a boolean to control if the display
-   * list builder should ignore paint suppression or not
-   * @param aIgnoreRootScrollFrame whether or not the display list builder
-   * should ignore the root scroll frame.
+   * @param aFlags some combination of FrameForPointFlags
    */
   static nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt,
-                                    bool aShouldIgnoreSuppression = false,
-                                    bool aIgnoreRootScrollFrame = false);
+                                    uint32_t aFlags = 0);
 
   /**
    * Given aFrame, the root frame of a stacking context, find all descendant
    * frames under the area of a rectangle that receives a mouse event,
    * or nullptr if there is no such frame.
    * @param aRect the rect, relative to the frame origin
    * @param aOutFrames an array to add all the frames found
-   * @param aShouldIgnoreSuppression a boolean to control if the display
-   * list builder should ignore paint suppression or not
-   * @param aIgnoreRootScrollFrame whether or not the display list builder
-   * should ignore the root scroll frame.
+   * @param aFlags some combination of FrameForPointFlags
    */
   static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
                                    nsTArray<nsIFrame*> &aOutFrames,
-                                   bool aShouldIgnoreSuppression = false,
-                                   bool aIgnoreRootScrollFrame = false);
+                                   uint32_t aFlags = 0);
 
   /**
    * Transform aRect relative to aAncestor down to the coordinate system of
    * aFrame. Computes the bounding-box of the true quadrilateral.
    */
   static nsRect TransformAncestorRectToFrame(nsIFrame* aFrame,
                                              const nsRect& aRect,
                                              const nsIFrame* aAncestor);
--- a/mfbt/MathAlgorithms.h
+++ b/mfbt/MathAlgorithms.h
@@ -409,21 +409,22 @@ FloorLog2(const T t)
 /** A FloorLog2 variant that accepts only size_t. */
 inline uint_fast8_t
 FloorLog2Size(size_t n)
 {
   return FloorLog2(n);
 }
 
 /*
- * Round x up to the nearest power of 2.  This function assumes that the most
- * significant bit of x is not set, which would lead to overflow.
+ * Compute the smallest power of 2 greater than or equal to |x|.  |x| must not
+ * be so great that the computed value would overflow |size_t|.
  */
 inline size_t
 RoundUpPow2(size_t x)
 {
-  MOZ_ASSERT(~x > x, "can't round up -- will overflow!");
+  MOZ_ASSERT(x <= (size_t(1) << (sizeof(size_t) * CHAR_BIT - 1)),
+             "can't round up -- will overflow!");
   return size_t(1) << CeilingLog2(x);
 }
 
 } /* namespace mozilla */
 
 #endif /* mozilla_MathAlgorithms_h */
--- a/mfbt/Vector.h
+++ b/mfbt/Vector.h
@@ -16,16 +16,18 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/ReentrancyGuard.h"
 #include "mozilla/TemplateLib.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Util.h"
 
+#include <new> // for placement new
+
 /* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4345)
 #endif
 
 namespace mozilla {
 
--- a/mfbt/tests/TestCeilingFloor.cpp
+++ b/mfbt/tests/TestCeilingFloor.cpp
@@ -2,16 +2,17 @@
 /* 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 "mozilla/MathAlgorithms.h"
 
 using mozilla::CeilingLog2;
 using mozilla::FloorLog2;
+using mozilla::RoundUpPow2;
 
 static void
 TestCeiling()
 {
   for (uint32_t i = 0; i <= 1; i++)
     MOZ_ASSERT(CeilingLog2(i) == 0);
 
   for (uint32_t i = 2; i <= 2; i++)
@@ -41,14 +42,44 @@ TestFloor()
 
   for (uint32_t i = 8; i <= 15; i++)
     MOZ_ASSERT(FloorLog2(i) == 3);
 
   for (uint32_t i = 16; i <= 31; i++)
     MOZ_ASSERT(FloorLog2(i) == 4);
 }
 
+static void
+TestRoundUpPow2()
+{
+  MOZ_ASSERT(RoundUpPow2(0) == 1);
+  MOZ_ASSERT(RoundUpPow2(1) == 1);
+  MOZ_ASSERT(RoundUpPow2(2) == 2);
+  MOZ_ASSERT(RoundUpPow2(3) == 4);
+  MOZ_ASSERT(RoundUpPow2(4) == 4);
+  MOZ_ASSERT(RoundUpPow2(5) == 8);
+  MOZ_ASSERT(RoundUpPow2(6) == 8);
+  MOZ_ASSERT(RoundUpPow2(7) == 8);
+  MOZ_ASSERT(RoundUpPow2(8) == 8);
+  MOZ_ASSERT(RoundUpPow2(9) == 16);
+
+  MOZ_ASSERT(RoundUpPow2(15) == 16);
+  MOZ_ASSERT(RoundUpPow2(16) == 16);
+  MOZ_ASSERT(RoundUpPow2(17) == 32);
+
+  MOZ_ASSERT(RoundUpPow2(31) == 32);
+  MOZ_ASSERT(RoundUpPow2(32) == 32);
+  MOZ_ASSERT(RoundUpPow2(33) == 64);
+
+  size_t MaxPow2 = size_t(1) << (sizeof(size_t) * CHAR_BIT - 1);
+  MOZ_ASSERT(RoundUpPow2(MaxPow2 - 1) == MaxPow2);
+  MOZ_ASSERT(RoundUpPow2(MaxPow2) == MaxPow2);
+  // not valid to round up when past the max power of two
+}
+
 int main()
 {
   TestCeiling();
   TestFloor();
+
+  TestRoundUpPow2();
   return 0;
 }
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -708,16 +708,21 @@ abstract public class BrowserApp extends
     public void onDestroy() {
         if (mPrefObserverId != null) {
             PrefsHelper.removeObserver(mPrefObserverId);
             mPrefObserverId = null;
         }
         if (mBrowserToolbar != null)
             mBrowserToolbar.onDestroy();
 
+        if (mFindInPageBar != null) {
+            mFindInPageBar.onDestroy();
+            mFindInPageBar = null;
+        }
+
         if (mSharedPreferencesHelper != null) {
             mSharedPreferencesHelper.uninit();
             mSharedPreferencesHelper = null;
         }
 
         if (mOrderedBroadcastHelper != null) {
             mOrderedBroadcastHelper.uninit();
             mOrderedBroadcastHelper = null;
--- a/mobile/android/base/FindInPageBar.java
+++ b/mobile/android/base/FindInPageBar.java
@@ -1,26 +1,32 @@
 /* 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/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.util.GeckoEventListener;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import org.json.JSONObject;
+
 import android.content.Context;
 import android.text.Editable;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.LinearLayout;
 
-public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnClickListener {
-    private static final String LOGTAG = "GeckoFindInPagePopup";
+public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnClickListener, GeckoEventListener  {
+    private static final String REQUEST_ID = "FindInPageBar";
 
     private final Context mContext;
     private CustomEditText mFindText;
     private boolean mInflated = false;
 
     public FindInPageBar(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
@@ -48,53 +54,45 @@ public class FindInPageBar extends Linea
                     hide();
                     return true;
                 }
                 return false;
             }
         });
 
         mInflated = true;
+        GeckoAppShell.getEventDispatcher().registerEventListener("SelectedText:Data", this);
     }
 
     public void show() {
         if (!mInflated)
             inflateContent();
 
         setVisibility(VISIBLE);
         mFindText.requestFocus();
 
-        // Show the virtual keyboard.
-        if (mFindText.hasWindowFocus()) {
-            getInputMethodManager(mFindText).showSoftInput(mFindText, 0);
-        } else {
-            // showSoftInput won't work until after the window is focused.
-            mFindText.setOnWindowFocusChangeListener(new CustomEditText.OnWindowFocusChangeListener() {
-                @Override
-                public void onWindowFocusChanged(boolean hasFocus) {
-                   if (!hasFocus)
-                       return;
-                   mFindText.setOnWindowFocusChangeListener(null);
-                   getInputMethodManager(mFindText).showSoftInput(mFindText, 0);
-               }
-            });
-        }
+        // handleMessage() receives response message and determines initial state of softInput
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SelectedText:Get", REQUEST_ID));
     }
 
     public void hide() {
         setVisibility(GONE);
         getInputMethodManager(mFindText).hideSoftInputFromWindow(mFindText.getWindowToken(), 0);
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Closed", null));
     }
 
     private InputMethodManager getInputMethodManager(View view) {
         Context context = view.getContext();
         return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
      }
 
+    public void onDestroy() {
+        GeckoAppShell.getEventDispatcher().unregisterEventListener("SelectedText:Data", this);
+    }
+
     // TextWatcher implementation
 
     @Override
     public void afterTextChanged(Editable s) {
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Find", s.toString()));
     }
 
     @Override
@@ -120,9 +118,49 @@ public class FindInPageBar extends Linea
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Next", mFindText.getText().toString()));
                 getInputMethodManager(mFindText).hideSoftInputFromWindow(mFindText.getWindowToken(), 0);
                 break;
             case R.id.find_close:
                 hide();
                 break;
         }
     }
+
+    // GeckoEventListener implementation
+
+    @Override
+    public void handleMessage(String event, JSONObject message) {
+        if (!event.equals("SelectedText:Data") || !REQUEST_ID.equals(message.optString("requestId"))) {
+            return;
+        }
+
+        final String text = message.optString("text");
+
+        // Populate an initial find string, virtual keyboard not required.
+        if (!TextUtils.isEmpty(text)) {
+            // Populate initial selection
+            ThreadUtils.postToUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mFindText.setText(text);
+                }
+            });
+            return;
+        }
+
+        // Show the virtual keyboard.
+        if (mFindText.hasWindowFocus()) {
+            getInputMethodManager(mFindText).showSoftInput(mFindText, 0);
+        } else {
+            // showSoftInput won't work until after the window is focused.
+            mFindText.setOnWindowFocusChangeListener(new CustomEditText.OnWindowFocusChangeListener() {
+                @Override
+                public void onWindowFocusChanged(boolean hasFocus) {
+                    if (!hasFocus)
+                        return;
+
+                    mFindText.setOnWindowFocusChangeListener(null);
+                    getInputMethodManager(mFindText).showSoftInput(mFindText, 0);
+               }
+            });
+        }
+    }
 }
--- a/mobile/android/chrome/content/SelectionHandler.js
+++ b/mobile/android/chrome/content/SelectionHandler.js
@@ -232,17 +232,20 @@ var SelectionHandler = {
 
   _getSelection: function sh_getSelection() {
     if (this._targetElement instanceof Ci.nsIDOMNSEditableElement)
       return this._targetElement.QueryInterface(Ci.nsIDOMNSEditableElement).editor.selection;
     else
       return this._contentWindow.getSelection();
   },
 
-  _getSelectedText: function sh_getSelectedText() {
+  getSelectedText: function sh_getSelectedText() {
+    if (!this._contentWindow)
+      return "";
+
     let selection = this._getSelection();
     if (selection)
       return selection.toString().trim();
     return "";
   },
 
   _getSelectionController: function sh_getSelectionController() {
     if (this._targetElement instanceof Ci.nsIDOMNSEditableElement)
@@ -368,27 +371,27 @@ var SelectionHandler = {
       aY -= 1;
     }
 
     this._domWinUtils.sendMouseEventToWindow("mousedown", aX, aY, 0, 0, useShift ? Ci.nsIDOMNSEvent.SHIFT_MASK : 0, true);
     this._domWinUtils.sendMouseEventToWindow("mouseup", aX, aY, 0, 0, useShift ? Ci.nsIDOMNSEvent.SHIFT_MASK : 0, true);
   },
 
   copySelection: function sh_copySelection() {
-    let selectedText = this._getSelectedText();
+    let selectedText = this.getSelectedText();
     if (selectedText.length) {
       let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
       clipboard.copyString(selectedText, this._contentWindow.document);
       NativeWindow.toast.show(Strings.browser.GetStringFromName("selectionHelper.textCopied"), "short");
     }
     this._closeSelection();
   },
 
   shareSelection: function sh_shareSelection() {
-    let selectedText = this._getSelectedText();
+    let selectedText = this.getSelectedText();
     if (selectedText.length) {
       sendMessageToJava({
         type: "Share:Text",
         text: selectedText
       });
     }
     this._closeSelection();
   },
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -290,16 +290,17 @@ var BrowserApp = {
     Services.obs.addObserver(this, "FullScreen:Exit", false);
     Services.obs.addObserver(this, "Viewport:Change", false);
     Services.obs.addObserver(this, "Viewport:Flush", false);
     Services.obs.addObserver(this, "Viewport:FixedMarginsChanged", false);
     Services.obs.addObserver(this, "Passwords:Init", false);
     Services.obs.addObserver(this, "FormHistory:Init", false);
     Services.obs.addObserver(this, "gather-telemetry", false);
     Services.obs.addObserver(this, "keyword-search", false);
+    Services.obs.addObserver(this, "SelectedText:Get", false);
 
     Services.obs.addObserver(this, "sessionstore-state-purge-complete", false);
 
     function showFullScreenWarning() {
       NativeWindow.toast.show(Strings.browser.GetStringFromName("alertFullScreenToast"), "short");
     }
 
     window.addEventListener("fullscreen", function() {
@@ -1437,16 +1438,24 @@ var BrowserApp = {
         let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
         sendMessageToJava({
           type: "Search:Keyword",
           identifier: engine.identifier,
           name: engine.name,
         });
         break;
 
+      case "SelectedText:Get":
+        sendMessageToJava({
+          type: "SelectedText:Data",
+          requestId: aData,
+          text: SelectionHandler.getSelectedText()
+        });
+        break;
+
       case "Browser:Quit":
         this.quit();
         break;
 
       case "SaveAs:PDF":
         this.saveAsPDF(browser);
         break;
 
new file mode 100644
--- /dev/null
+++ b/mozglue/build/AsanOptions.cpp
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "mozilla/Attributes.h"
+
+// When running with AddressSanitizer, we need to explicitely set some
+// options specific to our codebase to prevent errors during runtime.
+// 
+// Currently, these are:
+//
+//   allow_user_segv_handler=1 - Tell ASan to allow our code to use its
+//   own SIGSEGV handlers. This is required by ASM.js internally.
+//
+//   alloc_dealloc_mismatch=0 - Disable alloc-dealloc mismatch checking
+//   in ASan. This is required because we define our own new/delete
+//   operators that are backed by malloc/free. If one of them gets inlined
+//   while the other doesn't, ASan will report false positives.
+//
+extern "C" MOZ_ASAN_BLACKLIST
+const char* __asan_default_options() {
+    return "allow_user_segv_handler=1:alloc_dealloc_mismatch=0";
+}
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -40,12 +40,16 @@ if CONFIG['CPU_ARCH'].startswith('x86'):
         'SSE.cpp',
     ]
 
 if CONFIG['CPU_ARCH'] == 'arm':
     CPP_SOURCES += [
         'arm.cpp',
     ]
 
+if CONFIG['MOZ_ASAN']:
+    CPP_SOURCES += [
+        'AsanOptions.cpp',
+    ]
 
 
 LIBRARY_NAME = 'mozglue'
 
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim:set ts=4 sw=4 sts=4 ci et: */
 /* 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 "base/basictypes.h"
 
+#include "mozilla/Atomics.h"
 #include "mozilla/Poison.h"
 #include "mozilla/XPCOM.h"
 #include "nsXULAppAPI.h"
 
 #include "nsXPCOMPrivate.h"
 #include "nsXPCOMCIDInternal.h"
 
 #include "prlink.h"
@@ -124,16 +125,18 @@ extern nsresult nsStringInputStreamConst
 #include "mozilla/ClearOnShutdown.h"
 
 #ifdef MOZ_VISUAL_EVENT_TRACER
 #include "mozilla/VisualEventTracer.h"
 #endif
 
 #include "GeckoProfiler.h"
 
+#include "jsapi.h"
+
 using namespace mozilla;
 using base::AtExitManager;
 using mozilla::ipc::BrowserProcessSubThread;
 #ifdef MOZ_VISUAL_EVENT_TRACER
 using mozilla::eventtracer::VisualEventTracer;
 #endif
 
 namespace {
@@ -327,16 +330,62 @@ NS_GetTraceRefcnt(nsITraceRefcnt** resul
 
 EXPORT_XPCOM_API(nsresult)
 NS_InitXPCOM(nsIServiceManager* *result,
                              nsIFile* binDirectory)
 {
     return NS_InitXPCOM2(result, binDirectory, nullptr);
 }
 
+// |sSizeOfICU| can be accessed by multiple JSRuntimes, so it must be
+// thread-safe.
+static Atomic<size_t> sSizeOfICU;
+
+static int64_t
+GetICUSize()
+{
+    return sSizeOfICU;
+}
+
+NS_MEMORY_REPORTER_IMPLEMENT(ICU,
+    "explicit/icu",
+    KIND_HEAP,
+    UNITS_BYTES,
+    GetICUSize,
+    "Memory used by ICU, a Unicode and globalization support library."
+)
+
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_ON_ALLOC_FUN(ICUMallocSizeOfOnAlloc)
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_ON_FREE_FUN(ICUMallocSizeOfOnFree)
+
+static void*
+ICUAlloc(const void*, size_t size)
+{
+    void* p = malloc(size);
+    sSizeOfICU += ICUMallocSizeOfOnAlloc(p);
+    return p;
+}
+
+static void*
+ICURealloc(const void*, void* p, size_t size)
+{
+    size_t delta = 0 - ICUMallocSizeOfOnFree(p);
+    void* pnew = realloc(p, size);
+    delta += pnew ? ICUMallocSizeOfOnAlloc(pnew) : ICUMallocSizeOfOnAlloc(p);
+    sSizeOfICU += delta;
+    return pnew;
+}
+
+static void
+ICUFree(const void*, void* p)
+{
+    sSizeOfICU -= ICUMallocSizeOfOnFree(p);
+    free(p);
+}
+
 EXPORT_XPCOM_API(nsresult)
 NS_InitXPCOM2(nsIServiceManager* *result,
               nsIFile* binDirectory,
               nsIDirectoryServiceProvider* appFileLocationProvider)
 {
     mozPoisonValueInit();
 
     char aLocal;
@@ -466,16 +515,31 @@ NS_InitXPCOM2(nsIServiceManager* *result
     if (!nsCycleCollector_init()) {
         return NS_ERROR_UNEXPECTED;
     }
 
     // And start it up for this thread too.
     rv = nsCycleCollector_startup(CCSingleThread);
     if (NS_FAILED(rv)) return rv;
 
+    // Register ICU memory functions.  This really shouldn't be necessary: the
+    // JS engine should do this on its own inside JS_Init, and memory-reporting
+    // code should call a JSAPI function to observe ICU memory usage.  But we
+    // can't define the alloc/free functions in the JS engine, because it can't
+    // depend on the XPCOM-based memory reporting goop.  So for now, we have
+    // this oddness.
+    if (!JS_SetICUMemoryFunctions(ICUAlloc, ICURealloc, ICUFree)) {
+        NS_RUNTIMEABORT("JS_SetICUMemoryFunctions failed.");
+    }
+
+    // Initialize the JS engine.
+    if (!JS_Init()) {
+        NS_RUNTIMEABORT("JS_Init failed");
+    }
+
     rv = nsComponentManagerImpl::gComponentManager->Init();
     if (NS_FAILED(rv))
     {
         NS_RELEASE(nsComponentManagerImpl::gComponentManager);
         return rv;
     }
 
     if (result) {
@@ -503,16 +567,20 @@ NS_InitXPCOM2(nsIServiceManager* *result
                                   nullptr,
                                   NS_XPCOM_STARTUP_OBSERVER_ID);
 #ifdef XP_WIN
     CreateAnonTempFileRemover();
 #endif
 
     mozilla::MapsMemoryReporter::Init();
 
+    // The memory reporter manager is up and running -- register a reporter for
+    // ICU's memory usage.
+    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(ICU));
+
     mozilla::Telemetry::Init();
 
     mozilla::HangMonitor::Startup();
 
 #ifdef MOZ_VISUAL_EVENT_TRACER
     mozilla::eventtracer::Init();
 #endif
 
@@ -695,18 +763,22 @@ ShutdownXPCOM(nsIServiceManager* servMgr
     NS_ShutdownNativeCharsetUtils();
 #endif
 
     // Shutdown xpcom. This will release all loaders and cause others holding
     // a refcount to the component manager to release it.
     if (nsComponentManagerImpl::gComponentManager) {
         rv = (nsComponentManagerImpl::gComponentManager)->Shutdown();
         NS_ASSERTION(NS_SUCCEEDED(rv), "Component Manager shutdown failed.");
-    } else
+    } else {
         NS_WARNING("Component Manager was never created ...");
+    }
+
+    // Shut down the JS engine.
+    JS_ShutDown();
 
     // Release our own singletons
     // Do this _after_ shutting down the component manager, because the
     // JS component loader will use XPConnect to call nsIModule::canUnload,
     // and that will spin up the InterfaceInfoManager again -- bad mojo
     XPTInterfaceInfoManager::FreeInterfaceInfoManager();
 
     // Finally, release the component manager last because it unloads the