Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 09 Jun 2017 16:12:32 +0200
changeset 411344 5351949ada723f7be77bd49c4673390ded774e52
parent 411343 9933f2d4d1885c5b730fb5d5a5103d77219be57d (current diff)
parent 411322 eca8d0ea03af1d2424550a037f714f14c0f7b1be (diff)
child 411345 91ddd067412484aec8d482eabe27eb048bea7ef8
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
--- a/browser/extensions/formautofill/content/manageProfiles.xhtml
+++ b/browser/extensions/formautofill/content/manageProfiles.xhtml
@@ -7,18 +7,18 @@
 <head>
   <title>Form Autofill - Manage Addresses</title>
   <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" />
   <link rel="stylesheet" href="chrome://formautofill/content/manageProfiles.css" />
   <script src="chrome://formautofill/content/manageProfiles.js"></script>
 </head>
 <body>
   <p style="padding-left: 30px; background: url(chrome://browser/skin/warning.svg) no-repeat left center">
-    Autofill of addresses is only ready for testing in en-US on &lt;input&gt;s which specify field types
-    using the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autocomplete" target="_blank">autocomplete attribute</a>.
+    Autofill of addresses is only ready for testing with United States addresses on &lt;input&gt;s and some &lt;select&gt; elements.
+    Improvements to form field type detection are in progress.
     <a href="https://luke-chang.github.io/autofill-demo/basic.html" target="_blank">Demo page</a>
   </p>
   <fieldset>
     <legend>Addresses</legend>
     <select id="profiles" size="9" multiple="multiple"/>
   </fieldset>
   <div id="controls-container">
     <button id="remove" disabled="disabled">Remove</button>
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -176,18 +176,16 @@ public:
     }
 
     return true;
   }
 
   void
   Trace(const TraceCallbacks& aCallbacks, void* aClosure)
   {
-    AssertIsOnOwningThread();
-
     ConsoleCallData* tmp = this;
     for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
       NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCopiedArguments[i])
     }
 
     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal);
   }
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4029,17 +4029,16 @@ template <class Derived>
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
                                                 DOMEventTargetHelper)
   tmp->Terminate();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 template <class Derived>
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
                                                DOMEventTargetHelper)
-  tmp->AssertIsOnParentThread();
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 #ifdef DEBUG
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
 {
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -552,18 +552,18 @@ class GCSchedulingState
         inHighFrequencyGCMode_ =
             tunables.isDynamicHeapGrowthEnabled() && lastGCTime &&
             lastGCTime + tunables.highFrequencyThresholdUsec() > currentTime;
     }
 };
 
 template<typename F>
 struct Callback {
-    ActiveThreadData<F> op;
-    ActiveThreadData<void*> data;
+    ActiveThreadOrGCTaskData<F> op;
+    ActiveThreadOrGCTaskData<void*> data;
 
     Callback()
       : op(nullptr), data(nullptr)
     {}
     Callback(F op, void* data)
       : op(op), data(data)
     {}
 };
@@ -900,16 +900,18 @@ class GCRuntime
                                   const Class* clasp);
     template <AllowGC allowGC>
     static JSObject* tryNewTenuredObject(JSContext* cx, AllocKind kind, size_t thingSize,
                                          size_t nDynamicSlots);
     template <typename T, AllowGC allowGC>
     static T* tryNewTenuredThing(JSContext* cx, AllocKind kind, size_t thingSize);
     static TenuredCell* refillFreeListInGC(Zone* zone, AllocKind thingKind);
 
+    void bufferGrayRoots();
+
   private:
     enum IncrementalResult
     {
         Reset = 0,
         Ok
     };
 
     // For ArenaLists::allocateFromArena()
@@ -964,23 +966,24 @@ class GCRuntime
                                            JS::gcreason::Reason reason);
     bool shouldRepeatForDeadZone(JS::gcreason::Reason reason);
     void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason,
                                  AutoLockForExclusiveAccess& lock);
 
     void pushZealSelectedObjects();
     void purgeRuntime(AutoLockForExclusiveAccess& lock);
     MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
+    bool prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOut,
+                                   AutoLockForExclusiveAccess& lock);
     bool shouldPreserveJITCode(JSCompartment* comp, int64_t currentTime,
                                JS::gcreason::Reason reason, bool canAllocateMoreCode);
     void traceRuntimeForMajorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock);
     void traceRuntimeAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock);
     void traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
                             AutoLockForExclusiveAccess& lock);
-    void bufferGrayRoots();
     void maybeDoCycleCollection();
     void markCompartments();
     IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::PhaseKind phase);
     template <class CompartmentIterT> void markWeakReferences(gcstats::PhaseKind phase);
     void markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::PhaseKind phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::PhaseKind phase);
@@ -1134,17 +1137,17 @@ class GCRuntime
     // accumulate these roots in each zone'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.
     enum class GrayBufferState {
         Unused,
         Okay,
         Failed
     };
