merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Tue, 06 Jun 2017 11:20:56 +0200
changeset 410586 58ce95bc58ce4ba200413c8bed87786dccf3d105
parent 410575 d3b8e8571020ffad87bb140b190799fc6fae06d5 (current diff)
parent 410585 cac26305ac53144a8fad330cc27a1a0e66931c6f (diff)
child 410587 4dd1d17ba22660b8f5869a707f2e4e9f9dd5be5b
child 410612 7d7a141c99e72ca7b486f5408c7d926803a0a7a7
child 410666 278a95776dd62224625f73627f99e9356b5609a4
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)
reviewersmerge, merge
milestone55.0a1
first release with
nightly linux32
58ce95bc58ce / 55.0a1 / 20170606100219 / files
nightly linux64
58ce95bc58ce / 55.0a1 / 20170606100219 / files
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: BDyuSdNALvH
dom/network/tests/add_task.js
dom/network/tests/test_tcpsocket_default_permissions.html
dom/network/tests/test_tcpsocket_enabled_no_perm.html
dom/network/tests/test_tcpsocket_enabled_with_perm.html
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -131,18 +131,18 @@ EvaluationExceptionToNSResult(JSContext*
   }
   return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
 }
 
 nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
                                               JS::Handle<JSObject*> aGlobal)
   :
 #ifdef MOZ_GECKO_PROFILER
-    mProfilerRAII("nsJSUtils::ExecutionContext", /* PROFILER_LABEL */
-                  js::ProfileEntry::Category::JS, __LINE__),
+    mProfilerRAII("nsJSUtils::ExecutionContext", /* dynamicStr */ nullptr,
+                  __LINE__, js::ProfileEntry::Category::JS),
 #endif
     mCx(aCx)
   , mCompartment(aCx, aGlobal)
   , mRetValue(aCx)
   , mScopeChain(aCx)
   , mRv(NS_OK)
   , mSkip(false)
   , mCoerceToString(false)
deleted file mode 100644
--- a/dom/network/tests/add_task.js
+++ /dev/null
@@ -1,83 +0,0 @@
-// Temporary implementation of add_task for mochitest-plain until bug 1078657 is
-// implemented.
-SimpleTest.waitForExplicitFinish();
-(function(scope) {
-  var pendingTasks = [];
-  var pendingPromise = null;
-
-  // Strict spawn function that takes a known generatorFunc and assumes that
-  // every yielded value will be a Promise.  If nesting is desired, then yield*
-  // should be used!
-  function spawn(generatorFunc) {
-    return new Promise(function(resolve, reject) {
-      try {
-        var iterator = generatorFunc();
-      }
-      catch (ex) {
-        ok(false, 'Problem invoking generator func: ' + ex + ': ' + ex.stack);
-        return;
-      }
-      var stepResolved = function(result) {
-        try {
-          var iterStep = iterator.next(result);
-        }
-        catch (ex) {
-          ok(false, 'Problem invoking iterator step: ' + ex + ': ' + ex.stack);
-          return;
-        }
-        if (iterStep.done) {
-          resolve(iterStep.value);
-          return;
-        }
-        if (!iterStep.value || !iterStep.value.then) {
-          ok(false, 'Iterator step returned non-Promise: ' + iterStep.value);
-        }
-        iterStep.value.then(stepResolved, generalErrback);
-      };
-      stepResolved();
-    });
-  }
-
-  function maybeSpawn(promiseOrGenerator) {
-    if (promiseOrGenerator.then) {
-      return promiseOrGenerator;
-    }
-    return spawn(promiseOrGenerator);
-  }
-
-  scope.add_task = function(thing) {
-    pendingTasks.push(thing);
-  };
-
-  function generalErrback(ex) {
-    ok(false,
-       'A rejection happened: ' +
-       (ex ? (ex + ': ' + ex.stack) : ''));
-  }
-
-  function runNextTask() {
-    if (pendingTasks.length) {
-      pendingPromise = maybeSpawn(pendingTasks.shift());
-      pendingPromise.then(runNextTask, generalErrback);
-    } else {
-      SimpleTest.finish();
-    }
-  }
-
-  // Trigger runNextTask after we think all JS files have been loaded.
-  // The primary goal is that we can call SimpleTest.finish() after all test
-  // code has been loaded and run.  We gate this based on the document's
-  // readyState.
-  var running = false;
-  function maybeStartRunning() {
-    if (!running && document.readyState === 'complete') {
-      running = true;
-      document.removeEventListener('readystateChange', maybeStartRunning);
-      // Defer to a subsequent turn of the event loop to let micro-tasks and any
-      // other clever setTimeout(0) instances run first.
-      window.setTimeout(runNextTask, 0);
-    }
-  }
-  document.addEventListener('readystatechange', maybeStartRunning);
-  maybeStartRunning();
-})(this);
--- a/dom/network/tests/chrome.ini
+++ b/dom/network/tests/chrome.ini
@@ -1,12 +1,10 @@
 [DEFAULT]
 support-files =
   tcpsocket_test.jsm
   test_tcpsocket_client_and_server_basics.js
-  add_task.js
   file_udpsocket_iframe.html
 
 [test_tcpsocket_jsm.html]
 [test_tcpsocket_client_and_server_basics.html]
-[test_tcpsocket_enabled_with_perm.html]
 [test_tcpsocket_legacy.html]
 [test_udpsocket.html]
--- a/dom/network/tests/mochitest.ini
+++ b/dom/network/tests/mochitest.ini
@@ -1,11 +1,9 @@
 [DEFAULT]
 support-files =
-  add_task.js
   worker_network_basics.js
 
 [test_network_basics.html]
 skip-if = toolkit == 'android'
 [test_network_basics_worker.html]
 skip-if = toolkit == 'android'
-[test_tcpsocket_default_permissions.html]
-[test_tcpsocket_enabled_no_perm.html]
+[test_tcpsocket_not_exposed_to_content.html]
--- a/dom/network/tests/tcpsocket_test.jsm
+++ b/dom/network/tests/tcpsocket_test.jsm
@@ -1,20 +1,16 @@
 this.EXPORTED_SYMBOLS = [
-  'createSocket', 'createServer', 'enablePrefsAndPermissions',
+  'createSocket', 'createServer',
   'socketCompartmentInstanceOfArrayBuffer'];
 
 this.createSocket = function(host, port, options) {
   return new TCPSocket(host, port, options);
 }
 
 this.createServer = function(port, options, backlog) {
   return new TCPServerSocket(port, options, backlog);
 }
 
