Merge inbound to m-c
authorWes Kocher <wkocher@mozilla.com>
Tue, 22 Apr 2014 17:54:44 -0700
changeset 180071 02515cf4fcfd1512e5f5e747062c7023e0d37a44
parent 180040 1ab07aa4d004229b7537e7366c8d5ce1eea0d8be (current diff)
parent 179965 2e2566a604f18eb8ab3a6d6d929c6af2bfffc3df (diff)
child 180076 35737ab92f9eb9b860ab7aa188ba3416bf174254
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone31.0a1
Merge inbound to m-c
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -516,17 +516,21 @@ AudioStream::CheckForStart()
 }
 
 NS_IMETHODIMP
 AudioInitTask::Run()
 {
   MOZ_ASSERT(mThread);
   if (NS_IsMainThread()) {
     mThread->Shutdown(); // can't Shutdown from the thread itself, darn
-    mThread = nullptr;
+    // Don't null out mThread!
+    // See bug 999104.  We must hold a ref to the thread across Dispatch()
+    // since the internal mThread ref could be released while processing
+    // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
+    // assumes the caller does.
     return NS_OK;
   }
 
   nsresult rv = mAudioStream->OpenCubeb(mParams, mLatencyRequest);
 
   // and now kill this thread
   NS_DispatchToMainThread(this);
   return rv;
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -440,16 +440,17 @@ public:
     , mParams(aParams)
   {}
 
   nsresult Dispatch()
   {
     // Can't add 'this' as the event to run, since mThread may not be set yet
     nsresult rv = NS_NewNamedThread("CubebInit", getter_AddRefs(mThread));
     if (NS_SUCCEEDED(rv)) {
+      // Note: event must not null out mThread!
       rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
     }
     return rv;
   }
 
 protected:
   virtual ~AudioInitTask() {};
 
--- a/dom/tests/mochitest/beacon/test_beaconContentPolicy.html
+++ b/dom/tests/mochitest/beacon/test_beaconContentPolicy.html
@@ -20,17 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 var beaconUrl = "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-handler.sjs";
 
 const Cc = SpecialPowers.Cc;
 const Ci = SpecialPowers.Ci;
 
 // not enabled by default yet.
 SimpleTest.waitForExplicitFinish();
 
-var policy = setupPolicy();
+var policy;
 
 SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
 
 function setupPolicy() {
   var policyID = SpecialPowers.wrap(SpecialPowers.Components).ID("{b80e19d0-878f-d41b-2654-194714a4115c}");
   var policyName = "@mozilla.org/testpolicy;1";
   var policy = {
     // nsISupports implementation
@@ -83,15 +83,20 @@ function teardownPolicy() {
     var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager.QueryInterface(Ci.nsIComponentRegistrar);
     componentManager.unregisterFactory(policy.policyID, policy.policy);
     var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
     categoryManager.deleteCategoryEntry("content-policy", policy.policyName, false);
   }, 0);
 }
 
 function beginTest() {
-  navigator.sendBeacon(beaconUrl, "bacon would have been a better name than beacon");
+  policy = setupPolicy();
+  // Make sure to hit the event loop here in order to ensure that nsContentPolicy
+  // has been notified of the newly registered policy.
+  SimpleTest.executeSoon(function() {
+    navigator.sendBeacon(beaconUrl, "bacon would have been a better name than beacon");
+  });
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/embedding/tests/winEmbed/winEmbed.cpp
+++ b/embedding/tests/winEmbed/winEmbed.cpp
@@ -1072,17 +1072,17 @@ nsresult AppCallbacks::CreateBrowserWind
            nsIWebBrowserChrome **aNewWindow)
 {
   WebBrowserChrome * chrome = new WebBrowserChrome();
   if (!chrome)
     return NS_ERROR_FAILURE;
 
   // the interface to return and one addref, which we assume will be
   // immediately released
-  CallQueryInterface(static_cast<nsIWebBrowserChrome*>(chrome), aNewWindow);
+  *aNewWindow = static_cast<nsIWebBrowserChrome*>(chrome);
   // now an extra addref; the window owns itself (to be released by
   // WebBrowserChromeUI::Destroy)
   NS_ADDREF(*aNewWindow);
 
   chrome->SetChromeFlags(aChromeFlags);
   chrome->SetParent(aParent);
 
   // Insert the browser
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -8,16 +8,30 @@
 #define js_GCAPI_h
 
 #include "mozilla/NullPtr.h"
 
 #include "js/HeapAPI.h"
 #include "js/RootingAPI.h"
 #include "js/Value.h"
 
+typedef enum JSGCMode {
+    /* Perform only global GCs. */
+    JSGC_MODE_GLOBAL = 0,
+
+    /* Perform per-compartment GCs until too much garbage has accumulated. */
+    JSGC_MODE_COMPARTMENT = 1,
+
+    /*
+     * Collect in short time slices rather than all at once. Implies
+     * JSGC_MODE_COMPARTMENT.
+     */
+    JSGC_MODE_INCREMENTAL = 2
+} JSGCMode;
+
 namespace JS {
 
 #define GCREASONS(D)                            \
     /* Reasons internal to the JS engine */     \
     D(API)                                      \
     D(MAYBEGC)                                  \
     D(DESTROY_RUNTIME)                          \
     D(DESTROY_CONTEXT)                          \
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -1,27 +1,34 @@
 /* -*- 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 "gc/Tracer.h"
 
+#include "mozilla/DebugOnly.h"
+
 #include "jsapi.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsprf.h"
 #include "jsscript.h"
+#include "jsutil.h"
 #include "NamespaceImports.h"
 
+#include "gc/GCInternals.h"
 #include "gc/Marking.h"
 
+#include "jsgcinlines.h"
+
 using namespace js;
 using namespace js::gc;
+using mozilla::DebugOnly;
 
 JS_PUBLIC_API(void)
 JS_CallValueTracer(JSTracer *trc, Value *valuep, const char *name)
 {
     MarkValueUnbarriered(trc, valuep, name);
 }
 
 JS_PUBLIC_API(void)
@@ -321,8 +328,347 @@ JSTracer::unsetTracingLocation()
 }
 
 void **
 JSTracer::tracingLocation(void **thingp)
 {
     return realLocation_ ? (void **)realLocation_ : thingp;
 }
 #endif
+
+bool
+MarkStack::init(JSGCMode gcMode)
+{
+    setBaseCapacity(gcMode);
+
+    JS_ASSERT(!stack_);
+    uintptr_t *newStack = js_pod_malloc<uintptr_t>(baseCapacity_);
+    if (!newStack)
+        return false;
+
+    setStack(newStack, 0, baseCapacity_);
+    return true;
+}
+
+void
+MarkStack::setBaseCapacity(JSGCMode mode)
+{
+    switch (mode) {
+      case JSGC_MODE_GLOBAL:
+      case JSGC_MODE_COMPARTMENT:
+        baseCapacity_ = NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY;
+        break;
+      case JSGC_MODE_INCREMENTAL:
+        baseCapacity_ = INCREMENTAL_MARK_STACK_BASE_CAPACITY;
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("bad gc mode");
+    }
+
+    if (baseCapacity_ > maxCapacity_)
+        baseCapacity_ = maxCapacity_;
+}
+
+void
+MarkStack::setMaxCapacity(size_t maxCapacity)
+{
+    JS_ASSERT(isEmpty());
+    maxCapacity_ = maxCapacity;
+    if (baseCapacity_ > maxCapacity_)
+        baseCapacity_ = maxCapacity_;
+
+    reset();
+}
+
+void
+MarkStack::reset()
+{
+    if (capacity() == baseCapacity_) {
+        // No size change; keep the current stack.
+        setStack(stack_, 0, baseCapacity_);
+        return;
+    }
+
+    uintptr_t *newStack = (uintptr_t *)js_realloc(stack_, sizeof(uintptr_t) * baseCapacity_);
+    if (!newStack) {
+        // If the realloc fails, just keep using the existing stack; it's
+        // not ideal but better than failing.
+        newStack = stack_;
+        baseCapacity_ = capacity();
+    }
+    setStack(newStack, 0, baseCapacity_);
+}
+
+bool
+MarkStack::enlarge(unsigned count)
+{
+    size_t newCapacity = Min(maxCapacity_, capacity() * 2);
+    if (newCapacity < capacity() + count)
+        return false;
+
+    size_t tosIndex = position();
+
+    uintptr_t *newStack = (uintptr_t *)js_realloc(stack_, sizeof(uintptr_t) * newCapacity);
+    if (!newStack)
+        return false;
+
+    setStack(newStack, tosIndex, newCapacity);
+    return true;
+}
+
+void
+MarkStack::setGCMode(JSGCMode gcMode)
+{
+    // The mark stack won't be resized until the next call to reset(), but
+    // that will happen at the end of the next GC.
+    setBaseCapacity(gcMode);
+}
+
+size_t
+MarkStack::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    return mallocSizeOf(stack_);
+}
+
+/*
+ * DoNotTraceWeakMaps: the GC is recomputing the liveness of WeakMap entries,
+ * so we delay visting entries.
+ */
+GCMarker::GCMarker(JSRuntime *rt)
+  : JSTracer(rt, nullptr, DoNotTraceWeakMaps),
+    stack(size_t(-1)),
+    color(BLACK),
+    unmarkedArenaStackTop(nullptr),
+    markLaterArenas(0),
+    grayBufferState(GRAY_BUFFER_UNUSED),
+    started(false)
+{
+}
+
+bool
+GCMarker::init(JSGCMode gcMode)
+{
+    return stack.init(gcMode);
+}
+
+void
+GCMarker::start()
+{
+    JS_ASSERT(!started);
+    started = true;
+    color = BLACK;
+
+    JS_ASSERT(!unmarkedArenaStackTop);
+    JS_ASSERT(markLaterArenas == 0);
+
+}
+
+void
+GCMarker::stop()
+{
+    JS_ASSERT(isDrained());
+
+    JS_ASSERT(started);
+    started = false;
+
+    JS_ASSERT(!unmarkedArenaStackTop);
+    JS_ASSERT(markLaterArenas == 0);
+
+    /* Free non-ballast stack memory. */
+    stack.reset();
+
+    resetBufferedGrayRoots();
+    grayBufferState = GRAY_BUFFER_UNUSED;
+}
+
+void
+GCMarker::reset()
+{
+    color = BLACK;
+
+    stack.reset();
+    JS_ASSERT(isMarkStackEmpty());
+
+    while (unmarkedArenaStackTop) {
+        ArenaHeader *aheader = unmarkedArenaStackTop;
+        JS_ASSERT(aheader->hasDelayedMarking);
+        JS_ASSERT(markLaterArenas);
+        unmarkedArenaStackTop = aheader->getNextDelayedMarking();
+        aheader->unsetDelayedMarking();
+        aheader->markOverflow = 0;
+        aheader->allocatedDuringIncremental = 0;
+        markLaterArenas--;
+    }
+    JS_ASSERT(isDrained());
+    JS_ASSERT(!markLaterArenas);
+}
+
+void
+GCMarker::markDelayedChildren(ArenaHeader *aheader)
+{
+    if (aheader->markOverflow) {
+        bool always = aheader->allocatedDuringIncremental;
+        aheader->markOverflow = 0;
+
+        for (CellIterUnderGC i(aheader); !i.done(); i.next()) {
+            Cell *t = i.getCell();
+            if (always || t->isMarked()) {
+                t->markIfUnmarked();
+                JS_TraceChildren(this, t, MapAllocToTraceKind(aheader->getAllocKind()));
+            }
+        }
+    } else {
+        JS_ASSERT(aheader->allocatedDuringIncremental);
+        PushArena(this, aheader);
+    }
+    aheader->allocatedDuringIncremental = 0;
+    /*
+     * Note that during an incremental GC we may still be allocating into
+     * aheader. However, prepareForIncrementalGC sets the
+     * allocatedDuringIncremental flag if we continue marking.
+     */
+}
+
+bool
+GCMarker::markDelayedChildren(SliceBudget &budget)
+{
+    gcstats::MaybeAutoPhase ap;
+    if (runtime()->gcIncrementalState == MARK)
+        ap.construct(runtime()->gcStats, gcstats::PHASE_MARK_DELAYED);
+
+    JS_ASSERT(unmarkedArenaStackTop);
+    do {
+        /*
+         * If marking gets delayed at the same arena again, we must repeat
+         * marking of its things. For that we pop arena from the stack and
+         * clear its hasDelayedMarking flag before we begin the marking.
+         */
+        ArenaHeader *aheader = unmarkedArenaStackTop;
+        JS_ASSERT(aheader->hasDelayedMarking);
+        JS_ASSERT(markLaterArenas);
+        unmarkedArenaStackTop = aheader->getNextDelayedMarking();
+        aheader->unsetDelayedMarking();
+        markLaterArenas--;
+        markDelayedChildren(aheader);
+
+        budget.step(150);
+        if (budget.isOverBudget())
+            return false;
+    } while (unmarkedArenaStackTop);
+    JS_ASSERT(!markLaterArenas);
+
+    return true;
+}
+
+#ifdef DEBUG
+void
+GCMarker::checkZone(void *p)
+{
+    JS_ASSERT(started);
+    DebugOnly<Cell *> cell = static_cast<Cell *>(p);
+    JS_ASSERT_IF(cell->isTenured(), cell->tenuredZone()->isCollecting());
+}
+#endif
+
+bool
+GCMarker::hasBufferedGrayRoots() const
+{
+    return grayBufferState == GRAY_BUFFER_OK;
+}
+
+void
+GCMarker::startBufferingGrayRoots()
+{
+    JS_ASSERT(grayBufferState == GRAY_BUFFER_UNUSED);
+    grayBufferState = GRAY_BUFFER_OK;
+    for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
+        JS_ASSERT(zone->gcGrayRoots.empty());
+
+    JS_ASSERT(!callback);
+    callback = GrayCallback;
+    JS_ASSERT(IS_GC_MARKING_TRACER(this));
+}
+
+void
+GCMarker::endBufferingGrayRoots()
+{
+    JS_ASSERT(callback == GrayCallback);
+    callback = nullptr;
+    JS_ASSERT(IS_GC_MARKING_TRACER(this));
+    JS_ASSERT(grayBufferState == GRAY_BUFFER_OK ||
+              grayBufferState == GRAY_BUFFER_FAILED);
+}
+
+void
+GCMarker::resetBufferedGrayRoots()
+{
+    for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
+        zone->gcGrayRoots.clearAndFree();
+}
+
+void
+GCMarker::markBufferedGrayRoots(JS::Zone *zone)
+{
+    JS_ASSERT(grayBufferState == GRAY_BUFFER_OK);
+    JS_ASSERT(zone->isGCMarkingGray());
+
+    for (GrayRoot *elem = zone->gcGrayRoots.begin(); elem != zone->gcGrayRoots.end(); elem++) {
+#ifdef DEBUG
+        setTracingDetails(elem->debugPrinter, elem->debugPrintArg, elem->debugPrintIndex);
+#endif
+        void *tmp = elem->thing;
+        setTracingLocation((void *)&elem->thing);
+        MarkKind(this, &tmp, elem->kind);
+        JS_ASSERT(tmp == elem->thing);
+    }
+}
+
+void
+GCMarker::appendGrayRoot(void *thing, JSGCTraceKind kind)
+{
+    JS_ASSERT(started);
+
+    if (grayBufferState == GRAY_BUFFER_FAILED)
+        return;
+
+    GrayRoot root(thing, kind);
+#ifdef DEBUG
+    root.debugPrinter = debugPrinter();
+    root.debugPrintArg = debugPrintArg();
+    root.debugPrintIndex = debugPrintIndex();
+#endif
+
+    Zone *zone = static_cast<Cell *>(thing)->tenuredZone();
+    if (zone->isCollecting()) {
+        zone->maybeAlive = true;
+        if (!zone->gcGrayRoots.append(root)) {
+            resetBufferedGrayRoots();
+            grayBufferState = GRAY_BUFFER_FAILED;
+        }
+    }
+}
+
+void
+GCMarker::GrayCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind)
+{
+    JS_ASSERT(thingp);
+    JS_ASSERT(*thingp);
+    GCMarker *gcmarker = static_cast<GCMarker *>(trc);
+    gcmarker->appendGrayRoot(*thingp, kind);
+}
+
+size_t
+GCMarker::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    size_t size = stack.sizeOfExcludingThis(mallocSizeOf);
+    for (ZonesIter zone(runtime(), WithAtoms); !zone.done(); zone.next())
+        size += zone->gcGrayRoots.sizeOfExcludingThis(mallocSizeOf);
+    return size;
+}
+
+void
+js::SetMarkStackLimit(JSRuntime *rt, size_t limit)
+{
+    JS_ASSERT(!rt->isHeapBusy());
+    AutoStopVerifyingBarriers pauseVerification(rt, false);
+    rt->gcMarker.setMaxCapacity(limit);
+}
+
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -2,11 +2,306 @@
  * 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 js_Tracer_h
 #define js_Tracer_h
 
+#include "mozilla/DebugOnly.h"
+
+#include "js/GCAPI.h"
+#include "js/SliceBudget.h"
 #include "js/TracingAPI.h"
 
+namespace js {
+class GCMarker;
+class ObjectImpl;
+namespace gc {
+class ArenaHeader;
+}
+namespace jit {
+class JitCode;
+}
+namespace types {
+class TypeObject;
+}
+
+static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
+static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
+
+/*
+ * When the native stack is low, the GC does not call JS_TraceChildren to mark
+ * the reachable "children" of the thing. Rather the thing is put aside and
+ * JS_TraceChildren is called later with more space on the C stack.
+ *
+ * To implement such delayed marking of the children with minimal overhead for
+ * the normal case of sufficient native stack, the code adds a field per arena.
+ * The field markingDelay->link links all arenas with delayed things into a
+ * stack list with the pointer to stack top in GCMarker::unmarkedArenaStackTop.
+ * GCMarker::delayMarkingChildren adds arenas to the stack as necessary while
+ * markDelayedChildren pops the arenas from the stack until it empties.
+ */
+class MarkStack
+{
+    friend class GCMarker;
+
+    uintptr_t *stack_;
+    uintptr_t *tos_;
+    uintptr_t *end_;
+
+    // The capacity we start with and reset() to.
+    size_t baseCapacity_;
+    size_t maxCapacity_;
+
+  public:
+    MarkStack(size_t maxCapacity)
+      : stack_(nullptr),
+        tos_(nullptr),
+        end_(nullptr),
+        baseCapacity_(0),
+        maxCapacity_(maxCapacity)
+    {}
+
+    ~MarkStack() {
+        js_free(stack_);
+    }
+
+    size_t capacity() { return end_ - stack_; }
+
+    ptrdiff_t position() const { return tos_ - stack_; }
+
+    void setStack(uintptr_t *stack, size_t tosIndex, size_t capacity) {
+        stack_ = stack;
+        tos_ = stack + tosIndex;
+        end_ = stack + capacity;
+    }
+
+    bool init(JSGCMode gcMode);
+
+    void setBaseCapacity(JSGCMode mode);
+    size_t maxCapacity() const { return maxCapacity_; }
+    void setMaxCapacity(size_t maxCapacity);
+
+    bool push(uintptr_t item) {
+        if (tos_ == end_) {
+            if (!enlarge(1))
+                return false;
+        }
+        JS_ASSERT(tos_ < end_);
+        *tos_++ = item;
+        return true;
+    }
+
+    bool push(uintptr_t item1, uintptr_t item2, uintptr_t item3) {
+        uintptr_t *nextTos = tos_ + 3;
+        if (nextTos > end_) {
+            if (!enlarge(3))
+                return false;
+            nextTos = tos_ + 3;
+        }
+        JS_ASSERT(nextTos <= end_);
+        tos_[0] = item1;
+        tos_[1] = item2;
+        tos_[2] = item3;
+        tos_ = nextTos;
+        return true;
+    }
+
+    bool isEmpty() const {
+        return tos_ == stack_;
+    }
+
+    uintptr_t pop() {
+        JS_ASSERT(!isEmpty());
+        return *--tos_;
+    }
+
+    void reset();
+
+    /* Grow the stack, ensuring there is space for at least count elements. */
+    bool enlarge(unsigned count);
+
+    void setGCMode(JSGCMode gcMode);
+
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+};
+
+class GCMarker : public JSTracer
+{
+  public:
+    explicit GCMarker(JSRuntime *rt);
+    bool init(JSGCMode gcMode);
+
+    void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
+    size_t maxCapacity() const { return stack.maxCapacity(); }
+
+    void start();
+    void stop();
+    void reset();
+
+    void pushObject(ObjectImpl *obj) {
+        pushTaggedPtr(ObjectTag, obj);
+    }
+
+    void pushType(types::TypeObject *type) {
+        pushTaggedPtr(TypeTag, type);
+    }
+
+    void pushJitCode(jit::JitCode *code) {
+        pushTaggedPtr(JitCodeTag, code);
+    }
+
+    uint32_t getMarkColor() const {
+        return color;
+    }
+
+    /*
+     * Care must be taken changing the mark color from gray to black. The cycle
+     * collector depends on the invariant that there are no black to gray edges
+     * in the GC heap. This invariant lets the CC not trace through black
+     * objects. If this invariant is violated, the cycle collector may free
+     * objects that are still reachable.
+     */
+    void setMarkColorGray() {
+        JS_ASSERT(isDrained());
+        JS_ASSERT(color == gc::BLACK);
+        color = gc::GRAY;
+    }
+
+    void setMarkColorBlack() {
+        JS_ASSERT(isDrained());
+        JS_ASSERT(color == gc::GRAY);
+        color = gc::BLACK;
+    }
+
+    inline void delayMarkingArena(gc::ArenaHeader *aheader);
+    void delayMarkingChildren(const void *thing);
+    void markDelayedChildren(gc::ArenaHeader *aheader);
+    bool markDelayedChildren(SliceBudget &budget);
+    bool hasDelayedChildren() const {
+        return !!unmarkedArenaStackTop;
+    }
+
+    bool isDrained() {
+        return isMarkStackEmpty() && !unmarkedArenaStackTop;
+    }
+
+    bool drainMarkStack(SliceBudget &budget);
+
+    /*
+     * Gray marking must be done after all black marking is complete. However,
+     * we do not have write barriers on XPConnect roots. Therefore, XPConnect
+     * roots must be accumulated in the first slice of incremental GC. We
+     * accumulate these roots in the each compartment's gcGrayRoots vector and
+     * then mark them later, after black marking is complete for each
+     * compartment. This accumulation can fail, but in that case we switch to
+     * non-incremental GC.
+     */
+    bool hasBufferedGrayRoots() const;
+    void startBufferingGrayRoots();
+    void endBufferingGrayRoots();
+    void resetBufferedGrayRoots();
+    void markBufferedGrayRoots(JS::Zone *zone);
+
+    static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind);
+
+    void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
+
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+    /* This is public exclusively for ScanRope. */
+    MarkStack stack;
+
+  private:
+#ifdef DEBUG
+    void checkZone(void *p);
+#else
+    void checkZone(void *p) {}
+#endif
+
+    /*
+     * We use a common mark stack to mark GC things of different types and use
+     * the explicit tags to distinguish them when it cannot be deduced from
+     * the context of push or pop operation.
+     */
+    enum StackTag {
+        ValueArrayTag,
+        ObjectTag,
+        TypeTag,
+        XmlTag,
+        SavedValueArrayTag,
+        JitCodeTag,
+        LastTag = JitCodeTag
+    };
+
+    static const uintptr_t StackTagMask = 7;
+    static_assert(StackTagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags.");
+    static_assert(StackTagMask <= gc::CellMask, "The tag mask must be embeddable in a Cell*.");
+
+    void pushTaggedPtr(StackTag tag, void *ptr) {
+        checkZone(ptr);
+        uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
+        JS_ASSERT(!(addr & StackTagMask));
+        if (!stack.push(addr | uintptr_t(tag)))
+            delayMarkingChildren(ptr);
+    }
+
+    void pushValueArray(JSObject *obj, void *start, void *end) {
+        checkZone(obj);
+
+        JS_ASSERT(start <= end);
+        uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
+        uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
+        uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
+
+        /*
+         * Push in the reverse order so obj will be on top. If we cannot push
+         * the array, we trigger delay marking for the whole object.
+         */
+        if (!stack.push(endAddr, startAddr, tagged))
+            delayMarkingChildren(obj);
+    }
+
+    bool isMarkStackEmpty() {
+        return stack.isEmpty();
+    }
+
+    bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
+    void saveValueRanges();
+    inline void processMarkStackTop(SliceBudget &budget);
+    void processMarkStackOther(uintptr_t tag, uintptr_t addr);
+
+    void appendGrayRoot(void *thing, JSGCTraceKind kind);
+
+    /* The color is only applied to objects and functions. */
+    uint32_t color;
+
+    /* Pointer to the top of the stack of arenas we are delaying marking on. */
+    js::gc::ArenaHeader *unmarkedArenaStackTop;
+
+    /* Count of arenas that are currently in the stack. */
+    mozilla::DebugOnly<size_t> markLaterArenas;
+
+    enum GrayBufferState {
+        GRAY_BUFFER_UNUSED,
+        GRAY_BUFFER_OK,
+        GRAY_BUFFER_FAILED
+    };
+    GrayBufferState grayBufferState;
+
+    /* Assert that start and stop are called with correct ordering. */
+    mozilla::DebugOnly<bool> started;
+};
+
+void
+SetMarkStackLimit(JSRuntime *rt, size_t limit);
+
+} /* namespace js */
+
+/*
+ * Macro to test if a traversal is the marking phase of the GC.
+ */
+#define IS_GC_MARKING_TRACER(trc) \
+    ((trc)->callback == nullptr || (trc)->callback == GCMarker::GrayCallback)
+
 #endif /* js_Tracer_h */
--- a/js/src/jit/AsmJSSignalHandlers.cpp
+++ b/js/src/jit/AsmJSSignalHandlers.cpp
@@ -1,16 +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 "jit/AsmJSSignalHandlers.h"
 
+#include "mozilla/BinarySearch.h"
+
 #include "assembler/assembler/MacroAssembler.h"
 #include "jit/AsmJSModule.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace mozilla;
 
 using JS::GenericNaN;
@@ -202,45 +204,39 @@ SetXMMRegToNaN(bool isFloat32, T *xmm_re
     } else {
         JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double));
         double *dbls = reinterpret_cast<double*>(xmm_reg);
         dbls[0] = GenericNaN();
         dbls[1] = 0;
     }
 }
 