-    ActiveThreadData<GrayBufferState> grayBufferState;
+    ActiveThreadOrGCTaskData<GrayBufferState> grayBufferState;
     bool hasBufferedGrayRoots() const { return grayBufferState == GrayBufferState::Okay; }
 
     // Clear each zone's gray buffers, but do not change the current state.
     void resetBufferedGrayRoots() const;
 
     // Reset the gray buffering state to Unused.
     void clearBufferedGrayRoots() {
         grayBufferState = GrayBufferState::Unused;
@@ -1170,19 +1173,16 @@ class GCRuntime
     ActiveThreadData<uint64_t> majorGCNumber;
 
     /* The major GC number at which to release observed type information. */
     ActiveThreadData<uint64_t> jitReleaseNumber;
 
     /* Incremented on every GC slice. */
     ActiveThreadData<uint64_t> number;
 
-    /* The number at the time of the most recent GC's first slice. */
-    ActiveThreadData<uint64_t> startNumber;
-
     /* Whether the currently running GC can finish in multiple slices. */
     ActiveThreadData<bool> isIncremental;
 
     /* Whether all zones are being collected in first GC slice. */
     ActiveThreadData<bool> isFull;
 
     /* Whether the heap will be compacted at the end of GC. */
     ActiveThreadData<bool> isCompacting;
--- a/js/src/gc/GenerateStatsPhases.py
+++ b/js/src/gc/GenerateStatsPhases.py
@@ -57,37 +57,41 @@ class PhaseKind():
     def __init__(self, name, descr, bucket, children = []):
         self.name = name
         self.descr = descr
         self.bucket = bucket
         self.children = children
 
 # The root marking phase appears in several places in the graph.
 MarkRootsPhaseKind = PhaseKind("MARK_ROOTS", "Mark Roots", 48, [
-    PhaseKind("BUFFER_GRAY_ROOTS", "Buffer Gray Roots", 49),
     PhaseKind("MARK_CCWS", "Mark Cross Compartment Wrappers", 50),
     PhaseKind("MARK_STACK", "Mark C and JS stacks", 51),
     PhaseKind("MARK_RUNTIME_DATA", "Mark Runtime-wide Data", 52),
     PhaseKind("MARK_EMBEDDING", "Mark Embedding", 53),
-    PhaseKind("MARK_COMPARTMENTS", "Mark Compartments", 54),
+    PhaseKind("MARK_COMPARTMENTS", "Mark Compartments", 54)
 ])
 
 JoinParallelTasksPhaseKind = PhaseKind("JOIN_PARALLEL_TASKS", "Join Parallel Tasks", 67)
 
 PhaseKindGraphRoots = [
     PhaseKind("MUTATOR", "Mutator Running", 0),
     PhaseKind("GC_BEGIN", "Begin Callback", 1),
     PhaseKind("WAIT_BACKGROUND_THREAD", "Wait Background Thread", 2),
-    PhaseKind("MARK_DISCARD_CODE", "Mark Discard Code", 3),
-    PhaseKind("RELAZIFY_FUNCTIONS", "Relazify Functions", 4),
-    PhaseKind("PURGE", "Purge", 5),
+    PhaseKind("PREPARE", "Prepare For Collection", 69, [
+        PhaseKind("UNMARK", "Unmark", 7),
+        PhaseKind("BUFFER_GRAY_ROOTS", "Buffer Gray Roots", 49),
+        PhaseKind("MARK_DISCARD_CODE", "Mark Discard Code", 3),
+        PhaseKind("RELAZIFY_FUNCTIONS", "Relazify Functions", 4),
+        PhaseKind("PURGE", "Purge", 5),
+        PhaseKind("PURGE_SHAPE_TABLES", "Purge ShapeTables", 60),
+        JoinParallelTasksPhaseKind
+        ]),
     PhaseKind("MARK", "Mark", 6, [
-        PhaseKind("UNMARK", "Unmark", 7),
         MarkRootsPhaseKind,
-        PhaseKind("MARK_DELAYED", "Mark Delayed", 8),
+        PhaseKind("MARK_DELAYED", "Mark Delayed", 8)
         ]),
     PhaseKind("SWEEP", "Sweep", 9, [
         PhaseKind("SWEEP_MARK", "Mark During Sweeping", 10, [
             PhaseKind("SWEEP_MARK_TYPES", "Mark Types During Sweeping", 11),
             PhaseKind("SWEEP_MARK_INCOMING_BLACK", "Mark Incoming Black Pointers", 12),
             PhaseKind("SWEEP_MARK_WEAK", "Mark Weak", 13),
             PhaseKind("SWEEP_MARK_INCOMING_GRAY", "Mark Incoming Gray Pointers", 14),
             PhaseKind("SWEEP_MARK_GRAY", "Mark Gray", 15),
@@ -146,18 +150,17 @@ PhaseKindGraphRoots = [
     PhaseKind("EVICT_NURSERY", "Minor GCs to Evict Nursery", 46, [
         MarkRootsPhaseKind,
     ]),
     PhaseKind("TRACE_HEAP", "Trace Heap", 47, [
         MarkRootsPhaseKind,
     ]),
     PhaseKind("BARRIER", "Barriers", 55, [
         PhaseKind("UNMARK_GRAY", "Unmark gray", 56),
-    ]),
-    PhaseKind("PURGE_SHAPE_TABLES", "Purge ShapeTables", 60)
+    ])
 ]
 
 # Make a linear list of all unique phases by performing a depth first
 # search on the phase graph starting at the roots.  This will be used to
 # generate the PhaseKind enum.
 
 def findAllPhaseKinds():
     phases = []
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -212,18 +212,20 @@ js::CheckTracedThing(JSTracer* trc, T* t
      * with this runtime, but will be ignored during marking.
      */
     if (IsOwnedByOtherRuntime(trc->runtime(), thing))
         return;
 
     Zone* zone = thing->zoneFromAnyThread();
     JSRuntime* rt = trc->runtime();
 
-    MOZ_ASSERT_IF(!IsMovingTracer(trc), CurrentThreadCanAccessZone(zone));
-    MOZ_ASSERT_IF(!IsMovingTracer(trc), CurrentThreadCanAccessRuntime(rt));
+    if (!IsMovingTracer(trc) && !IsBufferGrayRootsTracer(trc)) {
+        MOZ_ASSERT(CurrentThreadCanAccessZone(zone));
+        MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
+    }
 
     MOZ_ASSERT(zone->runtimeFromAnyThread() == trc->runtime());
 
     // It shouldn't be possible to trace into zones used by helper threads.
     MOZ_ASSERT(!zone->usedByHelperThread());
 
     MOZ_ASSERT(thing->isAligned());
     MOZ_ASSERT(MapTypeToTraceKind<typename mozilla::RemovePointer<T>::Type>::kind ==
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -476,18 +476,16 @@ void
 js::gc::GCRuntime::bufferGrayRoots()
 {
     // Precondition: the state has been reset to "unused" after the last GC
     //               and the zone's buffers have been cleared.
     MOZ_ASSERT(grayBufferState == GrayBufferState::Unused);
     for (GCZonesIter zone(rt); !zone.done(); zone.next())
         MOZ_ASSERT(zone->gcGrayRoots().empty());
 
-    gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::BUFFER_GRAY_ROOTS);
-
     BufferGrayRootsTracer grayBufferer(rt);
     if (JSTraceDataOp op = grayRootTracer.op)
         (*op)(&grayBufferer, grayRootTracer.data);
 
     // Propagate the failure flag from the marker to the runtime.
     if (grayBufferer.failed()) {
       grayBufferState = GrayBufferState::Failed;
       resetBufferedGrayRoots();
@@ -508,18 +506,20 @@ BufferGrayRootsTracer::onChild(const JS:
     // Check if |thing| is corrupt by calling a method that touches the heap.
     MOZ_RELEASE_ASSERT(thing.asCell()->getTraceKind() <= JS::TraceKind::Null);
 
     if (bufferingGrayRootsFailed)
         return;
 
     gc::TenuredCell* tenured = gc::TenuredCell::fromPointer(thing.asCell());
 
-    Zone* zone = tenured->zone();
-    if (zone->isCollecting()) {
+    // This is run from a helper thread while the mutator is paused so we have
+    // to use *FromAnyThread methods here.
+    Zone* zone = tenured->zoneFromAnyThread();
+    if (zone->isCollectingFromAnyThread()) {
         // See the comment on SetMaybeAliveFlag to see why we only do this for
         // objects and scripts. We rely on gray root buffering for this to work,
         // but we only need to worry about uncollected dead compartments during
         // incremental GCs (when we do gray root buffering).
         DispatchTyped(SetMaybeAliveFunctor(), thing);
 
         if (!zone->gcGrayRoots().append(tenured))
             bufferingGrayRootsFailed = true;
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -331,17 +331,17 @@ struct Zone : public JS::shadow::Zone,
     // The set of compartments in this zone.
     js::ActiveThreadOrGCTaskData<CompartmentVector> compartments_;
   public:
     CompartmentVector& compartments() { return compartments_.ref(); }
 
     // This zone's gray roots.
     typedef js::Vector<js::gc::Cell*, 0, js::SystemAllocPolicy> GrayRootVector;
   private:
-    js::ZoneGroupData<GrayRootVector> gcGrayRoots_;
+    js::ZoneGroupOrGCTaskData<GrayRootVector> gcGrayRoots_;
   public:
     GrayRootVector& gcGrayRoots() { return gcGrayRoots_.ref(); }
 
     // This zone's weak edges found via graph traversal during marking,
     // preserved for re-scanning during sweeping.
     using WeakEdges = js::Vector<js::gc::TenuredCell**, 0, js::SystemAllocPolicy>;
   private:
     js::ZoneGroupOrGCTaskData<WeakEdges> gcWeakRefs_;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -887,17 +887,16 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
     grayBufferState(GCRuntime::GrayBufferState::Unused),
     grayBitsValid(false),
     majorGCTriggerReason(JS::gcreason::NO_REASON),
     fullGCForAtomsRequested_(false),
     minorGCNumber(0),
     majorGCNumber(0),
     jitReleaseNumber(0),
     number(0),
-    startNumber(0),
     isFull(false),
     incrementalState(gc::State::NotActive),
     lastMarkSlice(false),
     sweepOnBackgroundThread(false),
     blocksToFreeAfterSweeping((size_t) JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     sweepGroupIndex(0),
     sweepGroups(nullptr),
     currentSweepGroup(nullptr),
@@ -3619,19 +3618,49 @@ ArenaLists::checkEmptyArenaList(AllocKin
             }
         }
         fprintf(stderr, "ERROR: GC found %" PRIuSIZE " live Cells at shutdown\n", num_live);
     }
 #endif // DEBUG
     return num_live == 0;
 }
 
+class MOZ_RAII js::gc::AutoRunParallelTask : public GCParallelTask
+{
+    using Func = void (*)(JSRuntime*);
+
+    Func func_;
+    gcstats::PhaseKind phase_;
+    AutoLockHelperThreadState& lock_;
+
+  public:
+    AutoRunParallelTask(JSRuntime* rt, Func func, gcstats::PhaseKind phase,
+                        AutoLockHelperThreadState& lock)
+      : GCParallelTask(rt),
+        func_(func),
+        phase_(phase),
+        lock_(lock)
+    {
+        runtime()->gc.startTask(*this, phase_, lock_);
+    }
+
+    ~AutoRunParallelTask() {
+        runtime()->gc.joinTask(*this, phase_, lock_);
+    }
+
+    void run() override {
+        func_(runtime());
+    }
+};
+
 void
 GCRuntime::purgeRuntime(AutoLockForExclusiveAccess& lock)
 {
+    gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE);
+
     for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
         comp->purge();
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         zone->atomCache().clearAndShrink();
         zone->externalStringCache().purge();
     }
 
@@ -3803,45 +3832,43 @@ ShouldCollectZone(Zone* zone, JS::gcreas
         if (comp->scheduledForDestruction)
             return true;
     }
 
     return false;
 }
 
 bool
-GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock)
-{
-    int64_t currentTime = PRMJ_Now();
-
+GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOut,
+                                     AutoLockForExclusiveAccess& lock)
+{
 #ifdef DEBUG
-    if (fullCompartmentChecks)
-        checkForCompartmentMismatches();
+    /* Assert that zone state is as we expect */
+    for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+        MOZ_ASSERT(!zone->isCollecting());
+        MOZ_ASSERT(!zone->compartments().empty());
+        for (auto i : AllAllocKinds())
+            MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i));
+    }
 #endif
 
-    isFull = true;
+    *isFullOut = true;
     bool any = false;
 
+    int64_t currentTime = PRMJ_Now();
+
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
-        /* Assert that zone state is as we expect */
-        MOZ_ASSERT(!zone->isCollecting());
-        MOZ_ASSERT(!zone->compartments().empty());
-#ifdef DEBUG
-        for (auto i : AllAllocKinds())
-            MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i));
-#endif
-
         /* Set up which zones will be collected. */
         if (ShouldCollectZone(zone, reason)) {
             if (!zone->isAtomsZone()) {
                 any = true;
                 zone->setGCState(Zone::Mark);
             }
         } else {
-            isFull = false;
+            *isFullOut = false;
         }
 
         zone->setPreservingCode(false);
     }
 
     // Discard JIT code more aggressively if the process is approaching its
     // executable code limit.
     bool canAllocateMoreCode = jit::CanLikelyAllocateMoreExecutableMemory();