-this.enablePrefsAndPermissions = function() {
-  return false;
-}
-
 // See test_tcpsocket_client_and_server_basics.html's version for rationale.
 this.socketCompartmentInstanceOfArrayBuffer = function(obj) {
   return obj instanceof ArrayBuffer;
 }
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.html
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.html
@@ -6,31 +6,27 @@ separate xpcshell incarnations.  This mi
 of bug 1084245 in order to get coverage of the tests from content.
 
 https://bugzilla.mozilla.org/show_bug.cgi?id=1084245
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1084245</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-  <script type="application/javascript" src="add_task.js"></script>
   <script type="application/javascript">
     function createServer(port, options, backlog) {
       return new TCPServerSocket(port, options, backlog);
     }
 
     function createSocket(host, port, options) {
       return new TCPSocket(host, port, options);
     }
 
-    function enablePrefsAndPermissions() {
-      return true;
-    }
-
     // In the JSM case, ArrayBuffers will be created in the compartment of the
     // JSM with different globals than the
     // test_tcpsocket_client_and_server_basics.js test logic sees, so we (and
     // tcpsocket_test.jsm) need to do something.  To avoid complexity relating
     // to wrappers and the varying nuances of the module scope and global scope
     // in JSM's (they differ on B2G), we hardcode ArrayBuffer rather than taking
     // a string that we look up, etc.
     function socketCompartmentInstanceOfArrayBuffer(obj) {
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -1,10 +1,17 @@
 'use strict';
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+// Bug 788960 and later bug 1329245 have taught us that attempting to connect to
+// a port that is not listening or is no longer listening fails to consistently
+// result in the error (or any) event we expect on Darwin/OSX/"OS X".
+const isOSX = (Services.appinfo.OS === "Darwin");
+const testConnectingToNonListeningPort = !isOSX;
+
 const SERVER_BACKLOG = -1;
 
 const SOCKET_EVENTS = ['open', 'data', 'drain', 'error', 'close'];
 
 function concatUint8Arrays(a, b) {
   let newArr = new Uint8Array(a.length + b.length);
   newArr.set(a, 0);
   newArr.set(b, a.length);
@@ -406,18 +413,21 @@ function* test_basics() {
 
   is(serverReceived.length < (2 * bigUint8Array.length), true, 'Received array length less than sent array length');
 
   // -- Close the listening server (and try to connect)
   // We want to verify that the server actually closes / stops listening when
   // we tell it to.
   listeningServer.close();
 
-  // - try and connect, get an error
-  clientSocket = createSocket('127.0.0.1', serverPort,
-                              { binaryType: 'arraybuffer' });
-  clientQueue = listenForEventsOnSocket(clientSocket, 'client');
-  is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect');
-  is(clientSocket.readyState, 'closed',
-     'client readyState should be closed after the failure to connect');
+  // (We don't run this check on OS X where it's flakey; see definition up top.)
+  if (testConnectingToNonListeningPort) {
+    // - try and connect, get an error
+    clientSocket = createSocket('127.0.0.1', serverPort,
+                                { binaryType: 'arraybuffer' });
+    clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+    is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect');
+    is(clientSocket.readyState, 'closed',
+       'client readyState should be closed after the failure to connect');
+  }
 }
 
 add_task(test_basics);
deleted file mode 100644
--- a/dom/network/tests/test_tcpsocket_default_permissions.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test to ensure TCPSocket permission is disabled by default</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test to ensure TCPSocket permission is disabled by default **/
-
-var caught = false;
-try {
-  new TCPSocket("localhost", 80, {})
-} catch (e) {
-  caught = true;
-}
-
-ok(caught, "TCPSocket should not exist by default");
-
-var caught = false;
-try {
-  navigator.mozTCPSocket.open("localhost", 80, {})
-} catch (e) {
-  caught = true;
-}
-
-ok(caught, "navigator.mozTCPSocket.open should not exist by default");
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/network/tests/test_tcpsocket_enabled_with_perm.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test to ensure TCPSocket permission enabled and open works with tcp-socket perm</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test to ensure TCPSocket permission being turned on enables 
-  navigator.mozTCPSocket, and mozTCPSocket.open works when
-  the tcp-socket permission has been granted.
-**/
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest);
-
-function runTest() {
-  ok('TCPSocket' in this, "TCPSocket should be accessible if dom.mozTCPSocket.enabled is true");
-
-  ok(new TCPSocket('localhost', 80), "TCPSocket constructor should work for content that has the tcp-socket permission");
-  ok(navigator.mozTCPSocket.open('localhost', 80), "navigator.mozTCPSocket.open should work for content that has the tcp-socket permission");
-  // This just helps the test harness clean up quickly
-  SpecialPowers.forceCC();
-  SpecialPowers.forceGC();
-  SimpleTest.finish();
-}
-</script>
-</pre>
-</body>
-</html>
--- a/dom/network/tests/test_tcpsocket_jsm.html
+++ b/dom/network/tests/test_tcpsocket_jsm.html
@@ -1,21 +1,21 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for 1207090</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
     Components.utils.import("chrome://mochitests/content/chrome/dom/network/tests/tcpsocket_test.jsm");
   </script>
-  <script type="application/javascript" src="add_task.js"></script>
   <script type="application/javascript" src="test_tcpsocket_client_and_server_basics.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1207090">Mozilla Bug 1207090</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
--- a/dom/network/tests/test_tcpsocket_legacy.html
+++ b/dom/network/tests/test_tcpsocket_legacy.html
@@ -14,19 +14,16 @@ Test of legacy navigator interface for o
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script>
   SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv(
-    { set: [ ['dom.mozTCPSocket.enabled', true] ] },
-    runTest);
 
   function runTest() {
     // See bug 903830; in e10s mode we never get to find out the localPort if we
     // let it pick a free port by choosing 0.  This is the same port the xpcshell
     // test was using.
     var serverPort = 8085;
 
     var listeningServer = navigator.mozTCPSocket.listen(serverPort,
@@ -49,11 +46,12 @@ Test of legacy navigator interface for o
       setTimeout(function() {
         // This just helps the test harness clean up quickly
         SpecialPowers.forceCC();
         SpecialPowers.forceGC();
         SimpleTest.finish();
       }, 0);
     }
   }
+  runTest(); // we used to invoke this as part of a moot pref-setting callback
 </script>
 </body>
 </html>
rename from dom/network/tests/test_tcpsocket_enabled_no_perm.html
rename to dom/network/tests/test_tcpsocket_not_exposed_to_content.html
--- a/dom/network/tests/test_tcpsocket_enabled_no_perm.html
+++ b/dom/network/tests/test_tcpsocket_not_exposed_to_content.html
@@ -6,25 +6,20 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
+/**
+ * TCPSocket and its legacy mozTCPSocket variant should never be exposed to
+ * content.
+ */
 
-/** Test to ensure TCPSocket preference being turned on does not enable
-  navigator.mozTCPSocket.
-**/
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest);
-function runTest() {
-  is('TCPSocket' in this, false, "TCPSocket should not be accessible if dom.mozTCPSocket.enabled is true");
-  is('TCPServerSocket' in this, false, "TCPServerSocket should not be accessible if dom.mozTCPSocket.enabled is true");
-  is('mozTCPSocket' in navigator, false, "mozTCPSocket should not be accessible if dom.mozTCPSocket.enabled is true");
-
-  SimpleTest.finish();
-}
+is('TCPSocket' in this, false, "TCPSocket should not be accessible to content");
+is('TCPServerSocket' in this, false, "TCPServerSocket should not be accessible to content");
+is('mozTCPSocket' in navigator, false, "mozTCPSocket should not be accessible to content");
 </script>
 </pre>
 </body>
 </html>
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -24,45 +24,36 @@ namespace js {
 
 // A call stack can be specified to the JS engine such that all JS entry/exits
 // to functions push/pop an entry to/from the specified stack.
 //
 // For more detailed information, see vm/GeckoProfiler.h.
 //
 class ProfileEntry
 {
-    // All fields are marked volatile to prevent the compiler from re-ordering
-    // instructions. Namely this sequence:
-    //
-    //    entry[size] = ...;
-    //    size++;
-    //
-    // If the size modification were somehow reordered before the stores, then
-    // if a sample were taken it would be examining bogus information.
-    //
-    // A ProfileEntry represents both a C++ profile entry and a JS one.
+    // A ProfileEntry represents either a C++ profile entry or a JS one.
 
     // Descriptive label for this entry. Must be a static string! Can be an
     // empty string, but not a null pointer.
-    const char * volatile label_;
+    const char* label_;
 
     // An additional descriptive string of this entry which is combined with
     // |label_| in profiler output. Need not be (and usually isn't) static. Can
     // be null.
-    const char * volatile dynamicString_;
+    const char* dynamicString_;
 
     // Stack pointer for non-JS entries, the script pointer otherwise.
-    void * volatile spOrScript;
+    void* spOrScript;
 
     // Line number for non-JS entries, the bytecode offset otherwise.
-    int32_t volatile lineOrPcOffset;
+    int32_t lineOrPcOffset;
 
     // Bits 0..1 hold the Kind. Bits 2..3 are unused. Bits 4..12 hold the
     // Category.
-    uint32_t volatile kindAndCategory_;
+    uint32_t kindAndCategory_;
 
     static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
 
   public:
     enum class Kind : uint32_t {
         // A normal C++ frame.
         CPP_NORMAL = 0,
 
@@ -98,95 +89,90 @@ class ProfileEntry
         LAST     = EVENTS,
 
         CATEGORY_MASK = ~uint32_t(Kind::KIND_MASK),
     };
 
     static_assert((uint32_t(Category::FIRST) & uint32_t(Kind::KIND_MASK)) == 0,
                   "Category overlaps with Kind");
 
-    // All of these methods are marked with the 'volatile' keyword because the
-    // Gecko Profiler's representation of the stack is stored such that all
-    // ProfileEntry instances are volatile. These methods would not be
-    // available unless they were marked as volatile as well.
-
-    bool isCpp() const volatile
+    bool isCpp() const
     {
         Kind k = kind();
         return k == Kind::CPP_NORMAL || k == Kind::CPP_MARKER_FOR_JS;
     }
 
-    bool isJs() const volatile
+    bool isJs() const
     {
         Kind k = kind();
         return k == Kind::JS_NORMAL || k == Kind::JS_OSR;
     }
 
-    void setLabel(const char* aLabel) volatile { label_ = aLabel; }
-    const char* label() const volatile { return label_; }
+    void setLabel(const char* aLabel) { label_ = aLabel; }
+    const char* label() const { return label_; }
 
-    const char* dynamicString() const volatile { return dynamicString_; }
+    const char* dynamicString() const { return dynamicString_; }
 
     void initCppFrame(const char* aLabel, const char* aDynamicString, void* sp, uint32_t aLine,
-                      Kind aKind, Category aCategory) volatile
+                      Kind aKind, Category aCategory)
     {
         label_ = aLabel;
         dynamicString_ = aDynamicString;
         spOrScript = sp;
         lineOrPcOffset = static_cast<int32_t>(aLine);
         kindAndCategory_ = uint32_t(aKind) | uint32_t(aCategory);
         MOZ_ASSERT(isCpp());
     }
 
     void initJsFrame(const char* aLabel, const char* aDynamicString, JSScript* aScript,
-                     jsbytecode* aPc) volatile
+                     jsbytecode* aPc)
     {
         label_ = aLabel;
         dynamicString_ = aDynamicString;
         spOrScript = aScript;
         lineOrPcOffset = pcToOffset(aScript, aPc);
         kindAndCategory_ = uint32_t(Kind::JS_NORMAL) | uint32_t(Category::JS);
         MOZ_ASSERT(isJs());
     }
 