+struct GetHeapAccessOffset
+{
+    const AsmJSModule &module;
+    explicit GetHeapAccessOffset(const AsmJSModule &module) : module(module) {}
+    uintptr_t operator[](size_t index) const {
+        return module.heapAccess(index).offset();
+    }
+};
+
 // Perform a binary search on the projected offsets of the known heap accesses
 // in the module.
 static const AsmJSHeapAccess *
 LookupHeapAccess(const AsmJSModule &module, uint8_t *pc)
 {
     JS_ASSERT(module.containsPC(pc));
-    size_t targetOffset = pc - module.codeBase();
+
+    uintptr_t pcOff = pc - module.codeBase();
 
-    if (module.numHeapAccesses() == 0)
+    size_t match;
+    if (!BinarySearch(GetHeapAccessOffset(module), 0, module.numHeapAccesses(), pcOff, &match))
         return nullptr;
 
-    size_t low = 0;
-    size_t high = module.numHeapAccesses() - 1;
-    while (high - low >= 2) {
-        size_t mid = low + (high - low) / 2;
-        uint32_t midOffset = module.heapAccess(mid).offset();
-        if (targetOffset == midOffset)
-            return &module.heapAccess(mid);
-        if (targetOffset < midOffset)
-            high = mid;
-        else
-            low = mid;
-    }
-    if (targetOffset == module.heapAccess(low).offset())
-        return &module.heapAccess(low);
-    if (targetOffset == module.heapAccess(high).offset())
-        return &module.heapAccess(high);
-
-    return nullptr;
+    return &module.heapAccess(match);
 }
 #endif
 
 #if defined(XP_WIN)
 # include "jswin.h"
 #else
 # include <signal.h>
 # include <sys/mman.h>
