merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 06 Feb 2017 12:56:49 +0100
changeset 340897 1cc159c7a0445ec51e335c8a1a1cceea7bbf8380
parent 340878 c5621cb6f9075d731ccb28dcf20dd29deae2bddd (current diff)
parent 340896 65c5923d0feccdbe87b3dfc50a389e715d775a70 (diff)
child 340898 e893a43d5ecd7494c2264cc2c40a0d7b632edc28
child 340914 7a9666bfb431ba3f94ecbad92aea6690d39d4131
child 340979 efc2b3d143d0b422e391a015d6112b90ef66f066
push id31318
push usercbook@mozilla.com
push dateMon, 06 Feb 2017 11:56:59 +0000
treeherdermozilla-central@1cc159c7a044 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.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-inbound to mozilla-central a=merge
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -744,8 +744,12 @@ netmonitor.custom.send=Send
 
 # LOCALIZATION NOTE (netmonitor.custom.cancel): This is the label displayed
 # on the button which cancels and closes the custom request form
 netmonitor.custom.cancel=Cancel
 
 # LOCALIZATION NOTE (netmonitor.backButton): This is the label displayed
 # on the button which exists the performance statistics view
 netmonitor.backButton=Back
+
+# LOCALIZATION NOTE (netmonitor.headers.learnMore): This is the label displayed
+# next to a header list item, with a link to external documentation
+netmonitor.headers.learnMore=Learn More
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/shared/components/headers-mdn.js
@@ -0,0 +1,119 @@
+/* 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/. */
+
+/**
+ * A mapping of header names to external documentation. Any header included
+ * here will show a "Learn More" link alongside it.
+ */
+
+"use strict";
+
+var URL_DOMAIN = "https://developer.mozilla.org";
+const URL_PATH = "/en-US/docs/Web/HTTP/Headers/";
+const URL_PARAMS =
+  "?utm_source=mozilla&utm_medium=devtools-netmonitor&utm_campaign=default";
+
+var SUPPORTED_HEADERS = [
+  "Accept",
+  "Accept-Charset",
+  "Accept-Encoding",
+  "Accept-Language",
+  "Accept-Ranges",
+  "Access-Control-Allow-Credentials",
+  "Access-Control-Allow-Headers",
+  "Access-Control-Allow-Methods",
+  "Access-Control-Allow-Origin",
+  "Access-Control-Expose-Headers",
+  "Access-Control-Max-Age",
+  "Access-Control-Request-Headers",
+  "Access-Control-Request-Method",
+  "Age",
+  "Cache-Control",
+  "Connection",
+  "Content-Disposition",
+  "Content-Encoding",
+  "Content-Language",
+  "Content-Length",
+  "Content-Location",
+  "Content-Security-Policy",
+  "Content-Security-Policy-Report-Only",
+  "Content-Type",
+  "Cookie",
+  "Cookie2",
+  "DNT",
+  "Date",
+  "ETag",
+  "Expires",
+  "From",
+  "Host",
+  "If-Match",
+  "If-Modified-Since",
+  "If-None-Match",
+  "If-Range",
+  "If-Unmodified-Since",
+  "Keep-Alive",
+  "Last-Modified",
+  "Location",
+  "Origin",
+  "Pragma",
+  "Public-Key-Pins",
+  "Public-Key-Pins-Report-Only",
+  "Referer",
+  "Referrer-Policy",
+  "Retry-After",
+  "Server",
+  "Set-Cookie",
+  "Set-Cookie2",
+  "Strict-Transport-Security",
+  "TE",
+  "Tk",
+  "Trailer",
+  "Transfer-Encoding",
+  "Upgrade-Insecure-Requests",
+  "User-Agent",
+  "Vary",
+  "Via",
+  "Warning",
+  "X-Content-Type-Options",
+  "X-DNS-Prefetch-Control",
+  "X-Frame-Options",
+  "X-XSS-Protection"
+];
+
+/**
+ * Get the MDN URL for the specified header
+ *
+ * @param {string} Name of the header
+ * The baseURL to use.
+ *
+ * @return {string}
+ * The MDN URL for the header, or null if not available.
+ */
+exports.getURL = (header) => {
+  if (SUPPORTED_HEADERS.indexOf(header) === -1) {
+    return null;
+  }
+
+  return URL_DOMAIN + URL_PATH + header + URL_PARAMS;
+};
+
+/**
+ * Use a different domain for the URLs. Used only for testing.
+ *
+ * @param {string} domain
+ * The domain to use.
+ */
+exports.setDomain = (domain) => {
+  URL_DOMAIN = domain;
+};
+
+/**
+ * Use a different list of supported headers. Used only for testing.
+ *
+ * @param {array} headers
+ * The supported headers to use.
+ */
+exports.setSupportedHeaders = (headers) => {
+  SUPPORTED_HEADERS = headers;
+};
--- a/devtools/client/netmonitor/shared/components/headers-panel.js
+++ b/devtools/client/netmonitor/shared/components/headers-panel.js
@@ -10,21 +10,26 @@ const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../../l10n");
 const { writeHeaderText } = require("../../request-utils");
 const { getFormattedSize } = require("../../utils/format-utils");
+const Services = require("Services");
+const { gDevTools } = require("devtools/client/framework/devtools");
+const HeadersMDN = require("devtools/client/netmonitor/shared/components/headers-mdn");
+const { REPS, MODE } = require("devtools/client/shared/components/reps/load-reps");
+const Rep = createFactory(REPS.Rep);
 
 // Components
 const PropertiesView = createFactory(require("./properties-view"));
 
-const { div, input, textarea } = DOM;
+const { a, div, input, textarea } = DOM;
 const EDIT_AND_RESEND = L10N.getStr("netmonitor.summary.editAndResend");
 const RAW_HEADERS = L10N.getStr("netmonitor.summary.rawHeaders");
 const RAW_HEADERS_REQUEST = L10N.getStr("netmonitor.summary.rawHeaders.requestHeaders");
 const RAW_HEADERS_RESPONSE = L10N.getStr("netmonitor.summary.rawHeaders.responseHeaders");
 const HEADERS_EMPTY_TEXT = L10N.getStr("headersEmptyText");
 const HEADERS_FILTER_TEXT = L10N.getStr("headersFilterText");
 const REQUEST_HEADERS = L10N.getStr("requestHeaders");
 const REQUEST_HEADERS_FROM_UPLOAD = L10N.getStr("requestHeadersFromUpload");
@@ -40,16 +45,17 @@ const SUMMARY_VERSION = L10N.getStr("net
  * Lists basic information about the request
  */
 const HeadersPanel = createClass({
   displayName: "HeadersPanel",
 
   propTypes: {
     cloneSelectedRequest: PropTypes.func.isRequired,
     request: PropTypes.object.isRequired,
+    renderValue: PropTypes.func
   },
 
   getInitialState() {
     return {
       rawHeadersOpened: false,
     };
   },
 
@@ -208,15 +214,54 @@ const HeadersPanel = createClass({
           summaryStatus,
           summaryVersion,
           summaryRawHeaders,
         ),
         PropertiesView({
           object,
           filterPlaceHolder: HEADERS_FILTER_TEXT,
           sectionNames: Object.keys(object),
+          renderValue
         }),
       )
     );
   }
 });
 
+function onLearnMoreClick(e, headerDocURL) {
+  e.stopPropagation();
+  e.preventDefault();
+
+  let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
+  win.openUILinkIn(headerDocURL, "tab");
+}
+
+function renderValue(props) {
+  const { member, value } = props;
+
+  if (typeof value !== "string") {
+    return null;
+  }
+
+  let headerDocURL = HeadersMDN.getURL(member.name);
+
+  return (
+    div({ className: "treeValueCellDivider" },
+      Rep(Object.assign(props, {
+        // FIXME: A workaround for the issue in StringRep
+        // Force StringRep to crop the text everytime
+        member: Object.assign({}, member, { open: false }),
+        mode: MODE.TINY,
+        cropLimit: 60,
+      })),
+      headerDocURL ?
+        a({
+          className: "learn-more-link",
+          title: headerDocURL,
+          onClick: (e) => onLearnMoreClick(e, headerDocURL),
+        }, `[${L10N.getStr("netmonitor.headers.learnMore")}]`)
+        :
+        null
+    )
+  );
+}
+
 module.exports = HeadersPanel;