-    void setKind(Kind aKind) volatile {
+    void setKind(Kind aKind) {
         kindAndCategory_ = uint32_t(aKind) | uint32_t(category());
     }
 
-    Kind kind() const volatile {
+    Kind kind() const {
         return Kind(kindAndCategory_ & uint32_t(Kind::KIND_MASK));
     }
 
-    Category category() const volatile {
+    Category category() const {
         return Category(kindAndCategory_ & uint32_t(Category::CATEGORY_MASK));
     }
 
-    void* stackAddress() const volatile {
+    void* stackAddress() const {
         MOZ_ASSERT(!isJs());
         return spOrScript;
     }
 
-    JS_PUBLIC_API(JSScript*) script() const volatile;
+    JS_PUBLIC_API(JSScript*) script() const;
 
-    uint32_t line() const volatile {
+    uint32_t line() const {
         MOZ_ASSERT(!isJs());
         return static_cast<uint32_t>(lineOrPcOffset);
     }
 
     // Note that the pointer returned might be invalid.
-    JSScript* rawScript() const volatile {
+    JSScript* rawScript() const {
         MOZ_ASSERT(isJs());
         return (JSScript*)spOrScript;
     }
 
     // We can't know the layout of JSScript, so look in vm/GeckoProfiler.cpp.
-    JS_FRIEND_API(jsbytecode*) pc() const volatile;
-    void setPC(jsbytecode* pc) volatile;
+    JS_FRIEND_API(jsbytecode*) pc() const;
+    void setPC(jsbytecode* pc);
 
-    void trace(JSTracer* trc) volatile;
+    void trace(JSTracer* trc);
 
     // The offset of a pc into a script's code can actually be 0, so to
     // signify a nullptr pc, use a -1 index. This is checked against in
     // pc() and setPC() to set/get the right pc.
     static const int32_t NullPCOffset = -1;
 };
 
 JS_FRIEND_API(void)
@@ -195,19 +181,34 @@ SetContextProfilingStack(JSContext* cx, 
 JS_FRIEND_API(void)
 EnableContextProfilingStack(JSContext* cx, bool enabled);
 
 JS_FRIEND_API(void)
 RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
 
 } // namespace js
 
-// The PseudoStack members are accessed in parallel by multiple threads: the
-// profiler's sampler thread reads these members while other threads modify
-// them.
+// Each thread has its own PseudoStack. That thread modifies the PseudoStack,
+// pushing and popping elements as necessary.
+//
+// The PseudoStack is also read periodically by the profiler's sampler thread.
+// This happens only when the thread that owns the PseudoStack is suspended. So
+// there are no genuine parallel accesses.
+//
+// However, it is possible for pushing/popping to be interrupted by a periodic
+// sample. Because of this, we need pushing/popping to be effectively atomic.
+//
+// - When pushing a new entry, we increment the stack pointer -- making the new
+//   entry visible to the sampler thread -- only after the new entry has been
+//   fully written. The stack pointer is Atomic<> (with SequentiallyConsistent
+//   semantics) to ensure the incrementing is not reordered before the writes.
+//
+// - When popping an old entry, the only operation is the decrementing of the
+//   stack pointer, which is obviously atomic.
+//
 class PseudoStack
 {
   public:
     PseudoStack()
       : stackPointer(0)
     {}
 
     ~PseudoStack() {
@@ -250,18 +251,18 @@ class PseudoStack
     // No copying.
     PseudoStack(const PseudoStack&) = delete;
     void operator=(const PseudoStack&) = delete;
 
   public:
     static const uint32_t MaxEntries = 1024;
 
     // The stack entries.
-    js::ProfileEntry volatile entries[MaxEntries];
+    js::ProfileEntry entries[MaxEntries];
 
     // This may exceed MaxEntries, so instead use the stackSize() method to
     // determine the number of valid samples in entries. When this is less
     // than MaxEntries, it refers to the first free entry past the top of the
     // in-use stack (i.e. entries[stackPointer - 1] is the top stack entry).
-    mozilla::Atomic<uint32_t> stackPointer;
+    mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> stackPointer;
 };
 
 #endif  /* js_ProfilingStack_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1353356.js
@@ -0,0 +1,65 @@
+// |jit-test| allow-oom; --fuzzing-safe
+
+var lfLogBuffer = `
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-endofdata
+    setJitCompilerOption("ion.warmup.trigger", 4);
+    var g = newGlobal();
+    g.debuggeeGlobal = this;
+    g.eval("(" + function () {
+        dbg = new Debugger(debuggeeGlobal);
+        dbg.onExceptionUnwind = function (frame, exc) {
+            var s = '!';
+            for (var f = frame; f; f = f.older)
+            debuggeeGlobal.log += s;
+        };
+    } + ")();");
+      j('Number.prototype.toSource.call([])');
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-selectmode 4
+//corefuzz-dcd-endofdata
+}
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-selectmode 5
+//corefuzz-dcd-endofdata
+oomTest(() => i({
+    new  : (true  ),
+    thisprops: true
+}));
+`;
+lfLogBuffer = lfLogBuffer.split('\n');
+var lfRunTypeId = -1;
+var lfCodeBuffer = "";
+while (true) {
+    var line = lfLogBuffer.shift();
+    if (line == null) {
+        break;
+    } else if (line == "//corefuzz-dcd-endofdata") {
+        loadFile(lfCodeBuffer);
+        lfCodeBuffer = "";
+        loadFile(line);
+    } else {
+        lfCodeBuffer += line + "\n";
+    }
+}
+if (lfCodeBuffer) loadFile(lfCodeBuffer);
+function loadFile(lfVarx) {
+    try {
+        if (lfVarx.indexOf("//corefuzz-dcd-selectmode ") === 0) {
+            lfRunTypeId = parseInt(lfVarx.split(" ")[1]) % 6;
+        } else {
+            switch (lfRunTypeId) {
+                case 4:
+                    oomTest(function() {
+                        let m = parseModule(lfVarx);
+                    });
+                    break;
+                default:
+                    evaluate(lfVarx);
+            }
+        }
+    } catch (lfVare) {}
+}
--- a/js/src/make-source-package.sh
+++ b/js/src/make-source-package.sh
@@ -149,16 +149,20 @@ case $cmd in
         ${TOPSRCDIR}/memory/fallible \
         ${TOPSRCDIR}/memory/mozalloc \
         ${TOPSRCDIR}/memory/mozjemalloc \
         ${tgtpath}/memory
 
     # remove *.pyc and *.pyo files if any
     find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" |xargs rm -f
 
+    # Remove non-JS Cargo.toml files (for example, the invalid Cargo.toml files
+    # used for some testing).
+    find ${tgtpath} -type f -name Cargo.toml | grep -v js | xargs rm -f
+
     # copy or create INSTALL
     if [ -e ${STAGING}/INSTALL ]; then
         cp ${STAGING}/INSTALL ${tgtpath}
     else
         cat <<INSTALL_EOF >${tgtpath}/INSTALL
 Full build documentation for SpiderMonkey is hosted on MDN:
   https://developer.mozilla.org/en-US/docs/SpiderMonkey/Build_Documentation
 
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -234,25 +234,25 @@ GeckoProfiler::exit(JSScript* script, JS
         // Bug 822041
         if (!pseudoStack_->entries[sp].isJs()) {
             fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
             fprintf(stderr, " entries=%p size=%u/%u\n",
                             (void*) pseudoStack_->entries,
                             uint32_t(pseudoStack_->stackPointer),
                             PseudoStack::MaxEntries);
             for (int32_t i = sp; i >= 0; i--) {
-                volatile ProfileEntry& entry = pseudoStack_->entries[i];
+                ProfileEntry& entry = pseudoStack_->entries[i];
                 if (entry.isJs())
                     fprintf(stderr, "  [%d] JS %s\n", i, entry.dynamicString());
                 else
                     fprintf(stderr, "  [%d] C line %d %s\n", i, entry.line(), entry.dynamicString());
             }
         }
 
-        volatile ProfileEntry& entry = pseudoStack_->entries[sp];
+        ProfileEntry& entry = pseudoStack_->entries[sp];
         MOZ_ASSERT(entry.isJs());
         MOZ_ASSERT(entry.script() == script);
         MOZ_ASSERT(strcmp((const char*) entry.dynamicString(), dynamicString) == 0);
     }
 #endif
 }
 
 /*
@@ -305,17 +305,17 @@ GeckoProfiler::allocProfileString(JSScri
     }
 
     MOZ_ASSERT(ret == len, "Computed length should match actual length!");
 
     return cstr;
 }
 
 void
-GeckoProfiler::trace(JSTracer* trc) volatile
+GeckoProfiler::trace(JSTracer* trc)
 {
     if (pseudoStack_) {
         size_t size = pseudoStack_->stackSize();
         for (size_t i = 0; i < size; i++)
             pseudoStack_->entries[i].trace(trc);
     }
 }
 
@@ -348,17 +348,17 @@ GeckoProfiler::checkStringsMapAfterMovin
         CheckGCThingAfterMovingGC(script);
         auto ptr = locked->lookup(script);
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
     }
 }
 #endif
 
 void
-ProfileEntry::trace(JSTracer* trc) volatile
+ProfileEntry::trace(JSTracer* trc)
 {
     if (isJs()) {
         JSScript* s = rawScript();
         TraceNullableRoot(trc, &s, "ProfileEntry script");
         spOrScript = s;
     }
 }
 
@@ -435,38 +435,38 @@ GeckoProfilerBaselineOSRMarker::GeckoPro
         profiler = nullptr;
         return;
     }
 
     spBefore_ = sp;
     if (sp == 0)
         return;
 
-    volatile ProfileEntry& entry = profiler->pseudoStack_->entries[sp - 1];
+    ProfileEntry& entry = profiler->pseudoStack_->entries[sp - 1];
     MOZ_ASSERT(entry.kind() == ProfileEntry::Kind::JS_NORMAL);
     entry.setKind(ProfileEntry::Kind::JS_OSR);
 }
 
 GeckoProfilerBaselineOSRMarker::~GeckoProfilerBaselineOSRMarker()
 {
     if (profiler == nullptr)
         return;
 
     uint32_t sp = profiler->stackPointer();
     MOZ_ASSERT(spBefore_ == sp);
     if (sp == 0)
         return;
 
-    volatile ProfileEntry& entry = profiler->stack()[sp - 1];
+    ProfileEntry& entry = profiler->stack()[sp - 1];
     MOZ_ASSERT(entry.kind() == ProfileEntry::Kind::JS_OSR);
     entry.setKind(ProfileEntry::Kind::JS_NORMAL);
 }
 
 JS_PUBLIC_API(JSScript*)
-ProfileEntry::script() const volatile
+ProfileEntry::script() const
 {
     MOZ_ASSERT(isJs());
     auto script = reinterpret_cast<JSScript*>(spOrScript);
     if (!script)
         return nullptr;
 
     // If profiling is supressed then we can't trust the script pointers to be
     // valid as they could be in the process of being moved by a compacting GC
@@ -479,33 +479,33 @@ ProfileEntry::script() const volatile
     if (!cx->isProfilerSamplingEnabled())
         return nullptr;
 
     MOZ_ASSERT(!IsForwarded(script));
     return script;
 }
 
 JS_FRIEND_API(jsbytecode*)
-ProfileEntry::pc() const volatile
+ProfileEntry::pc() const
 {
     MOZ_ASSERT(isJs());
     if (lineOrPcOffset == NullPCOffset)
         return nullptr;
 
     JSScript* script = this->script();
     return script ? script->offsetToPC(lineOrPcOffset) : nullptr;
 }
 
 /* static */ int32_t
 ProfileEntry::pcToOffset(JSScript* aScript, jsbytecode* aPc) {
     return aPc ? aScript->pcToOffset(aPc) : NullPCOffset;
 }
 
 void
-ProfileEntry::setPC(jsbytecode* pc) volatile
+ProfileEntry::setPC(jsbytecode* pc)
 {
     MOZ_ASSERT(isJs());
     JSScript* script = this->script();
     MOZ_ASSERT(script); // This should not be called while profiling is suppressed.
     lineOrPcOffset = pcToOffset(script, pc);
 }
 
 JS_FRIEND_API(void)
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -137,17 +137,17 @@ class GeckoProfiler
     UniqueChars allocProfileString(JSScript* script, JSFunction* function);
 
   public:
     explicit GeckoProfiler(JSRuntime* rt);
 
     bool init();
 
     uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; }
-    volatile ProfileEntry* stack() { return pseudoStack_->entries; }
+    ProfileEntry* stack() { return pseudoStack_->entries; }
 
     /* management of whether instrumentation is on or off */
     bool enabled() { MOZ_ASSERT_IF(enabled_, installed()); return enabled_; }
     bool installed() { return pseudoStack_ != nullptr; }
     MOZ_MUST_USE bool enable(bool enabled);
     void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
     bool slowAssertionsEnabled() { return slowAssertions; }
 
@@ -184,17 +184,17 @@ class GeckoProfiler
     /* meant to be used for testing, not recommended to call in normal code */
     size_t stringsCount();
     void stringsReset();
 
     uint32_t* addressOfEnabled() {
         return &enabled_;
     }
 
-    void trace(JSTracer* trc) volatile;
+    void trace(JSTracer* trc);
     void fixupStringsMapAfterMovingGC();
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkStringsMapAfterMovingGC();
 #endif
 };
 
 inline size_t
 GeckoProfiler::stringsCount()
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1534,38 +1534,36 @@ jit::JitActivation::getRematerializedFra
             ReportOutOfMemory(cx);
             return nullptr;
         }
     }
 
     uint8_t* top = iter.fp();
     RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
     if (!p) {
-        RematerializedFrameVector empty(cx);
-        if (!rematerializedFrames_->add(p, top, Move(empty))) {
-            ReportOutOfMemory(cx);
-            return nullptr;
-        }
+        RematerializedFrameVector frames(cx);
 
         // The unit of rematerialization is an uninlined frame and its inlined
         // frames. Since inlined frames do not exist outside of snapshots, it
         // is impossible to synchronize their rematerialized copies to
         // preserve identity. Therefore, we always rematerialize an uninlined
         // frame and all its inlined frames at once.
         InlineFrameIterator inlineIter(cx, &iter);
         MaybeReadFallback recover(cx, this, &iter);
 
         // Frames are often rematerialized with the cx inside a Debugger's
         // compartment. To recover slots and to create CallObjects, we need to
         // be in the activation's compartment.
         AutoCompartmentUnchecked ac(cx, compartment_);
 
-        if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover,
-                                                            p->value()))
-        {
+        if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover, frames))
+            return nullptr;
+
+        if (!rematerializedFrames_->add(p, top, Move(frames))) {
+            ReportOutOfMemory(cx);
             return nullptr;
         }
 
         // See comment in unsetPrevUpToDateUntil.
         DebugEnvironments::unsetPrevUpToDateUntil(cx, p->value()[inlineDepth]);
     }
 
     return p->value()[inlineDepth];