--- a/js/src/jit/ValueNumbering.cpp
+++ b/js/src/jit/ValueNumbering.cpp
@@ -501,16 +501,17 @@ ValueNumberer::breakClass(MDefinition *d
         // If the def was the only member of the class, then there is nothing to do.
         if (defdata->classNext == nullptr)
             return;
         // If upon closer inspection, we are still equivalent to this class
         // then there isn't anything for us to do.
         MDefinition *newRep = findSplit(def);
         if (!newRep)
             return;
+        markConsumers(def);
         ValueNumberData *newdata = newRep->valueNumberData();
 
         // Right now, |defdata| is at the front of the list, and |newdata| is
         // somewhere in the middle.
         //
         // We want to move |defdata| and everything up to but excluding
         // |newdata| to a new list, with |defdata| still as the canonical
         // element.
@@ -539,18 +540,20 @@ ValueNumberer::breakClass(MDefinition *d
         //new canonical element. Mark the remaining elements in the list
         //(including |newRep|)
         newdata->classPrev = nullptr;
         IonSpew(IonSpew_GVN, "Choosing a new representative: %d", newRep->id());
 
         // make the VN of every member in the class the VN of the new representative number.
         for (MDefinition *tmp = newRep; tmp != nullptr; tmp = tmp->valueNumberData()->classNext) {
             // if this instruction is already scheduled to be processed, don't do anything.
-            if (tmp->isInWorklist())
+            if (tmp->isInWorklist()) {
+                IonSpew(IonSpew_GVN, "Defer  to a new congruence class: %d", tmp->id());
                 continue;
+            }
             IonSpew(IonSpew_GVN, "Moving to a new congruence class: %d", tmp->id());
             tmp->setValueNumber(newRep->id());
             markConsumers(tmp);
             markDefinition(tmp);
         }
 
         // Insert the new representative => number mapping into the table
         // Logically, there should not be anything in the table currently, but
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2115,30 +2115,16 @@ typedef enum JSGCParamKey {
     /*
      * We decommit memory lazily. If more than this number of megabytes is
      * available to be decommitted, then JS_MaybeGC will trigger a shrinking GC
      * to decommit it.
      */
     JSGC_DECOMMIT_THRESHOLD = 20
 } JSGCParamKey;
 
-typedef enum JSGCMode {
-    /* Perform only global GCs. */
-    JSGC_MODE_GLOBAL = 0,
-
-    /* Perform per-compartment GCs until too much garbage has accumulated. */
-    JSGC_MODE_COMPARTMENT = 1,
-
-    /*
-     * Collect in short time slices rather than all at once. Implies
-     * JSGC_MODE_COMPARTMENT.
-     */
-    JSGC_MODE_INCREMENTAL = 2
-} JSGCMode;
-
 extern JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value);
 
 extern JS_PUBLIC_API(uint32_t)
 JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key);
 
 extern JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32_t value);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -18,16 +18,17 @@
 #include "jsfun.h"
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Sort.h"
+#include "gc/Heap.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
 
 #include "jsatominlines.h"
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1334,16 +1334,36 @@ Zone::reduceGCTriggerBytes(size_t amount
     gcTriggerBytes -= amount;
 }
 
 Allocator::Allocator(Zone *zone)
   : zone_(zone)
 {}
 
 inline void
+GCMarker::delayMarkingArena(ArenaHeader *aheader)
+{
+    if (aheader->hasDelayedMarking) {
+        /* Arena already scheduled to be marked later */
+        return;
+    }
+    aheader->setNextDelayedMarking(unmarkedArenaStackTop);
+    unmarkedArenaStackTop = aheader;
+    markLaterArenas++;
+}
+
+void
+GCMarker::delayMarkingChildren(const void *thing)
+{
+    const Cell *cell = reinterpret_cast<const Cell *>(thing);
+    cell->arenaHeader()->markOverflow = 1;
+    delayMarkingArena(cell->arenaHeader());
+}
+
+inline void
 ArenaLists::prepareForIncrementalGC(JSRuntime *rt)
 {
     for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
         FreeSpan *headSpan = &freeLists[i];
         if (!headSpan->isEmpty()) {
             ArenaHeader *aheader = headSpan->arenaHeader();
             aheader->allocatedDuringIncremental = true;
             rt->gcMarker.delayMarkingArena(aheader);
@@ -1844,294 +1864,16 @@ bool
 SliceBudget::checkOverBudget()
 {
     bool over = PRMJ_Now() > deadline;
     if (!over)
         counter = CounterReset;
     return over;
 }
 
-/*
- * DoNotTraceWeakMaps: the GC is recomputing the liveness of WeakMap entries,
- * so we delay visting entries.
- */
-GCMarker::GCMarker(JSRuntime *rt)
-  : JSTracer(rt, nullptr, DoNotTraceWeakMaps),
-    stack(size_t(-1)),
-    color(BLACK),
-    started(false),
-    unmarkedArenaStackTop(nullptr),
-    markLaterArenas(0),
-    grayBufferState(GRAY_BUFFER_UNUSED)
-{
-}
-
-bool
-GCMarker::init(JSGCMode gcMode)
-{
-    return stack.init(gcMode);
-}
-
-void
-GCMarker::start()
-{
-    JS_ASSERT(!started);
-    started = true;
-    color = BLACK;
-
-    JS_ASSERT(!unmarkedArenaStackTop);
-    JS_ASSERT(markLaterArenas == 0);
-
-}
-
-void
-GCMarker::stop()
-{
-    JS_ASSERT(isDrained());
-
-    JS_ASSERT(started);
-    started = false;
-
-    JS_ASSERT(!unmarkedArenaStackTop);
-    JS_ASSERT(markLaterArenas == 0);
-
-    /* Free non-ballast stack memory. */
-    stack.reset();
-
-    resetBufferedGrayRoots();
-    grayBufferState = GRAY_BUFFER_UNUSED;
-}
-
-void
-GCMarker::reset()
-{
-    color = BLACK;
-
-    stack.reset();
-    JS_ASSERT(isMarkStackEmpty());
-
-    while (unmarkedArenaStackTop) {
-        ArenaHeader *aheader = unmarkedArenaStackTop;
-        JS_ASSERT(aheader->hasDelayedMarking);
-        JS_ASSERT(markLaterArenas);
-        unmarkedArenaStackTop = aheader->getNextDelayedMarking();
-        aheader->unsetDelayedMarking();
-        aheader->markOverflow = 0;
-        aheader->allocatedDuringIncremental = 0;
-        markLaterArenas--;
-    }
-    JS_ASSERT(isDrained());
-    JS_ASSERT(!markLaterArenas);
-}
-
-/*
- * When the native stack is low, the GC does not call JS_TraceChildren to mark
- * the reachable "children" of the thing. Rather the thing is put aside and
- * JS_TraceChildren is called later with more space on the C stack.
- *
- * To implement such delayed marking of the children with minimal overhead for
- * the normal case of sufficient native stack, the code adds a field per
- * arena. The field markingDelay->link links all arenas with delayed things
- * into a stack list with the pointer to stack top in
- * GCMarker::unmarkedArenaStackTop. delayMarkingChildren adds
- * arenas to the stack as necessary while markDelayedChildren pops the arenas
- * from the stack until it empties.
- */
-
-inline void
-GCMarker::delayMarkingArena(ArenaHeader *aheader)
-{
-    if (aheader->hasDelayedMarking) {
-        /* Arena already scheduled to be marked later */
-        return;
-    }
-    aheader->setNextDelayedMarking(unmarkedArenaStackTop);
-    unmarkedArenaStackTop = aheader;
-    markLaterArenas++;
-}
-
-void
-GCMarker::delayMarkingChildren(const void *thing)
-{
-    const Cell *cell = reinterpret_cast<const Cell *>(thing);
-    cell->arenaHeader()->markOverflow = 1;
-    delayMarkingArena(cell->arenaHeader());
-}
-
-void
-GCMarker::markDelayedChildren(ArenaHeader *aheader)
-{
-    if (aheader->markOverflow) {
-        bool always = aheader->allocatedDuringIncremental;
-        aheader->markOverflow = 0;
-
-        for (CellIterUnderGC i(aheader); !i.done(); i.next()) {
-            Cell *t = i.getCell();
-            if (always || t->isMarked()) {
-                t->markIfUnmarked();
-                JS_TraceChildren(this, t, MapAllocToTraceKind(aheader->getAllocKind()));
-            }
-        }
-    } else {
-        JS_ASSERT(aheader->allocatedDuringIncremental);
-        PushArena(this, aheader);
-    }
-    aheader->allocatedDuringIncremental = 0;
-    /*
-     * Note that during an incremental GC we may still be allocating into
-     * aheader. However, prepareForIncrementalGC sets the
-     * allocatedDuringIncremental flag if we continue marking.
-     */
-}
-
-bool
-GCMarker::markDelayedChildren(SliceBudget &budget)
-{
-    gcstats::MaybeAutoPhase ap;
-    if (runtime()->gcIncrementalState == MARK)
-        ap.construct(runtime()->gcStats, gcstats::PHASE_MARK_DELAYED);
-
-    JS_ASSERT(unmarkedArenaStackTop);
-    do {
-        /*
-         * If marking gets delayed at the same arena again, we must repeat
-         * marking of its things. For that we pop arena from the stack and
-         * clear its hasDelayedMarking flag before we begin the marking.
-         */
-        ArenaHeader *aheader = unmarkedArenaStackTop;
-        JS_ASSERT(aheader->hasDelayedMarking);
-        JS_ASSERT(markLaterArenas);
-        unmarkedArenaStackTop = aheader->getNextDelayedMarking();
-        aheader->unsetDelayedMarking();
-        markLaterArenas--;
-        markDelayedChildren(aheader);
-
-        budget.step(150);
-        if (budget.isOverBudget())
-            return false;
-    } while (unmarkedArenaStackTop);
-    JS_ASSERT(!markLaterArenas);
-
-    return true;
-}
-
-#ifdef DEBUG
-void
-GCMarker::checkZone(void *p)
-{
-    JS_ASSERT(started);
-    DebugOnly<Cell *> cell = static_cast<Cell *>(p);
-    JS_ASSERT_IF(cell->isTenured(), cell->tenuredZone()->isCollecting());
-}
-#endif
-
-bool
-GCMarker::hasBufferedGrayRoots() const
-{
-    return grayBufferState == GRAY_BUFFER_OK;
-}
-
-void
-GCMarker::startBufferingGrayRoots()
-{
-    JS_ASSERT(grayBufferState == GRAY_BUFFER_UNUSED);
-    grayBufferState = GRAY_BUFFER_OK;
-    for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
-        JS_ASSERT(zone->gcGrayRoots.empty());
-
-    JS_ASSERT(!callback);
-    callback = GrayCallback;
-    JS_ASSERT(IS_GC_MARKING_TRACER(this));
-}
-
-void
-GCMarker::endBufferingGrayRoots()
-{
-    JS_ASSERT(callback == GrayCallback);
-    callback = nullptr;
-    JS_ASSERT(IS_GC_MARKING_TRACER(this));
-    JS_ASSERT(grayBufferState == GRAY_BUFFER_OK ||
-              grayBufferState == GRAY_BUFFER_FAILED);
-}
-
-void
-GCMarker::resetBufferedGrayRoots()
-{
-    for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
-        zone->gcGrayRoots.clearAndFree();
-}
-
-void
-GCMarker::markBufferedGrayRoots(JS::Zone *zone)
-{
-    JS_ASSERT(grayBufferState == GRAY_BUFFER_OK);
-    JS_ASSERT(zone->isGCMarkingGray());
-
-    for (GrayRoot *elem = zone->gcGrayRoots.begin(); elem != zone->gcGrayRoots.end(); elem++) {
-#ifdef DEBUG
-        setTracingDetails(elem->debugPrinter, elem->debugPrintArg, elem->debugPrintIndex);
-#endif
-        void *tmp = elem->thing;
-        setTracingLocation((void *)&elem->thing);
-        MarkKind(this, &tmp, elem->kind);
-        JS_ASSERT(tmp == elem->thing);
-    }
-}
-
-void
-GCMarker::appendGrayRoot(void *thing, JSGCTraceKind kind)
-{
-    JS_ASSERT(started);
-
-    if (grayBufferState == GRAY_BUFFER_FAILED)
-        return;
-
-    GrayRoot root(thing, kind);
-#ifdef DEBUG
-    root.debugPrinter = debugPrinter();
-    root.debugPrintArg = debugPrintArg();
-    root.debugPrintIndex = debugPrintIndex();
-#endif
-
-    Zone *zone = static_cast<Cell *>(thing)->tenuredZone();
-    if (zone->isCollecting()) {
-        zone->maybeAlive = true;
-        if (!zone->gcGrayRoots.append(root)) {
-            resetBufferedGrayRoots();
-            grayBufferState = GRAY_BUFFER_FAILED;
-        }
-    }
-}
-
-void
-GCMarker::GrayCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind)
-{
-    JS_ASSERT(thingp);
-    JS_ASSERT(*thingp);
-    GCMarker *gcmarker = static_cast<GCMarker *>(trc);
-    gcmarker->appendGrayRoot(*thingp, kind);
-}
-
-size_t
-GCMarker::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
-{
-    size_t size = stack.sizeOfExcludingThis(mallocSizeOf);
-    for (ZonesIter zone(runtime(), WithAtoms); !zone.done(); zone.next())
-        size += zone->gcGrayRoots.sizeOfExcludingThis(mallocSizeOf);
-    return size;
-}
-
-void
-js::SetMarkStackLimit(JSRuntime *rt, size_t limit)
-{
-    JS_ASSERT(!rt->isHeapBusy());
-    AutoStopVerifyingBarriers pauseVerification(rt, false);
-    rt->gcMarker.setMaxCapacity(limit);
-}
-
 void
 js::MarkCompartmentActive(InterpreterFrame *fp)
 {
     fp->script()->compartment()->zone()->active = true;
 }
 
 static void
 RequestInterrupt(JSRuntime *rt, JS::gcreason::Reason reason)
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -10,17 +10,16 @@
 #define jsgc_h
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jslock.h"
 #include "jsobj.h"
 
-#include "gc/Tracer.h"
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "js/Vector.h"
 
 class JSAtom;
 struct JSCompartment;
 class JSFlatString;
 class JSLinearString;
@@ -925,352 +924,29 @@ struct GCChunkHasher {
         JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask));
         JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask));
         return k == l;
     }
 };
 
 typedef HashSet<js::gc::Chunk *, GCChunkHasher, SystemAllocPolicy> GCChunkSet;
 