@@ -3849,17 +3876,17 @@ GCRuntime::beginMarkPhase(JS::gcreason::
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
         c->marked = false;
         c->scheduledForDestruction = false;
         c->maybeAlive = c->hasBeenEntered() || !c->zone()->isGCScheduled();
         if (shouldPreserveJITCode(c, currentTime, reason, canAllocateMoreCode))
             c->zone()->setPreservingCode(true);
     }
 
-    if (!rt->gc.cleanUpEverything && canAllocateMoreCode) {
+    if (!cleanUpEverything && canAllocateMoreCode) {
         jit::JitActivationIterator activation(TlsContext.get());
         if (!activation.done())
             activation->compartment()->zone()->setPreservingCode(true);
     }
 
     /*
      * If keepAtoms() is true then either an instance of AutoKeepAtoms is
      * currently on the stack or parsing is currently happening on another
@@ -3882,130 +3909,177 @@ GCRuntime::beginMarkPhase(JS::gcreason::
         if (atomsZone->isGCScheduled()) {
             MOZ_ASSERT(!atomsZone->isCollecting());
             atomsZone->setGCState(Zone::Mark);
             any = true;
         }
     }
 
     /* Check that at least one zone is scheduled for collection. */
-    if (!any)
+    return any;
+}
+
+static void
+DiscardJITCodeForIncrementalGC(JSRuntime* rt)
+{
+    js::CancelOffThreadIonCompile(rt, JS::Zone::Mark);
+    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+        gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::MARK_DISCARD_CODE);
+        zone->discardJitCode(rt->defaultFreeOp());
+    }
+}
+
+static void
+RelazifyFunctionsForShrinkingGC(JSRuntime* rt)
+{
+    gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::RELAZIFY_FUNCTIONS);
+    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+        if (zone->isSelfHostingZone())
+            continue;
+        RelazifyFunctions(zone, AllocKind::FUNCTION);
+        RelazifyFunctions(zone, AllocKind::FUNCTION_EXTENDED);
+    }
+}
+
+static void
+PurgeShapeTablesForShrinkingGC(JSRuntime* rt)
+{
+    gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::PURGE_SHAPE_TABLES);
+    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+        if (zone->keepShapeTables() || zone->isSelfHostingZone())
+            continue;
+        for (auto baseShape = zone->cellIter<BaseShape>(); !baseShape.done(); baseShape.next())
+            baseShape->maybePurgeTable();
+    }
+}
+
+static void
+UnmarkCollectedZones(JSRuntime* rt)
+{
+    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+        /* Unmark everything in the zones being collected. */
+        zone->arenas.unmarkAll();
+    }
+
+    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+        /* Unmark all weak maps in the zones being collected. */
+        WeakMapBase::unmarkZone(zone);
+    }
+}
+
+static void
+BufferGrayRoots(JSRuntime* rt)
+{
+    rt->gc.bufferGrayRoots();
+}
+
+bool
+GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock)
+{
+#ifdef DEBUG
+    if (fullCompartmentChecks)
+        checkForCompartmentMismatches();
+#endif
+
+    if (!prepareZonesForCollection(reason, &isFull.ref(), lock))
         return false;
 
     /*
      * Ensure that after the start of a collection we don't allocate into any
      * existing arenas, as this can cause unreachable things to be marked.
      */
     if (isIncremental) {
         for (GCZonesIter zone(rt); !zone.done(); zone.next())
             zone->arenas.prepareForIncrementalGC();
     }
 
     MemProfiler::MarkTenuredStart(rt);
     marker.start();
     GCMarker* gcmarker = &marker;
 
-    /* For non-incremental GC the following sweep discards the jit code. */
-    if (isIncremental) {
-        js::CancelOffThreadIonCompile(rt, JS::Zone::Mark);
-        for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-            gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_DISCARD_CODE);
-            zone->discardJitCode(rt->defaultFreeOp());
+    {
+        gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::PREPARE);
+        AutoLockHelperThreadState helperLock;
+
+        /*
+         * Clear all mark state for the zones we are collecting. This is linear
+         * in the size of the heap we are collecting and so can be slow. Do this
+         * in parallel with the rest of this block.
+         */
+        AutoRunParallelTask
+            unmarkCollectedZones(rt, UnmarkCollectedZones, gcstats::PhaseKind::UNMARK, helperLock);
+
+        /*
+         * Buffer gray roots for incremental collections. This is linear in the
+         * number of roots which can be in the tens of thousands. Do this in
+         * parallel with the rest of this block.
+         */
+        Maybe<AutoRunParallelTask> bufferGrayRoots;
+        if (isIncremental)
+            bufferGrayRoots.emplace(rt, BufferGrayRoots, gcstats::PhaseKind::BUFFER_GRAY_ROOTS, helperLock);
+        AutoUnlockHelperThreadState unlock(helperLock);
+
+        /*
+         * Discard JIT code for incremental collections (for non-incremental
+         * collections the following sweep discards the jit code).
+         */
+        if (isIncremental)
+            DiscardJITCodeForIncrementalGC(rt);
+
+        /*
+         * Relazify functions after discarding JIT code (we can't relazify
+         * functions with JIT code) and before the actual mark phase, so that
+         * the current GC can collect the JSScripts we're unlinking here.  We do
+         * this only when we're performing a shrinking GC, as too much
+         * relazification can cause performance issues when we have to reparse
+         * the same functions over and over.
+         */
+        if (invocationKind == GC_SHRINK) {
+            RelazifyFunctionsForShrinkingGC(rt);
+            PurgeShapeTablesForShrinkingGC(rt);
         }
+
+        /*
+         * We must purge the runtime at the beginning of an incremental GC. The
+         * danger if we purge later is that the snapshot invariant of
+         * incremental GC will be broken, as follows. If some object is
+         * reachable only through some cache (say the dtoaCache) then it will
+         * not be part of the snapshot.  If we purge after root marking, then
+         * the mutator could obtain a pointer to the object and start using
+         * it. This object might never be marked, so a GC hazard would exist.
+         */
+        purgeRuntime(lock);
     }
 
     /*
-     * Relazify functions after discarding JIT code (we can't relazify
-     * functions with JIT code) and before the actual mark phase, so that
-     * the current GC can collect the JSScripts we're unlinking here.
-     * We do this only when we're performing a shrinking GC, as too much
-     * relazification can cause performance issues when we have to reparse
-     * the same functions over and over.
+     * Mark phase.
      */