--- a/devtools/client/netmonitor/shared/components/moz.build
+++ b/devtools/client/netmonitor/shared/components/moz.build
@@ -1,16 +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/.
 
 DevToolsModules(
     'cookies-panel.js',
     'details-panel.js',
     'editor.js',
+    'headers-mdn.js',
     'headers-panel.js',
     'params-panel.js',
     'preview-panel.js',
     'properties-view.js',
     'response-panel.js',
     'security-panel.js',
     'timings-panel.js',
 )
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -93,16 +93,17 @@ subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_net_copy_as_curl.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_net_cors_requests.js]
 [browser_net_cyrillic-01.js]
 [browser_net_cyrillic-02.js]
 [browser_net_frame.js]
+[browser_net_header-docs.js]
 skip-if = (os == 'linux' && debug && bits == 32) # Bug 1321434
 [browser_net_filter-01.js]
 skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439
 [browser_net_filter-02.js]
 [browser_net_filter-03.js]
 [browser_net_filter-04.js]
 [browser_net_footer-summary.js]
 [browser_net_html-preview.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_header-docs.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const HeadersMDN = require("devtools/client/netmonitor/shared/components/headers-mdn");
+
+/**
+ * Tests if "Learn More" links are correctly displayed
+ * next to headers.
+ */
+add_task(function* () {
+  let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
+  info("Starting test... ");
+
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { RequestsMenu } = NetMonitorView;
+
+  RequestsMenu.lazyUpdate = false;
+
+  let wait = waitForNetworkEvents(monitor, 0, 2);
+  yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
+    content.wrappedJSObject.performRequests();
+  });
+  yield wait;
+
+  let origItem = RequestsMenu.getItemAtIndex(0);
+  RequestsMenu.selectedItem = origItem;
+
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelectorAll(".request-list-item")[0]);
+
+  testShowLearnMore(origItem);
+
+  return teardown(monitor);
+
+  /*
+   * Tests that a "Learn More" button is only shown if
+   * and only if a header is documented in MDN.
+   */
+  function testShowLearnMore(data) {
+    document.querySelectorAll(".properties-view .treeRow.stringRow").forEach((rowEl, index) => {
+      let headerName = rowEl.querySelectorAll(".treeLabelCell .treeLabel")[0].textContent;
+      let headerDocURL = HeadersMDN.getURL(headerName);
+      let learnMoreEl = rowEl.querySelectorAll(".treeValueCell .learn-more-link");
+
+      if (headerDocURL === null) {
+        ok(learnMoreEl.length === 0,
+          "undocumented header does not include a \"Learn More\" button");
+      } else {
+        ok(learnMoreEl[0].getAttribute("title") === headerDocURL,
+          "documented header includes a \"Learn More\" button with a link to MDN");
+      }
+    });
+  }
+});
--- a/devtools/client/shared/components/tree/tree-view.css
+++ b/devtools/client/shared/components/tree/tree-view.css
@@ -72,16 +72,33 @@
   text-decoration: underline;
 }
 
 /* Filtering */
 .treeTable .treeRow.hidden {
   display: none;
 }
 