-static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
-static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
-
-template<class T>
-struct MarkStack {
-    T *stack_;
-    T *tos_;
-    T *end_;
-
-    // The capacity we start with and reset() to.
-    size_t baseCapacity_;
-    size_t maxCapacity_;
-
-    MarkStack(size_t maxCapacity)
-      : stack_(nullptr),
-        tos_(nullptr),
-        end_(nullptr),
-        baseCapacity_(0),
-        maxCapacity_(maxCapacity)
-    {}
-
-    ~MarkStack() {
-        js_free(stack_);
-    }
-
-    size_t capacity() { return end_ - stack_; }
-
-    ptrdiff_t position() const { return tos_ - stack_; }
-
-    void setStack(T *stack, size_t tosIndex, size_t capacity) {
-        stack_ = stack;
-        tos_ = stack + tosIndex;
-        end_ = stack + capacity;
-    }
-
-    void setBaseCapacity(JSGCMode mode) {
-        switch (mode) {
-          case JSGC_MODE_GLOBAL:
-          case JSGC_MODE_COMPARTMENT:
-            baseCapacity_ = NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY;
-            break;
-          case JSGC_MODE_INCREMENTAL:
-            baseCapacity_ = INCREMENTAL_MARK_STACK_BASE_CAPACITY;
-            break;
-          default:
-            MOZ_ASSUME_UNREACHABLE("bad gc mode");
-        }
-
-        if (baseCapacity_ > maxCapacity_)
-            baseCapacity_ = maxCapacity_;
-    }
-
-    bool init(JSGCMode gcMode) {
-        setBaseCapacity(gcMode);
-
-        JS_ASSERT(!stack_);
-        T *newStack = js_pod_malloc<T>(baseCapacity_);
-        if (!newStack)
-            return false;
-
-        setStack(newStack, 0, baseCapacity_);
-        return true;
-    }
-
-    void setMaxCapacity(size_t maxCapacity) {
-        JS_ASSERT(isEmpty());
-        maxCapacity_ = maxCapacity;
-        if (baseCapacity_ > maxCapacity_)
-            baseCapacity_ = maxCapacity_;
-
-        reset();
-    }
-
-    bool push(T item) {
-        if (tos_ == end_) {
-            if (!enlarge(1))
-                return false;
-        }
-        JS_ASSERT(tos_ < end_);
-        *tos_++ = item;
-        return true;
-    }
-
-    bool push(T item1, T item2, T item3) {
-        T *nextTos = tos_ + 3;
-        if (nextTos > end_) {
-            if (!enlarge(3))
-                return false;
-            nextTos = tos_ + 3;
-        }
-        JS_ASSERT(nextTos <= end_);
-        tos_[0] = item1;
-        tos_[1] = item2;
-        tos_[2] = item3;
-        tos_ = nextTos;
-        return true;
-    }
-
-    bool isEmpty() const {
-        return tos_ == stack_;
-    }
-
-    T pop() {
-        JS_ASSERT(!isEmpty());
-        return *--tos_;
-    }
-
-    void reset() {
-        if (capacity() == baseCapacity_) {
-            // No size change; keep the current stack.
-            setStack(stack_, 0, baseCapacity_);
-            return;
-        }
-
-        T *newStack = (T *)js_realloc(stack_, sizeof(T) * baseCapacity_);
-        if (!newStack) {
-            // If the realloc fails, just keep using the existing stack; it's
-            // not ideal but better than failing.
-            newStack = stack_;
-            baseCapacity_ = capacity();
-        }
-        setStack(newStack, 0, baseCapacity_);
-    }
-
-    /* Grow the stack, ensuring there is space for at least count elements. */
-    bool enlarge(unsigned count) {
-        size_t newCapacity = Min(maxCapacity_, capacity() * 2);
-        if (newCapacity < capacity() + count)
-            return false;
-
-        size_t tosIndex = position();
-
-        T *newStack = (T *)js_realloc(stack_, sizeof(T) * newCapacity);
-        if (!newStack)
-            return false;
-
-        setStack(newStack, tosIndex, newCapacity);
-        return true;
-    }
-
-    void setGCMode(JSGCMode gcMode) {
-        // The mark stack won't be resized until the next call to reset(), but
-        // that will happen at the end of the next GC.
-        setBaseCapacity(gcMode);
-    }
-
-    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
-        return mallocSizeOf(stack_);
-    }
-};
-
 struct GrayRoot {
     void *thing;
     JSGCTraceKind kind;
 #ifdef DEBUG
     JSTraceNamePrinter debugPrinter;
     const void *debugPrintArg;
     size_t debugPrintIndex;
 #endif
 
     GrayRoot(void *thing, JSGCTraceKind kind)
         : thing(thing), kind(kind) {}
 };
 
-struct GCMarker : public JSTracer {
-  private:
-    /*
-     * We use a common mark stack to mark GC things of different types and use
-     * the explicit tags to distinguish them when it cannot be deduced from
-     * the context of push or pop operation.
-     */
-    enum StackTag {
-        ValueArrayTag,
-        ObjectTag,
-        TypeTag,
-        XmlTag,
-        SavedValueArrayTag,
-        JitCodeTag,
-        LastTag = JitCodeTag
-    };
-
-    static const uintptr_t StackTagMask = 7;
-
-    static void staticAsserts() {
-        JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag));
-        JS_STATIC_ASSERT(StackTagMask <= gc::CellMask);
-    }
-
-  public:
-    explicit GCMarker(JSRuntime *rt);
-    bool init(JSGCMode gcMode);
-
-    void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
-    size_t maxCapacity() const { return stack.maxCapacity_; }
-
-    void start();
-    void stop();
-    void reset();
-
-    void pushObject(ObjectImpl *obj) {
-        pushTaggedPtr(ObjectTag, obj);
-    }
-
-    void pushType(types::TypeObject *type) {
-        pushTaggedPtr(TypeTag, type);
-    }
-
-    void pushJitCode(jit::JitCode *code) {
-        pushTaggedPtr(JitCodeTag, code);
-    }
-
-    uint32_t getMarkColor() const {
-        return color;
-    }
-
-    /*
-     * Care must be taken changing the mark color from gray to black. The cycle
-     * collector depends on the invariant that there are no black to gray edges
-     * in the GC heap. This invariant lets the CC not trace through black
-     * objects. If this invariant is violated, the cycle collector may free
-     * objects that are still reachable.
-     */
-    void setMarkColorGray() {
-        JS_ASSERT(isDrained());
-        JS_ASSERT(color == gc::BLACK);
-        color = gc::GRAY;
-    }
-
-    void setMarkColorBlack() {
-        JS_ASSERT(isDrained());
-        JS_ASSERT(color == gc::GRAY);
-        color = gc::BLACK;
-    }
-
-    inline void delayMarkingArena(gc::ArenaHeader *aheader);
-    void delayMarkingChildren(const void *thing);
-    void markDelayedChildren(gc::ArenaHeader *aheader);
-    bool markDelayedChildren(SliceBudget &budget);
-    bool hasDelayedChildren() const {
-        return !!unmarkedArenaStackTop;
-    }
-
-    bool isDrained() {
-        return isMarkStackEmpty() && !unmarkedArenaStackTop;
-    }
-
-    bool drainMarkStack(SliceBudget &budget);
-
-    /*
-     * Gray marking must be done after all black marking is complete. However,
-     * we do not have write barriers on XPConnect roots. Therefore, XPConnect
-     * roots must be accumulated in the first slice of incremental GC. We
-     * accumulate these roots in the each compartment's gcGrayRoots vector and
-     * then mark them later, after black marking is complete for each
-     * compartment. This accumulation can fail, but in that case we switch to
-     * non-incremental GC.
-     */
-    bool hasBufferedGrayRoots() const;
-    void startBufferingGrayRoots();
-    void endBufferingGrayRoots();
-    void resetBufferedGrayRoots();
-    void markBufferedGrayRoots(JS::Zone *zone);
-
-    static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind);
-
-    void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
-
-    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
-
-    MarkStack<uintptr_t> stack;
-
-  private:
-#ifdef DEBUG
-    void checkZone(void *p);
-#else
-    void checkZone(void *p) {}
-#endif
-
-    void pushTaggedPtr(StackTag tag, void *ptr) {
-        checkZone(ptr);
-        uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
-        JS_ASSERT(!(addr & StackTagMask));
-        if (!stack.push(addr | uintptr_t(tag)))
-            delayMarkingChildren(ptr);
-    }
-
-    void pushValueArray(JSObject *obj, void *start, void *end) {
-        checkZone(obj);
-
-        JS_ASSERT(start <= end);
-        uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
-        uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
-        uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
-
-        /*
-         * Push in the reverse order so obj will be on top. If we cannot push
-         * the array, we trigger delay marking for the whole object.
-         */
-        if (!stack.push(endAddr, startAddr, tagged))
-            delayMarkingChildren(obj);
-    }
-
-    bool isMarkStackEmpty() {
-        return stack.isEmpty();
-    }
-
-    bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
-    void saveValueRanges();
-    inline void processMarkStackTop(SliceBudget &budget);
-    void processMarkStackOther(uintptr_t tag, uintptr_t addr);
-
-    void appendGrayRoot(void *thing, JSGCTraceKind kind);
-
-    /* The color is only applied to objects and functions. */
-    uint32_t color;
-
-    mozilla::DebugOnly<bool> started;
-
-    /* Pointer to the top of the stack of arenas we are delaying marking on. */
-    js::gc::ArenaHeader *unmarkedArenaStackTop;
-    /* Count of arenas that are currently in the stack. */
-    mozilla::DebugOnly<size_t> markLaterArenas;
-
-    enum GrayBufferState
-    {
-        GRAY_BUFFER_UNUSED,
-        GRAY_BUFFER_OK,
-        GRAY_BUFFER_FAILED
-    };
-
-    GrayBufferState grayBufferState;
-};
-
-void
-SetMarkStackLimit(JSRuntime *rt, size_t limit);
-
 void
 MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
 
 typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk);
 typedef void (*IterateZoneCallback)(JSRuntime *rt, void *data, JS::Zone *zone);
 typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena,
                                      JSGCTraceKind traceKind, size_t thingSize);
 typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing,
@@ -1315,22 +991,16 @@ extern void
 IterateScripts(JSRuntime *rt, JSCompartment *compartment,
                void *data, IterateScriptCallback scriptCallback);
 
 } /* namespace js */
 
 extern void
 js_FinalizeStringRT(JSRuntime *rt, JSString *str);
 