-    if (invocationKind == GC_SHRINK) {
-        {
-            gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::RELAZIFY_FUNCTIONS);
-            for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-                if (zone->isSelfHostingZone())
-                    continue;
-                RelazifyFunctions(zone, AllocKind::FUNCTION);
-                RelazifyFunctions(zone, AllocKind::FUNCTION_EXTENDED);
-            }
-        }
-
-        /* Purge ShapeTables. */
-        gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE_SHAPE_TABLES);
-        for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-            if (zone->keepShapeTables() || zone->isSelfHostingZone())
-                continue;
-            for (auto baseShape = zone->cellIter<BaseShape>(); !baseShape.done(); baseShape.next())
-                baseShape->maybePurgeTable();
-        }
-    }
+    gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK);
+    traceRuntimeForMajorGC(gcmarker, lock);
+
+    if (isIncremental)
+        markCompartments();
 
     /*
      * Process any queued source compressions during the start of a major
      * GC.
      */
     {
         AutoLockHelperThreadState helperLock;
         HelperThreadState().startHandlingCompressionTasks(helperLock);
     }
 
-    startNumber = number;
-
-    /*
-     * We must purge the runtime at the beginning of an incremental GC. The
-     * danger if we purge later is that the snapshot invariant of incremental
-     * GC will be broken, as follows. If some object is reachable only through
-     * some cache (say the dtoaCache) then it will not be part of the snapshot.
-     * If we purge after root marking, then the mutator could obtain a pointer
-     * to the object and start using it. This object might never be marked, so
-     * a GC hazard would exist.
-     */
-    {
-        gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE);
-        purgeRuntime(lock);
-    }
-
-    /*
-     * Mark phase.
-     */
-    gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::MARK);
-
-    {
-        gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::UNMARK);
-
-        for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-            /* Unmark everything in the zones being collected. */
-            zone->arenas.unmarkAll();
-        }
-
-        for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-            /* Unmark all weak maps in the zones being collected. */
-            WeakMapBase::unmarkZone(zone);
-        }
-    }
-
-    traceRuntimeForMajorGC(gcmarker, lock);
-
-    gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::MARK_ROOTS);
-
-    if (isIncremental) {
-        bufferGrayRoots();
-        markCompartments();
-    }
-
     return true;
 }
 
 void
 GCRuntime::markCompartments()
 {
-    gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_COMPARTMENTS);
+    gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::MARK_ROOTS);
+    gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::MARK_COMPARTMENTS);
 
     /*
      * This code ensures that if a compartment is "dead", then it will be
      * collected in this GC. A compartment is considered dead if its maybeAlive
      * flag is false. The maybeAlive flag is set if:
      *
      *   (1) the compartment has been entered (set in beginMarkPhase() above)
      *   (2) the compartment is not being collected (set in beginMarkPhase()
@@ -4261,30 +4335,34 @@ js::gc::MarkingValidator::nonIncremental
      */
     initialized = true;
 
     /* Re-do all the marking, but non-incrementally. */
     js::gc::State state = gc->incrementalState;
     gc->incrementalState = State::MarkRoots;
 
     {
-        gcstats::AutoPhase ap(gc->stats(), gcstats::PhaseKind::MARK);
+        gcstats::AutoPhase ap(gc->stats(), gcstats::PhaseKind::PREPARE);
 
         {
             gcstats::AutoPhase ap(gc->stats(), gcstats::PhaseKind::UNMARK);
 
             for (GCZonesIter zone(runtime); !zone.done(); zone.next())
                 WeakMapBase::unmarkZone(zone);
 
             MOZ_ASSERT(gcmarker->isDrained());
             gcmarker->reset();
 
             for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next())
                 chunk->bitmap.clear();
         }