--- a/mobile/android/chrome/geckoview/geckoview.js
+++ b/mobile/android/chrome/geckoview/geckoview.js
@@ -43,17 +43,20 @@ var ModuleManager = {
       return;
     }
     delete this.modules[aType];
   }
 };
 
 function startup() {
   ModuleManager.init();
+
+  // GeckoViewNavigation needs to go first because nsIDOMBrowserWindow must set up
+  // before the first remote browser. Bug 1365364.
+  ModuleManager.add("resource://gre/modules/GeckoViewNavigation.jsm",
+                    "GeckoViewNavigation");
   ModuleManager.add("resource://gre/modules/GeckoViewSettings.jsm",
                     "GeckoViewSettings");
   ModuleManager.add("resource://gre/modules/GeckoViewContent.jsm",
                     "GeckoViewContent");
-  ModuleManager.add("resource://gre/modules/GeckoViewNavigation.jsm",
-                    "GeckoViewNavigation");
   ModuleManager.add("resource://gre/modules/GeckoViewProgress.jsm",
                     "GeckoViewProgress");
 }
--- a/testing/talos/talos/tests/video/video_playback.html
+++ b/testing/talos/talos/tests/video/video_playback.html
@@ -15,17 +15,18 @@ var start2 = 0;
 var paintedFramesStart2 = 0;
 var testIndex = 0;
 var test = [
   'testsrc.240p.120fps.mp4',
   'testsrc.480p.60fps.webm',
   'testsrc.1080p.60fps.mp4',
 ];
 var viewModeIndex = 0;
-var viewMode = ['fullscreen', 1, 1.1, 2];
+// Remove fullscreen mode since it causes intermittent failures on try server. See bug 1192317.
+var viewMode = [1, 1.1, 2];
 var testResult = {names: [], values: []};
 
 function init() {
   TalosPowersContent.focus(content_focused)
 }
 
 function content_focused() {
   vdo = document.getElementById('vdo');
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -581,26 +581,26 @@ private:
 };
 
 MOZ_THREAD_LOCAL(ThreadInfo*) TLSInfo::sThreadInfo;
 
 // Although you can access a thread's PseudoStack via TLSInfo::sThreadInfo, we
 // also have a second TLS pointer directly to the PseudoStack. Here's why.
 //
 // - We need to be able to push to and pop from the PseudoStack in
-//   profiler_call_{enter,exit}.
+//   ProfilerStackFrameRAII.
 //
 // - Those two functions are hot and must be defined in GeckoProfiler.h so they
 //   can be inlined.
 //
 // - We don't want to expose TLSInfo (and ThreadInfo) in GeckoProfiler.h.
 //
 // This second pointer isn't ideal, but does provide a way to satisfy those
 // constraints. TLSInfo manages it, except for the uses in
-// profiler_call_{enter,exit}.
+// ProfilerStackFrameRAII.
 MOZ_THREAD_LOCAL(PseudoStack*) sPseudoStack;
 
 // The name of the main thread.
 static const char* const kMainThreadName = "GeckoMain";
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN tick/unwinding code
 
@@ -719,18 +719,17 @@ AddDynamicCodeLocationTag(ProfileBuffer*
 
     // Cast to *((void**) to pass the text data to a void*.
     aBuffer->addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0]))));
   }
 }
 
 static void
 AddPseudoEntry(PSLockRef aLock, ProfileBuffer* aBuffer,
-               volatile js::ProfileEntry& entry,
-               NotNull<RacyThreadInfo*> aRacyInfo)
+               js::ProfileEntry& entry, NotNull<RacyThreadInfo*> aRacyInfo)
 {
   MOZ_ASSERT(entry.kind() == js::ProfileEntry::Kind::CPP_NORMAL ||
              entry.kind() == js::ProfileEntry::Kind::JS_NORMAL);
 
   int lineno = -1;
 
   // First entry has kind CodeLocation.
   const char* label = entry.label();
@@ -822,17 +821,17 @@ struct AutoWalkJSStack
   }
 };
 
 static void
 MergeStacksIntoProfile(PSLockRef aLock, ProfileBuffer* aBuffer,
                        const TickSample& aSample, NativeStack& aNativeStack)
 {
   NotNull<RacyThreadInfo*> racyInfo = aSample.mRacyInfo;
-  volatile js::ProfileEntry* pseudoEntries = racyInfo->entries;
+  js::ProfileEntry* pseudoEntries = racyInfo->entries;
   uint32_t pseudoCount = racyInfo->stackSize();
   JSContext* context = aSample.mJSContext;
 
   // Make a copy of the JS stack into a JSFrame array. This is necessary since,
   // like the native stack, the JS stack is iterated youngest-to-oldest and we
   // need to iterate oldest-to-youngest when adding entries to aInfo.
 
   // Synchronous sampling reports an invalid buffer generation to
@@ -897,17 +896,17 @@ MergeStacksIntoProfile(PSLockRef aLock, 
   // Iterate as long as there is at least one frame remaining.
   while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
     // There are 1 to 3 frames available. Find and add the oldest.
     uint8_t* pseudoStackAddr = nullptr;
     uint8_t* jsStackAddr = nullptr;
     uint8_t* nativeStackAddr = nullptr;
 
     if (pseudoIndex != pseudoCount) {
-      volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
+      js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
 
       if (pseudoEntry.isCpp()) {
         lastPseudoCppStackAddr = (uint8_t*) pseudoEntry.stackAddress();
       }
 
       // Skip any JS_OSR frames. Such frames are used when the JS interpreter
       // enters a jit frame on a loop edge (via on-stack-replacement, or OSR).
       // To avoid both the pseudoframe and jit frame being recorded (and
@@ -947,17 +946,17 @@ MergeStacksIntoProfile(PSLockRef aLock, 
     MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
                                jsStackAddr != nativeStackAddr);
     MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
                                    nativeStackAddr != jsStackAddr);
 
     // Check to see if pseudoStack frame is top-most.
     if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
       MOZ_ASSERT(pseudoIndex < pseudoCount);
-      volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
+      js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
 
       // Pseudo-frames with the CPP_MARKER_FOR_JS kind are just annotations and
       // should not be recorded in the profile.
       if (pseudoEntry.kind() != js::ProfileEntry::Kind::CPP_MARKER_FOR_JS) {
         AddPseudoEntry(aLock, aBuffer, pseudoEntry, racyInfo);
       }
       pseudoIndex++;
       continue;
@@ -1095,17 +1094,17 @@ DoNativeBacktrace(PSLockRef aLock, Profi
 
   // The pseudostack contains an "EnterJIT" frame whenever we enter
   // JIT code with profiling enabled; the stack pointer value points
   // the saved registers.  We use this to unwind resume unwinding
   // after encounting JIT code.
   for (uint32_t i = racyInfo->stackSize(); i > 0; --i) {
     // The pseudostack grows towards higher indices, so we iterate
     // backwards (from callee to caller).
-    volatile js::ProfileEntry& entry = racyInfo->entries[i - 1];
+    js::ProfileEntry& entry = racyInfo->entries[i - 1];
     if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
       // Found JIT entry frame.  Unwind up to that point (i.e., force
       // the stack walk to stop before the block of saved registers;
       // note that it yields nondecreasing stack pointers), then restore
       // the saved state.
       uint32_t* vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
 
       nativeStack.count += EHABIStackWalk(*mcontext,
@@ -2852,17 +2851,17 @@ profiler_get_backtrace_noalloc(char *out
 
   PseudoStack* pseudoStack = TLSInfo::Stack();
   if (!pseudoStack) {
     return;
   }
 
   bool includeDynamicString = !ActivePS::FeaturePrivacy(lock);
 
-  volatile js::ProfileEntry* pseudoEntries = pseudoStack->entries;
+  js::ProfileEntry* pseudoEntries = pseudoStack->entries;
   uint32_t pseudoCount = pseudoStack->stackSize();
 
   for (uint32_t i = 0; i < pseudoCount; i++) {
     const char* label = pseudoEntries[i].label();
     const char* dynamicString =
       includeDynamicString ? pseudoEntries[i].dynamicString() : nullptr;
     size_t labelLength = strlen(label);
     if (dynamicString) {
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -93,17 +93,18 @@ using UniqueProfilerBacktrace =
 // profile buffer. So there's one string copy operation, and it happens at
 // sample time.
 // Compare this to the plain PROFILER_LABEL macro, which only accepts literal
 // strings: When the pseudo stack frames generated by PROFILER_LABEL are
 // sampled, no string copy needs to be made because the profile buffer can
 // just store the raw pointers to the literal strings. Consequently,
 // PROFILER_LABEL frames take up considerably less space in the profile buffer
 // than PROFILER_LABEL_DYNAMIC frames.
-#define PROFILER_LABEL_DYNAMIC(name_space, info, category, str) do {} while (0)
+#define PROFILER_LABEL_DYNAMIC(name_space, info, category, dynamicStr) \
+  do {} while (0)
 
 // Insert a marker in the profile timeline. This is useful to delimit something
 // important happening such as the first paint. Unlike profiler_label that are
 // only recorded if a sample is collected while it is active, marker will always
 // be collected.
 #define PROFILER_MARKER(marker_name) do {} while (0)
 #define PROFILER_MARKER_PAYLOAD(marker_name, payload) \
   do { \
@@ -123,30 +124,30 @@ using UniqueProfilerBacktrace =
 #define PROFILER_FUNC_VOID(decl) void decl;
 
 // we want the class and function name but can't easily get that using preprocessor macros
 // __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
 
 #define PROFILER_LABEL(name_space, info, category) \
   PROFILER_PLATFORM_TRACING(name_space "::" info) \
   mozilla::ProfilerStackFrameRAII \
-  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(name_space "::" info, category, \
-                                             __LINE__)
+  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(name_space "::" info, nullptr, \
+                                             __LINE__, category)
 
 #define PROFILER_LABEL_FUNC(category) \
   PROFILER_PLATFORM_TRACING(PROFILER_FUNCTION_NAME) \
   mozilla::ProfilerStackFrameRAII \
-  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(PROFILER_FUNCTION_NAME, category, \
-                                             __LINE__)
+  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(PROFILER_FUNCTION_NAME, nullptr, \
+                                             __LINE__, category)
 
-#define PROFILER_LABEL_DYNAMIC(name_space, info, category, str) \
+#define PROFILER_LABEL_DYNAMIC(name_space, info, category, dynamicStr) \
   PROFILER_PLATFORM_TRACING(name_space "::" info) \
-  mozilla::ProfilerStackFrameDynamicRAII \
-  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(name_space "::" info, category, \
-                                             __LINE__, str)
+  mozilla::ProfilerStackFrameRAII \
+  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(name_space "::" info, dynamicStr, \
+                                             __LINE__, category)
 
 #define PROFILER_MARKER(marker_name) profiler_add_marker(marker_name)
 #define PROFILER_MARKER_PAYLOAD(marker_name, payload) \
   profiler_add_marker(marker_name, payload)
 
 #endif  // defined(MOZ_GECKO_PROFILER)
 
 // Higher-order macro containing all the feature info in one place. Define
@@ -400,61 +401,20 @@ PROFILER_FUNC(void* profiler_get_stack_t
 // Make sure that we can use std::min here without the Windows headers messing with us.
 #ifdef min
 # undef min
 #endif
 
 class nsISupports;
 class ProfilerMarkerPayload;
 
-// This exists purely for profiler_call_{enter,exit}. See the comment on the
+// This exists purely for ProfilerStackFrameRAII. See the comment on the
 // definition in platform.cpp for details.
 extern MOZ_THREAD_LOCAL(PseudoStack*) sPseudoStack;
 
-// Returns a handle to pass on exit. This can check that we are popping the
-// correct callstack. Operates the same whether the profiler is active or not.
-//
-// A short-lived, non-owning PseudoStack reference is created between each
-// profiler_call_enter() / profiler_call_exit() call pair. RAII objects (e.g.
-// ProfilerStackFrameRAII) ensure that these calls are balanced. Furthermore,
-// the RAII objects exist within the thread itself, which means they are
-// necessarily bounded by the lifetime of the thread, which ensures that the
-// references held can't be used after the PseudoStack is destroyed.
-inline void*
-profiler_call_enter(const char* aLabel, js::ProfileEntry::Category aCategory,
-                    void* aFrameAddress, uint32_t aLine,
-                    const char* aDynamicString = nullptr)
-{
-  // This function runs both on and off the main thread.
-
-  PseudoStack* pseudoStack = sPseudoStack.get();
-  if (!pseudoStack) {
-    return pseudoStack;
-  }
-  pseudoStack->pushCppFrame(aLabel, aDynamicString, aFrameAddress, aLine,
-                            js::ProfileEntry::Kind::CPP_NORMAL, aCategory);
-
-  // The handle is meant to support future changes but for now it is simply
-  // used to avoid having to call TLSInfo::RacyInfo() in profiler_call_exit().
-  return pseudoStack;
-}
-
-inline void
-profiler_call_exit(void* aHandle)
-{
-  // This function runs both on and off the main thread.
-
-  if (!aHandle) {
-    return;
-  }
-
-  PseudoStack* pseudoStack = static_cast<PseudoStack*>(aHandle);
-  pseudoStack->pop();
-}
-
 // Adds a marker to the PseudoStack. A no-op if the profiler is inactive or in
 // privacy mode.
 void profiler_add_marker(const char* aMarkerName,
                          ProfilerMarkerPayload* aPayload = nullptr);
 
 #define PROFILER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
 #define PROFILER_APPEND_LINE_NUMBER_EXPAND(id, line) \
   PROFILER_APPEND_LINE_NUMBER_PASTE(id, line)
@@ -502,51 +462,51 @@ void profiler_add_marker(const char* aMa
 // In the case of profiler_get_backtrace we know that we only need enough space
 // for a single backtrace.
 #define PROFILER_GET_BACKTRACE_ENTRIES 1000
 
 #define PROFILER_DEFAULT_INTERVAL 1
 
 namespace mozilla {
 
+// This class creates a non-owning PseudoStack reference. Objects of this class
+// are stack-allocated, and so exist within a thread, and are thus bounded by
+// the lifetime of the thread, which ensures that the references held can't be
+// used after the PseudoStack is destroyed.
 class MOZ_RAII ProfilerStackFrameRAII {
 public:
-  // We only copy the strings at save time, so to take multiple parameters we'd
-  // need to copy them then.
-  ProfilerStackFrameRAII(const char* aLabel,
-    js::ProfileEntry::Category aCategory, uint32_t aLine
-    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+  ProfilerStackFrameRAII(const char* aLabel, const char* aDynamicString,
+                         uint32_t aLine, js::ProfileEntry::Category aCategory
+                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    mHandle = profiler_call_enter(aLabel, aCategory, this, aLine);
-  }
-  ~ProfilerStackFrameRAII() {
-    profiler_call_exit(mHandle);
-  }
-private:
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-  void* mHandle;
-};
+
+    // This function runs both on and off the main thread.
 
-class MOZ_RAII ProfilerStackFrameDynamicRAII {
-public:
-  ProfilerStackFrameDynamicRAII(const char* aLabel,
-    js::ProfileEntry::Category aCategory, uint32_t aLine,
-    const char* aDynamicString)
-  {
-    mHandle = profiler_call_enter(aLabel, aCategory, this, aLine,
-                                  aDynamicString);
+    mPseudoStack = sPseudoStack.get();
+    if (mPseudoStack) {
+      mPseudoStack->pushCppFrame(aLabel, aDynamicString, this, aLine,
+                                js::ProfileEntry::Kind::CPP_NORMAL, aCategory);
+    }
   }
 
-  ~ProfilerStackFrameDynamicRAII() {
-    profiler_call_exit(mHandle);
+  ~ProfilerStackFrameRAII()
+  {
+    // This function runs both on and off the main thread.
+
+    if (mPseudoStack) {
+      mPseudoStack->pop();
+    }
   }
 
 private:
-  void* mHandle;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  // We save a PseudoStack pointer in the ctor so we don't have to redo the TLS
+  // lookup in the dtor.
+  PseudoStack* mPseudoStack;
 };
 
 } // namespace mozilla
 
 PseudoStack* profiler_get_pseudo_stack();
 
 void profiler_set_js_context(JSContext* aCx);
 void profiler_clear_js_context();
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp
+++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp
@@ -578,25 +578,21 @@ TEST(GeckoProfiler, PseudoStack)
 
     profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
                    features, filters, MOZ_ARRAY_LENGTH(filters));
 
     ASSERT_TRUE(profiler_get_backtrace());
   }
 
 #if defined(MOZ_GECKO_PROFILER)
-  ProfilerStackFrameRAII raii1("A", js::ProfileEntry::Category::STORAGE, 888);
-  ProfilerStackFrameDynamicRAII raii2("A", js::ProfileEntry::Category::STORAGE,
-                                      888, dynamic.get());
-  void* handle = profiler_call_enter("A", js::ProfileEntry::Category::NETWORK,
-                                     this, 999);
+  ProfilerStackFrameRAII raii1("A", nullptr, 888,
+                               js::ProfileEntry::Category::STORAGE);
+  ProfilerStackFrameRAII raii2("A", dynamic.get(), 888,
+                               js::ProfileEntry::Category::NETWORK);
   ASSERT_TRUE(profiler_get_backtrace());
-  profiler_call_exit(handle);
-
-  profiler_call_exit(nullptr);  // a no-op
 #endif
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_get_profile());
 }
 
 TEST(GeckoProfiler, Bug1355807)
--- a/xpcom/threads/ThreadStackHelper.cpp
+++ b/xpcom/threads/ThreadStackHelper.cpp
@@ -400,17 +400,17 @@ GetPathAfterComponent(const char* filena
     next = strstr(found - 1, component);
   }
   return found;
 }
 
 } // namespace
 
 const char*
-ThreadStackHelper::AppendJSEntry(const volatile js::ProfileEntry* aEntry,
+ThreadStackHelper::AppendJSEntry(const js::ProfileEntry* aEntry,
                                  intptr_t& aAvailableBufferSize,
                                  const char* aPrevLabel)
 {
   // May be called from another thread or inside a signal handler.
   // We assume querying the script is safe but we must not manupulate it.
   // Also we must not allocate any memory from heap.
   MOZ_ASSERT(aEntry->isJs());
 
@@ -487,18 +487,18 @@ ThreadStackHelper::FillStackBuffer()
   MOZ_ASSERT(mStackToFill->empty());
 
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
   size_t reservedSize = mStackToFill->capacity();
   size_t reservedBufferSize = mStackToFill->AvailableBufferSize();
   intptr_t availableBufferSize = intptr_t(reservedBufferSize);
 
   // Go from front to back
-  const volatile js::ProfileEntry* entry = mPseudoStack->entries;
-  const volatile js::ProfileEntry* end = entry + mPseudoStack->stackSize();
+  const js::ProfileEntry* entry = mPseudoStack->entries;
+  const js::ProfileEntry* end = entry + mPseudoStack->stackSize();
   // Deduplicate identical, consecutive frames
   const char* prevLabel = nullptr;
   for (; reservedSize-- && entry != end; entry++) {
     if (entry->isJs()) {
       prevLabel = AppendJSEntry(entry, availableBufferSize, prevLabel);
       continue;
     }
     const char* const label = entry->label();
--- a/xpcom/threads/ThreadStackHelper.h
+++ b/xpcom/threads/ThreadStackHelper.h
@@ -72,17 +72,17 @@ private:
   const PseudoStack* const mPseudoStack;
   size_t mMaxStackSize;
   size_t mMaxBufferSize;
 #endif
 
   bool PrepareStackBuffer(Stack& aStack);
   void FillStackBuffer();
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
-  const char* AppendJSEntry(const volatile js::ProfileEntry* aEntry,
+  const char* AppendJSEntry(const js::ProfileEntry* aEntry,
                             intptr_t& aAvailableBufferSize,
                             const char* aPrevLabel);
 #endif
 
 public:
   /**
    * Initialize ThreadStackHelper. Must be called from main thread.
    */