-/*
- * Macro to test if a traversal is the marking phase of the GC.
- */
-#define IS_GC_MARKING_TRACER(trc) \
-    ((trc)->callback == nullptr || (trc)->callback == GCMarker::GrayCallback)
-
 namespace js {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JS::Zone *zone, JSPrincipals *principals,
                const JS::CompartmentOptions &options);
 
 namespace gc {
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -13,16 +13,17 @@
  * A JS object consists of a possibly-shared object descriptor containing
  * ordered property names, called the map; and a dense vector of property
  * values, called slots.  The map/slot pointer pair is GC'ed, while the map
  * is reference counted and the slot vector is malloc'ed.
  */
 
 #include "mozilla/MemoryReporting.h"
 
+#include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "js/GCAPI.h"
 #include "vm/ObjectImpl.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
 
 namespace JS {
 struct ObjectsExtraSizes;
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -29,16 +29,17 @@
 #include "frontend/ParseMaps.h"
 #ifdef JSGC_GENERATIONAL
 # include "gc/Nursery.h"
 #endif
 #include "gc/Statistics.h"
 #ifdef JSGC_GENERATIONAL
 # include "gc/StoreBuffer.h"
 #endif
+#include "gc/Tracer.h"
 #ifdef XP_MACOSX
 # include "jit/AsmJSSignalHandlers.h"
 #endif
 #include "js/HashTable.h"
 #include "js/Vector.h"
 #include "vm/CommonPropertyNames.h"
 #include "vm/DateTime.h"
 #include "vm/MallocProvider.h"
new file mode 100644
--- /dev/null
+++ b/media/libopus/Makefile.in
@@ -0,0 +1,30 @@
+# 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/.
+
+
+ifdef GNU_AS
+ifeq ($(CPU_ARCH),arm)
+
+# These flags are a lie; they're just used to enable the requisite
+# opcodes; actual arch detection is done at runtime.
+ASFLAGS = -march=armv7-a -mfpu=neon
+
+celt_pitch_xcorr_arm-gnu.$(ASM_SUFFIX): celt_arm_dir celt/arm/armopts-gnu.S
+
+# we need to throw this here because upstream has this path hardcoded
+celt_arm_dir:
+	mkdir -p celt/arm
+
+# armopts needs a specific rule, because arm2gnu.pl will always add the .S
+# suffix when translating the files that include it.
+celt/arm/armopts-gnu.S: celt/arm/armopts.s
+	$(PERL) $(srcdir)/celt/arm/arm2gnu.pl < $< > $@
+# For all others, we can use an implicit rule with the configured $(ASM_SUFFIX).
+%-gnu.$(ASM_SUFFIX): celt/arm/%.s
+	$(PERL) $(srcdir)/celt/arm/arm2gnu.pl < $< > $@
+
+endif
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100755
--- /dev/null
+++ b/media/libopus/celt/arm/arm2gnu.pl
@@ -0,0 +1,316 @@
+#!/usr/bin/perl
+
+my $bigend;  # little/big endian
+my $nxstack;
+
+$nxstack = 0;
+
+eval 'exec /usr/local/bin/perl -S $0 ${1+"$@"}'
+    if $running_under_some_shell;
+
+while ($ARGV[0] =~ /^-/) {
+    $_ = shift;
+  last if /^--/;
+    if (/^-n/) {
+    $nflag++;
+    next;
+    }
+    die "I don't recognize this switch: $_\\n";
+}
+$printit++ unless $nflag;
+
+$\ = "\n";      # automatically add newline on print
+$n=0;
+
+$thumb = 0;     # ARM mode by default, not Thumb.
+@proc_stack = ();
+
+LINE:
+while (<>) {
+
+    # For ADRLs we need to add a new line after the substituted one.
+    $addPadding = 0;
+
+    # First, we do not dare to touch *anything* inside double quotes, do we?
+    # Second, if you want a dollar character in the string,
+    # insert two of them -- that's how ARM C and assembler treat strings.
+    s/^([A-Za-z_]\w*)[ \t]+DCB[ \t]*\"/$1:   .ascii \"/   && do { s/\$\$/\$/g; next };
+    s/\bDCB\b[ \t]*\"/.ascii \"/                          && do { s/\$\$/\$/g; next };
+    s/^(\S+)\s+RN\s+(\S+)/$1 .req r$2/                    && do { s/\$\$/\$/g; next };
+    # If there's nothing on a line but a comment, don't try to apply any further
+    #  substitutions (this is a cheap hack to avoid mucking up the license header)
+    s/^([ \t]*);/$1@/                                     && do { s/\$\$/\$/g; next };
+    # If substituted -- leave immediately !
+
+    s/@/,:/;
+    s/;/@/;
+    while ( /@.*'/ ) {
+      s/(@.*)'/$1/g;
+    }
+    s/\{FALSE\}/0/g;
+    s/\{TRUE\}/1/g;
+    s/\{(\w\w\w\w+)\}/$1/g;
+    s/\bINCLUDE[ \t]*([^ \t\n]+)/.include \"$1\"/;
+    s/\bGET[ \t]*([^ \t\n]+)/.include \"${ my $x=$1; $x =~ s|\.s|-gnu.S|; \$x }\"/;
+    s/\bIMPORT\b/.extern/;
+    s/\bEXPORT\b/.global/;
+    s/^(\s+)\[/$1IF/;
+    s/^(\s+)\|/$1ELSE/;
+    s/^(\s+)\]/$1ENDIF/;
+    s/IF *:DEF:/ .ifdef/;
+    s/IF *:LNOT: *:DEF:/ .ifndef/;
+    s/ELSE/ .else/;
+    s/ENDIF/ .endif/;
+
+    if( /\bIF\b/ ) {
+      s/\bIF\b/ .if/;
+      s/=/==/;
+    }
+    if ( $n == 2) {
+        s/\$/\\/g;
+    }
+    if ($n == 1) {
+        s/\$//g;
+        s/label//g;
+    $n = 2;
+      }
+    if ( /MACRO/ ) {
+      s/MACRO *\n/.macro/;
+      $n=1;
+    }
+    if ( /\bMEND\b/ ) {
+      s/\bMEND\b/.endm/;
+      $n=0;
+    }
+
+    # ".rdata" doesn't work in 'as' version 2.13.2, as it is ".rodata" there.
+    #
+    if ( /\bAREA\b/ ) {
+        my $align;
+        $align = "2";
+        if ( /ALIGN=(\d+)/ ) {
+            $align = $1;
+        }
+        if ( /CODE/ ) {
+            $nxstack = 1;
+        }
+        s/^(.+)CODE(.+)READONLY(.*)/    .text/;
+        s/^(.+)DATA(.+)READONLY(.*)/    .section .rdata/;
+        s/^(.+)\|\|\.data\|\|(.+)/    .data/;
+        s/^(.+)\|\|\.bss\|\|(.+)/    .bss/;
+        s/$/;   .p2align $align/;
+        # Enable NEON instructions but don't produce a binary that requires
+        # ARMv7. RVCT does not have equivalent directives, so we just do this
+        # for all CODE areas.
+        if ( /.text/ ) {
+            # Separating .arch, .fpu, etc., by semicolons does not work (gas
+            # thinks the semicolon is part of the arch name, even when there's
+            # whitespace separating them). Sadly this means our line numbers
+            # won't match the original source file (we could use the .line
+            # directive, which is documented to be obsolete, but then gdb will
+            # show the wrong line in the translated source file).
+            s/$/;   .arch armv7-a\n   .fpu neon\n   .object_arch armv4t/;
+        }
+    }
+
+    s/\|\|\.constdata\$(\d+)\|\|/.L_CONST$1/;       # ||.constdata$3||
+    s/\|\|\.bss\$(\d+)\|\|/.L_BSS$1/;               # ||.bss$2||
+    s/\|\|\.data\$(\d+)\|\|/.L_DATA$1/;             # ||.data$2||
+    s/\|\|([a-zA-Z0-9_]+)\@([a-zA-Z0-9_]+)\|\|/@ $&/;
+    s/^(\s+)\%(\s)/    .space $1/;
+
+    s/\|(.+)\.(\d+)\|/\.$1_$2/;                     # |L80.123| -> .L80_123
+    s/\bCODE32\b/.code 32/ && do {$thumb = 0};
+    s/\bCODE16\b/.code 16/ && do {$thumb = 1};
+    if (/\bPROC\b/)
+    {
+        my $prefix;
+        my $proc;
+        /^([A-Za-z_\.]\w+)\b/;
+        $proc = $1;
+        $prefix = "";
+        if ($proc)
+        {
+            $prefix = $prefix.sprintf("\t.type\t%s, %%function; ",$proc);
+            push(@proc_stack, $proc);
+            s/^[A-Za-z_\.]\w+/$&:/;
+        }
+        $prefix = $prefix."\t.thumb_func; " if ($thumb);
+        s/\bPROC\b/@ $&/;
+        $_ = $prefix.$_;
+    }
+    s/^(\s*)(S|Q|SH|U|UQ|UH)ASX\b/$1$2ADDSUBX/;
+    s/^(\s*)(S|Q|SH|U|UQ|UH)SAX\b/$1$2SUBADDX/;
+    if (/\bENDP\b/)
+    {
+        my $proc;
+        s/\bENDP\b/@ $&/;
+        $proc = pop(@proc_stack);
+        $_ = "\t.size $proc, .-$proc".$_ if ($proc);
+    }
+    s/\bSUBT\b/@ $&/;
+    s/\bDATA\b/@ $&/;   # DATA directive is deprecated -- Asm guide, p.7-25
+    s/\bKEEP\b/@ $&/;
+    s/\bEXPORTAS\b/@ $&/;
+    s/\|\|(.)+\bEQU\b/@ $&/;
+    s/\|\|([\w\$]+)\|\|/$1/;
+    s/\bENTRY\b/@ $&/;
+    s/\bASSERT\b/@ $&/;
+    s/\bGBLL\b/@ $&/;
+    s/\bGBLA\b/@ $&/;
+    s/^\W+OPT\b/@ $&/;
+    s/:OR:/|/g;
+    s/:SHL:/<</g;
+    s/:SHR:/>>/g;
+    s/:AND:/&/g;
+    s/:LAND:/&&/g;
+    s/CPSR/cpsr/;
+    s/SPSR/spsr/;
+    s/ALIGN$/.balign 4/;
+    s/ALIGN\s+([0-9x]+)$/.balign $1/;
+    s/psr_cxsf/psr_all/;
+    s/LTORG/.ltorg/;
+    s/^([A-Za-z_]\w*)[ \t]+EQU/ .set $1,/;
+    s/^([A-Za-z_]\w*)[ \t]+SETL/ .set $1,/;
+    s/^([A-Za-z_]\w*)[ \t]+SETA/ .set $1,/;
+    s/^([A-Za-z_]\w*)[ \t]+\*/ .set $1,/;
+
+    #  {PC} + 0xdeadfeed  -->  . + 0xdeadfeed
+    s/\{PC\} \+/ \. +/;
+
+    # Single hex constant on the line !
+    #
+    # >>> NOTE <<<
+    #   Double-precision floats in gcc are always mixed-endian, which means
+    #   bytes in two words are little-endian, but words are big-endian.
+    #   So, 0x0000deadfeed0000 would be stored as 0x0000dead at low address
+    #   and 0xfeed0000 at high address.
+    #
+    s/\bDCFD\b[ \t]+0x([a-fA-F0-9]{8})([a-fA-F0-9]{8})/.long 0x$1, 0x$2/;
+    # Only decimal constants on the line, no hex !
+    s/\bDCFD\b[ \t]+([0-9\.\-]+)/.double $1/;
+
+    # Single hex constant on the line !
+#    s/\bDCFS\b[ \t]+0x([a-f0-9]{8})([a-f0-9]{8})/.long 0x$1, 0x$2/;
+    # Only decimal constants on the line, no hex !
+#    s/\bDCFS\b[ \t]+([0-9\.\-]+)/.double $1/;
+    s/\bDCFS[ \t]+0x/.word 0x/;
+    s/\bDCFS\b/.float/;
+
+    s/^([A-Za-z_]\w*)[ \t]+DCD/$1 .word/;
+    s/\bDCD\b/.word/;
+    s/^([A-Za-z_]\w*)[ \t]+DCW/$1 .short/;
+    s/\bDCW\b/.short/;
+    s/^([A-Za-z_]\w*)[ \t]+DCB/$1 .byte/;
+    s/\bDCB\b/.byte/;
+    s/^([A-Za-z_]\w*)[ \t]+\%/.comm $1,/;
+    s/^[A-Za-z_\.]\w+/$&:/;
+    s/^(\d+)/$1:/;
+    s/\%(\d+)/$1b_or_f/;
+    s/\%[Bb](\d+)/$1b/;
+    s/\%[Ff](\d+)/$1f/;
+    s/\%[Ff][Tt](\d+)/$1f/;
+    s/&([\dA-Fa-f]+)/0x$1/;
+    if ( /\b2_[01]+\b/ ) {
+      s/\b2_([01]+)\b/conv$1&&&&/g;
+      while ( /[01][01][01][01]&&&&/ ) {
+        s/0000&&&&/&&&&0/g;
+        s/0001&&&&/&&&&1/g;
+        s/0010&&&&/&&&&2/g;
+        s/0011&&&&/&&&&3/g;
+        s/0100&&&&/&&&&4/g;
+        s/0101&&&&/&&&&5/g;
+        s/0110&&&&/&&&&6/g;
+        s/0111&&&&/&&&&7/g;
+        s/1000&&&&/&&&&8/g;
+        s/1001&&&&/&&&&9/g;
+        s/1010&&&&/&&&&A/g;
+        s/1011&&&&/&&&&B/g;
+        s/1100&&&&/&&&&C/g;
+        s/1101&&&&/&&&&D/g;
+        s/1110&&&&/&&&&E/g;
+        s/1111&&&&/&&&&F/g;
+      }
+      s/000&&&&/&&&&0/g;
+      s/001&&&&/&&&&1/g;
+      s/010&&&&/&&&&2/g;
+      s/011&&&&/&&&&3/g;
+      s/100&&&&/&&&&4/g;
+      s/101&&&&/&&&&5/g;
+      s/110&&&&/&&&&6/g;
+      s/111&&&&/&&&&7/g;
+      s/00&&&&/&&&&0/g;
+      s/01&&&&/&&&&1/g;
+      s/10&&&&/&&&&2/g;
+      s/11&&&&/&&&&3/g;
+      s/0&&&&/&&&&0/g;
+      s/1&&&&/&&&&1/g;
+      s/conv&&&&/0x/g;
+    }
+
+    if ( /commandline/)
+    {
+        if( /-bigend/)
+        {
+            $bigend=1;
+        }
+    }
+
+    if ( /\bDCDU\b/ )
+    {
+        my $cmd=$_;
+        my $value;
+        my $prefix;
+        my $w1;
+        my $w2;
+        my $w3;
+        my $w4;
+
+        s/\s+DCDU\b/@ $&/;
+
+        $cmd =~ /\bDCDU\b\s+0x(\d+)/;
+        $value = $1;
+        $value =~ /(\w\w)(\w\w)(\w\w)(\w\w)/;
+        $w1 = $1;
+        $w2 = $2;
+        $w3 = $3;
+        $w4 = $4;
+
+        if( $bigend ne "")
+        {
+            # big endian
+            $prefix = "\t.byte\t0x".$w1.";".
+                      "\t.byte\t0x".$w2.";".
+                      "\t.byte\t0x".$w3.";".
+                      "\t.byte\t0x".$w4."; ";
+        }
+        else
+        {
+            # little endian
+            $prefix = "\t.byte\t0x".$w4.";".
+                      "\t.byte\t0x".$w3.";".
+                      "\t.byte\t0x".$w2.";".
+                      "\t.byte\t0x".$w1."; ";
+        }
+        $_=$prefix.$_;
+    }
+
+    if ( /\badrl\b/i )
+    {
+        s/\badrl\s+(\w+)\s*,\s*(\w+)/ldr $1,=$2/i;
+        $addPadding = 1;
+    }
+    s/\bEND\b/@ END/;
+} continue {
+    printf ("%s", $_) if $printit;
+    if ($addPadding != 0)
+    {
+        printf ("   mov r0,r0\n");
+        $addPadding = 0;
+    }
+}
+#If we had a code section, mark that this object doesn't need an executable
+# stack.
+if ($nxstack) {
+    printf ("    .section\t.note.GNU-stack,\"\",\%\%progbits\n");
+}
new file mode 100644
--- /dev/null
+++ b/media/libopus/celt/arm/armopts.s
@@ -0,0 +1,37 @@
+/* Copyright (C) 2013 Mozilla Corporation */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+; Set the following to 1 if we have EDSP instructions
+;  (LDRD/STRD, etc., ARMv5E and later).
+OPUS_ARM_MAY_HAVE_EDSP  * 1
+
+; Set the following to 1 if we have ARMv6 media instructions.
+OPUS_ARM_MAY_HAVE_MEDIA * 1
+
+; Set the following to 1 if we have NEON (some ARMv7)
+OPUS_ARM_MAY_HAVE_NEON  * 1
+
+END
--- a/media/libopus/moz.build
+++ b/media/libopus/moz.build
@@ -14,16 +14,25 @@ EXPORTS.opus += [
 MSVC_ENABLE_PGO = True
 
 FINAL_LIBRARY = 'gkmedias'
 
 DEFINES['OPUS_BUILD'] = True
 DEFINES['OPUS_VERSION'] = '"v1.1-mozilla"'
 DEFINES['USE_ALLOCA'] = True
 
+if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_AS']:
+    DEFINES['OPUS_ARM_ASM'] = True
+    DEFINES['OPUS_ARM_EXTERNAL_ASM'] = True
+    DEFINES['OPUS_ARM_INLINE_ASM'] = True
+    DEFINES['OPUS_ARM_INLINE_EDSP'] = True
+    DEFINES['OPUS_ARM_MAY_HAVE_EDSP'] = True
+    DEFINES['OPUS_ARM_MAY_HAVE_MEDIA'] = True
+    DEFINES['OPUS_ARM_MAY_HAVE_NEON'] = True
+
 if CONFIG['MOZ_DEBUG']:
     DEFINES['ENABLE_ASSERTIONS'] = True
 
 if CONFIG['OS_ARCH'] in ('Linux', 'Darwin', 'DragonFly', 'FreeBSD',
                          'NetBSD', 'OpenBSD'):
     DEFINES['HAVE_LRINTF'] = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
@@ -63,13 +72,27 @@ if CONFIG['MOZ_SAMPLE_TYPE_FLOAT32']:
     SOURCES += silk_sources_float
     SOURCES += opus_sources_float
 else:
     LOCAL_INCLUDES += [
         'silk/fixed',
     ]
     SOURCES += silk_sources_fixed
 
+if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_AS']:
+    SOURCES += celt_sources_arm
+    GENERATED_SOURCES += [ '%s.%s' % (f, CONFIG['ASM_SUFFIX']) for f in [
+        'celt_pitch_xcorr_arm-gnu',
+    ]]
+    # -Os is significantly slower, enable -O3 unless optimization is disabled
+    if CONFIG['MOZ_OPTIMIZE']:
+      CFLAGS += [
+        '-O3',
+      ]
+      CXXFLAGS += [
+        '-O3',
+      ]
+
 # Suppress warnings in third-party code.
 if CONFIG['GNU_CC']:
     CFLAGS += ['-Wno-declaration-after-statement']
     if CONFIG['CLANG_CXX']:
         CFLAGS += ['-Wno-\#pragma-messages']
old mode 100644
new mode 100755
--- a/media/libopus/update.sh
+++ b/media/libopus/update.sh
@@ -6,17 +6,17 @@
 #
 # Usage: ./update.sh <opus_src_directory>
 #
 # Copies the needed files from a directory containing the original
 # libopus source, and applies any local patches we're carrying.
 
 TARGET='.'
 
-STATIC_FILES="COPYING"
+STATIC_FILES="COPYING celt/arm/arm2gnu.pl"
 MK_FILES="opus_sources.mk celt_sources.mk silk_sources.mk \
           opus_headers.mk celt_headers.mk silk_headers.mk"
 
 # Make sure we have a source directory
 if test -z $1 || ! test -r $1/include/opus.h; then
   echo "Update the current directory from a source checkout"
   echo "usage: $0 ../opus"
   exit 1
@@ -44,16 +44,22 @@ done
  
 # copy files into the target directory
 for file in ${STATIC_FILES} ${SRC_FILES} ${HDR_FILES}; do
   cmd="cp $1/${file} ${TARGET}/${file}"
   echo ${cmd}
   ${cmd}
 done
 
+sed \
+ -e s/@OPUS_ARM_MAY_HAVE_EDSP@/1/g \
+ -e s/@OPUS_ARM_MAY_HAVE_MEDIA@/1/g \
+ -e s/@OPUS_ARM_MAY_HAVE_NEON@/1/g \
+ $1/celt/arm/armopts.s.in > ${TARGET}/celt/arm/armopts.s
+
 # query git for the revision we're copying from
 if test -d $1/.git; then
   version=$(cd $1 && git describe --tags --match 'v*' --dirty)
 else
   version="UNKNOWN"
 fi
 echo "copied from revision ${version}"
 # update README revision
new file mode 100644
--- /dev/null
+++ b/mfbt/BinarySearch.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BinarySearch_h
+#define mozilla_BinarySearch_h
+
+#include "mozilla/Assertions.h"
+
+#include <stddef.h>
+
+namespace mozilla {
+
+/*
+ * The algorithm searches the given container 'c' over the sorted index range
+ * [begin, end) for an index 'i' where 'c[i] == target'. If such an index 'i' is
+ * found, BinarySearch returns 'true' and the index is returned via the outparam
+ * 'matchOrInsertionPoint'. If no index is found, BinarySearch returns 'false'
+ * and the outparam returns the first index in [begin, end] where 'target' can
+ * be inserted to maintain sorted order.
+ *
+ * Example:
+ *
+ *   Vector<int> sortedInts = ...
+ *
+ *   size_t match;
+ *   if (BinarySearch(sortedInts, 0, sortedInts.length(), 13, &match))
+ *     printf("found 13 at %lu\n", match);
+ */
+
+template <typename Container, typename T>
+bool
+BinarySearch(const Container &c, size_t begin, size_t end, T target, size_t *matchOrInsertionPoint)
+{
+  MOZ_ASSERT(begin <= end);
+
+  size_t low = begin;
+  size_t high = end;
+  while (low != high) {
+    size_t middle = low + (high - low) / 2;
+    const T &middleValue = c[middle];
+
+    MOZ_ASSERT(c[low] <= c[middle]);
+    MOZ_ASSERT(c[middle] <= c[high - 1]);
+    MOZ_ASSERT(c[low] <= c[high - 1]);
+
+    if (target == middleValue) {
+      *matchOrInsertionPoint = middle;
+      return true;
+    }
+
+    if (target < middleValue)
+      high = middle;
+    else
+      low = middle + 1;
+  }
+
+  *matchOrInsertionPoint = low;
+  return false;
+}
+
+} // namespace mozilla
+
+#endif // mozilla_BinarySearch_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -11,16 +11,17 @@ LIBRARY_NAME = 'mfbt'
 EXPORTS.mozilla = [
     'Alignment.h',
     'AllocPolicy.h',
     'Array.h',
     'ArrayUtils.h',
     'Assertions.h',
     'Atomics.h',
     'Attributes.h',
+    'BinarySearch.h',
     'BloomFilter.h',
     'Casting.h',
     'ChaosMode.h',
     'Char16.h',
     'CheckedInt.h',
     'Compiler.h',
     'Compression.h',
     'Constants.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestBinarySearch.cpp
@@ -0,0 +1,75 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/Vector.h"
+
+using mozilla::Vector;
+using mozilla::BinarySearch;
+
+struct Person
+{
+  int age;
+  int id;
+  Person(int age, int id) : age(age), id(id) {}
+};
+
+struct GetAge
+{
+  Vector<Person> &v;
+  GetAge(Vector<Person> &v) : v(v) {}
+  int operator[](size_t index) const { return v[index].age; }
+};
+
+int main()
+{
+  size_t m;
+
+  Vector<int> v1;
+  v1.append(2);
+  v1.append(4);
+  v1.append(6);
+  v1.append(8);
+
+  MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 1, &m) && m == 0);
+  MOZ_ASSERT( BinarySearch(v1, 0, v1.length(), 2, &m) && m == 0);
+  MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 3, &m) && m == 1);
+  MOZ_ASSERT( BinarySearch(v1, 0, v1.length(), 4, &m) && m == 1);
+  MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 5, &m) && m == 2);
+  MOZ_ASSERT( BinarySearch(v1, 0, v1.length(), 6, &m) && m == 2);
+  MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 7, &m) && m == 3);
+  MOZ_ASSERT( BinarySearch(v1, 0, v1.length(), 8, &m) && m == 3);
+  MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 9, &m) && m == 4);
+
+  MOZ_ASSERT(!BinarySearch(v1, 1, 3, 1, &m) && m == 1);
+  MOZ_ASSERT(!BinarySearch(v1, 1, 3, 2, &m) && m == 1);
+  MOZ_ASSERT(!BinarySearch(v1, 1, 3, 3, &m) && m == 1);
+  MOZ_ASSERT( BinarySearch(v1, 1, 3, 4, &m) && m == 1);
+  MOZ_ASSERT(!BinarySearch(v1, 1, 3, 5, &m) && m == 2);
+  MOZ_ASSERT( BinarySearch(v1, 1, 3, 6, &m) && m == 2);
+  MOZ_ASSERT(!BinarySearch(v1, 1, 3, 7, &m) && m == 3);
+  MOZ_ASSERT(!BinarySearch(v1, 1, 3, 8, &m) && m == 3);
+  MOZ_ASSERT(!BinarySearch(v1, 1, 3, 9, &m) && m == 3);
+
+  MOZ_ASSERT(!BinarySearch(v1, 0, 0, 0, &m) && m == 0);
+  MOZ_ASSERT(!BinarySearch(v1, 0, 0, 9, &m) && m == 0);
+
+  Vector<int> v2;
+  MOZ_ASSERT(!BinarySearch(v2, 0, 0, 0, &m) && m == 0);
+  MOZ_ASSERT(!BinarySearch(v2, 0, 0, 9, &m) && m == 0);
+
+  Vector<Person> v3;
+  v3.append(Person(2, 42));
+  v3.append(Person(4, 13));
+  v3.append(Person(6, 360));
+  MOZ_ASSERT(!BinarySearch(GetAge(v3), 0, v3.length(), 1, &m) && m == 0);
+  MOZ_ASSERT( BinarySearch(GetAge(v3), 0, v3.length(), 2, &m) && m == 0);
+  MOZ_ASSERT(!BinarySearch(GetAge(v3), 0, v3.length(), 3, &m) && m == 1);
+  MOZ_ASSERT( BinarySearch(GetAge(v3), 0, v3.length(), 4, &m) && m == 1);
+  MOZ_ASSERT(!BinarySearch(GetAge(v3), 0, v3.length(), 5, &m) && m == 2);
+  MOZ_ASSERT( BinarySearch(GetAge(v3), 0, v3.length(), 6, &m) && m == 2);
+  MOZ_ASSERT(!BinarySearch(GetAge(v3), 0, v3.length(), 7, &m) && m == 3);
+}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -1,16 +1,17 @@
 # -*- 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/.
 
 CPP_UNIT_TESTS += [
     'TestAtomics.cpp',
+    'TestBinarySearch.cpp',
     'TestBloomFilter.cpp',
     'TestCasting.cpp',
     'TestCeilingFloor.cpp',
     'TestCheckedInt.cpp',
     'TestCountPopulation.cpp',
     'TestCountZeroes.cpp',
     'TestEndian.cpp',
     'TestEnumSet.cpp',
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -87,16 +87,17 @@ nsFtpState::nsFtpState()
     , mRetryPass(false)
     , mStorReplyReceived(false)
     , mInternalError(NS_OK)
     , mReconnectAndLoginAgain(false)
     , mCacheConnection(true)
     , mPort(21)
     , mAddressChecked(false)
     , mServerIsIPv6(false)
+    , mUseUTF8(false)
     , mControlStatus(NS_OK)
     , mDeferredCallbackPending(false)
 {
     LOG_ALWAYS(("FTP:(%x) nsFtpState created", this));
 
     // make sure handler stays around
     NS_ADDREF(gFtpHandler);
 }
@@ -271,17 +272,22 @@ nsFtpState::EstablishControlConnection()
         {
             // set stream listener of the control connection to be us.        
             mControlConnection->WaitData(this);
             
             // read cached variables into us. 
             mServerType = mControlConnection->mServerType;           
             mPassword   = mControlConnection->mPassword;
             mPwd        = mControlConnection->mPwd;
+            mUseUTF8    = mControlConnection->mUseUTF8;
             mTryingCachedControl = true;
+
+            // we have to set charset to connection if server supports utf-8
+            if (mUseUTF8)
+                mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
             
             // we're already connected to this server, skip login.
             mState = FTP_S_PASV;
             mResponseCode = 530;  // assume the control connection was dropped.
             mControlStatus = NS_OK;
             mReceivedControlData = false;  // For this request, we have not.
 
             // if we succeed, return.  Otherwise, we need to create a transport
@@ -641,17 +647,53 @@ nsFtpState::Process()
             
           case FTP_R_PWD:
             mState = R_pwd();
 
             if (FTP_ERROR == mState) 
                 mInternalError = NS_ERROR_FTP_PWD;
 
             break;
-            
+
+// FEAT for RFC2640 support
+          case FTP_S_FEAT:
+            rv = S_feat();
+
+            if (NS_FAILED(rv))
+                mInternalError = rv;
+
+            MoveToNextState(FTP_R_FEAT);
+            break;
+
+          case FTP_R_FEAT:
+            mState = R_feat();
+
+            // Don't want to overwrite a more explicit status code
+            if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
+                mInternalError = NS_ERROR_FAILURE;
+            break;
+
+// OPTS for some non-RFC2640-compliant servers support
+          case FTP_S_OPTS:
+            rv = S_opts();
+
+            if (NS_FAILED(rv))
+                mInternalError = rv;
+
+            MoveToNextState(FTP_R_OPTS);
+            break;
+
+          case FTP_R_OPTS:
+            mState = R_opts();
+
+            // Don't want to overwrite a more explicit status code
+            if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
+                mInternalError = NS_ERROR_FAILURE;
+            break;
+
           default:
             ;
             
         }
     }
 
     return rv;
 }
@@ -918,25 +960,25 @@ nsFtpState::R_syst() {
                 prompter->Alert(nullptr, formattedString.get());
             
             // since we just alerted the user, clear mResponseMsg,
             // which is displayed to the user.
             mResponseMsg = "";
             return FTP_ERROR;
         }
         
-        return FTP_S_PWD;
+        return FTP_S_FEAT;
     }
 
     if (mResponseCode/100 == 5) {   
         // server didn't like the SYST command.  Probably (500, 501, 502)
         // No clue.  We will just hope it is UNIX type server.
         mServerType = FTP_UNIX_TYPE;
 
-        return FTP_S_PWD;
+        return FTP_S_FEAT;
     }
     return FTP_ERROR;
 }
 
 nsresult
 nsFtpState::S_acct() {
     return SendFTPCommand(NS_LITERAL_CSTRING("ACCT noaccount" CRLF));
 }
@@ -1130,16 +1172,20 @@ nsFtpState::S_list() {
     }
     
     if (mCacheEntry) {
         // save off the server type if we are caching.
         nsAutoCString serverType;
         serverType.AppendInt(mServerType);
         mCacheEntry->SetMetaDataElement("servertype", serverType.get());
 
+        nsAutoCString useUTF8;
+        useUTF8.AppendInt(mUseUTF8);
+        mCacheEntry->SetMetaDataElement("useUTF8", useUTF8.get());
+
         // open cache entry for writing, and configure it to receive data.
         if (NS_FAILED(InstallCacheListener())) {
             mCacheEntry->AsyncDoom(nullptr);
             mCacheEntry = nullptr;
         }
     }
 
     // dir listings aren't resumable
@@ -1546,16 +1592,49 @@ nsFtpState::R_pasv() {
         mDataStream = do_QueryInterface(input);
     }
 
     if (mRETRFailed || mPath.IsEmpty() || mPath.Last() == '/')
         return FTP_S_CWD;
     return FTP_S_SIZE;
 }
 
+nsresult
+nsFtpState::S_feat() {
+    return SendFTPCommand(NS_LITERAL_CSTRING("FEAT" CRLF));
+}
+
+FTP_STATE
+nsFtpState::R_feat() {
+    if (mResponseCode/100 == 2) {
+        if (mResponseMsg.Find(NS_LITERAL_CSTRING(CRLF " UTF8" CRLF), true) > -1) {
+            // This FTP server supports UTF-8 encoding
+            mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
+            mUseUTF8 = true;
+            return FTP_S_OPTS;
+        }
+    }
+
+    mUseUTF8 = false;
+    return FTP_S_PWD;
+}
+
+nsresult
+nsFtpState::S_opts() {
+     // This command is for compatibility of old FTP spec (IETF Draft)
+    return SendFTPCommand(NS_LITERAL_CSTRING("OPTS UTF8 ON" CRLF));
+}
+
+FTP_STATE
+nsFtpState::R_opts() {
+    // Ignore error code because "OPTS UTF8 ON" is for compatibility of
+    // FTP server using IETF draft
+    return FTP_S_PWD;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsIRequest methods:
 
 static inline
 uint32_t GetFtpTime()
 {
     return uint32_t(PR_Now() / PR_USEC_PER_SEC);
 }
@@ -1842,16 +1921,17 @@ nsFtpState::KillControlConnection()
         mCacheConnection) {
 
         LOG_ALWAYS(("FTP:(%p) caching CC(%p)", this, mControlConnection.get()));
 
         // Store connection persistent data
         mControlConnection->mServerType = mServerType;           
         mControlConnection->mPassword = mPassword;
         mControlConnection->mPwd = mPwd;
+        mControlConnection->mUseUTF8 = mUseUTF8;
         
         nsresult rv = NS_OK;
         // Don't cache controlconnection if anonymous (bug #473371)
         if (!mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
             rv = gFtpHandler->InsertConnection(mChannel->URI(),
                                                mControlConnection);
         // Can't cache it?  Kill it then.  
         mControlConnection->Disconnect(rv);
@@ -1860,38 +1940,38 @@ nsFtpState::KillControlConnection()
     }     
 
     mControlConnection = nullptr;
 }
 
 class nsFtpAsyncAlert : public nsRunnable
 {
 public:
-    nsFtpAsyncAlert(nsIPrompt *aPrompter, nsACString& aResponseMsg)
+    nsFtpAsyncAlert(nsIPrompt *aPrompter, nsString aResponseMsg)
         : mPrompter(aPrompter)
         , mResponseMsg(aResponseMsg)
     {
         MOZ_COUNT_CTOR(nsFtpAsyncAlert);
     }
     virtual ~nsFtpAsyncAlert()
     {
         MOZ_COUNT_DTOR(nsFtpAsyncAlert);
     }
     NS_IMETHOD Run()
     {
         if (mPrompter) {
-            mPrompter->Alert(nullptr, NS_ConvertASCIItoUTF16(mResponseMsg).get());
+            mPrompter->Alert(nullptr, mResponseMsg.get());
         }
         return NS_OK;
     }
 private:
     nsCOMPtr<nsIPrompt> mPrompter;
-    nsCString mResponseMsg;
+    nsString mResponseMsg;
 };
-    
+
 
 nsresult
 nsFtpState::StopProcessing()
 {
     // Only do this function once.
     if (!mKeepRunning)
         return NS_OK;
     mKeepRunning = false;
@@ -1905,18 +1985,24 @@ nsFtpState::StopProcessing()
     if (NS_FAILED(mInternalError) && !mResponseMsg.IsEmpty()) {
         // check to see if the control status is bad.
         // web shell wont throw an alert.  we better:
 
         // XXX(darin): this code should not be dictating UI like this!
         nsCOMPtr<nsIPrompt> prompter;
         mChannel->GetCallback(prompter);
         if (prompter) {
-            nsCOMPtr<nsIRunnable> alertEvent =
-                new nsFtpAsyncAlert(prompter, mResponseMsg);
+            nsCOMPtr<nsIRunnable> alertEvent;
+            if (mUseUTF8) {
+                alertEvent = new nsFtpAsyncAlert(prompter,
+                    NS_ConvertUTF8toUTF16(mResponseMsg));
+            } else {
+                alertEvent = new nsFtpAsyncAlert(prompter,
+                    NS_ConvertASCIItoUTF16(mResponseMsg));
+            }
             NS_DispatchToMainThread(alertEvent, NS_DISPATCH_NORMAL);
         }
     }
     
     nsresult broadcastErrorCode = mControlStatus;
     if (NS_SUCCEEDED(broadcastErrorCode))
         broadcastErrorCode = mInternalError;
 
@@ -2367,16 +2453,22 @@ nsFtpState::ReadCacheEntry()
     // make sure the channel knows wassup
     SetContentType();
 
     nsXPIDLCString serverType;
     mCacheEntry->GetMetaDataElement("servertype", getter_Copies(serverType));
     nsAutoCString serverNum(serverType.get());
     nsresult err;
     mServerType = serverNum.ToInteger(&err);
+
+    nsXPIDLCString charset;
+    mCacheEntry->GetMetaDataElement("useUTF8", getter_Copies(charset));
+    const char *useUTF8 = charset.get();
+    if (useUTF8 && atoi(useUTF8) == 1)
+        mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
     
     mChannel->PushStreamConverter("text/ftp-dir",
                                   APPLICATION_HTTP_INDEX_FORMAT);
     
     mChannel->SetEntityID(EmptyCString());
 
     if (NS_FAILED(OpenCacheDataStream()))
         return false;
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.h
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.h
@@ -52,17 +52,19 @@ typedef enum _FTP_STATE {
     FTP_S_CWD,  FTP_R_CWD,
     FTP_S_SIZE, FTP_R_SIZE,
     FTP_S_MDTM, FTP_R_MDTM,
     FTP_S_REST, FTP_R_REST,
     FTP_S_RETR, FTP_R_RETR,
     FTP_S_STOR, FTP_R_STOR,
     FTP_S_LIST, FTP_R_LIST,
     FTP_S_PASV, FTP_R_PASV,
-    FTP_S_PWD,  FTP_R_PWD
+    FTP_S_PWD,  FTP_R_PWD,
+    FTP_S_FEAT, FTP_R_FEAT,
+    FTP_S_OPTS, FTP_R_OPTS
 } FTP_STATE;
 
 // higher level ftp actions
 typedef enum _FTP_ACTION {GET, PUT} FTP_ACTION;
 
 class nsFtpChannel;
 class nsICancelable;
 class nsICacheEntryDescriptor;
@@ -124,16 +126,18 @@ private:
     nsresult        S_mdtm(); FTP_STATE       R_mdtm();
     nsresult        S_list(); FTP_STATE       R_list();
 
     nsresult        S_rest(); FTP_STATE       R_rest();
     nsresult        S_retr(); FTP_STATE       R_retr();
     nsresult        S_stor(); FTP_STATE       R_stor();
     nsresult        S_pasv(); FTP_STATE       R_pasv();
     nsresult        S_pwd();  FTP_STATE       R_pwd();
+    nsresult        S_feat(); FTP_STATE       R_feat();
+    nsresult        S_opts(); FTP_STATE       R_opts();
     // END: STATE METHODS
     ///////////////////////////////////
 
     // internal methods
     void        MoveToNextState(FTP_STATE nextState);
     nsresult    Process();
 
     void KillControlConnection();
@@ -238,16 +242,17 @@ private:
     nsCString              mPwd;        // login Path
 
         // ****** other vars
     nsCOMPtr<nsITransport>        mDataTransport;
     nsCOMPtr<nsIAsyncInputStream> mDataStream;
     nsCOMPtr<nsIRequest>    mUploadRequest;
     bool                    mAddressChecked;
     bool                    mServerIsIPv6;
+    bool                    mUseUTF8;
 
     static uint32_t         mSessionStartTime;
 
     mozilla::net::NetAddr   mServerAddress;
 
     // ***** control read gvars
     nsresult                mControlStatus;
     nsCString               mControlReadCarryOverBuf;
--- a/netwerk/protocol/ftp/nsFtpControlConnection.cpp
+++ b/netwerk/protocol/ftp/nsFtpControlConnection.cpp
@@ -60,18 +60,18 @@ nsFtpControlConnection::OnInputStreamRea
         listener->OnControlDataAvailable(data, avail);
     }
 
     return NS_OK;
 }
 
 nsFtpControlConnection::nsFtpControlConnection(const nsCSubstring& host,
                                                uint32_t port)
-    : mServerType(0), mSessionId(gFtpHandler->GetSessionId()), mHost(host)
-    , mPort(port)
+    : mServerType(0), mSessionId(gFtpHandler->GetSessionId())
+    , mUseUTF8(false), mHost(host), mPort(port)
 {
     LOG_ALWAYS(("FTP:CC created @%p", this));
 }
 
 nsFtpControlConnection::~nsFtpControlConnection() 
 {
     LOG_ALWAYS(("FTP:CC destroyed @%p", this));
 }
--- a/netwerk/protocol/ftp/nsFtpControlConnection.h
+++ b/netwerk/protocol/ftp/nsFtpControlConnection.h
@@ -63,16 +63,17 @@ public:
      */
     nsresult WaitData(nsFtpControlConnectionListener *listener);
 
     uint32_t         mServerType;           // what kind of server is it.
     nsString         mPassword;
     int32_t          mSuspendedWrite;
     nsCString        mPwd;
     uint32_t         mSessionId;
+    bool             mUseUTF8;
 
 private:
     nsCString mHost;
     uint32_t  mPort;
 
     nsCOMPtr<nsISocketTransport>     mSocket;
     nsCOMPtr<nsIOutputStream>        mSocketOutput;
     nsCOMPtr<nsIAsyncInputStream>    mSocketInput;
--- a/security/manager/ssl/src/CryptoTask.cpp
+++ b/security/manager/ssl/src/CryptoTask.cpp
@@ -41,17 +41,21 @@ CryptoTask::Run()
     }
 
     CallCallback(mRv);
 
     // Not all uses of CryptoTask use a transient thread
     if (mThread) {
       // Don't leak threads!
       mThread->Shutdown(); // can't Shutdown from the thread itself, darn
-      mThread = nullptr;
+      // Don't null out mThread!
+      // See bug 999104.  We must hold a ref to the thread across Dispatch()
+      // since the internal mThread ref could be released while processing
+      // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
+      // assumes the caller does.
     }
   }
 
   return NS_OK;
 }
 
 void
 CryptoTask::virtualDestroyNSSReference()
--- a/security/manager/ssl/src/CryptoTask.h
+++ b/security/manager/ssl/src/CryptoTask.h
@@ -37,16 +37,17 @@ public:
   template <size_t LEN>
   nsresult Dispatch(const char (&taskThreadName)[LEN])
   {
     static_assert(LEN <= 15,
                   "Thread name must be no more than 15 characters");
     // Can't add 'this' as the event to run, since mThread may not be set yet
     nsresult rv = NS_NewNamedThread(taskThreadName, getter_AddRefs(mThread));
     if (NS_SUCCEEDED(rv)) {
+      // Note: event must not null out mThread!
       rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
     }
     return rv;
   }
 
 protected:
   CryptoTask()
     : mRv(NS_ERROR_NOT_INITIALIZED),
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -185,17 +185,17 @@ class MochitestRunner(MozbuildObject):
 
     def run_desktop_test(self, context, suite=None, test_paths=None, debugger=None,
         debugger_args=None, slowscript=False, screenshot_on_fail = False, shuffle=False, keep_open=False,
         rerun_failures=False, no_autorun=False, repeat=0, run_until_failure=False,
         slow=False, chunk_by_dir=0, total_chunks=None, this_chunk=None,
         jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None,
         e10s=False, dmd=False, dump_output_directory=None,
         dump_about_memory_after_test=False, dump_dmd_after_test=False,
-        install_extension=None, quiet=False, **kwargs):
+        install_extension=None, quiet=False, environment=[], **kwargs):
         """Runs a mochitest.
 
         test_paths are path to tests. They can be a relative path from the
         top source directory, an absolute filename, or a directory containing
         test files.
 
         suite is the type of mochitest to run. It can be one of ('plain',
         'chrome', 'browser', 'metro', 'a11y').
@@ -309,16 +309,17 @@ class MochitestRunner(MozbuildObject):
         options.debugOnFailure = debug_on_failure
         options.startAt = start_at
         options.endAt = end_at
         options.e10s = e10s
         options.dumpAboutMemoryAfterTest = dump_about_memory_after_test
         options.dumpDMDAfterTest = dump_dmd_after_test
         options.dumpOutputDirectory = dump_output_directory
         options.quiet = quiet
+        options.environment = environment
 
         options.failureFile = failure_file_path
         if install_extension != None:
             options.extensionsToInstall = [os.path.join(self.topsrcdir,install_extension)]
 
         for k, v in kwargs.iteritems():
             setattr(options, k, v)
 
@@ -510,16 +511,21 @@ def MochitestCommand(func):
         help='Install given extension before running selected tests. ' \
             'Parameter is a path to xpi file.')
     func = install_extension(func)
 
     quiet = CommandArgument('--quiet', default=False, action='store_true',
         help='Do not print test log lines unless a failure occurs.')
     func = quiet(func)
 
+    setenv = CommandArgument('--setenv', default=[], action='append',
+                             metavar='NAME=VALUE', dest='environment',
+                             help="Sets the given variable in the application's environment")
+    func = setenv(func)
+
     return func
 
 def B2GCommand(func):
     """Decorator that adds shared command arguments to b2g mochitest commands."""
 
     busybox = CommandArgument('--busybox', default=None,
         help='Path to busybox binary to install on device')
     func = busybox(func)
--- a/testing/mozbase/mozprofile/mozprofile/permissions.py
+++ b/testing/mozbase/mozprofile/mozprofile/permissions.py
@@ -299,53 +299,66 @@ class Permissions(object):
                 proxy["remote"] = l.host
                 proxy[l.scheme] = l.port
 
         # overwrite defaults with user specified proxy
         if isinstance(user_proxy, dict):
             proxy.update(user_proxy)
 
         # TODO: this should live in a template!
-        # TODO: So changing the 5th line of the regex below from (\\\\\\\\d+)
-        # to (\\\\d+) makes this code work. Not sure why there would be this
-        # difference between automation.py.in and this file.
+        # If you must escape things in this string with backslashes, be aware
+        # of the multiple layers of escaping at work:
+        #
+        # - Python will unescape backslashes;
+        # - Writing out the prefs will escape things via JSON serialization;
+        # - The prefs file reader will unescape backslashes;
+        # - The JS engine parser will unescape backslashes.
         pacURL = """data:text/plain,
+var knownOrigins = (function () {
+  return [%(origins)s].reduce(function(t, h) { t[h] = true; return t; }, {})
+})();
+var uriRegex = new RegExp('^([a-z][-a-z0-9+.]*)' +
+                          '://' +
+                          '(?:[^/@]*@)?' +
+                          '(.*?)' +
+                          '(?::(\\\\d+))?/');
+var defaultPortsForScheme = {
+  'http': 80,
+  'ws': 80,
+  'https': 443,
+  'wss': 443
+};
+var originSchemesRemap = {
+  'ws': 'http',
+  'wss': 'https'
+};
+var proxyForScheme = {
+  'http': 'PROXY %(remote)s:%(http)s',
+  'https': 'PROXY %(remote)s:%(https)s',
+  'ws': 'PROXY %(remote)s:%(ws)s',
+  'wss': 'PROXY %(remote)s:%(wss)s'
+};
+
 function FindProxyForURL(url, host)
 {
-  var origins = [%(origins)s];
-  var regex = new RegExp('^([a-z][-a-z0-9+.]*)' +
-                         '://' +
-                         '(?:[^/@]*@)?' +
-                         '(.*?)' +
-                         '(?::(\\\\d+))?/');
-  var matches = regex.exec(url);
+  var matches = uriRegex.exec(url);
   if (!matches)
     return 'DIRECT';
-  var isHttp = matches[1] == 'http';
-  var isHttps = matches[1] == 'https';
-  var isWebSocket = matches[1] == 'ws';
-  var isWebSocketSSL = matches[1] == 'wss';
-  if (!matches[3])
-  {
-    if (isHttp | isWebSocket) matches[3] = '80';
-    if (isHttps | isWebSocketSSL) matches[3] = '443';
+  var originalScheme = matches[1];
+  var host = matches[2];
+  var port = matches[3];
+  if (!port && originalScheme in defaultPortsForScheme) {
+    port = defaultPortsForScheme[originalScheme];
   }
-  if (isWebSocket)
-    matches[1] = 'http';
-  if (isWebSocketSSL)
-    matches[1] = 'https';
+  var schemeForOriginChecking = originSchemesRemap[originalScheme] || originalScheme;
 
-  var origin = matches[1] + '://' + matches[2] + ':' + matches[3];
-  if (origins.indexOf(origin) < 0)
+  var origin = schemeForOriginChecking + '://' + host + ':' + port;
+  if (!(origin in knownOrigins))
     return 'DIRECT';
-  if (isHttp) return 'PROXY %(remote)s:%(http)s';
-  if (isHttps) return 'PROXY %(remote)s:%(https)s';
-  if (isWebSocket) return 'PROXY %(remote)s:%(ws)s';
-  if (isWebSocketSSL) return 'PROXY %(remote)s:%(wss)s';
-  return 'DIRECT';
+  return proxyForScheme[originalScheme] || 'DIRECT';
 }""" % proxy
         pacURL = "".join(pacURL.splitlines())
 
         prefs = []
         prefs.append(("network.proxy.type", 2))
         prefs.append(("network.proxy.autoconfig_url", pacURL))
 
         return prefs
old mode 100644
new mode 100755
--- a/testing/mozbase/mozprofile/tests/permissions.py
+++ b/testing/mozbase/mozprofile/tests/permissions.py
@@ -118,23 +118,23 @@ http://127.0.0.1:8888           privileg
         self.assertEqual(len(user_prefs), 0)
         self.assertEqual(len(prefs), 0)
 
         prefs, user_prefs = perms.network_prefs(True)
         self.assertEqual(len(user_prefs), 2)
         self.assertEqual(user_prefs[0], ('network.proxy.type', 2))
         self.assertEqual(user_prefs[1][0], 'network.proxy.autoconfig_url')
 
-        origins_decl = "var origins = ['http://mochi.test:8888', 'http://127.0.0.1:80', 'http://127.0.0.1:8888'];"
+        origins_decl = "var knownOrigins = (function () {  return ['http://mochi.test:8888', 'http://127.0.0.1:80', 'http://127.0.0.1:8888'].reduce"
         self.assertTrue(origins_decl in user_prefs[1][1])
 
-        proxy_check = ("if (isHttp) return 'PROXY mochi.test:8888';",
-                       "if (isHttps) return 'PROXY mochi.test:4443';",
-                       "if (isWebSocket) return 'PROXY mochi.test:4443';",
-                       "if (isWebSocketSSL) return 'PROXY mochi.test:4443';")
+        proxy_check = ("'http': 'PROXY mochi.test:8888'",
+                       "'https': 'PROXY mochi.test:4443'",
+                       "'ws': 'PROXY mochi.test:4443'",
+                       "'wss': 'PROXY mochi.test:4443'")
         self.assertTrue(all(c in user_prefs[1][1] for c in proxy_check))
 
     def verify_user_version(self, version):
         """Verifies that we call INSERT statements using the correct number
         of columns for existing databases.
         """
         self.write_perm_db(version=version)
         Permissions(self.profile_dir, self.locations_file.name)
--- a/toolkit/components/downloads/ApplicationReputation.cpp
+++ b/toolkit/components/downloads/ApplicationReputation.cpp
@@ -293,25 +293,25 @@ NS_IMETHODIMP
 PendingDBLookup::HandleEvent(const nsACString& tables)
 {
   // HandleEvent is guaranteed to call either:
   // 1) PendingLookup::OnComplete if the URL can be classified locally, or
   // 2) PendingLookup::LookupNext if the URL can be cannot classified locally.
   // Blocklisting trumps allowlisting.
   nsAutoCString blockList;
   Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
-  if (!mAllowlistOnly && FindInReadable(tables, blockList)) {
+  if (!mAllowlistOnly && FindInReadable(blockList, tables)) {
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
     LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
     return mPendingLookup->OnComplete(true, NS_OK);
   }
 
   nsAutoCString allowList;
   Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
-  if (FindInReadable(tables, allowList)) {
+  if (FindInReadable(allowList, tables)) {
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
     LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
     return mPendingLookup->OnComplete(false, NS_OK);
   }
 
   LOG(("Didn't find principal %s on any list [this = %p]", mSpec.get(), this));
   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
   return mPendingLookup->LookupNext();
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -458,18 +458,19 @@ nsThread::DispatchInternal(nsIRunnable *
         new nsThreadSyncDispatch(thread, event);
     if (!wrapper)
       return NS_ERROR_OUT_OF_MEMORY;
     nsresult rv = PutEvent(wrapper, target);
     // Don't wait for the event to finish if we didn't dispatch it...
     if (NS_FAILED(rv))
       return rv;
 
+    // Allows waiting; ensure no locks are held that would deadlock us!
     while (wrapper->IsPending())
-      NS_ProcessNextEvent(thread);
+      NS_ProcessNextEvent(thread, true);
     return wrapper->Result();
   }
 
   NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
   return PutEvent(event, target);
 }
 
 //-----------------------------------------------------------------------------
@@ -534,18 +535,19 @@ nsThread::Shutdown()
   // XXXroc What if posting the event fails due to OOM?
   PutEvent(event, nullptr);
 
   // We could still end up with other events being added after the shutdown
   // task, but that's okay because we process pending events in ThreadFunc
   // after setting mShutdownContext just before exiting.
   
   // Process events on the current thread until we receive a shutdown ACK.
+  // Allows waiting; ensure no locks are held that would deadlock us!
   while (!context.shutdownAck)
-    NS_ProcessNextEvent(context.joiningThread);
+    NS_ProcessNextEvent(context.joiningThread, true);
 
   // Now, it should be safe to join without fear of dead-locking.
 
   PR_JoinThread(mThread);
   mThread = nullptr;
 
   // We hold strong references to our event observers, and once the thread is
   // shut down the observers can't easily unregister themselves. Do it here