+    }
+
+    {
+        gcstats::AutoPhase ap(gc->stats(), gcstats::PhaseKind::MARK);
 
         gc->traceRuntimeForMajorGC(gcmarker, lock);
 
         gc->incrementalState = State::Mark;
         auto unlimited = SliceBudget::unlimited();
         MOZ_RELEASE_ASSERT(gc->marker.drainMarkStack(unlimited));
     }
 
@@ -5166,44 +5244,16 @@ PrepareWeakCacheTasks(JSRuntime* rt)
             return true;
         });
         tasks.clear();
     }
 
     return tasks;
 }
 
-class MOZ_RAII js::gc::AutoRunParallelTask : public GCParallelTask
-{
-    using Func = void (*)(JSRuntime*);
-
-    Func func_;
-    gcstats::PhaseKind phase_;
-    AutoLockHelperThreadState& lock_;
-
-  public:
-    AutoRunParallelTask(JSRuntime* rt, Func func, gcstats::PhaseKind phase,
-                        AutoLockHelperThreadState& lock)
-      : GCParallelTask(rt),
-        func_(func),
-        phase_(phase),
-        lock_(lock)
-    {
-        runtime()->gc.startTask(*this, phase_, lock_);
-    }
-
-    ~AutoRunParallelTask() {
-        runtime()->gc.joinTask(*this, phase_, lock_);
-    }
-
-    void run() override {
-        func_(runtime());
-    }
-};
-
 void
 GCRuntime::beginSweepingSweepGroup()
 {
     /*
      * Begin sweeping the group of zones in currentSweepGroup, performing
      * actions that must be done before yielding to caller.
      */
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/clipPath-polygon-01.svg
@@ -0,0 +1,51 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+
+  <title>Testcase for CSS polygon clipPath</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=1246741 -->
+
+  <rect width="100%" height="100%" fill="lime"/>
+
+  <!-- test the clip is not too big (or ignored altogether) -->
+  <rect width="100%" height="100%" fill="red"
+        clip-path="polygon(100px 100px,200px 100px,200px 200px,100px 200px)"/>
+  <rect x="98" y="98" width="105" height="105" fill="lime"/>
+
+  <!-- test the clip does not simply make the element not render -->
+  <rect x="300" y="100" width="100" height="100" fill="red"/>
+  <rect x="280" y="80" width="150" height="150" fill="lime"
+        clip-path="polygon(20px 20px,120px 20px,120px 120px,20px 120px)"/>
+
+  <!-- percentage values -->
+  <svg x="100" y="300" width="100" height="100">
+    <rect width="100%" height="100%" fill="red"
+          clip-path="polygon(0 0,50% 0,50% 50%,0 50%)"/>
+    <rect width="55" height="55" fill="lime"/>
+  </svg>
+
+  <!-- mixed absolute and percentage values -->
+  <svg x="300" y="300" width="100" height="100">
+    <rect width="100%" height="100%" fill="red"
+          clip-path="polygon(0 0,50% 0,50px 50%,0 50px)"/>
+    <rect width="55" height="55" fill="lime"/>
+  </svg>
+
+  <!-- mixed other units -->
+  <svg x="500" y="300" width="100" height="100">
+    <rect width="100%" height="100%" fill="red"
+          clip-path="polygon(0 0,5em 0,5em 10%,0 10px)"/>
+    <rect width="5em" height="10%" fill="lime"/>
+  </svg>
+
+  <!-- check fill-rule and clip-rule are ignored for polygon clip-path -->
+  <svg x="500" y="100" width="100" height="100" fill-rule="evenodd" clip-rule="evenodd">
+    <rect width="100%" height="100%" fill="red"
+          clip-path="polygon(0 0,50px 0,50px 50px,0 50px,0 0,50px 0,50px 50px,0 50px,0 0)"/>
+    <rect width="55" height="55" fill="lime"/>
+  </svg>
+
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/clipPath-polygon-elementFromPoint-01.svg
@@ -0,0 +1,43 @@
+<!--
+      Any copyright is dedicated to the Public Domain.
+      http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg onload="testElementFromPoint();" xmlns="http://www.w3.org/2000/svg">
+
+  <title>Testcase for CSS polygon clipPath</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=1246741 -->
+
+  <rect id="out" width="100%" height="100%"/>
+
+  <!-- test elementFromPoint can get the element using clip-path -->
+  <rect id="in" x="100" y="100" width="100" height="100"
+        clip-path="polygon(0 0,50px 0,50px 50px,0 50px)"/>
+  <script>
+    function testElementFromPoint() {
+      let inCount = outCount = 0, inElem, outElem;
+      document.getElementById("in").style.fill = "red";
+      document.getElementById("out").style.fill = "blue";
+
+      inElem = document.elementFromPoint(100, 100);
+      if (inElem.style.fill == "red") { ++inCount; }
+      inElem = document.elementFromPoint(150, 100);
+      if (inElem.style.fill == "red") { ++inCount; }
+      inElem = document.elementFromPoint(150, 150);
+      if (inElem.style.fill == "red") { ++inCount; }
+      inElem = document.elementFromPoint(100, 150);
+      if (inElem.style.fill == "red") { ++inCount; }
+      if (inCount == 4) { document.getElementById("in").style.fill = "lime"; }
+
+      outElem = document.elementFromPoint(99, 100);
+      if (outElem.style.fill == "blue") { ++outCount; }
+      outElem = document.elementFromPoint(150, 99);
+      if (outElem.style.fill == "blue") { ++outCount; }
+      outElem = document.elementFromPoint(151, 150);
+      if (outElem.style.fill == "blue") { ++outCount; }
+      outElem = document.elementFromPoint(100, 151);
+      if (outElem.style.fill == "blue") { ++outCount; }
+      if (outCount == 4) { document.getElementById("out").style.fill = "lime"; }
+    }
+  </script>
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -75,16 +75,18 @@ fuzzy-if(/^Windows\x20NT\x2010\.0/.test(
 == clipPath-basic-05.svg pass.svg
 == clipPath-basic-06.svg pass.svg
 == clipPath-basic-07.svg pass.svg
 == clipPath-on-outflowElement-01a.html clipPath-on-outflowElement-01-ref.html
 == clipPath-on-outflowElement-01b.html clipPath-on-outflowElement-01-ref.html
 default-preferences pref(layout.css.clip-path-shapes.enabled,true)
 fuzzy(1,32400) == clipPath-on-outflowElement-02a.html clipPath-on-outflowElement-02-ref.html
 fuzzy(1,32400) == clipPath-on-outflowElement-02b.html clipPath-on-outflowElement-02-ref.html
+== clipPath-polygon-01.svg pass.svg
+== clipPath-polygon-elementFromPoint-01.svg pass.svg
 default-preferences
 != clipPath-on-thin-object.svg about:blank
 == clipPath-winding-01.svg pass.svg
 
 == comments-in-pres-attrs.svg pass.svg
 
 == conditions-01.svg pass.svg
 == conditions-02.svg pass.svg
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2721,34 +2721,41 @@ nsCookieService::AsyncReadComplete()
 {
   // We may be in the private browsing DB state, with a pending read on the
   // default DB state. (This would occur if we started up in private browsing
   // mode.) As long as we do all our operations on the default state, we're OK.
   NS_ASSERTION(mDefaultDBState, "no default DBState");
   NS_ASSERTION(mDefaultDBState->pendingRead, "no pending read");
   NS_ASSERTION(mDefaultDBState->readListener, "no read listener");
 
+  mozStorageTransaction transaction(mDefaultDBState->dbConn, false);
   // Merge the data read on the background thread with the data synchronously
   // read on the main thread. Note that transactions on the cookie table may
   // have occurred on the main thread since, making the background data stale.
   for (uint32_t i = 0; i < mDefaultDBState->hostArray.Length(); ++i) {
     const CookieDomainTuple &tuple = mDefaultDBState->hostArray[i];
 
     // Tiebreak: if the given base domain has already been read in, ignore
     // the background data. Note that readSet may contain domains that were
     // queried but found not to be in the db -- that's harmless.
     if (mDefaultDBState->readSet.GetEntry(tuple.key))
       continue;
 
     AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, nullptr, false);
   }
+  DebugOnly<nsresult> rv = transaction.Commit();
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   mDefaultDBState->stmtReadDomain = nullptr;
   mDefaultDBState->pendingRead = nullptr;
   mDefaultDBState->readListener = nullptr;
+
+  // Close sync connection asynchronously: if we let destructor close, it may
+  // cause an expensive fsync operation on the main-thread.
+  mDefaultDBState->syncConn->AsyncClose(nullptr);
   mDefaultDBState->syncConn = nullptr;
   mDefaultDBState->hostArray.Clear();
   mDefaultDBState->readSet.Clear();
 
   COOKIE_LOGSTRING(LogLevel::Debug, ("Read(): %" PRIu32 " cookies read",
                                   mDefaultDBState->cookieCount));
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
@@ -2859,21 +2866,24 @@ nsCookieService::EnsureReadDomain(const 
 
     if (!hasResult)
       break;
 
     array.AppendElement(GetCookieFromRow(mDefaultDBState->stmtReadDomain,
                                          aKey.mOriginAttributes));
   }
 
+  mozStorageTransaction transaction(mDefaultDBState->dbConn, false);
   // Add the cookies to the table in a single operation. This makes sure that
   // either all the cookies get added, or in the case of corruption, none.
   for (uint32_t i = 0; i < array.Length(); ++i) {
     AddCookieToList(aKey, array[i], mDefaultDBState, nullptr, false);
   }
+  rv = transaction.Commit();
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   // Add it to the hashset of read entries, so we don't read it again.
   mDefaultDBState->readSet.PutEntry(aKey);
 
   COOKIE_LOGSTRING(LogLevel::Debug,
     ("EnsureReadDomain(): %" PRIuSIZE " cookies read for base domain %s, "
      " originAttributes = %s", array.Length(), aKey.mBaseDomain.get(),
      suffix.get()));
@@ -2951,23 +2961,26 @@ nsCookieService::EnsureReadComplete()
     if (mDefaultDBState->readSet.GetEntry(key))
       continue;
 
     CookieDomainTuple* tuple = array.AppendElement();
     tuple->key = key;
     tuple->cookie = GetCookieFromRow(stmt, attrs);
   }
 
+  mozStorageTransaction transaction(mDefaultDBState->dbConn, false);
   // Add the cookies to the table in a single operation. This makes sure that
   // either all the cookies get added, or in the case of corruption, none.
   for (uint32_t i = 0; i < array.Length(); ++i) {
     CookieDomainTuple& tuple = array[i];
     AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, nullptr,
       false);
   }
+  rv = transaction.Commit();
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   mDefaultDBState->syncConn = nullptr;
   mDefaultDBState->readSet.Clear();
 
   COOKIE_LOGSTRING(LogLevel::Debug,
     ("EnsureReadComplete(): %" PRIuSIZE " cookies read", array.Length()));
 }
 