+.treeTable .treeValueCellDivider {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+}
+
+/* Learn More link */
+.treeTable .treeValueCell .learn-more-link {
+  color: var(--theme-highlight-blue);
+  cursor: pointer;
+  margin: 0 5px;
+}
+
+.treeTable .treeValueCell .learn-more-link:hover {
+  text-decoration: underline;
+}
+
 /******************************************************************************/
 /* Toggle Icon */
 
 .treeTable .treeRow .treeIcon {
   height: 14px;
   width: 14px;
   font-size: 10px; /* Set the size of loading spinner */
   display: inline-block;
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -62,52 +62,56 @@ static int32_t gTimeoutBucketingStrategy
 #define DOM_MAX_TIMEOUT_VALUE    DELAY_INTERVAL_LIMIT
 
 uint32_t TimeoutManager::sNestingLevel = 0;
 
 namespace {
 
 // The number of queued runnables within the TabGroup ThrottledEventQueue
 // at which to begin applying back pressure to the window.
-const uint32_t kThrottledEventQueueBackPressure = 5000;
+#define DEFAULT_THROTTLED_EVENT_QUEUE_BACK_PRESSURE 5000
+static uint32_t gThrottledEventQueueBackPressure;
 
 // The amount of delay to apply to timers when back pressure is triggered.
 // As the length of the ThrottledEventQueue grows delay is increased.  The
 // delay is scaled such that every kThrottledEventQueueBackPressure runnables
 // in the queue equates to an additional kBackPressureDelayMS.
-const double kBackPressureDelayMS = 500;
+#define DEFAULT_BACK_PRESSURE_DELAY_MS 250
+static uint32_t gBackPressureDelayMS;
 
 // This defines a limit for how much the delay must drop before we actually
 // reduce back pressure throttle amount.  This makes the throttle delay
 // a bit "sticky" once we enter back pressure.
-const double kBackPressureDelayReductionThresholdMS = 400;
+#define DEFAULT_BACK_PRESSURE_DELAY_REDUCTION_THRESHOLD_MS 1000
+static uint32_t gBackPressureDelayReductionThresholdMS;
 
 // The minimum delay we can reduce back pressure to before we just floor
 // the value back to zero.  This allows us to ensure that we can exit
 // back pressure event if there are always a small number of runnables
 // queued up.
-const double kBackPressureDelayMinimumMS = 100;
+#define DEFAULT_BACK_PRESSURE_DELAY_MINIMUM_MS 100
+static uint32_t gBackPressureDelayMinimumMS;
 
 // Convert a ThrottledEventQueue length to a timer delay in milliseconds.
 // This will return a value between 0 and INT32_MAX.
 int32_t
 CalculateNewBackPressureDelayMS(uint32_t aBacklogDepth)
 {
   double multiplier = static_cast<double>(aBacklogDepth) /
-                      static_cast<double>(kThrottledEventQueueBackPressure);
-  double value = kBackPressureDelayMS * multiplier;
+                      static_cast<double>(gThrottledEventQueueBackPressure);
+  double value = static_cast<double>(gBackPressureDelayMS) * multiplier;
   // Avoid overflow
   if (value > INT32_MAX) {
     value = INT32_MAX;
   }
 
   // Once we get close to an empty queue just floor the delay back to zero.
   // We want to ensure we don't get stuck in a condition where there is a
   // small amount of delay remaining due to an active, but reasonable, queue.
-  else if (value < kBackPressureDelayMinimumMS) {
+  else if (value < static_cast<double>(gBackPressureDelayMinimumMS)) {
     value = 0;
   }
   return static_cast<int32_t>(value);
 }
 
 } // anonymous namespace
 
 TimeoutManager::TimeoutManager(nsGlobalWindow& aWindow)
@@ -148,16 +152,29 @@ TimeoutManager::Initialize()
                               "dom.min_tracking_background_timeout_value",
                               DEFAULT_MIN_TRACKING_BACKGROUND_TIMEOUT_VALUE);
   Preferences::AddIntVarCache(&gTimeoutBucketingStrategy,
                               "dom.timeout_bucketing_strategy",
                               TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY);
   Preferences::AddBoolVarCache(&gAnnotateTrackingChannels,
                                "privacy.trackingprotection.annotate_channels",
                                false);
+
+  Preferences::AddUintVarCache(&gThrottledEventQueueBackPressure,
+                               "dom.timeout.throttled_event_queue_back_pressure",
+                               DEFAULT_THROTTLED_EVENT_QUEUE_BACK_PRESSURE);
+  Preferences::AddUintVarCache(&gBackPressureDelayMS,
+                               "dom.timeout.back_pressure_delay_ms",
+                               DEFAULT_BACK_PRESSURE_DELAY_MS);
+  Preferences::AddUintVarCache(&gBackPressureDelayReductionThresholdMS,
+                               "dom.timeout.back_pressure_delay_reduction_threshold_ms",
+                               DEFAULT_BACK_PRESSURE_DELAY_REDUCTION_THRESHOLD_MS);
+  Preferences::AddUintVarCache(&gBackPressureDelayMinimumMS,
+                               "dom.timeout.back_pressure_delay_minimum_ms",
+                               DEFAULT_BACK_PRESSURE_DELAY_MINIMUM_MS);
 }
 
 uint32_t
 TimeoutManager::GetTimeoutId(Timeout::Reason aReason)
 {
   switch (aReason) {
     case Timeout::Reason::eIdleCallbackTimeout:
       return ++mIdleCallbackTimeoutCounter;
@@ -649,17 +666,17 @@ TimeoutManager::MaybeApplyBackPressure()
     return;
   }
 
   // Only begin back pressure if the window has greatly fallen behind the main
   // thread.  This is a somewhat arbitrary threshold chosen such that it should
   // rarely fire under normaly circumstances.  Its low enough, though,
   // that we should have time to slow new runnables from being added before an
   // OOM occurs.
-  if (queue->Length() < kThrottledEventQueueBackPressure) {
+  if (queue->Length() < gThrottledEventQueueBackPressure) {
     return;
   }
 
   // First attempt to dispatch a runnable to update our back pressure state.  We
   // do this first in order to verify we can dispatch successfully before
   // entering the back pressure state.
   nsCOMPtr<nsIRunnable> r =
     NewNonOwningRunnableMethod<StoreRefPtrPassByPtr<nsGlobalWindow>>(this,
@@ -707,18 +724,18 @@ TimeoutManager::CancelOrUpdateBackPressu
 
   // If the delay has decreased, though, we only apply the new value if it has
   // reduced significantly.  This hysteresis avoids thrashing the back pressure
   // value back and forth rapidly.  This is important because reducing the
   // backpressure delay requires calling ResetTimerForThrottleReduction() which
   // can be quite expensive.  We only want to call that method if the back log
   // is really clearing.
   else if (newBackPressureDelayMS == 0 ||
-           (newBackPressureDelayMS <=
-           (mBackPressureDelayMS - kBackPressureDelayReductionThresholdMS))) {
+           (static_cast<uint32_t>(mBackPressureDelayMS) >
+           (newBackPressureDelayMS + gBackPressureDelayReductionThresholdMS))) {
     int32_t oldBackPressureDelayMS = mBackPressureDelayMS;
     mBackPressureDelayMS = newBackPressureDelayMS;
 
     // If the back pressure delay has gone down we must reset any existing
     // timers to use the new value.  Otherwise we run the risk of executing
     // timer callbacks out-of-order.
     ResetTimersForThrottleReduction(oldBackPressureDelayMS);
   }
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -142,17 +142,16 @@ namespace mozilla {
 
 
 namespace layers {
 
 class ImageClient;
 class ImageCompositeNotification;
 class ImageContainer;
 class ImageContainerChild;
-class PImageContainerChild;
 class SharedPlanarYCbCrImage;
 class PlanarYCbCrImage;
 class TextureClient;
 class KnowsCompositor;
 class NVImage;
 
 struct ImageBackendData
 {
@@ -578,18 +577,16 @@ public:
   uint32_t GetDroppedImageCount()
   {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     return mDroppedImageCount;
   }
 
   void NotifyComposite(const ImageCompositeNotification& aNotification);
 
-  PImageContainerChild* GetPImageContainerChild();
-
   ImageContainerListener* GetImageContainerListener()
   {
     return mNotifyCompositeListener;
   }
 
   /**
    * Main thread only.
    */
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -27,17 +27,16 @@
 #include "nsIWidget.h"                  // For plugin window configuration information structs
 
 namespace mozilla {
 namespace layers {
 
 class ClientPaintedLayer;
 class CompositorBridgeChild;
 class ImageLayer;
-class PLayerChild;
 class FrameUniformityData;
 
 class ClientLayerManager final : public LayerManager
 {
   typedef nsTArray<RefPtr<Layer> > LayerRefArray;
 
 public:
   explicit ClientLayerManager(nsIWidget* aWidget);
@@ -375,19 +374,19 @@ public:
       ToClientLayer(aLayer->GetMaskLayer())->RenderLayer();
     }
     for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
       ToClientLayer(aLayer->GetAncestorMaskLayerAt(i))->RenderLayer();
     }
   }
 };
 
-// Create a shadow layer (PLayerChild) for aLayer, if we're forwarding
-// our layer tree to a parent process.  Record the new layer creation
-// in the current open transaction as a side effect.
+// Create a LayerHandle for aLayer, if we're forwarding our layer tree
+// to a parent process.  Record the new layer creation in the current
+// open transaction as a side effect.
 template<typename CreatedMethod> void
 CreateShadowFor(ClientLayer* aLayer,
                 ClientLayerManager* aMgr,
                 CreatedMethod aMethod)
 {
   LayerHandle shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer);
   if (!shadow) {
     return;
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -20,17 +20,16 @@
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
 class ImageBridgeChild;
 class ImageContainer;
 class CompositableForwarder;
 class CompositableChild;
-class PCompositableChild;
 class TextureClientRecycleAllocator;
 class ContentClientRemote;
 
 /**
  * CompositableClient manages the texture-specific logic for composite layers,
  * independently of the layer. It is the content side of a CompositableClient/
  * CompositableHost pair.
  *
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -32,17 +32,16 @@ namespace mozilla {
 namespace layers {
 
 class ClientLayerManager;
 class CompositorBridgeChild;
 class EditReply;
 class FixedSizeSmallShmemSectionAllocator;
 class ImageContainer;
 class Layer;
-class PLayerChild;
 class PLayerTransactionChild;
 class LayerTransactionChild;
 class ShadowableLayer;
 class SurfaceDescriptor;
 class TextureClient;
 class ThebesBuffer;
 class ThebesBufferData;
 class Transaction;
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -18,17 +18,16 @@
 namespace mozilla {
 namespace dom {
 class GamepadManager;
 class Navigator;
 class VRDisplay;
 class VREventObserver;
 } // namespace dom
 namespace layers {
-class PCompositableChild;
 class TextureClient;
 }
 namespace gfx {
 class VRLayerChild;
 class VRDisplayClient;
 
 class VRManagerChild : public PVRManagerChild
                      , public layers::TextureForwarder
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/TypeInference-inl.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/Sprintf.h"
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsgc.h"
@@ -2601,16 +2602,30 @@ TypeZone::addPendingRecompile(JSContext*
 
     // When one script is inlined into another the caller listens to state
     // changes on the callee's script, so trigger these to force recompilation
     // of any such callers.
     if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyGroup())
         ObjectStateChange(cx, script->functionNonDelazifying()->group(), false);
 }
 
+#ifdef JS_CRASH_DIAGNOSTICS
+static char sCrashReason[256];
+
+MOZ_NORETURN MOZ_COLD MOZ_NEVER_INLINE void
+js::ReportMagicWordFailure(uintptr_t actual, uintptr_t expected)
+{
+    SprintfLiteral(sCrashReason,
+                   "MOZ_CRASH(Got 0x%" PRIxPTR " expected magic word 0x%" PRIxPTR ")",
+                   actual, expected);
+    MOZ_CRASH_ANNOTATE(sCrashReason);
+    MOZ_REALLY_CRASH();
+}
+#endif
+
 void
 js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force)
 {
 #ifdef DEBUG
     gc::AutoSuppressGC suppressGC(cx);
     JSAutoRequest request(cx);
 
     Zone* zone = comp->zone();
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -538,16 +538,21 @@ class TypeSet
 #if JS_BITS_PER_WORD == 32
 static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4;
 #else
 static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4c5c6d7d8;
 #endif
 static const uintptr_t TypeConstraintMagic = BaseTypeInferenceMagic + 1;
 static const uintptr_t ConstraintTypeSetMagic = BaseTypeInferenceMagic + 2;
 
+#ifdef JS_CRASH_DIAGNOSTICS
+extern MOZ_NORETURN MOZ_COLD MOZ_NEVER_INLINE void
+ReportMagicWordFailure(uintptr_t actual, uintptr_t expected);
+#endif
+
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
  */
 class TypeConstraint
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     uintptr_t magic_;
@@ -562,17 +567,18 @@ class TypeConstraint
     {
 #ifdef JS_CRASH_DIAGNOSTICS
         magic_ = TypeConstraintMagic;
 #endif
     }
 
     void checkMagic() const {
 #ifdef JS_CRASH_DIAGNOSTICS
-        MOZ_RELEASE_ASSERT(magic_ == TypeConstraintMagic);
+        if (MOZ_UNLIKELY(magic_ != TypeConstraintMagic))
+            ReportMagicWordFailure(magic_, TypeConstraintMagic);
 #endif
     }
 
     TypeConstraint* next() const {
         checkMagic();
         if (next_)
             next_->checkMagic();
         return next_;
@@ -664,17 +670,18 @@ class ConstraintTypeSet : public TypeSet
     void initMagic() {
         MOZ_ASSERT(!magic_);
         magic_ = ConstraintTypeSetMagic;
     }
 #endif
 
     void checkMagic() const {
 #ifdef JS_CRASH_DIAGNOSTICS
-        MOZ_RELEASE_ASSERT(magic_ == ConstraintTypeSetMagic);
+        if (MOZ_UNLIKELY(magic_ != ConstraintTypeSetMagic))
+            ReportMagicWordFailure(magic_, ConstraintTypeSetMagic);
 #endif
     }
 
     TypeConstraint* constraintList() const {
         checkMagic();
         if (constraintList_)
             constraintList_->checkMagic();
         return constraintList_;
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1712,17 +1712,17 @@ needs-focus != 703186-1.html 703186-2.ht
 == 713856-dynamic.html 713856-ref.html
 == 714519-1-as.html 714519-1-ref.html
 == 714519-1-q.html 714519-1-ref.html
 == 714519-2-as.html 714519-2-ref.html
 == 714519-2-q.html 714519-2-ref.html
 fuzzy-if(true,1,21) fuzzy-if(d2d,68,173) fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html # bug 773482
 == 720987.html 720987-ref.html
 == 722888-1.html 722888-1-ref.html
-== 722923-1.html 722923-1-ref.html
+fuzzy-if(Android,1,40000) == 722923-1.html 722923-1-ref.html
 == 723484-1.html 723484-1-ref.html
 random-if(Android) == 728983-1.html 728983-1-ref.html
 == 729143-1.html 729143-1-ref.html
 == 731521-1.html 731521-1-ref.html
 needs-focus == 731726-1.html 731726-1-ref.html
 == 735481-1.html 735481-1-ref.html
 fuzzy-if(cocoaWidget,1,300000) fuzzy-if(skiaContent,2,300000) == 745934-1.html 745934-1-ref.html
 == 748692-1a.html 748692-1-ref.html
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1,14 +1,13 @@
 # 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/.
 
 import base64
-import copy
 import datetime
 import json
 import os
 import socket
 import sys
 import time
 import traceback
 import warnings
@@ -540,17 +539,17 @@ class Alert(object):
         self.marionette._send_message("sendKeysToDialog", body)
 
 
 class Marionette(object):
     """Represents a Marionette connection to a browser or device."""
 
     CONTEXT_CHROME = "chrome"  # non-browser content: windows, dialogs, etc.
     CONTEXT_CONTENT = "content"  # browser content: iframes, divs, etc.
-    DEFAULT_SOCKET_TIMEOUT = 65
+    DEFAULT_SOCKET_TIMEOUT = 60
     DEFAULT_STARTUP_TIMEOUT = 120
     DEFAULT_SHUTDOWN_TIMEOUT = 65  # Firefox will kill hanging threads after 60s
 
     def __init__(self, host="localhost", port=2828, app=None, bin=None,
                  baseurl=None, socket_timeout=DEFAULT_SOCKET_TIMEOUT,
                  startup_timeout=None, **instance_args):
         """Construct a holder for the Marionette connection.
 
@@ -1243,22 +1242,23 @@ class Marionette(object):
         '''
         Returns an absolute url for files served from Marionette's www directory.
 
         :param relative_url: The url of a static file, relative to Marionette's www directory.
         '''
         return "{0}{1}".format(self.baseurl, relative_url)
 
     @do_process_check
-    def start_session(self, capabilities=None, session_id=None, timeout=60):
+    def start_session(self, desired_capabilities=None, session_id=None, timeout=60):
         """Create a new Marionette session.
 
         This method must be called before performing any other action.
 
-        :param capabilities: An optional dict of desired or required capabilities.
+        :param desired_capabilities: An optional dict of desired
+            capabilities.  This is currently ignored.
         :param timeout: Timeout in seconds for the server to be ready.
         :param session_id: unique identifier for the session. If no session id is
             passed in then one will be generated by the marionette server.
 
         :returns: A dict of the capabilities offered.
 
         """
         self.crashed = 0
@@ -1274,35 +1274,17 @@ class Marionette(object):
             self.port,
             self.socket_timeout)
 
         # Call wait_for_port() before attempting to connect in
         # the event gecko hasn't started yet.
         self.wait_for_port(timeout=timeout)
         self.protocol, _ = self.client.connect()
 
-        if capabilities is not None:
-            caps = copy.deepcopy(capabilities)
-        else:
-            caps = {}
-
-        # Bug 1322277 - Override some default capabilities which are defined by
-        # the Webdriver spec but which don't really apply to tests executed with
-        # the Marionette client. Using "desiredCapabilities" here will allow tests
-        # to override the settings via "desiredCapabilities" and requiredCapabilities".
-        if "desiredCapabilities" not in caps:
-            caps.update({
-                "desiredCapabilities": {
-                    "timeouts": {
-                        "page load": 60000,  # webdriver specifies 300000ms
-                    }
-                }
-            })
-
-        body = {"capabilities": caps, "sessionId": session_id}
+        body = {"capabilities": desired_capabilities, "sessionId": session_id}
         resp = self._send_message("newSession", body)
 
         self.session_id = resp["sessionId"]
         self.session = resp["value"] if self.protocol == 1 else resp["capabilities"]
         # fallback to processId can be removed in Firefox 55
         self.process_id = self.session.get("moz:processID", self.session.get("processId"))
         self.profile = self.session.get("moz:profile")
 
--- a/testing/marionette/harness/marionette_harness/runner/base.py
+++ b/testing/marionette/harness/marionette_harness/runner/base.py
@@ -235,17 +235,17 @@ class MarionetteTextTestRunner(Structure
 
     def run(self, test):
         result = super(MarionetteTextTestRunner, self).run(test)
         result.printLogs(test)
         return result
 
 
 class BaseMarionetteArguments(ArgumentParser):
-    socket_timeout_default = 65.0
+    socket_timeout_default = 60.0
 
     def __init__(self, **kwargs):
         ArgumentParser.__init__(self, **kwargs)
 
         def dir_path(path):
             path = os.path.abspath(os.path.expanduser(path))
             if not os.access(path, os.F_OK):
                 os.makedirs(path)
@@ -342,18 +342,17 @@ class BaseMarionetteArguments(ArgumentPa
                           default=False,
                           help='Enable the jsdebugger for marionette javascript.')
         self.add_argument('--pydebugger',
                           help='Enable python post-mortem debugger when a test fails.'
                                ' Pass in the debugger you want to use, eg pdb or ipdb.')
         self.add_argument('--socket-timeout',
                           type=float,
                           default=self.socket_timeout_default,
-                          help='Set the global timeout for marionette socket operations.'
-                               ' Default: %(default)ss.')
+                          help='Set the global timeout for marionette socket operations.')
         self.add_argument('--disable-e10s',
                           action='store_false',
                           dest='e10s',
                           default=True,
                           help='Disable e10s when running marionette tests.')
         self.add_argument('--tag',
                           action='append', dest='test_tags',
                           default=None,
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
@@ -6,24 +6,17 @@ from marionette_driver.errors import Ses
 
 from marionette_harness import MarionetteTestCase
 
 
 class TestCapabilities(MarionetteTestCase):
 
     def setUp(self):
         super(TestCapabilities, self).setUp()
-
-        # Force default webdriver capabilities by default to test
-        # Marionette server.
-        self.marionette.delete_session()
-        self.marionette.start_session({"desiredCapabilities": {}})
-
         self.caps = self.marionette.session_capabilities
-
         with self.marionette.using_context("chrome"):
             self.appinfo = self.marionette.execute_script(
                 "return Services.appinfo")
             self.os_name = self.marionette.execute_script(
                 "return Services.sysinfo.getProperty('name')").lower()
             self.os_version = self.marionette.execute_script(
                 "return Services.sysinfo.getProperty('version')")
 
@@ -35,18 +28,20 @@ class TestCapabilities(MarionetteTestCas
         self.assertIn("acceptInsecureCerts", self.caps)
         self.assertIn("timeouts", self.caps)
 
         self.assertEqual(self.caps["browserName"], self.appinfo["name"].lower())
         self.assertEqual(self.caps["browserVersion"], self.appinfo["version"])
         self.assertEqual(self.caps["platformName"], self.os_name)
         self.assertEqual(self.caps["platformVersion"], self.os_version)
         self.assertFalse(self.caps["acceptInsecureCerts"])
-        self.assertEqual(self.caps["timeouts"],
-                         {"implicit": 0, "page load": 300000, "script": 30000})
+        self.assertDictEqual(self.caps["timeouts"],
+                             {"implicit": 0,
+                              "page load": 300000,
+                              "script": 30000})
 
     def test_supported_features(self):
         self.assertIn("rotatable", self.caps)
 
     def test_additional_capabilities(self):
         self.assertIn("moz:processID", self.caps)
         self.assertEqual(self.caps["moz:processID"], self.appinfo["processID"])
         self.assertEqual(self.marionette.process_id, self.appinfo["processID"])
@@ -60,24 +55,16 @@ class TestCapabilities(MarionetteTestCas
             self.assertEqual(self.caps["moz:profile"], current_profile)
             self.assertEqual(self.marionette.profile, current_profile)
 
         self.assertIn("moz:accessibilityChecks", self.caps)
         self.assertFalse(self.caps["moz:accessibilityChecks"])
         self.assertIn("specificationLevel", self.caps)
         self.assertEqual(self.caps["specificationLevel"], 0)
 
-    def test_default_client_capabilities(self):
-        self.marionette.delete_session()
-        self.marionette.start_session()
-        caps = self.marionette.session_capabilities
-
-        self.assertEqual(caps["timeouts"],
-                         {"implicit": 0, "page load": 60000, "script": 30000})
-
     def test_set_specification_level(self):
         self.marionette.delete_session()
         self.marionette.start_session({"desiredCapabilities": {"specificationLevel": 2}})
         caps = self.marionette.session_capabilities
         self.assertEqual(2, caps["specificationLevel"])
 
         self.marionette.delete_session()
         self.marionette.start_session({"requiredCapabilities": {"specificationLevel": 3}})
@@ -130,104 +117,97 @@ class TestCapabilityMatching(MarionetteT
     def test_browser_name_required(self):
         self.marionette.start_session({"requiredCapabilities": {"browserName": self.browser_name}})
         self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name)
 
     def test_browser_name_desired_allowed_types(self):
         for typ in self.allowed:
             self.marionette.delete_session()
             self.marionette.start_session({"desiredCapabilities": {"browserName": typ}})
-            self.assertEqual(self.marionette.session_capabilities["browserName"],
-                             self.browser_name)
+            self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name)
 
     def test_browser_name_desired_disallowed_types(self):
         for typ in self.disallowed:
             with self.assertRaises(SessionNotCreatedException):
                 self.marionette.start_session({"desiredCapabilities": {"browserName": typ}})
 
     def test_browser_name_required_allowed_types(self):
         for typ in self.allowed:
             self.marionette.delete_session()
             self.marionette.start_session({"requiredCapabilities": {"browserName": typ}})
-            self.assertEqual(self.marionette.session_capabilities["browserName"],
-                             self.browser_name)
+            self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name)
 
     def test_browser_name_requried_disallowed_types(self):
         for typ in self.disallowed:
             with self.assertRaises(SessionNotCreatedException):
                 self.marionette.start_session({"requiredCapabilities": {"browserName": typ}})
 
     def test_browser_name_prefers_required(self):
         caps = {"desiredCapabilities": {"browserName": "invalid"},
-                "requiredCapabilities": {"browserName": "*"}}
+                    "requiredCapabilities": {"browserName": "*"}}
         self.marionette.start_session(caps)
 
     def test_browser_name_error_on_invalid_required(self):
         with self.assertRaises(SessionNotCreatedException):
             caps = {"desiredCapabilities": {"browserName": "*"},
-                    "requiredCapabilities": {"browserName": "invalid"}}
+                        "requiredCapabilities": {"browserName": "invalid"}}
             self.marionette.start_session(caps)
 
     # TODO(ato): browser version comparison not implemented yet
 
     def test_platform_name_desired(self):
-        self.marionette.start_session(
-            {"desiredCapabilities": {"platformName": self.platform_name}})
+        self.marionette.start_session({"desiredCapabilities": {"platformName": self.platform_name}})
         self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name)
 
     def test_platform_name_required(self):
-        self.marionette.start_session(
-            {"requiredCapabilities": {"platformName": self.platform_name}})
+        self.marionette.start_session({"requiredCapabilities": {"platformName": self.platform_name}})
         self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name)
 
     def test_platform_name_desired_allowed_types(self):
         for typ in self.allowed:
             self.marionette.delete_session()
             self.marionette.start_session({"desiredCapabilities": {"platformName": typ}})
-            self.assertEqual(self.marionette.session_capabilities["platformName"],
-                             self.platform_name)
+            self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name)
 
     def test_platform_name_desired_disallowed_types(self):
         for typ in self.disallowed:
             with self.assertRaises(SessionNotCreatedException):
                 self.marionette.start_session({"desiredCapabilities": {"platformName": typ}})
 
     def test_platform_name_required_allowed_types(self):
         for typ in self.allowed:
             self.marionette.delete_session()
             self.marionette.start_session({"requiredCapabilities": {"platformName": typ}})
-            self.assertEqual(self.marionette.session_capabilities["platformName"],
-                             self.platform_name)
+            self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name)
 
     def test_platform_name_requried_disallowed_types(self):
         for typ in self.disallowed:
             with self.assertRaises(SessionNotCreatedException):
                 self.marionette.start_session({"requiredCapabilities": {"platformName": typ}})
 
     def test_platform_name_prefers_required(self):
         caps = {"desiredCapabilities": {"platformName": "invalid"},
-                "requiredCapabilities": {"platformName": "*"}}
+                    "requiredCapabilities": {"platformName": "*"}}
         self.marionette.start_session(caps)
 
     def test_platform_name_error_on_invalid_required(self):
         with self.assertRaises(SessionNotCreatedException):
             caps = {"desiredCapabilities": {"platformName": "*"},
-                    "requiredCapabilities": {"platformName": "invalid"}}
+                        "requiredCapabilities": {"platformName": "invalid"}}
             self.marionette.start_session(caps)
 
     # TODO(ato): platform version comparison not imlpemented yet
 
     def test_accept_insecure_certs(self):
         for capability_type in ["desiredCapabilities", "requiredCapabilities"]:
             print("testing {}".format(capability_type))
             for value in ["", 42, {}, []]:
                 print("  type {}".format(type(value)))
                 with self.assertRaises(SessionNotCreatedException):
-                    self.marionette.start_session(
-                        {capability_type: {"acceptInsecureCerts": value}})
+                    self.marionette.start_session({capability_type: {"acceptInsecureCerts": value}})
 
         self.marionette.delete_session()
         self.marionette.start_session({"desiredCapabilities": {"acceptInsecureCerts": True}})
         self.assertTrue(self.marionette.session_capabilities["acceptInsecureCerts"])
         self.marionette.delete_session()
         self.marionette.start_session({"requiredCapabilities": {"acceptInsecureCerts": True}})
 
         self.assertTrue(self.marionette.session_capabilities["acceptInsecureCerts"])
@@ -256,14 +236,14 @@ class TestCapabilityMatching(MarionetteT
 
     def test_proxy_required(self):
         self.marionette.start_session({"requiredCapabilities": {"proxy": {"proxyType": "manual"}}})
         self.assertIn("proxy", self.marionette.session_capabilities)
         self.assertEqual(self.marionette.session_capabilities["proxy"]["proxyType"], "manual")
         self.assertEqual(self.marionette.get_pref("network.proxy.type"), 1)
 
     def test_timeouts(self):
-        timeouts = {"implicit": 123, "page load": 456, "script": 789}
+        timeouts = {u"implicit": 123, u"page load": 456, u"script": 789}
         caps = {"desiredCapabilities": {"timeouts": timeouts}}
         self.marionette.start_session(caps)
         self.assertIn("timeouts", self.marionette.session_capabilities)
-        self.assertEqual(self.marionette.session_capabilities["timeouts"], timeouts)
-        self.assertEqual(self.marionette._send_message("getTimeouts"), timeouts)
+        self.assertDictEqual(self.marionette.session_capabilities["timeouts"], timeouts)
+        self.assertDictEqual(self.marionette._send_message("getTimeouts"), timeouts)
--- a/testing/web-platform/meta/vr/idlharness.html.ini
+++ b/testing/web-platform/meta/vr/idlharness.html.ini
@@ -1,10 +1,11 @@
 [idlharness.html]
   type: testharness
+  prefs: [dom.vr.enabled:true]
   [Window interface: attribute onvrdisplayblur]
     expected: FAIL
 
   [Window interface: attribute onvrdisplayfocus]
     expected: FAIL
 
   [Navigator interface: attribute vrEnabled]
     expected: FAIL
--- a/tools/profiler/core/Sampler.cpp
+++ b/tools/profiler/core/Sampler.cpp
@@ -194,17 +194,17 @@ hasFeature(const char** aFeatures, uint3
   for(size_t i = 0; i < aFeatureCount; i++) {
     if (strcmp(aFeatures[i], aFeature) == 0)
       return true;
   }
   return false;
 }
 
 std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr;
-mozilla::UniquePtr<mozilla::Mutex> Sampler::sRegisteredThreadsMutex;
+StaticMutex Sampler::sRegisteredThreadsMutex;
 
 Sampler::Sampler(double aInterval, int aEntrySize,
                  const char** aFeatures, uint32_t aFeatureCount,
                  const char** aThreadNameFilters, uint32_t aFilterCount)
   : interval_(aInterval)
   , paused_(false)
   , active_(false)
   , entrySize_(aEntrySize)
@@ -246,17 +246,17 @@ Sampler::Sampler(double aInterval, int a
   for (uint32_t i = 0; i < aFeatureCount; ++i) {
     mFeatures[i] = aFeatures[i];
   }
 
   bool ignore;
   sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
 
   {
-    MutexAutoLock lock(*sRegisteredThreadsMutex);
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
     // Set up profiling for each registered thread, if appropriate
     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
       ThreadInfo* info = sRegisteredThreads->at(i);
 
       RegisterThread(info);
     }
   }
@@ -274,17 +274,17 @@ Sampler::~Sampler()
 {
   MOZ_COUNT_DTOR(Sampler);
 
   if (IsActive())
     Stop();
 
   // Destroy ThreadInfo for all threads
   {
-    MutexAutoLock lock(*sRegisteredThreadsMutex);
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
       ThreadInfo* info = sRegisteredThreads->at(i);
       // We've stopped profiling. We no longer need to retain
       // information for an old thread.
       if (info->IsPendingDelete()) {
         delete info;
         sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
@@ -302,63 +302,63 @@ Sampler::~Sampler()
     mozilla::tasktracer::StopLogging();
   }
 #endif
 }
 
 void
 Sampler::Startup()
 {
+  StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
   sRegisteredThreads = new std::vector<ThreadInfo*>();
-  sRegisteredThreadsMutex = MakeUnique<Mutex>("sRegisteredThreadsMutex");
 
   // We could create the sLUL object and read unwind info into it at
   // this point.  That would match the lifetime implied by destruction
   // of it in Sampler::Shutdown just below.  However, that gives a big
   // delay on startup, even if no profiling is actually to be done.
   // So, instead, sLUL is created on demand at the first call to
   // Sampler::Start.
 }
 
 void
 Sampler::Shutdown()
 {
+  StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
   while (sRegisteredThreads->size() > 0) {
     delete sRegisteredThreads->back();
     sRegisteredThreads->pop_back();
   }
 
-  sRegisteredThreadsMutex = nullptr;
-  delete sRegisteredThreads;
-
   // UnregisterThread can be called after shutdown in XPCShell. Thus
   // we need to point to null to ignore such a call after shutdown.
-  sRegisteredThreadsMutex = nullptr;
+  delete sRegisteredThreads;
   sRegisteredThreads = nullptr;
 
 #if defined(USE_LUL_STACKWALK)
   // Delete the sLUL object, if it actually got created.
   if (sLUL) {
     delete sLUL;
     sLUL = nullptr;
   }
 #endif
 }
 
 bool
 Sampler::RegisterCurrentThread(const char* aName,
                                PseudoStack* aPseudoStack,
                                bool aIsMainThread, void* stackTop)
 {
-  if (!sRegisteredThreadsMutex) {
+  StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
+  if (!sRegisteredThreads) {
     return false;
   }
 
-  MutexAutoLock lock(*sRegisteredThreadsMutex);
-
   Thread::tid_t id = Thread::GetCurrentId();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       // Thread already registered. This means the first unregister will be
       // too early.
       MOZ_ASSERT(false);
@@ -377,22 +377,22 @@ Sampler::RegisterCurrentThread(const cha
   sRegisteredThreads->push_back(info);
 
   return true;
 }
 
 void
 Sampler::UnregisterCurrentThread()
 {
-  if (!sRegisteredThreadsMutex) {
+  StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
+  if (!sRegisteredThreads) {
     return;
   }
 
-  MutexAutoLock lock(*sRegisteredThreadsMutex);
-
   Thread::tid_t id = Thread::GetCurrentId();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       if (profiler_is_active()) {
         // We still want to show the results of this thread if you
         // save the profile shortly after a thread is terminated.
@@ -421,30 +421,35 @@ Sampler::StreamTaskTracer(SpliceableJSON
   aWriter.StartArrayProperty("data");
     UniquePtr<nsTArray<nsCString>> data = mozilla::tasktracer::GetLoggedData(sStartTime);
     for (uint32_t i = 0; i < data->Length(); ++i) {
       aWriter.StringElement((data->ElementAt(i)).get());
     }
   aWriter.EndArray();
 
   aWriter.StartArrayProperty("threads");
-    MutexAutoLock lock(*sRegisteredThreadsMutex);
+  {
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
     for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
       // Thread meta data
       ThreadInfo* info = sRegisteredThreads->at(i);
       aWriter.StartObjectElement();
+      {
         if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
           // TODO Add the proper plugin name
           aWriter.StringProperty("name", "Plugin");
         } else {
           aWriter.StringProperty("name", info->Name());
         }
         aWriter.IntProperty("tid", static_cast<int>(info->ThreadId()));
+      }
       aWriter.EndObject();
     }
+  }
   aWriter.EndArray();
 
   aWriter.DoubleProperty("start", static_cast<double>(mozilla::tasktracer::GetStartTime()));
 #endif
 }
 
 
 void
@@ -660,17 +665,17 @@ Sampler::StreamJSON(SpliceableJSONWriter
     }
 
     // Lists the samples for each thread profile
     aWriter.StartArrayProperty("threads");
     {
       SetPaused(true);
 
       {
-        MutexAutoLock lock(*sRegisteredThreadsMutex);
+        StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
         for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
           // Thread not being profiled, skip it
           ThreadInfo* info = sRegisteredThreads->at(i);
           if (!info->hasProfile()) {
             continue;
           }
 
@@ -716,17 +721,17 @@ Sampler::StreamJSON(SpliceableJSONWriter
 }
 
 void
 Sampler::FlushOnJSShutdown(JSContext* aContext)
 {
   SetPaused(true);
 
   {
-    MutexAutoLock lock(*sRegisteredThreadsMutex);
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
     for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
       // Thread not being profiled, skip it.
       ThreadInfo* info = sRegisteredThreads->at(i);
       if (!info->hasProfile() || info->IsPendingDelete()) {
         continue;
       }
 
--- a/tools/profiler/core/platform-linux.cc
+++ b/tools/profiler/core/platform-linux.cc
@@ -156,29 +156,16 @@ static void paf_parent(void) {
 
 // Set up the fork handlers.
 static void* setup_atfork() {
   pthread_atfork(paf_prepare, paf_parent, NULL);
   return NULL;
 }
 #endif /* !defined(ANDROID) */
 
-struct SamplerRegistry {
-  static void AddActiveSampler(Sampler *sampler) {
-    MOZ_ASSERT(!SamplerRegistry::sampler);
-    SamplerRegistry::sampler = sampler;
-  }
-  static void RemoveActiveSampler(Sampler *sampler) {
-    SamplerRegistry::sampler = NULL;
-  }
-  static Sampler *sampler;
-};
-
-Sampler *SamplerRegistry::sampler = NULL;
-
 static mozilla::Atomic<ThreadInfo*> sCurrentThreadInfo;
 static sem_t sSignalHandlingDone;
 
 static void SetSampleContext(TickSample* sample, void* context)
 {
   // Extracting the sample from the context is extremely machine dependent.
   ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
   mcontext_t& mcontext = ucontext->uc_mcontext;
@@ -299,28 +286,26 @@ static void* SignalSender(void* arg) {
 
   int vm_tgid_ = getpid();
   DebugOnly<int> my_tid = gettid();
 
   unsigned int nSignalsSent = 0;
 
   TimeDuration lastSleepOverhead = 0;
   TimeStamp sampleStart = TimeStamp::Now();
-  while (SamplerRegistry::sampler->IsActive()) {
-
-    SamplerRegistry::sampler->DeleteExpiredMarkers();
+  // XXX: this loop is an off-main-thread use of gSampler
+  while (gSampler->IsActive()) {
+    gSampler->DeleteExpiredMarkers();
 
-    if (!SamplerRegistry::sampler->IsPaused()) {
-      MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-      const std::vector<ThreadInfo*>& threads =
-        SamplerRegistry::sampler->GetRegisteredThreads();
+    if (!gSampler->IsPaused()) {
+      StaticMutexAutoLock lock(Sampler::sRegisteredThreadsMutex);
 
       bool isFirstProfiledThread = true;
-      for (uint32_t i = 0; i < threads.size(); i++) {
-        ThreadInfo* info = threads[i];
+      for (uint32_t i = 0; i < Sampler::sRegisteredThreads->size(); i++) {
+        ThreadInfo* info = (*Sampler::sRegisteredThreads)[i];
 
         // This will be null if we're not interested in profiling this thread.
         if (!info->hasProfile() || info->IsPendingDelete()) {
           continue;
         }
 
         PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
         if (sleeping == PseudoStack::SLEEPING_AGAIN) {
@@ -366,17 +351,18 @@ static void* SignalSender(void* arg) {
         if ((++nSignalsSent & 0xF) == 0) {
 #          if defined(USE_LUL_STACKWALK)
            sLUL->MaybeShowStats();
 #          endif
         }
       }
     }
 
-    TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(SamplerRegistry::sampler->interval() * 1000);
+    TimeStamp targetSleepEndTime =
+      sampleStart + TimeDuration::FromMicroseconds(gSampler->interval() * 1000);
     TimeStamp beforeSleep = TimeStamp::Now();
     TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
     double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds());
     OS::SleepMicro(sleepTime);
     sampleStart = TimeStamp::Now();
     lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
   }
   return 0;
@@ -390,18 +376,16 @@ void Sampler::Start() {
 #elif defined(USE_LUL_STACKWALK)
   // NOTE: this isn't thread-safe.  But we expect Sampler::Start to be
   // called only from the main thread, so this is OK in general.
   if (!sLUL) {
      sLUL_initialization_routine();
   }
 #endif
 
-  SamplerRegistry::AddActiveSampler(this);
-
   // Initialize signal handler communication
   sCurrentThreadInfo = nullptr;
   if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) {
     LOG("Error initializing semaphore");
     return;
   }
 
   // Request profiling signals.
@@ -447,18 +431,16 @@ void Sampler::Stop() {
 
   // Wait for signal sender termination (it will exit after setting
   // active_ to false).
   if (signal_sender_launched_) {
     pthread_join(signal_sender_thread_, NULL);
     signal_sender_launched_ = false;
   }
 
-  SamplerRegistry::RemoveActiveSampler(this);
-
   // Restore old signal handler
   if (signal_handler_installed_) {
     sigaction(SIGPROF, &old_sigprof_signal_handler_, 0);
     signal_handler_installed_ = false;
   }
 }
 
 #ifdef ANDROID
--- a/tools/profiler/core/platform-macos.cc
+++ b/tools/profiler/core/platform-macos.cc
@@ -38,31 +38,16 @@
 #include "platform.h"
 #include "mozilla/TimeStamp.h"
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
 // this port is based off of v8 svn revision 9837
 
-// XXX: this is a very stubbed out implementation
-// that only supports a single Sampler
-struct SamplerRegistry {
-  static void AddActiveSampler(Sampler *sampler) {
-    MOZ_ASSERT(!SamplerRegistry::sampler);
-    SamplerRegistry::sampler = sampler;
-  }
-  static void RemoveActiveSampler(Sampler *sampler) {
-    SamplerRegistry::sampler = NULL;
-  }
-  static Sampler *sampler;
-};
-
-Sampler *SamplerRegistry::sampler = NULL;
-
 #ifdef DEBUG
 // 0 is never a valid thread id on MacOSX since a pthread_t is a pointer.
 static const pthread_t kNoThread = (pthread_t) 0;
 #endif
 
 void OS::Startup() {
 }
 
@@ -148,76 +133,75 @@ public:
     pthread_create(&mThread, attr_ptr, ThreadEntry, this);
     MOZ_ASSERT(mThread != kNoThread);
   }
 
   void Join() {
     pthread_join(mThread, NULL);
   }
 
-  static void AddActiveSampler(Sampler* sampler) {
-    SamplerRegistry::AddActiveSampler(sampler);
+  static void AddActiveSampler() {
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
     if (mInstance == NULL) {
-      mInstance = new SamplerThread(sampler->interval());
+      mInstance = new SamplerThread(gSampler->interval());
       mInstance->Start();
     }
   }
 
-  static void RemoveActiveSampler(Sampler* sampler) {
+  static void RemoveActiveSampler() {
     mInstance->Join();
-    //XXX: unlike v8 we need to remove the active sampler after doing the Join
-    // because we drop the sampler immediately
-    SamplerRegistry::RemoveActiveSampler(sampler);
     delete mInstance;
     mInstance = NULL;
   }
 
   void Run() {
     TimeDuration lastSleepOverhead = 0;
     TimeStamp sampleStart = TimeStamp::Now();
-    while (SamplerRegistry::sampler->IsActive()) {
-      SamplerRegistry::sampler->DeleteExpiredMarkers();
-      if (!SamplerRegistry::sampler->IsPaused()) {
-        MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-        const std::vector<ThreadInfo*>& threads =
-          SamplerRegistry::sampler->GetRegisteredThreads();
+
+    // XXX: this loop is an off-main-thread use of gSampler
+    while (gSampler->IsActive()) {
+      gSampler->DeleteExpiredMarkers();
+
+      if (!gSampler->IsPaused()) {
+        StaticMutexAutoLock lock(Sampler::sRegisteredThreadsMutex);
+
         bool isFirstProfiledThread = true;
-        for (uint32_t i = 0; i < threads.size(); i++) {
-          ThreadInfo* info = threads[i];
+        for (uint32_t i = 0; i < Sampler::sRegisteredThreads->size(); i++) {
+          ThreadInfo* info = (*Sampler::sRegisteredThreads)[i];
 
           // This will be null if we're not interested in profiling this thread.
           if (!info->hasProfile() || info->IsPendingDelete()) {
             continue;
           }
 
           PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
           if (sleeping == PseudoStack::SLEEPING_AGAIN) {
             info->DuplicateLastSample();
             continue;
           }
 
           info->UpdateThreadResponsiveness();
 
-          SampleContext(SamplerRegistry::sampler, info, isFirstProfiledThread);
+          SampleContext(info, isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
       }
 
       TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(mIntervalMicro);
       TimeStamp beforeSleep = TimeStamp::Now();
       TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
       double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds());
       OS::SleepMicro(sleepTime);
       sampleStart = TimeStamp::Now();
       lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
     }
   }
 
-  void SampleContext(Sampler* sampler, ThreadInfo* aThreadInfo,
-                     bool isFirstProfiledThread)
+  void SampleContext(ThreadInfo* aThreadInfo, bool isFirstProfiledThread)
   {
     thread_act_t profiled_thread =
       aThreadInfo->GetPlatformData()->profiled_thread();
 
     TickSample sample_obj;
     TickSample* sample = &sample_obj;
 
     // Unique Set Size is not supported on Mac.
@@ -262,17 +246,18 @@ public:
                          reinterpret_cast<natural_t*>(&state),
                          &count) == KERN_SUCCESS) {
       sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
       sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
       sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
       sample->timestamp = mozilla::TimeStamp::Now();
       sample->threadInfo = aThreadInfo;
 
-      sampler->Tick(sample);
+      // XXX: this is an off-main-thread use of gSampler
+      gSampler->Tick(sample);
     }
     thread_resume(profiled_thread);
   }
 
 private:
   pthread_t mThread;
 
   int mIntervalMicro;
@@ -284,23 +269,23 @@ private:
 
 #undef REGISTER_FIELD
 
 SamplerThread* SamplerThread::mInstance = NULL;
 
 void Sampler::Start() {
   MOZ_ASSERT(!IsActive());
   SetActive(true);
-  SamplerThread::AddActiveSampler(this);
+  SamplerThread::AddActiveSampler();
 }
 
 void Sampler::Stop() {
   MOZ_ASSERT(IsActive());
   SetActive(false);
-  SamplerThread::RemoveActiveSampler(this);
+  SamplerThread::RemoveActiveSampler();
 }
 
 /* static */ Thread::tid_t
 Thread::GetCurrentId()
 {
   return gettid();
 }
 
--- a/tools/profiler/core/platform-win32.cc
+++ b/tools/profiler/core/platform-win32.cc
@@ -91,20 +91,19 @@ static const HANDLE kNoThread = INVALID_
 // Start() method is called the new thread starts running the Run() method in
 // the new thread. The SamplerThread object should not be deallocated before
 // the thread has terminated.
 class SamplerThread
 {
  public:
   // Initialize a Win32 thread object. The thread has an invalid thread
   // handle until it is started.
-  SamplerThread(double interval, Sampler* sampler)
+  explicit SamplerThread(double interval)
     : mStackSize(0)
     , mThread(kNoThread)
-    , mSampler(sampler)
     , mInterval(interval)
   {
     mInterval = floor(interval + 0.5);
     if (mInterval <= 0) {
       mInterval = 1;
     }
   }
 
@@ -135,76 +134,77 @@ class SamplerThread
   }
 
   void Join() {
     if (mThreadId != Thread::GetCurrentId()) {
       WaitForSingleObject(mThread, INFINITE);
     }
   }
 
-  static void StartSampler(Sampler* sampler) {
+  static void StartSampler() {
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
     if (mInstance == NULL) {
-      mInstance = new SamplerThread(sampler->interval(), sampler);
+      mInstance = new SamplerThread(gSampler->interval());
       mInstance->Start();
     } else {
-      MOZ_ASSERT(mInstance->mInterval == sampler->interval());
+      MOZ_ASSERT(mInstance->mInterval == gSampler->interval());
     }
   }
 
   static void StopSampler() {
     mInstance->Join();
     delete mInstance;
     mInstance = NULL;
   }
 
   void Run() {
     // By default we'll not adjust the timer resolution which tends to be around
     // 16ms. However, if the requested interval is sufficiently low we'll try to
     // adjust the resolution to match.
     if (mInterval < 10)
         ::timeBeginPeriod(mInterval);
 
-    while (mSampler->IsActive()) {
-      mSampler->DeleteExpiredMarkers();
+    // XXX: this loop is an off-main-thread use of gSampler
+    while (gSampler->IsActive()) {
+      gSampler->DeleteExpiredMarkers();
 
-      if (!mSampler->IsPaused()) {
-        mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-        const std::vector<ThreadInfo*>& threads =
-          mSampler->GetRegisteredThreads();
+      if (!gSampler->IsPaused()) {
+        mozilla::StaticMutexAutoLock lock(Sampler::sRegisteredThreadsMutex);
+
         bool isFirstProfiledThread = true;
-        for (uint32_t i = 0; i < threads.size(); i++) {
-          ThreadInfo* info = threads[i];
+        for (uint32_t i = 0; i < Sampler::sRegisteredThreads->size(); i++) {
+          ThreadInfo* info = (*Sampler::sRegisteredThreads)[i];
 
           // This will be null if we're not interested in profiling this thread.
           if (!info->hasProfile() || info->IsPendingDelete()) {
             continue;
           }
 
           PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
           if (sleeping == PseudoStack::SLEEPING_AGAIN) {
             info->DuplicateLastSample();
             continue;
           }
 
           info->UpdateThreadResponsiveness();
 
-          SampleContext(mSampler, info, isFirstProfiledThread);
+          SampleContext(info, isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
       }
       OS::Sleep(mInterval);
     }
 
     // disable any timer resolution changes we've made
     if (mInterval < 10)
         ::timeEndPeriod(mInterval);
   }
 
-  void SampleContext(Sampler* sampler, ThreadInfo* aThreadInfo,
-                     bool isFirstProfiledThread)
+  void SampleContext(ThreadInfo* aThreadInfo, bool isFirstProfiledThread)
   {
     uintptr_t thread = Sampler::GetThreadHandle(aThreadInfo->GetPlatformData());
     HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread);
     if (profiled_thread == NULL)
       return;
 
     // Context used for sampling the register state of the profiled thread.
     CONTEXT context;
@@ -276,41 +276,42 @@ class SamplerThread
     sample->fp = reinterpret_cast<Address>(context.Rbp);
 #else
     sample->pc = reinterpret_cast<Address>(context.Eip);
     sample->sp = reinterpret_cast<Address>(context.Esp);
     sample->fp = reinterpret_cast<Address>(context.Ebp);
 #endif
 
     sample->context = &context;
-    sampler->Tick(sample);
+
+    // XXX: this is an off-main-thread use of gSampler
+    gSampler->Tick(sample);
 
     ResumeThread(profiled_thread);
   }
 
 private:
   int mStackSize;
   HANDLE mThread;
   Thread::tid_t mThreadId;
 
-  Sampler* mSampler;
   int mInterval; // units: ms
 
   // Protects the process wide state below.
   static SamplerThread* mInstance;
 
   DISALLOW_COPY_AND_ASSIGN(SamplerThread);
 };
 
 SamplerThread* SamplerThread::mInstance = NULL;
 
 void Sampler::Start() {
   MOZ_ASSERT(!IsActive());
   SetActive(true);
-  SamplerThread::StartSampler(this);
+  SamplerThread::StartSampler();
 }
 
 void Sampler::Stop() {
   MOZ_ASSERT(IsActive());
   SetActive(false);
   SamplerThread::StopSampler();
 }
 
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -739,21 +739,20 @@ profiler_start(int aProfileEntries, doub
 
   gSampler =
     new Sampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
                 aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
                 aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount);
 
   gSampler->Start();
   if (gSampler->ProfileJS() || gSampler->InPrivacyMode()) {
-    mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-    const std::vector<ThreadInfo*>& threads = gSampler->GetRegisteredThreads();
+    mozilla::StaticMutexAutoLock lock(Sampler::sRegisteredThreadsMutex);
 
-    for (uint32_t i = 0; i < threads.size(); i++) {
-      ThreadInfo* info = threads[i];
+    for (uint32_t i = 0; i < Sampler::sRegisteredThreads->size(); i++) {
+      ThreadInfo* info = (*Sampler::sRegisteredThreads)[i];
       if (info->IsPendingDelete() || !info->hasProfile()) {
         continue;
       }
       info->Stack()->reinitializeOnResume();
       if (gSampler->ProfileJS()) {
         info->Stack()->enableJSSampling();
       }
       if (gSampler->InPrivacyMode()) {
--- a/tools/profiler/core/platform.h
+++ b/tools/profiler/core/platform.h
@@ -37,17 +37,17 @@
 
 #ifdef XP_UNIX
 #include <pthread.h>
 #endif
 
 #include <stdint.h>
 #include <math.h>
 #include "MainThreadUtils.h"
-#include "mozilla/Mutex.h"
+#include "mozilla/StaticMutex.h"
 #include "ThreadResponsiveness.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Vector.h"
 #include "PlatformMacros.h"
 #include "v8-support.h"
 #include <vector>
@@ -292,30 +292,27 @@ public:
 
   // If we move the backtracing code into the platform files we won't
   // need to have these hacks
 #ifdef XP_WIN
   // xxxehsan sucky hack :(
   static uintptr_t GetThreadHandle(PlatformData*);
 #endif
 
-  static const std::vector<ThreadInfo*>& GetRegisteredThreads() {
-    return *sRegisteredThreads;
-  }
-
   static bool RegisterCurrentThread(const char* aName,
                                     PseudoStack* aPseudoStack,
                                     bool aIsMainThread, void* stackTop);
   static void UnregisterCurrentThread();
 
   static void Startup();
   // Should only be called on shutdown
   static void Shutdown();
 
-  static mozilla::UniquePtr<mozilla::Mutex> sRegisteredThreadsMutex;
+  static mozilla::StaticMutex sRegisteredThreadsMutex;
+  static std::vector<ThreadInfo*>* sRegisteredThreads;
 
   static bool CanNotifyObservers() {
 #ifdef MOZ_WIDGET_GONK
     // We use profile.sh on b2g to manually select threads and options per process.
     return false;
 #elif defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
     // Android ANR reporter uses the profiler off the main thread
     return NS_IsMainThread();
@@ -360,18 +357,16 @@ private:
 
   void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime);
 
   // Called within a signal. This function must be reentrant
   void InplaceTick(TickSample* sample);
 
   void SetActive(bool value) { NoBarrier_Store(&active_, value); }
 
-  static std::vector<ThreadInfo*>* sRegisteredThreads;
-
   const double interval_;
   Atomic32 paused_;
   Atomic32 active_;
   const int entrySize_;
 
   // Refactor me!
 #if defined(SPS_OS_linux) || defined(SPS_OS_android)
   bool signal_handler_installed_;