@@ -4894,16 +4907,17 @@ nsCookieService::RemoveCookiesWithOrigin
     const mozilla::OriginAttributesPattern& aPattern,
     const nsCString& aBaseDomain)
 {
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already close?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  mozStorageTransaction transaction(mDBState->dbConn, false);
   // Iterate the hash table of nsCookieEntry.
   for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
     nsCookieEntry* entry = iter.Get();
 
     if (!aBaseDomain.IsEmpty() && !aBaseDomain.Equals(entry->mBaseDomain)) {
       continue;
     }
 
@@ -4926,16 +4940,18 @@ nsCookieService::RemoveCookiesWithOrigin
       nsAutoCString path;
       cookie->GetPath(path);
 
       // Remove the cookie.
       nsresult rv = Remove(host, entry->mOriginAttributes, name, path, false);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
+  DebugOnly<nsresult> rv = transaction.Commit();
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   return NS_OK;
 }
 
 // find an secure cookie specified by host and name
 bool
 nsCookieService::FindSecureCookie(const nsCookieKey    &aKey,
                                   nsCookie             *aCookie)
--- a/toolkit/components/satchel/FormHistoryStartup.js
+++ b/toolkit/components/satchel/FormHistoryStartup.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
                                   "resource://gre/modules/FormHistory.jsm");
 
 function FormHistoryStartup() { }
 
 FormHistoryStartup.prototype = {
   classID: Components.ID("{3A0012EB-007F-4BB8-AA81-A07385F77A25}"),
--- a/toolkit/components/satchel/formSubmitListener.js
+++ b/toolkit/components/satchel/formSubmitListener.js
@@ -3,19 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env mozilla/frame-script */
 
 (function() {
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 var satchelFormListener = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver,
                                          Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   debug: true,
   enabled: true,
--- a/toolkit/components/satchel/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/nsFormAutoComplete.js
@@ -510,23 +510,29 @@ FormAutoComplete.prototype = {
    * searchTokens -- array of tokens of the search string
    *
    * Returns: an int
    */
   _calculateScore(entry, aSearchString, searchTokens) {
     let boundaryCalc = 0;
     // for each word, calculate word boundary weights
     for (let token of searchTokens) {
-      boundaryCalc += (entry.textLowerCase.indexOf(token) == 0);
-      boundaryCalc += (entry.textLowerCase.includes(" " + token));
+      if (entry.textLowerCase.startsWith(token)) {
+        boundaryCalc++;
+      }
+      if (entry.textLowerCase.includes(" " + token)) {
+        boundaryCalc++;
+      }
     }
     boundaryCalc = boundaryCalc * this._boundaryWeight;
     // now add more weight if we have a traditional prefix match and
     // multiply boundary bonuses by boundary weight
-    boundaryCalc += this._prefixWeight * (entry.textLowerCase.indexOf(aSearchString) == 0);
+    if (entry.textLowerCase.startsWith(aSearchString)) {
+      boundaryCalc += this._prefixWeight;
+    }
     entry.totalScore = Math.round(entry.frecency * Math.max(1, boundaryCalc));
   }
 
 }; // end of FormAutoComplete implementation
 
 // nsIAutoCompleteResult implementation
 function FormAutoCompleteResult(client,
                                 entries,
--- a/toolkit/components/satchel/nsFormAutoCompleteResult.jsm
+++ b/toolkit/components/satchel/nsFormAutoCompleteResult.jsm
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 this.EXPORTED_SYMBOLS = [ "FormAutoCompleteResult" ];
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 this.FormAutoCompleteResult =
  function FormAutoCompleteResult(searchString,
                                  searchResult,
                                  defaultIndex,
                                  errorDescription,
                                  values,
                                  labels,
--- a/toolkit/components/satchel/nsInputListAutoComplete.js
+++ b/toolkit/components/satchel/nsInputListAutoComplete.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/nsFormAutoCompleteResult.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/nsFormAutoCompleteResult.jsm");
 
 function InputListAutoComplete() {}
 
 InputListAutoComplete.prototype = {
   classID: Components.ID("{bf1e01d0-953e-11df-981c-0800200c9a66}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIInputListAutoComplete]),
 
   autoCompleteSearch(aUntrimmedSearchString, aField) {
--- a/toolkit/components/satchel/test/unit/head_satchel.js
+++ b/toolkit/components/satchel/test/unit/head_satchel.js
@@ -1,23 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/FormHistory.jsm");
-
-var Ci = Components.interfaces;
-var Cc = Components.classes;
-var Cu = Components.utils;
-
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 const CURRENT_SCHEMA = 4;
 const PR_HOURS = 60 * 60 * 1000000;
 
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/FormHistory.jsm");
+
 do_get_profile();
 
 var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
              getService(Ci.nsIProperties);
 
 // Send the profile-after-change notification to the form history component to ensure
 // that it has been initialized.
 var formHistoryStartup = Cc["@mozilla.org/satchel/form-history-startup;1"].
--- a/toolkit/components/satchel/test/unit/test_previous_result.js
+++ b/toolkit/components/satchel/test/unit/test_previous_result.js
@@ -14,11 +14,11 @@ var aaListener = {
     do_check_eq(result.searchString, "aa");
     search.startSearch("aaa", "", result, aaaListener);
   }
 };
 
 function run_test() {
   do_test_pending();
   let search = Cc["@mozilla.org/autocomplete/search;1?name=form-history"].
-               getService(Components.interfaces.nsIAutoCompleteSearch);
+               getService(Ci.nsIAutoCompleteSearch);
   search.startSearch("aa", "", null, aaListener);
 }
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -1,20 +1,22 @@
 navigation:
   search:
     objects: ["about_home", "about_newtab", "contextmenu", "oneoff",
               "suggestion", "alias", "enter", "searchbar", "urlbar"]
-    release_channel_collection: opt-in
+    release_channel_collection: opt-out
     record_in_processes: ["main"]
     description: >
       This is recorded on each search navigation.
       The value field records the action used to trigger the search:
         "enter", "oneoff", "suggestion", "alias", null (for contextmenu)
     bug_numbers: [1316281]
-    notification_emails: ["past@mozilla.com"]
+    notification_emails:
+      - "past@mozilla.com"
+      - "dzeber@mozilla.com"
     expiry_version: "58.0"
     extra_keys:
       engine: The id of the search engine used.
 
 # This category contains event entries used for Telemetry tests.
 # They will not be sent out with any pings.
 telemetry.test:
   test: