Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 06 Nov 2016 18:21:25 -0800
changeset 321333 908557c762f798605a2f96e4c943791cbada1b50
parent 321319 f43363843981d821e3bfb7d533b56e0615c4368e (current diff)
parent 321332 4128e57e39bda4d6fe223e911906c81851d76e70 (diff)
child 321334 0bccf79996a7537d4dd405751ea4e5b5bcca03dd
child 321347 45ed47afd5181810fc662cc87eefc986bf939778
child 321370 4ffa6e380d58d41cf7ea567e9485a09c07d7ba87
push id30923
push userphilringnalda@gmail.com
push dateMon, 07 Nov 2016 02:21:38 +0000
treeherdermozilla-central@908557c762f7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
908557c762f7 / 52.0a1 / 20161107030203 / files
nightly linux64
908557c762f7 / 52.0a1 / 20161107030203 / files
nightly mac
908557c762f7 / 52.0a1 / 20161107030203 / files
nightly win32
908557c762f7 / 52.0a1 / 20161107030203 / files
nightly win64
908557c762f7 / 52.0a1 / 20161107030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge MozReview-Commit-ID: 5HP4v5psQL6
--- a/browser/components/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -1,23 +1,42 @@
 [DEFAULT]
 tags = usercontextid firstpartyisolation originattributes
 support-files =
   dummy.html
   file_broadcastChannel.html
   file_broadcastChanneliFrame.html
+  file_cache.html
   file_favicon.html
   file_favicon.png
   file_favicon.png^headers^
   file_favicon_cache.html
   file_favicon_cache.png
   file_favicon_thirdParty.html
   file_firstPartyBasic.html
   file_sharedworker.html
   file_sharedworker.js
+  file_thirdPartyChild.audio.ogg
+  file_thirdPartyChild.embed.png
+  file_thirdPartyChild.fetch.html
+  file_thirdPartyChild.iframe.html
+  file_thirdPartyChild.img.png
+  file_thirdPartyChild.import.js
+  file_thirdPartyChild.link.css
+  file_thirdPartyChild.object.png
+  file_thirdPartyChild.request.html
+  file_thirdPartyChild.script.js
+  file_thirdPartyChild.sharedworker.js
+  file_thirdPartyChild.track.vtt
+  file_thirdPartyChild.video.ogv
+  file_thirdPartyChild.worker.fetch.html
+  file_thirdPartyChild.worker.js
+  file_thirdPartyChild.worker.request.html
+  file_thirdPartyChild.worker.xhr.html
+  file_thirdPartyChild.xhr.html
   head.js
   test.js
   test.js^headers^
   test.html
   test2.html
   test2.js
   test2.js^headers^
   test_firstParty.html
@@ -27,16 +46,17 @@ support-files =
   test_firstParty_http_redirect.html^headers^
   test_firstParty_iframe_http_redirect.html
   test_firstParty_postMessage.html
   window.html
   worker_blobify.js
   worker_deblobify.js
 
 [browser_broadcastChannel.js]
+[browser_cache.js]
 [browser_cookieIsolation.js]
 [browser_favicon_firstParty.js]
 [browser_favicon_userContextId.js]
 [browser_firstPartyIsolation.js]
 [browser_localStorageIsolation.js]
 [browser_blobURLIsolation.js]
 [browser_imageCacheIsolation.js]
 [browser_sharedworker.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_cache.js
@@ -0,0 +1,247 @@
+/*
+ * Bug 1264577 - A test case for testing caches of various submodules.
+ *   This test case will load two pages that each page loads various resources
+ *   within the same third party domain for the same originAttributes or different
+ *   originAttributes. And then, it verifies the number of cache entries and
+ *   the originAttributes of loading channels. If these two pages belong to
+ *   the same originAttributes, the number of cache entries for a certain
+ *   resource would be one. Otherwise, it would be two.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+let {LoadContextInfo} = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
+let protocolProxyService = Cc["@mozilla.org/network/protocol-proxy-service;1"]
+                             .getService(Ci.nsIProtocolProxyService);
+
+const TEST_DOMAIN = "http://example.net";
+const TEST_PATH = "/browser/browser/components/originattributes/test/browser/";
+const TEST_PAGE = TEST_DOMAIN + TEST_PATH + "file_cache.html";
+
+let suffixes = ["iframe.html", "link.css", "script.js", "img.png", "object.png",
+                "embed.png", "xhr.html", "worker.xhr.html", "audio.ogg",
+                "video.ogv", "track.vtt",
+                "fetch.html", "worker.fetch.html",
+                "request.html", "worker.request.html",
+                "import.js", "worker.js", "sharedworker.js"];
+
+// A random value for isolating video/audio elements across different tests.
+let randomSuffix;
+
+function clearAllImageCaches() {
+  let tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                             .getService(SpecialPowers.Ci.imgITools);
+  let imageCache = tools.getImgCacheForDocument(window.document);
+  imageCache.clearCache(true);  // true=chrome
+  imageCache.clearCache(false); // false=content
+}
+
+function cacheDataForContext(loadContextInfo) {
+  return new Promise(resolve => {
+    let cacheEntries = [];
+    let cacheVisitor = {
+      onCacheStorageInfo(num, consumption) {},
+      onCacheEntryInfo(uri, idEnhance) {
+        cacheEntries.push({ uri: uri,
+                            idEnhance: idEnhance });
+      },
+      onCacheEntryVisitCompleted() {
+        resolve(cacheEntries);
+      },
+      QueryInterface(iid) {
+        if (iid.equals(Ci.nsICacheStorageVisitor))
+          return this;
+
+        throw Components.results.NS_ERROR_NO_INTERFACE;
+      }
+    };
+    // Visiting the disk cache also visits memory storage so we do not
+    // need to use Services.cache2.memoryCacheStorage() here.
+    let storage = Services.cache2.diskCacheStorage(loadContextInfo, false);
+    storage.asyncVisitStorage(cacheVisitor, true);
+  });
+}
+
+let countMatchingCacheEntries = function (cacheEntries, domain, fileSuffix) {
+  return cacheEntries.map(entry => entry.uri.asciiSpec)
+                     .filter(spec => spec.includes(domain))
+                     .filter(spec => spec.includes("file_thirdPartyChild." + fileSuffix))
+                     .length;
+};
+
+function observeChannels(onChannel) {
+  // We use a dummy proxy filter to catch all channels, even those that do not
+  // generate an "http-on-modify-request" notification, such as link preconnects.
+  let proxyFilter = {
+    applyFilter : function (aProxyService, aChannel, aProxy) {
+      // We have the channel; provide it to the callback.
+      onChannel(aChannel);
+      // Pass on aProxy unmodified.
+      return aProxy;
+    }
+  };
+  protocolProxyService.registerChannelFilter(proxyFilter, 0);
+  // Return the stop() function:
+  return () => protocolProxyService.unregisterChannelFilter(proxyFilter);
+}
+
+function startObservingChannels(aMode) {
+  let stopObservingChannels = observeChannels(function (channel) {
+    let originalURISpec = channel.originalURI.spec;
+    if (originalURISpec.includes("example.net")) {
+      let loadInfo = channel.loadInfo;
+
+      switch (aMode) {
+        case TEST_MODE_FIRSTPARTY:
+          ok(loadInfo.originAttributes.firstPartyDomain === "example.com" ||
+             loadInfo.originAttributes.firstPartyDomain === "example.org",
+             "first party for " + originalURISpec + " is " + loadInfo.originAttributes.firstPartyDomain);
+          break;
+
+        case TEST_MODE_NO_ISOLATION:
+          ok(ChromeUtils.isOriginAttributesEqual(loadInfo.originAttributes, ChromeUtils.fillNonDefaultOriginAttributes()),
+             "OriginAttributes for " + originalURISpec + " is default.");
+          break;
+
+        case TEST_MODE_CONTAINERS:
+          ok(loadInfo.originAttributes.userContextId === 1 ||
+             loadInfo.originAttributes.userContextId === 2,
+             "userContextId for " + originalURISpec + " is " + loadInfo.originAttributes.userContextId);
+          break;
+
+        default:
+          ok(false, "Unknown test mode.");
+      }
+    }
+  });
+  return stopObservingChannels;
+}
+
+let stopObservingChannels;
+
+// The init function, which clears image and network caches, and generates
+// the random value for isolating video and audio elements across different
+// test runs.
+function* doInit(aMode) {
+  yield SpecialPowers.pushPrefEnv({"set": [["network.predictor.enabled",         false],
+                                           ["network.predictor.enable-prefetch", false]]});
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  randomSuffix = Math.random();
+  stopObservingChannels = startObservingChannels(aMode);
+}
+
+// In the test function, we dynamically generate the video and audio element,
+// and assign a random suffix to their URL to isolate them across different
+// test runs.
+function* doTest(aBrowser) {
+
+  let argObj = {
+    randomSuffix: randomSuffix,
+    urlPrefix: TEST_DOMAIN + TEST_PATH,
+  };
+
+  yield ContentTask.spawn(aBrowser, argObj, function* (arg) {
+    let videoURL = arg.urlPrefix + "file_thirdPartyChild.video.ogv";
+    let audioURL = arg.urlPrefix + "file_thirdPartyChild.audio.ogg";
+    let trackURL = arg.urlPrefix + "file_thirdPartyChild.track.vtt";
+    let URLSuffix = "?r=" + arg.randomSuffix;
+
+    // Create the audio and video elements.
+    let audio = content.document.createElement('audio');
+    let video = content.document.createElement('video');
+    let audioSource = content.document.createElement('source');
+    let audioTrack = content.document.createElement('track');
+
+    // Assign attributes for the audio element.
+    audioSource.setAttribute("src", audioURL + URLSuffix);
+    audioSource.setAttribute("type", "audio/ogg");
+    audioTrack.setAttribute("src", trackURL);
+    audioTrack.setAttribute("kind", "subtitles");
+
+    // Append the audio and track element into the body, and wait until they're finished.
+    yield new Promise(resolve => {
+      let audioLoaded = false;
+      let trackLoaded = false;
+
+      let audioListener = () => {
+        audio.removeEventListener("canplaythrough", audioListener);
+
+        audioLoaded = true;
+        if (audioLoaded && trackLoaded) {
+          resolve();
+        }
+      };
+
+      let trackListener = () => {
+        audioTrack.removeEventListener("load", trackListener);
+
+        trackLoaded = true;
+        if (audioLoaded && trackLoaded) {
+          resolve();
+        }
+      };
+
+      audio.appendChild(audioSource);
+      audio.appendChild(audioTrack);
+      audio.autoplay = true;
+
+      audioTrack.addEventListener("load", trackListener, false);
+      audio.addEventListener("canplaythrough", audioListener, false);
+      content.document.body.appendChild(audio);
+    });
+
+    // Append the video element into the body, and wait until it's finished.
+    yield new Promise(resolve => {
+      let listener = () => {
+        video.removeEventListener("canplaythrough", listener);
+        resolve();
+      };
+
+      // Assign attributes for the video element.
+      video.setAttribute("src", videoURL + URLSuffix);
+      video.setAttribute("type", "video/ogg");
+
+      video.addEventListener("canplaythrough", listener, false);
+      content.document.body.appendChild(video);
+    });
+  });
+
+  return 0;
+}
+
+// The check function, which checks the number of cache entries.
+function* doCheck(aShouldIsolate, aInputA, aInputB) {
+  let expectedEntryCount = 1;
+  let data = [];
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.default));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.private));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, {})));
+
+  if (aShouldIsolate) {
+    expectedEntryCount = 2;
+  }
+
+  for (let suffix of suffixes) {
+    let foundEntryCount = countMatchingCacheEntries(data, "example.net", suffix);
+    let result = (expectedEntryCount === foundEntryCount);
+    ok(result, "Cache entries expected for " + suffix + ": " + expectedEntryCount +
+               ", and found " + foundEntryCount);
+  }
+
+  stopObservingChannels();
+  stopObservingChannels = undefined;
+  return true;
+}
+
+let testArgs = {
+  url: TEST_PAGE,
+  firstFrameSetting: DEFAULT_FRAME_SETTING,
+  secondFrameSetting: [TEST_TYPE_FRAME],
+};
+
+IsolationTestTools.runTests(testArgs, doTest, doCheck, doInit);
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_cache.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<head>
+  <link rel="stylesheet" type="text/css"
+        href="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.link.css">
+  <link rel="preconnect" href="http://example.net">
+</head>
+<body>
+<div>file_cache.html</div>
+
+<iframe src="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.iframe.html">
+</iframe>
+
+<script src="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.script.js">
+</script>
+
+<img src="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.img.png">
+
+<embed src="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.embed.png">
+
+<object data="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.object.png"
+        type="image/png"></object>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..edda4e9128435b0cc9ba500c9daca90e2be30acf
GIT binary patch
literal 2603
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c5916f289705642eec4975cf51458b9afeefe46c
GIT binary patch
literal 95
zc%17D@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)ga%mF?juK#@*VoWXSL2@NQe!*uh
mnS}iXa=1KQ978JRBqsscYz)k1<~1vTECx?kKbLh*2~7ZT-W2Wt
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.fetch.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.fetch.html</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.iframe.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.html</div>
+<script>
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", "http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.xhr.html", true);
+  xhr.send();
+  var worker = new Worker("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js");
+  var sharedWorker = new SharedWorker("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js");
+
+  fetch("file_thirdPartyChild.fetch.html", {cache: "force-cache"} );
+  fetch(new Request("file_thirdPartyChild.request.html"), {cache: "force-cache"} );
+</script>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c5916f289705642eec4975cf51458b9afeefe46c
GIT binary patch
literal 95
zc%17D@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)ga%mF?juK#@*VoWXSL2@NQe!*uh
mnS}iXa=1KQ978JRBqsscYz)k1<~1vTECx?kKbLh*2~7ZT-W2Wt
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.import.js
@@ -0,0 +1,1 @@
+// dummy script, to be called by self.importScripts(...)
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.link.css
@@ -0,0 +1,1 @@
+/* Dummy CSS file, used by browser_cache.js. */
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c5916f289705642eec4975cf51458b9afeefe46c
GIT binary patch
literal 95
zc%17D@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)ga%mF?juK#@*VoWXSL2@NQe!*uh
mnS}iXa=1KQ978JRBqsscYz)k1<~1vTECx?kKbLh*2~7ZT-W2Wt
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.request.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.request.html</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.script.js
@@ -0,0 +1,1 @@
+// Dummy child script, used by browser_cache.js
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js
@@ -0,0 +1,1 @@
+// dummy file
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.track.vtt
@@ -0,0 +1,13 @@
+WEBVTT FILE
+
+1
+00:00:00.500 --> 00:00:02.000 D:vertical A:start
+blah blah blah
+
+2
+00:00:02.500 --> 00:00:04.300
+this is a test
+
+3
+00:00:05.000 --> 00:00:07.000
+one more line
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..68dee3cf2b1de3d820a7b606cdca809f8224e855
GIT binary patch
literal 16049
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
NW^56gvQ&sQ|3Brd7xw@F
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.worker.fetch.html</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js
@@ -0,0 +1,9 @@
+var xhr = new XMLHttpRequest();
+xhr.open("GET", "http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.xhr.html", true);
+xhr.send();
+
+fetch("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html", {cache: "force-cache"} );
+var myRequest = new Request("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html");
+fetch(myRequest, {cache: "force-cache"} );
+
+self.importScripts("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.import.js");
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.worker.request.html</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.xhr.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.worker.xhr.html</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.xhr.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.html</div>
+</body>
+</html>
--- a/browser/components/originattributes/test/browser/head.js
+++ b/browser/components/originattributes/test/browser/head.js
@@ -304,17 +304,17 @@ this.IsolationTestTools = {
                       ];
 
     this._add_task(function* (aMode) {
       let tabSettingA = 0;
 
       for (let tabSettingB of [0, 1]) {
         // Give the test a chance to set up before each case is run.
         if (aBeforeFunc) {
-          yield aBeforeFunc();
+          yield aBeforeFunc(aMode);
         }
 
         // Create Tabs.
         let tabInfoA = yield IsolationTestTools._addTab(aMode,
                                                         pageURL,
                                                         tabSettings[tabSettingA],
                                                         firstFrameSetting);
         let tabInfoB = yield IsolationTestTools._addTab(aMode,
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -1463,43 +1463,43 @@ RequiresAdditiveAnimation(const nsTArray
  * @param aSpacingRange The sequence of keyframes between whose endpoints we
  *   should apply distribute spacing.
  * @param aRangeToAdjust The range of keyframes we want to apply to.
  */
 static void
 DistributeRange(const Range<Keyframe>& aSpacingRange,
                 const Range<Keyframe>& aRangeToAdjust)
 {
-  MOZ_ASSERT(aRangeToAdjust.start() >= aSpacingRange.start() &&
+  MOZ_ASSERT(aRangeToAdjust.begin() >= aSpacingRange.begin() &&
              aRangeToAdjust.end() <= aSpacingRange.end(),
              "Out of range");
   const size_t n = aSpacingRange.length() - 1;
   const double startOffset = aSpacingRange[0].mComputedOffset;
   const double diffOffset = aSpacingRange[n].mComputedOffset - startOffset;
-  for (auto iter = aRangeToAdjust.start();
+  for (auto iter = aRangeToAdjust.begin();
        iter != aRangeToAdjust.end();
        ++iter) {
-    size_t index = iter - aSpacingRange.start();
+    size_t index = iter - aSpacingRange.begin();
     iter->mComputedOffset = startOffset + double(index) / n * diffOffset;
   }
 }
 
 /**
  * Overload of DistributeRange to apply distribute spacing to all keyframes in
  * between the endpoints of the given range.
  *
  * @param aSpacingRange The sequence of keyframes between whose endpoints we
  *   should apply distribute spacing.
  */
 static void
 DistributeRange(const Range<Keyframe>& aSpacingRange)
 {
   // We don't need to apply distribute spacing to keyframe A and keyframe B.
   DistributeRange(aSpacingRange,
-                  Range<Keyframe>(aSpacingRange.start() + 1,
+                  Range<Keyframe>(aSpacingRange.begin() + 1,
                                   aSpacingRange.end() - 1));
 }
 
 /**
  * Apply paced spacing to all paceable keyframes in between the endpoints of the
  * given range.
  *
  * @param aKeyframes The range of keyframes between whose endpoints we should
@@ -1520,41 +1520,41 @@ PaceRange(const Range<Keyframe>& aKeyfra
              "Range length mismatch");
 
   const size_t len = aKeyframes.length();
   // If there is nothing between the end points, there is nothing to space.
   if (len < 3) {
     return;
   }
 
-  const double distA = *(aCumulativeDistances.start());
+  const double distA = *(aCumulativeDistances.begin());
   const double distB = *(aCumulativeDistances.end() - 1);
   MOZ_ASSERT(distA != kNotPaceable && distB != kNotPaceable,
              "Both Paced A and Paced B should be paceable");
 
   // If the total distance is zero, we should fall back to distribute spacing.
   // The caller will fill-in any keyframes without a computed offset using
   // distribute spacing so we can just return here.
   if (distA == distB) {
     return;
   }
 
-  const RangedPtr<Keyframe> pacedA = aKeyframes.start();
+  const RangedPtr<Keyframe> pacedA = aKeyframes.begin();
   const RangedPtr<Keyframe> pacedB = aKeyframes.end() - 1;
   MOZ_ASSERT(pacedA->mComputedOffset != Keyframe::kComputedOffsetNotSet &&
              pacedB->mComputedOffset != Keyframe::kComputedOffsetNotSet,
              "Both Paced A and Paced B should have valid computed offsets");
 
   // Apply computed offset.
   const double offsetA     = pacedA->mComputedOffset;
   const double diffOffset  = pacedB->mComputedOffset - offsetA;
   const double initialDist = distA;
   const double totalDist   = distB - initialDist;
   for (auto iter = pacedA + 1; iter != pacedB; ++iter) {
-    size_t k = iter - aKeyframes.start();
+    size_t k = iter - aKeyframes.begin();
     if (aCumulativeDistances[k] == kNotPaceable) {
       continue;
     }
 
     double dist = aCumulativeDistances[k] - initialDist;
     iter->mComputedOffset = offsetA + diffOffset * dist / totalDist;
   }
 }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -232,16 +232,17 @@
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "mozilla/dom/U2F.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
+#include "mozilla/dom/Worklet.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
@@ -14843,11 +14844,25 @@ nsGlobalWindow::TemporarilyDisableDialog
 
 nsGlobalWindow::TemporarilyDisableDialogs::~TemporarilyDisableDialogs()
 {
   if (mTopWindow) {
     mTopWindow->mAreDialogsEnabled = mSavedDialogsEnabled;
   }
 }
 
+already_AddRefed<Worklet>
+nsGlobalWindow::CreateWorklet(ErrorResult& aRv)
+{
+  MOZ_RELEASE_ASSERT(IsInnerWindow());
+
+  if (!mDoc) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  RefPtr<Worklet> worklet = new Worklet(AsInner(), mDoc->NodePrincipal());
+  return worklet.forget();
+}
+
 template class nsPIDOMWindow<mozIDOMWindowProxy>;
 template class nsPIDOMWindow<mozIDOMWindow>;
 template class nsPIDOMWindow<nsISupports>;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -131,16 +131,17 @@ class TabGroup;
 class Timeout;
 class U2F;
 class VRDisplay;
 class VREventObserver;
 class WakeLock;
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class WindowOrientationObserver;
 #endif
+class Worklet;
 namespace cache {
 class CacheStorage;
 } // namespace cache
 class IDBFactory;
 } // namespace dom
 } // namespace mozilla
 
 extern already_AddRefed<nsIScriptTimeoutHandler>
@@ -921,16 +922,20 @@ public:
 
   // Exposed only for testing
   static bool
   TokenizeDialogOptions(nsAString& aToken, nsAString::const_iterator& aIter,
                         nsAString::const_iterator aEnd);
   static void
   ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult);
 
+  // Exposed only for testing
+  already_AddRefed<mozilla::dom::Worklet>
+  CreateWorklet(mozilla::ErrorResult& aRv);
+
 protected:
   bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
                       nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
 
 public:
   void Alert(nsIPrincipal& aSubjectPrincipal,
              mozilla::ErrorResult& aError);
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2701,17 +2701,18 @@ IsNonExposedGlobal(JSContext* aCx, JSObj
 {
   MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?");
   MOZ_ASSERT((aNonExposedGlobals &
               ~(GlobalNames::Window |
                 GlobalNames::BackstagePass |
                 GlobalNames::DedicatedWorkerGlobalScope |
                 GlobalNames::SharedWorkerGlobalScope |
                 GlobalNames::ServiceWorkerGlobalScope |
-                GlobalNames::WorkerDebuggerGlobalScope)) == 0,
+                GlobalNames::WorkerDebuggerGlobalScope |
+                GlobalNames::WorkletGlobalScope)) == 0,
              "Unknown non-exposed global type");
 
   const char* name = js::GetObjectClass(aGlobal)->name;
 
   if ((aNonExposedGlobals & GlobalNames::Window) &&
       !strcmp(name, "Window")) {
     return true;
   }
@@ -2736,16 +2737,21 @@ IsNonExposedGlobal(JSContext* aCx, JSObj
     return true;
   }
 
   if ((aNonExposedGlobals & GlobalNames::WorkerDebuggerGlobalScope) &&
       !strcmp(name, "WorkerDebuggerGlobalScopex")) {
     return true;
   }
 
+  if ((aNonExposedGlobals & GlobalNames::WorkletGlobalScope) &&
+      !strcmp(name, "WorkletGlobalScope")) {
+    return true;
+  }
+
   return false;
 }
 
 void
 HandlePrerenderingViolation(nsPIDOMWindowInner* aWindow)
 {
   // Freeze the window and its workers, and its children too.
   aWindow->Freeze();
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -13235,16 +13235,41 @@ class CGRegisterWorkerDebuggerBindings(C
                     "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
                     + condition)
             conditions.append(condition)
         lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
                  condition in conditions]
         lines.append(CGGeneric("return true;\n"))
         return CGList(lines, "\n").define()
 
+class CGRegisterWorkletBindings(CGAbstractMethod):
+    def __init__(self, config):
+        CGAbstractMethod.__init__(self, None, 'RegisterWorkletBindings', 'bool',
+                                  [Argument('JSContext*', 'aCx'),
+                                   Argument('JS::Handle<JSObject*>', 'aObj')])
+        self.config = config
+
+    def definition_body(self):
+        descriptors = self.config.getDescriptors(hasInterfaceObject=True,
+                                                 isExposedInAnyWorklet=True,
+                                                 register=True)
+        conditions = []
+        for desc in descriptors:
+            bindingNS = toBindingNamespace(desc.name)
+            condition = "!%s::GetConstructorObject(aCx)" % bindingNS
+            if desc.isExposedConditionally():
+                condition = (
+                    "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
+                    + condition)
+            conditions.append(condition)
+        lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
+                 condition in conditions]
+        lines.append(CGGeneric("return true;\n"))
+        return CGList(lines, "\n").define()
+
 class CGResolveSystemBinding(CGAbstractMethod):
     def __init__(self, config):
         CGAbstractMethod.__init__(self, None, 'ResolveSystemBinding', 'bool',
                                   [Argument('JSContext*', 'aCx'),
                                    Argument('JS::Handle<JSObject*>', 'aObj'),
                                    Argument('JS::Handle<jsid>', 'aId'),
                                    Argument('bool*', 'aResolvedp')])
         self.config = config
@@ -16604,16 +16629,41 @@ class GlobalGenRoots():
 
         # Add include guards.
         curr = CGIncludeGuard('RegisterWorkerDebuggerBindings', curr)
 
         # Done.
         return curr
 
     @staticmethod
+    def RegisterWorkletBindings(config):
+
+        curr = CGRegisterWorkletBindings(config)
+
+        # Wrap all of that in our namespaces.
+        curr = CGNamespace.build(['mozilla', 'dom'],
+                                 CGWrapper(curr, post='\n'))
+        curr = CGWrapper(curr, post='\n')
+
+        # Add the includes
+        defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
+                          for desc in config.getDescriptors(hasInterfaceObject=True,
+                                                            register=True,
+                                                            isExposedInAnyWorklet=True)]
+
+        curr = CGHeaders([], [], [], [], [], defineIncludes,
+                         'RegisterWorkletBindings', curr)
+
+        # Add include guards.
+        curr = CGIncludeGuard('RegisterWorkletBindings', curr)
+
+        # Done.
+        return curr
+
+    @staticmethod
     def ResolveSystemBinding(config):
 
         curr = CGResolveSystemBinding(config)
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'],
                                  CGWrapper(curr, post='\n'))
         curr = CGWrapper(curr, post='\n')
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -193,16 +193,18 @@ class Configuration(DescriptorProvider):
             elif key == 'isJSImplemented':
                 getter = lambda x: x.interface.isJSImplemented()
             elif key == 'isNavigatorProperty':
                 getter = lambda x: x.interface.isNavigatorProperty()
             elif key == 'isExposedInAnyWorker':
                 getter = lambda x: x.interface.isExposedInAnyWorker()
             elif key == 'isExposedInWorkerDebugger':
                 getter = lambda x: x.interface.isExposedInWorkerDebugger()
+            elif key == 'isExposedInAnyWorklet':
+                getter = lambda x: x.interface.isExposedInAnyWorklet()
             elif key == 'isExposedInSystemGlobals':
                 getter = lambda x: x.interface.isExposedInSystemGlobals()
             elif key == 'isExposedInWindow':
                 getter = lambda x: x.interface.isExposedInWindow()
             else:
                 # Have to watch out: just closing over "key" is not enough,
                 # since we're about to mutate its value
                 getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -98,16 +98,17 @@ namespace GlobalNames {
 // interfaces, not of the global names used to refer to them in IDL [Exposed]
 // annotations.
 static const uint32_t Window = 1u << 0;
 static const uint32_t BackstagePass = 1u << 1;
 static const uint32_t DedicatedWorkerGlobalScope = 1u << 2;
 static const uint32_t SharedWorkerGlobalScope = 1u << 3;
 static const uint32_t ServiceWorkerGlobalScope = 1u << 4;
 static const uint32_t WorkerDebuggerGlobalScope = 1u << 5;
+static const uint32_t WorkletGlobalScope = 1u << 6;
 } // namespace GlobalNames
 
 struct PrefableDisablers {
   inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
     // Reading "enabled" on a worker thread is technically undefined behavior,
     // because it's written only on main threads, with no barriers of any sort.
     // So we want to avoid doing that.  But we don't particularly want to make
     // expensive NS_IsMainThread calls here.
--- a/dom/bindings/mozwebidlcodegen/__init__.py
+++ b/dom/bindings/mozwebidlcodegen/__init__.py
@@ -128,26 +128,28 @@ class WebIDLCodegenManager(LoggingMixin)
     # Global parser derived declaration files.
     GLOBAL_DECLARE_FILES = {
         'GeneratedAtomList.h',
         'GeneratedEventList.h',
         'PrototypeList.h',
         'RegisterBindings.h',
         'RegisterWorkerBindings.h',
         'RegisterWorkerDebuggerBindings.h',
+        'RegisterWorkletBindings.h',
         'ResolveSystemBinding.h',
         'UnionConversions.h',
         'UnionTypes.h',
     }
 
     # Global parser derived definition files.
     GLOBAL_DEFINE_FILES = {
         'RegisterBindings.cpp',
         'RegisterWorkerBindings.cpp',
         'RegisterWorkerDebuggerBindings.cpp',
+        'RegisterWorkletBindings.cpp',
         'ResolveSystemBinding.cpp',
         'UnionTypes.cpp',
         'PrototypeList.cpp',
     }
 
     def __init__(self, config_path, inputs, exported_header_dir,
                  codegen_dir, state_path, cache_dir=None, make_deps_path=None,
                  make_deps_target=None):
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -510,16 +510,19 @@ class IDLExposureMixins():
                 self.isExposedInSystemGlobals())
 
     def isExposedInAnyWorker(self):
         return len(self.getWorkerExposureSet()) > 0
 
     def isExposedInWorkerDebugger(self):
         return len(self.getWorkerDebuggerExposureSet()) > 0
 
+    def isExposedInAnyWorklet(self):
+        return len(self.getWorkletExposureSet()) > 0
+
     def isExposedInSystemGlobals(self):
         return 'BackstagePass' in self.exposureSet
 
     def isExposedInSomeButNotAllWorkers(self):
         """
         Returns true if the Exposed extended attribute for this interface
         exposes it in some worker globals but not others.  The return value does
         not depend on whether the interface is exposed in Window or System
@@ -529,16 +532,20 @@ class IDLExposureMixins():
             return False
         workerScopes = self.parentScope.globalNameMapping["Worker"]
         return len(workerScopes.difference(self.exposureSet)) > 0
 
     def getWorkerExposureSet(self):
         workerScopes = self._globalScope.globalNameMapping["Worker"]
         return workerScopes.intersection(self.exposureSet)
 
+    def getWorkletExposureSet(self):
+        workletScopes = self._globalScope.globalNameMapping["Worklet"]
+        return workletScopes.intersection(self.exposureSet)
+
     def getWorkerDebuggerExposureSet(self):
         workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"]
         return workerDebuggerScopes.intersection(self.exposureSet)
 
 
 class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
     def __init__(self, location, parentScope, identifier):
         assert isinstance(identifier, IDLUnresolvedIdentifier)
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -9,16 +9,17 @@
 
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/WorkletGlobalScope.h"
 #include "mozilla/Maybe.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDocument.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsGlobalWindow.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "ScriptSettings.h"
@@ -2358,73 +2359,82 @@ Console::IsShuttingDown() const
 {
   MOZ_ASSERT(mStatus != eUnknown);
   return mStatus == eShuttingDown;
 }
 
 /* static */ already_AddRefed<Console>
 Console::GetConsole(const GlobalObject& aGlobal)
 {
-  RefPtr<Console> console;
-
+  ErrorResult rv;
+  RefPtr<Console> console = GetConsoleInternal(aGlobal, rv);
+  if (NS_WARN_IF(rv.Failed()) || !console) {
+    rv.SuppressException();
+    return nullptr;
+  }
+
+  console->AssertIsOnOwningThread();
+
+  if (console->IsShuttingDown()) {
+    return nullptr;
+  }
+
+  return console.forget();
+}
+
+/* static */ Console*
+Console::GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult& aRv)
+{
+  // Worklet
+  if (NS_IsMainThread()) {
+    nsCOMPtr<WorkletGlobalScope> workletScope =
+      do_QueryInterface(aGlobal.GetAsSupports());
+    if (workletScope) {
+      return workletScope->GetConsole(aRv);
+    }
+  }
+
+  // Window
   if (NS_IsMainThread()) {
     nsCOMPtr<nsPIDOMWindowInner> innerWindow =
       do_QueryInterface(aGlobal.GetAsSupports());
     if (NS_WARN_IF(!innerWindow)) {
       return nullptr;
     }
 
     nsGlobalWindow* window = nsGlobalWindow::Cast(innerWindow);
-
-    ErrorResult rv;
-    console = window->GetConsole(rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      rv.SuppressException();
-      return nullptr;
-    }
-  } else {
-    JSContext* cx = aGlobal.Context();
-    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
-    MOZ_ASSERT(workerPrivate);
-
-    nsCOMPtr<nsIGlobalObject> global =
-      do_QueryInterface(aGlobal.GetAsSupports());
-    if (NS_WARN_IF(!global)) {
-      return nullptr;
-    }
-
-    WorkerGlobalScope* scope = workerPrivate->GlobalScope();
-    MOZ_ASSERT(scope);
-
-    // Normal worker scope.
-    ErrorResult rv;
-    if (scope == global) {
-      console = scope->GetConsole(rv);
-    }
-
-    // Debugger worker scope
-    else {
-      WorkerDebuggerGlobalScope* debuggerScope =
-        workerPrivate->DebuggerGlobalScope();
-      MOZ_ASSERT(debuggerScope);
-      MOZ_ASSERT(debuggerScope == global, "Which kind of global do we have?");
-
-      console = debuggerScope->GetConsole(rv);
-    }
-
-    if (NS_WARN_IF(rv.Failed())) {
-      rv.SuppressException();
-      return nullptr;
-    }
+    return window->GetConsole(aRv);
   }
 
-  MOZ_ASSERT(console);
-  console->AssertIsOnOwningThread();
-
-  if (console->IsShuttingDown()) {
+  // Workers
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  JSContext* cx = aGlobal.Context();
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+  MOZ_ASSERT(workerPrivate);
+
+  nsCOMPtr<nsIGlobalObject> global =
+    do_QueryInterface(aGlobal.GetAsSupports());
+  if (NS_WARN_IF(!global)) {
     return nullptr;
   }
 
-  return console.forget();
+  WorkerGlobalScope* scope = workerPrivate->GlobalScope();
+  MOZ_ASSERT(scope);
+
+  // Normal worker scope.
+  if (scope == global) {
+    return scope->GetConsole(aRv);
+  }
+
+  // Debugger worker scope
+  else {
+    WorkerDebuggerGlobalScope* debuggerScope =
+      workerPrivate->DebuggerGlobalScope();
+    MOZ_ASSERT(debuggerScope);
+    MOZ_ASSERT(debuggerScope == global, "Which kind of global do we have?");
+
+    return debuggerScope->GetConsole(aRv);
+  }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/console/Console.h
+++ b/dom/console/Console.h
@@ -156,16 +156,19 @@ private:
     MethodAssert,
     MethodCount,
     MethodClear
   };
 
   static already_AddRefed<Console>
   GetConsole(const GlobalObject& aGlobal);
 
+  static Console*
+  GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult &aRv);
+
   static void
   ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
                 const Sequence<JS::Value>& aData);
 
   void
   ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
                         const Sequence<JS::Value>& aData);
 
--- a/dom/json/nsJSON.cpp
+++ b/dom/json/nsJSON.cpp
@@ -526,17 +526,17 @@ nsJSONListener::OnStopRequest(nsIRequest
     rv = ProcessBytes(nullptr, 0);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   JS::Rooted<JS::Value> reviver(mCx, JS::NullValue()), value(mCx);
 
   JS::ConstTwoByteChars chars(reinterpret_cast<const char16_t*>(mBufferedChars.Elements()),
                               mBufferedChars.Length());
-  bool ok = JS_ParseJSONWithReviver(mCx, chars.start().get(),
+  bool ok = JS_ParseJSONWithReviver(mCx, chars.begin().get(),
                                       uint32_t(mBufferedChars.Length()),
                                       reviver, &value);
 
   *mRootVal = value;
   mBufferedChars.TruncateLength(0);
   return ok ? NS_OK : NS_ERROR_FAILURE;
 }
 
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -96,16 +96,17 @@ DIRS += [
     'xslt',
     'xul',
     'manifest',
     'vr',
     'u2f',
     'console',
     'performance',
     'xhr',
+    'worklet',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['plugins/ipc/hangui']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     DIRS += [
         'speakermanager',
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-[Exposed=(Window,Worker,WorkerDebugger),
+[Exposed=(Window,Worker,WorkerDebugger,Worklet),
  ClassString="Console",
  ProtoObjectHack]
 namespace console {
   void log(any... data);
   void info(any... data);
   void warn(any... data);
   void error(any... data);
   void _exception(any... data);
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -481,16 +481,22 @@ partial interface Window {
   [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplayconnect;
   [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplaydisconnect;
   [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplaypresentchange;
 };
 
+// For testing worklet only
+partial interface Window {
+  [Pref="dom.worklet.testing.enabled", Throws]
+  Worklet createWorklet();
+};
+
 Window implements ChromeWindow;
 Window implements WindowOrWorkerGlobalScope;
 
 partial interface Window {
   [Throws, Pref="dom.requestIdleCallback.enabled"]
   unsigned long requestIdleCallback(IdleRequestCallback callback,
                                     optional IdleRequestOptions options);
   [Pref="dom.requestIdleCallback.enabled"]
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Worklet.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.css-houdini.org/worklets/#idl-index
+ */
+
+[Pref="dom.worklet.enabled"]
+interface Worklet {
+  [NewObject, Throws]
+  Promise<void> import(USVString moduleURL);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/WorkletGlobalScope.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.css-houdini.org/worklets/#idl-index
+ */
+
+[Global=(Worklet), Exposed=(Worklet)]
+interface WorkletGlobalScope {
+};
+
+// Mozilla extensions
+partial interface WorkletGlobalScope {
+  void dump(optional DOMString str);
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -576,16 +576,18 @@ WEBIDL_FILES = [
     'WifiOptions.webidl',
     'WindowOrWorkerGlobalScope.webidl',
     'WindowRoot.webidl',
     'Worker.webidl',
     'WorkerDebuggerGlobalScope.webidl',
     'WorkerGlobalScope.webidl',
     'WorkerLocation.webidl',
     'WorkerNavigator.webidl',
+    'Worklet.webidl',
+    'WorkletGlobalScope.webidl',
     'XMLDocument.webidl',
     'XMLHttpRequest.webidl',
     'XMLHttpRequestEventTarget.webidl',
     'XMLHttpRequestUpload.webidl',
     'XMLSerializer.webidl',
     'XMLStylesheetProcessingInstruction.webidl',
     'XPathEvaluator.webidl',
     'XPathExpression.webidl',
new file mode 100644
--- /dev/null
+++ b/dom/worklet/Worklet.cpp
@@ -0,0 +1,391 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Worklet.h"
+#include "WorkletGlobalScope.h"
+#include "mozilla/dom/WorkletBinding.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/Fetch.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+#include "mozilla/dom/RegisterWorkletBindings.h"
+#include "mozilla/dom/Response.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsIThreadRetargetableRequest.h"
+#include "nsNetUtil.h"
+#include "nsScriptLoader.h"
+#include "xpcprivate.h"
+
+namespace mozilla {
+namespace dom {
+
+// ---------------------------------------------------------------------------
+// WorkletFetchHandler
+
+class WorkletFetchHandler : public PromiseNativeHandler
+                          , public nsIStreamLoaderObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  static already_AddRefed<Promise>
+  Fetch(Worklet* aWorklet, const nsAString& aModuleURL, ErrorResult& aRv)
+  {
+    MOZ_ASSERT(aWorklet);
+
+    nsCOMPtr<nsIGlobalObject> global =
+      do_QueryInterface(aWorklet->GetParentObject());
+    MOZ_ASSERT(global);
+
+    RefPtr<Promise> promise = Promise::Create(global, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    nsCOMPtr<nsPIDOMWindowInner> window = aWorklet->GetParentObject();
+    MOZ_ASSERT(window);
+
+    nsCOMPtr<nsIDocument> doc;
+    doc = window->GetExtantDoc();
+    if (!doc) {
+      promise->MaybeReject(NS_ERROR_FAILURE);
+      return promise.forget();
+    }
+
+    nsCOMPtr<nsIURI> baseURI = doc->GetBaseURI();
+    nsCOMPtr<nsIURI> resolvedURI;
+    nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), aModuleURL, nullptr, baseURI);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      promise->MaybeReject(rv);
+      return promise.forget();
+    }
+
+    nsAutoCString spec;
+    rv = resolvedURI->GetSpec(spec);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      promise->MaybeReject(rv);
+      return promise.forget();
+    }
+
+    // Maybe we already have an handler for this URI
+    {
+      WorkletFetchHandler* handler = aWorklet->GetImportFetchHandler(spec);
+      if (handler) {
+        handler->AddPromise(promise);
+        return promise.forget();
+      }
+    }
+
+    RequestOrUSVString request;
+    request.SetAsUSVString().Rebind(aModuleURL.Data(), aModuleURL.Length());
+
+    RequestInit init;
+
+    RefPtr<Promise> fetchPromise = FetchRequest(global, request, init, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      promise->MaybeReject(aRv);
+      return promise.forget();
+    }
+
+    RefPtr<WorkletFetchHandler> handler =
+      new WorkletFetchHandler(aWorklet, aModuleURL, promise);
+    fetchPromise->AppendNativeHandler(handler);
+
+    aWorklet->AddImportFetchHandler(spec, handler);
+    return promise.forget();
+  }
+
+  virtual void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    if (!aValue.isObject()) {
+      RejectPromises(NS_ERROR_FAILURE);
+      return;
+    }
+
+    RefPtr<Response> response;
+    nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      RejectPromises(NS_ERROR_FAILURE);
+      return;
+    }
+
+    if (!response->Ok()) {
+      RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
+      return;
+    }
+
+    nsCOMPtr<nsIInputStream> inputStream;
+    response->GetBody(getter_AddRefs(inputStream));
+    if (!inputStream) {
+      RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
+      return;
+    }
+
+    nsCOMPtr<nsIInputStreamPump> pump;
+    rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      RejectPromises(rv);
+      return;
+    }
+
+    nsCOMPtr<nsIStreamLoader> loader;
+    rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      RejectPromises(rv);
+      return;
+    }
+
+    rv = pump->AsyncRead(loader, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      RejectPromises(rv);
+      return;
+    }
+
+    nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump);
+    if (rr) {
+      nsCOMPtr<nsIEventTarget> sts =
+        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+      rv = rr->RetargetDeliveryTo(sts);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread.");
+      }
+    }
+  }
+
+  NS_IMETHOD
+  OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
+                   nsresult aStatus, uint32_t aStringLen,
+                   const uint8_t* aString) override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (NS_FAILED(aStatus)) {
+      RejectPromises(aStatus);
+      return NS_OK;
+    }
+
+    char16_t* scriptTextBuf;
+    size_t scriptTextLength;
+    nsresult rv =
+      nsScriptLoader::ConvertToUTF16(nullptr, aString, aStringLen,
+                                     NS_LITERAL_STRING("UTF-8"), nullptr,
+                                     scriptTextBuf, scriptTextLength);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      RejectPromises(rv);
+      return NS_OK;
+    }
+
+    // Moving the ownership of the buffer
+    JS::SourceBufferHolder buffer(scriptTextBuf, scriptTextLength,
+                                  JS::SourceBufferHolder::GiveOwnership);
+
+    AutoJSAPI jsapi;
+    jsapi.Init();
+
+    RefPtr<WorkletGlobalScope> globalScope =
+      mWorklet->GetOrCreateGlobalScope(jsapi.cx());
+    MOZ_ASSERT(globalScope);
+
+    AutoEntryScript aes(globalScope, "Worklet");
+    JSContext* cx = aes.cx();
+
+    JS::Rooted<JSObject*> globalObj(cx, globalScope->GetGlobalJSObject());
+
+    (void) new XPCWrappedNativeScope(cx, globalObj);
+
+    JS::CompileOptions compileOptions(cx);
+    compileOptions.setIntroductionType("Worklet");
+    compileOptions.setFileAndLine(NS_ConvertUTF16toUTF8(mURL).get(), 0);
+    compileOptions.setVersion(JSVERSION_DEFAULT);
+    compileOptions.setIsRunOnce(true);
+
+    // We only need the setNoScriptRval bit when compiling off-thread here,
+    // since otherwise nsJSUtils::EvaluateString will set it up for us.
+    compileOptions.setNoScriptRval(true);
+
+    JS::Rooted<JS::Value> unused(cx);
+    if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
+      ErrorResult error;
+      error.StealExceptionFromJSContext(cx);
+      RejectPromises(error.StealNSResult());
+      return NS_OK;
+    }
+
+    // All done.
+    ResolvePromises();
+    return NS_OK;
+  }
+
+  virtual void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
+  }
+
+private:
+  WorkletFetchHandler(Worklet* aWorklet, const nsAString& aURL,
+                      Promise* aPromise)
+    : mWorklet(aWorklet)
+    , mStatus(ePending)
+    , mErrorStatus(NS_OK)
+    , mURL(aURL)
+  {
+    MOZ_ASSERT(aWorklet);
+    MOZ_ASSERT(aPromise);
+
+    mPromises.AppendElement(aPromise);
+  }
+
+  ~WorkletFetchHandler()
+  {}
+
+  void
+  AddPromise(Promise* aPromise)
+  {
+    MOZ_ASSERT(aPromise);
+
+    switch (mStatus) {
+      case ePending:
+        mPromises.AppendElement(aPromise);
+        return;
+
+      case eRejected:
+        MOZ_ASSERT(NS_FAILED(mErrorStatus));
+        aPromise->MaybeReject(mErrorStatus);
+        return;
+
+      case eResolved:
+        aPromise->MaybeResolveWithUndefined();
+        return;
+    }
+  }
+
+  void
+  RejectPromises(nsresult aResult)
+  {
+    MOZ_ASSERT(mStatus == ePending);
+    MOZ_ASSERT(NS_FAILED(aResult));
+
+    for (uint32_t i = 0; i < mPromises.Length(); ++i) {
+      mPromises[i]->MaybeReject(aResult);
+    }
+    mPromises.Clear();
+
+    mStatus = eRejected;
+    mErrorStatus = aResult;
+    mWorklet = nullptr;
+  }
+
+  void
+  ResolvePromises()
+  {
+    MOZ_ASSERT(mStatus == ePending);
+
+    for (uint32_t i = 0; i < mPromises.Length(); ++i) {
+      mPromises[i]->MaybeResolveWithUndefined();
+    }
+    mPromises.Clear();
+
+    mStatus = eResolved;
+    mWorklet = nullptr;
+  }
+
+  RefPtr<Worklet> mWorklet;
+  nsTArray<RefPtr<Promise>> mPromises;
+
+  enum {
+    ePending,
+    eRejected,
+    eResolved
+  } mStatus;
+
+  nsresult mErrorStatus;
+
+  nsString mURL;
+};
+
+NS_IMPL_ISUPPORTS(WorkletFetchHandler, nsIStreamLoaderObserver)
+
+// ---------------------------------------------------------------------------
+// Worklet
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Worklet, mWindow, mScope)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Worklet)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Worklet)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worklet)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+Worklet::Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal)
+  : mWindow(aWindow)
+  , mPrincipal(aPrincipal)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aPrincipal);
+
+#ifdef RELEASE_OR_BETA
+  MOZ_CRASH("This code should not go to release/beta yet!");
+#endif
+}
+
+Worklet::~Worklet()
+{}
+
+JSObject*
+Worklet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return WorkletBinding::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<Promise>
+Worklet::Import(const nsAString& aModuleURL, ErrorResult& aRv)
+{
+  return WorkletFetchHandler::Fetch(this, aModuleURL, aRv);
+}
+
+WorkletGlobalScope*
+Worklet::GetOrCreateGlobalScope(JSContext* aCx)
+{
+  if (!mScope) {
+    mScope = new WorkletGlobalScope(mWindow);
+
+    JS::Rooted<JSObject*> global(aCx);
+    NS_ENSURE_TRUE(mScope->WrapGlobalObject(aCx, mPrincipal, &global), nullptr);
+
+    JSAutoCompartment ac(aCx, global);
+
+    // Init Web IDL bindings
+    if (!RegisterWorkletBindings(aCx, global)) {
+      mScope = nullptr;
+      return nullptr;
+    }
+
+    JS_FireOnNewGlobalObject(aCx, global);
+  }
+
+  return mScope;
+}
+
+WorkletFetchHandler*
+Worklet::GetImportFetchHandler(const nsACString& aURI)
+{
+  return mImportHandlers.GetWeak(aURI);
+}
+
+void
+Worklet::AddImportFetchHandler(const nsACString& aURI,
+                               WorkletFetchHandler* aHandler)
+{
+  MOZ_ASSERT(aHandler);
+  MOZ_ASSERT(!mImportHandlers.GetWeak(aURI));
+
+  mImportHandlers.Put(aURI, aHandler);
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/worklet/Worklet.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_Worklet_h
+#define mozilla_dom_Worklet_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "nsRefPtrHashtable.h"
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsPIDOMWindowInner;
+class nsIPrincipal;
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+class WorkletGlobalScope;
+class WorkletFetchHandler;
+
+class Worklet final : public nsISupports
+                    , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Worklet)
+
+  Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal);
+
+  nsPIDOMWindowInner* GetParentObject() const
+  {
+    return mWindow;
+  }
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<Promise>
+  Import(const nsAString& aModuleURL, ErrorResult& aRv);
+
+  WorkletGlobalScope*
+  GetOrCreateGlobalScope(JSContext* aCx);
+
+private:
+  ~Worklet();
+
+  WorkletFetchHandler*
+  GetImportFetchHandler(const nsACString& aURI);
+
+  void
+  AddImportFetchHandler(const nsACString& aURI, WorkletFetchHandler* aHandler);
+
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+
+  RefPtr<WorkletGlobalScope> mScope;
+  nsRefPtrHashtable<nsCStringHashKey, WorkletFetchHandler> mImportHandlers;
+
+  friend class WorkletFetchHandler;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_Worklet_h
new file mode 100644
--- /dev/null
+++ b/dom/worklet/WorkletGlobalScope.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WorkletGlobalScope.h"
+#include "mozilla/dom/WorkletGlobalScopeBinding.h"
+#include "mozilla/dom/Console.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(WorkletGlobalScope)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkletGlobalScope)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
+  tmp->UnlinkHostObjectURIs();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkletGlobalScope)
+
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
+  tmp->TraverseHostObjectURIs(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(WorkletGlobalScope)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkletGlobalScope)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkletGlobalScope)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkletGlobalScope)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
+  NS_INTERFACE_MAP_ENTRY(WorkletGlobalScope)
+NS_INTERFACE_MAP_END
+
+WorkletGlobalScope::WorkletGlobalScope(nsPIDOMWindowInner* aWindow)
+  : mWindow(aWindow)
+{
+  MOZ_ASSERT(aWindow);
+}
+
+WorkletGlobalScope::~WorkletGlobalScope()
+{
+}
+
+JSObject*
+WorkletGlobalScope::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  MOZ_CRASH("We should never get here!");
+  return nullptr;
+}
+
+bool
+WorkletGlobalScope::WrapGlobalObject(JSContext* aCx,
+                                     nsIPrincipal* aPrincipal,
+                                     JS::MutableHandle<JSObject*> aReflector)
+{
+  JS::CompartmentOptions options;
+  return WorkletGlobalScopeBinding::Wrap(aCx, this, this,
+                                         options,
+                                         nsJSPrincipals::get(aPrincipal),
+                                         true, aReflector);
+}
+
+Console*
+WorkletGlobalScope::GetConsole(ErrorResult& aRv)
+{
+  if (!mConsole) {
+    mConsole = Console::Create(mWindow, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+  }
+
+  return mConsole;
+}
+
+void
+WorkletGlobalScope::Dump(const Optional<nsAString>& aString) const
+{
+  if (!nsContentUtils::DOMWindowDumpEnabled()) {
+    return;
+  }
+
+  if (!aString.WasPassed()) {
+    return;
+  }
+
+  NS_ConvertUTF16toUTF8 str(aString.Value());
+
+#ifdef ANDROID
+  __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get());
+#endif
+
+  fputs(str.get(), stdout);
+  fflush(stdout);
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/worklet/WorkletGlobalScope.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_WorkletGlobalScope_h
+#define mozilla_dom_WorkletGlobalScope_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsIGlobalObject.h"
+#include "nsWrapperCache.h"
+
+#define WORKLET_IID \
+  { 0x1b3f62e7, 0xe357, 0x44be, \
+    { 0xbf, 0xe0, 0xdf, 0x85, 0xe6, 0x56, 0x85, 0xac } }
+
+class nsIPrincipal;
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+namespace dom {
+
+class Console;
+
+class WorkletGlobalScope final : public nsIGlobalObject
+                               , public nsWrapperCache
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(WORKLET_IID)
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkletGlobalScope)
+
+  explicit WorkletGlobalScope(nsPIDOMWindowInner* aWindow);
+
+  nsIGlobalObject* GetParentObject() const
+  {
+    return nullptr;
+  }
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  bool
+  WrapGlobalObject(JSContext* aCx, nsIPrincipal* aPrincipal,
+                   JS::MutableHandle<JSObject*> aReflector);
+
+  virtual JSObject*
+  GetGlobalJSObject() override
+  {
+    return GetWrapper();
+  }
+
+  Console*
+  GetConsole(ErrorResult& aRv);
+
+  void
+  Dump(const Optional<nsAString>& aString) const;
+
+private:
+  ~WorkletGlobalScope();
+
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+  RefPtr<Console> mConsole;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(WorkletGlobalScope, WORKLET_IID)
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WorkletGlobalScope_h
new file mode 100644
--- /dev/null
+++ b/dom/worklet/moz.build
@@ -0,0 +1,25 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla.dom += [
+    'Worklet.h',
+    'WorkletGlobalScope.h',
+]
+
+UNIFIED_SOURCES += [
+    'Worklet.cpp',
+    'WorkletGlobalScope.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/js/xpconnect/src',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/common.js
@@ -0,0 +1,14 @@
+function loadTest(file) {
+  var iframe = document.createElement('iframe');
+  iframe.src = file;
+
+  document.body.appendChild(iframe);
+}
+
+function setupTest() {
+  window.SimpleTest = parent.SimpleTest;
+  window.is = parent.is;
+  window.isnot = parent.isnot;
+  window.ok = parent.ok;
+  window.info = parent.info;
+}
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/file_basic.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet</title>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+setupTest();
+
+var worklet = window.createWorklet();
+ok(!!worklet, "We have a Worklet");
+
+// First loading
+worklet.import("common.js")
+.then(() => {
+  ok(true, "Import should load a resource.");
+})
+
+// Second loading - same file
+.then(() => {
+  return worklet.import("common.js")
+})
+.then(() => {
+  ok(true, "Import should load a resource.");
+})
+
+// 3rd loading - a network error
+.then(() => {
+  return worklet.import("404.js");
+})
+.then(() => {
+  ok(false, "The loading should fail.");
+}, () => {
+  ok(true, "The loading should fail.");
+})
+
+// 4th loading - a network error
+.then(() => {
+  return worklet.import("404.js");
+})
+.then(() => {
+  ok(false, "The loading should fail.");
+}, () => {
+  ok(true, "The loading should fail.");
+})
+
+// done
+.then(() => {
+  SimpleTest.finish();
+});
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/file_console.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet</title>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+setupTest();
+
+var worklet = window.createWorklet();
+ok(!!worklet, "We have a Worklet");
+worklet.import("worklet_console.js");
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/file_dump.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet</title>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+setupTest();
+
+var worklet = window.createWorklet();
+ok(!!worklet, "We have a Worklet");
+worklet.import("worklet_dump.js")
+.then(() => {
+  SimpleTest.finish();
+});
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/file_import_with_cache.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet</title>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+setupTest();
+
+var worklet = window.createWorklet();
+ok(!!worklet, "We have a Worklet");
+
+function loading() {
+  worklet.import("server_import_with_cache.sjs")
+  .then(() => {
+    ok(true, "Import should load a resource.");
+  }, () => {
+    ok(false, "Import should load a resource.");
+  })
+  .then(() => {
+    done();
+  });
+}
+
+var count = 0;
+const MAX = 10;
+
+function done() {
+  if (++count == MAX) {
+    SimpleTest.finish();
+  }
+}
+
+for (var i = 0; i < MAX; ++i) {
+  loading();
+}
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/mochitest.ini
@@ -0,0 +1,13 @@
+[DEFAULT]
+skip-if = release_or_beta
+support-files =
+  common.js
+
+[test_basic.html]
+support-files=file_basic.html
+[test_console.html]
+support-files=file_console.html worklet_console.js
+[test_import_with_cache.html]
+support-files=file_import_with_cache.html server_import_with_cache.sjs
+[test_dump.html]
+support-files=file_dump.html worklet_dump.js
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/server_import_with_cache.sjs
@@ -0,0 +1,13 @@
+function handleRequest(request, response)
+{
+  response.setHeader("Content-Type", "text/javascript", false);
+
+  var state = getState("alreadySent");
+  if (!state) {
+    setState("alreadySent", "1");
+  } else {
+    response.setStatusLine('1.1', 404, "Not Found");
+  }
+
+  response.write("42");
+}
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/test_basic.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+  {"set": [["dom.worklet.testing.enabled", true],
+           ["dom.worklet.enabled", true]]},
+  function() { loadTest("file_basic.html"); });
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/test_console.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet - Console</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+function consoleListener() {
+  SpecialPowers.addObserver(this, "console-api-log-event", false);
+}
+
+consoleListener.prototype  = {
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == "console-api-log-event") {
+      var obj = aSubject.wrappedJSObject;
+      if (obj.arguments[0] == "Hello world from a worklet") {
+        ok(true, "Message received \\o/");
+
+        SpecialPowers.removeObserver(this, "console-api-log-event");
+        SimpleTest.finish();
+        return;
+      }
+    }
+  }
+}
+
+var cl = new consoleListener();
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+  {"set": [["dom.worklet.testing.enabled", true],
+           ["dom.worklet.enabled", true]]},
+  function() { loadTest("file_console.html"); });
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/test_dump.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet - Console</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+  {"set": [["dom.worklet.testing.enabled", true],
+           ["dom.worklet.enabled", true]]},
+  function() { loadTest("file_dump.html"); });
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/test_import_with_cache.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+  {"set": [["dom.worklet.testing.enabled", true],
+           ["dom.worklet.enabled", true]]},
+  function() { loadTest("file_import_with_cache.html"); });
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/worklet_console.js
@@ -0,0 +1,1 @@
+console.log("Hello world from a worklet");
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/worklet_dump.js
@@ -0,0 +1,1 @@
+dump("Hello world from a worklet");
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -153,17 +153,17 @@ EvalStringMightBeJSON(const mozilla::Ran
     {
         // Remarkably, JavaScript syntax is not a superset of JSON syntax:
         // strings in JavaScript cannot contain the Unicode line and paragraph
         // terminator characters U+2028 and U+2029, but strings in JSON can.
         // Rather than force the JSON parser to handle this quirk when used by
         // eval, we simply don't use the JSON parser when either character
         // appears in the provided string.  See bug 657367.
         if (sizeof(CharT) > 1) {
-            for (RangedPtr<const CharT> cp = chars.start() + 1, end = chars.end() - 1;
+            for (RangedPtr<const CharT> cp = chars.begin() + 1, end = chars.end() - 1;
                  cp < end;
                  cp++)
             {
                 char16_t c = *cp;
                 if (c == 0x2028 || c == 0x2029)
                     return false;
             }
         }
@@ -178,17 +178,17 @@ static EvalJSONResult
 ParseEvalStringAsJSON(JSContext* cx, const mozilla::Range<const CharT> chars, MutableHandleValue rval)
 {
     size_t len = chars.length();
     MOZ_ASSERT((chars[0] == '(' && chars[len - 1] == ')') ||
                (chars[0] == '[' && chars[len - 1] == ']'));
 
     auto jsonChars = (chars[0] == '[')
                      ? chars
-                     : mozilla::Range<const CharT>(chars.start().get() + 1U, len - 2);
+                     : mozilla::Range<const CharT>(chars.begin().get() + 1U, len - 2);
 
     Rooted<JSONParser<CharT>> parser(cx, JSONParser<CharT>(cx, jsonChars, JSONParserBase::NoError));
     if (!parser.parse(rval))
         return EvalJSON_Failure;
 
     return rval.isUndefined() ? EvalJSON_NotJSON : EvalJSON_Success;
 }
 
@@ -303,17 +303,17 @@ EvalKernel(JSContext* cx, HandleValue v,
             options.setFileAndLine("eval", 1);
             options.setIntroductionType("eval");
         }
 
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr))
             return false;
 
-        const char16_t* chars = linearChars.twoByteRange().start().get();
+        const char16_t* chars = linearChars.twoByteRange().begin().get();
         SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
         SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
         JSScript* compiled = frontend::CompileEvalScript(cx, cx->tempLifoAlloc(),
                                                          env, enclosing,
                                                          options, srcBuf);
         if (!compiled)
@@ -384,17 +384,17 @@ js::DirectEvalStringFromIon(JSContext* c
             options.setFileAndLine("eval", 1);
             options.setIntroductionType("eval");
         }
 
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr))
             return false;
 
-        const char16_t* chars = linearChars.twoByteRange().start().get();
+        const char16_t* chars = linearChars.twoByteRange().begin().get();
         SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
         SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
         JSScript* compiled = frontend::CompileEvalScript(cx, cx->tempLifoAlloc(),
                                                          env, enclosing,
                                                          options, srcBuf);
         if (!compiled)
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -1140,18 +1140,18 @@ intl_CompareStrings(JSContext* cx, UColl
     AutoStableStringChars stableChars2(cx);
     if (!stableChars2.initTwoByte(cx, str2))
         return false;
 
     mozilla::Range<const char16_t> chars1 = stableChars1.twoByteRange();
     mozilla::Range<const char16_t> chars2 = stableChars2.twoByteRange();
 
     UCollationResult uresult = ucol_strcoll(coll,
-                                            Char16ToUChar(chars1.start().get()), chars1.length(),
-                                            Char16ToUChar(chars2.start().get()), chars2.length());
+                                            Char16ToUChar(chars1.begin().get()), chars1.length(),
+                                            Char16ToUChar(chars2.begin().get()), chars2.length());
     int32_t res;
     switch (uresult) {
         case UCOL_LESS: res = -1; break;
         case UCOL_EQUAL: res = 0; break;
         case UCOL_GREATER: res = 1; break;
         default: MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
     }
     result.setInt32(res);
@@ -1495,17 +1495,17 @@ NewUNumberFormat(JSContext* cx, HandleOb
         if (!GetProperty(cx, internals, internals, cx->names().currency, &value))
             return nullptr;
         currency = value.toString();
         MOZ_ASSERT(currency->length() == 3,
                    "IsWellFormedCurrencyCode permits only length-3 strings");
         if (!currency->ensureFlat(cx) || !stableChars.initTwoByte(cx, currency))
             return nullptr;
         // uCurrency remains owned by stableChars.
-        uCurrency = Char16ToUChar(stableChars.twoByteRange().start().get());
+        uCurrency = Char16ToUChar(stableChars.twoByteRange().begin().get());
         if (!uCurrency)
             return nullptr;
 
         if (!GetProperty(cx, internals, internals, cx->names().currencyDisplay, &value))
             return nullptr;
         JSAutoByteString currencyDisplay(cx, value.toString());
         if (!currencyDisplay)
             return nullptr;
@@ -2061,25 +2061,25 @@ js::intl_canonicalizeTimeZone(JSContext*
     mozilla::Range<const char16_t> tzchars = stableChars.twoByteRange();
 
     Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
     if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
         return false;
 
     UBool* isSystemID = nullptr;
     UErrorCode status = U_ZERO_ERROR;
-    int32_t size = ucal_getCanonicalTimeZoneID(Char16ToUChar(tzchars.start().get()),
+    int32_t size = ucal_getCanonicalTimeZoneID(Char16ToUChar(tzchars.begin().get()),
                                                tzchars.length(), Char16ToUChar(chars.begin()),
                                                INITIAL_CHAR_BUFFER_SIZE, isSystemID, &status);
     if (status == U_BUFFER_OVERFLOW_ERROR) {
         MOZ_ASSERT(size >= 0);
         if (!chars.resize(size_t(size)))
             return false;
         status = U_ZERO_ERROR;
-        ucal_getCanonicalTimeZoneID(Char16ToUChar(tzchars.start().get()), tzchars.length(),
+        ucal_getCanonicalTimeZoneID(Char16ToUChar(tzchars.begin().get()), tzchars.length(),
                                     Char16ToUChar(chars.begin()), size, isSystemID, &status);
     }
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
     MOZ_ASSERT(size >= 0);
@@ -2170,38 +2170,38 @@ js::intl_patternForSkeleton(JSContext* c
     if (!skeletonFlat)
         return false;
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, skeletonFlat))
         return false;
 
     mozilla::Range<const char16_t> skeletonChars = stableChars.twoByteRange();
-    uint32_t skeletonLen = u_strlen(Char16ToUChar(skeletonChars.start().get()));
+    uint32_t skeletonLen = u_strlen(Char16ToUChar(skeletonChars.begin().get()));
 
     UErrorCode status = U_ZERO_ERROR;
     UDateTimePatternGenerator* gen = udatpg_open(icuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
     ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
 
-    int32_t size = udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.start().get()),
+    int32_t size = udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.begin().get()),
                                          skeletonLen, nullptr, 0, &status);
     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
     ScopedJSFreePtr<UChar> pattern(cx->pod_malloc<UChar>(size + 1));
     if (!pattern)
         return false;
     pattern[size] = '\0';
     status = U_ZERO_ERROR;
-    udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.start().get()),
+    udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.begin().get()),
                           skeletonLen, pattern, size, &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
     RootedString str(cx, JS_NewUCStringCopyZ(cx, reinterpret_cast<char16_t*>(pattern.get())));
     if (!str)
@@ -2236,28 +2236,28 @@ NewUDateFormat(JSContext* cx, HandleObje
     if (!GetProperty(cx, internals, internals, cx->names().timeZone, &value))
         return nullptr;
 
     AutoStableStringChars timeZoneChars(cx);
     Rooted<JSFlatString*> timeZoneFlat(cx, value.toString()->ensureFlat(cx));
     if (!timeZoneFlat || !timeZoneChars.initTwoByte(cx, timeZoneFlat))
         return nullptr;
 
-    const UChar* uTimeZone = Char16ToUChar(timeZoneChars.twoByteRange().start().get());
+    const UChar* uTimeZone = Char16ToUChar(timeZoneChars.twoByteRange().begin().get());
     uint32_t uTimeZoneLength = u_strlen(uTimeZone);
 
     if (!GetProperty(cx, internals, internals, cx->names().pattern, &value))
         return nullptr;
 
     AutoStableStringChars patternChars(cx);
     Rooted<JSFlatString*> patternFlat(cx, value.toString()->ensureFlat(cx));
     if (!patternFlat || !patternChars.initTwoByte(cx, patternFlat))
         return nullptr;
 
-    const UChar* uPattern = Char16ToUChar(patternChars.twoByteRange().start().get());
+    const UChar* uPattern = Char16ToUChar(patternChars.twoByteRange().begin().get());
     uint32_t uPatternLength = u_strlen(uPattern);
 
     UErrorCode status = U_ZERO_ERROR;
     UDateFormat* df =
         udat_open(UDAT_PATTERN, UDAT_PATTERN, icuLocale(locale.ptr()), uTimeZone, uTimeZoneLength,
                   uPattern, uPatternLength, &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -127,17 +127,17 @@ obj_toSource(JSContext* cx, unsigned arg
 /*
  * Given a function source string, return the offset and length of the part
  * between '(function $name' and ')'.
  */
 template <typename CharT>
 static bool
 ArgsAndBodySubstring(mozilla::Range<const CharT> chars, size_t* outOffset, size_t* outLen)
 {
-    const CharT* const start = chars.start().get();
+    const CharT* const start = chars.begin().get();
     const CharT* const end = chars.end().get();
     const CharT* s = start;
 
     uint8_t parenChomp = 0;
     if (s[0] == '(') {
         s++;
         parenChomp = 1;
     }
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3690,17 +3690,17 @@ reflect_parse(JSContext* cx, uint32_t ar
 
     CompileOptions options(cx);
     options.setFileAndLine(filename, lineno);
     options.setCanLazilyParse(false);
     mozilla::Range<const char16_t> chars = linearChars.twoByteRange();
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
-    Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars.start().get(),
+    Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars.begin().get(),
                                     chars.length(), /* foldConstants = */ false, usedNames,
                                     nullptr, nullptr);
     if (!parser.checkOptions())
         return false;
 
     serialize.setParser(&parser);
 
     ParseNode* pn;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3098,17 +3098,17 @@ EvalReturningScope(JSContext* cx, unsign
     }
 
     AutoStableStringChars strChars(cx);
     if (!strChars.initTwoByte(cx, str))
         return false;
 
     mozilla::Range<const char16_t> chars = strChars.twoByteRange();
     size_t srclen = chars.length();
-    const char16_t* src = chars.start().get();
+    const char16_t* src = chars.begin().get();
 
     JS::AutoFilename filename;
     unsigned lineno;
 
     JS::DescribeScriptedCaller(cx, &filename, &lineno);
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(filename.get(), lineno);
@@ -3184,17 +3184,17 @@ ShellCloneAndExecuteScript(JSContext* cx
         return false;
 
     AutoStableStringChars strChars(cx);
     if (!strChars.initTwoByte(cx, str))
         return false;
 
     mozilla::Range<const char16_t> chars = strChars.twoByteRange();
     size_t srclen = chars.length();
-    const char16_t* src = chars.start().get();
+    const char16_t* src = chars.begin().get();
 
     JS::AutoFilename filename;
     unsigned lineno;
 
     JS::DescribeScriptedCaller(cx, &filename, &lineno);
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(filename.get(), lineno);
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1065,19 +1065,23 @@ RNaNToZero::RNaNToZero(CompactBufferRead
 { }
 
 
 bool
 RNaNToZero::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue v(cx, iter.read());
     RootedValue result(cx);
+    MOZ_ASSERT(v.isDouble() || v.isInt32());
 
-    MOZ_ASSERT(v.isDouble());
-    result.setDouble((mozilla::IsNaN(v.toDouble()) || mozilla::IsNegativeZero(v.toDouble())) ? 0.0 : v.toDouble());
+    // x ? x : 0.0
+    if (ToBoolean(v))
+        result = v;
+    else
+        result.setDouble(0.0);
 
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MRegExpMatcher::writeRecoverData(CompactBufferWriter& writer) const
 {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5131,17 +5131,17 @@ JS_CopyStringChars(JSContext* cx, mozill
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, str);
 
     JSLinearString* linear = str->ensureLinear(cx);
     if (!linear)
         return false;
 
     MOZ_ASSERT(linear->length() <= dest.length());
-    CopyChars(dest.start().get(), *linear);
+    CopyChars(dest.begin().get(), *linear);
     return true;
 }
 
 JS_PUBLIC_API(const Latin1Char*)
 JS_GetLatin1InternedStringChars(const JS::AutoCheckCannotGC& nogc, JSString* str)
 {
     MOZ_ASSERT(str->isAtom());
     JSFlatString* flat = str->ensureFlat(nullptr);
@@ -6345,17 +6345,17 @@ JS_PUBLIC_API(bool)
 JS_IndexToId(JSContext* cx, uint32_t index, MutableHandleId id)
 {
     return IndexToId(cx, index, id);
 }
 
 JS_PUBLIC_API(bool)
 JS_CharsToId(JSContext* cx, JS::TwoByteChars chars, MutableHandleId idp)
 {
-    RootedAtom atom(cx, AtomizeChars(cx, chars.start().get(), chars.length()));
+    RootedAtom atom(cx, AtomizeChars(cx, chars.begin().get(), chars.length()));
     if (!atom)
         return false;
 #ifdef DEBUG
     uint32_t dummy;
     MOZ_ASSERT(!atom->isIndex(&dummy), "API misuse: |chars| must not encode an index");
 #endif
     idp.set(AtomToId(atom));
     return true;
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -271,18 +271,18 @@ CopyStringPure(JSContext* cx, JSString* 
         if (copy)
             return copy;
 
         AutoStableStringChars chars(cx);
         if (!chars.init(cx, str))
             return nullptr;
 
         return chars.isLatin1()
-               ? NewStringCopyN<CanGC>(cx, chars.latin1Range().start().get(), len)
-               : NewStringCopyNDontDeflate<CanGC>(cx, chars.twoByteRange().start().get(), len);
+               ? NewStringCopyN<CanGC>(cx, chars.latin1Range().begin().get(), len)
+               : NewStringCopyNDontDeflate<CanGC>(cx, chars.twoByteRange().begin().get(), len);
     }
 
     if (str->hasLatin1Chars()) {
         ScopedJSFreePtr<Latin1Char> copiedChars;
         if (!str->asRope().copyLatin1CharsZ(cx, copiedChars))
             return nullptr;
 
         return NewString<CanGC>(cx, copiedChars.forget(), len);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -925,17 +925,17 @@ js::FindBody(JSContext* cx, HandleFuncti
 
     AutoKeepAtoms keepAtoms(cx->perThreadData);
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, src))
         return false;
 
     const mozilla::Range<const char16_t> srcChars = stableChars.twoByteRange();
-    TokenStream ts(cx, options, srcChars.start().get(), srcChars.length(), nullptr);
+    TokenStream ts(cx, options, srcChars.begin().get(), srcChars.length(), nullptr);
     int nest = 0;
     bool onward = true;
     // Skip arguments list.
     do {
         TokenKind tt;
         if (!ts.getToken(&tt))
             return false;
         switch (tt) {
@@ -970,17 +970,17 @@ js::FindBody(JSContext* cx, HandleFuncti
     mozilla::RangedPtr<const char16_t> end = srcChars.end();
     if (end[-1] == '}') {
         end--;
     } else {
         MOZ_ASSERT(!braced);
         for (; unicode::IsSpaceOrBOM2(end[-1]); end--)
             ;
     }
-    *bodyEnd = end - srcChars.start();
+    *bodyEnd = end - srcChars.begin();
     MOZ_ASSERT(*bodyStart <= *bodyEnd);
     return true;
 }
 
 JSString*
 js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen)
 {
     if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
@@ -1895,17 +1895,17 @@ FunctionConstructor(JSContext* cx, unsig
     if (hasRest)
         fun->setHasRest();
 
     mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
     SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
                                               ? SourceBufferHolder::GiveOwnership
                                               : SourceBufferHolder::NoOwnership;
     bool ok;
-    SourceBufferHolder srcBuf(chars.start().get(), chars.length(), ownership);
+    SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership);
     if (isAsync)
         ok = frontend::CompileAsyncFunctionBody(cx, &fun, options, formals, srcBuf);
     else if (isStarGenerator)
         ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, srcBuf);
     else
         ok = frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf);
     args.rval().setObject(*fun);
     return ok;
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -183,17 +183,17 @@ ComputeAccurateBinaryBaseInteger(const C
 }
 
 template <typename CharT>
 double
 js::ParseDecimalNumber(const mozilla::Range<const CharT> chars)
 {
     MOZ_ASSERT(chars.length() > 0);
     uint64_t dec = 0;
-    RangedPtr<const CharT> s = chars.start(), end = chars.end();
+    RangedPtr<const CharT> s = chars.begin(), end = chars.end();
     do {
         CharT c = *s;
         MOZ_ASSERT('0' <= c && c <= '9');
         uint8_t digit = c - '0';
         uint64_t next = dec * 10 + digit;
         MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
                    "next value won't be an integrally-precise double");
         dec = next;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -271,34 +271,34 @@ Unescape(StringBuffer& sb, const mozilla
             goto step_14;
 
 #define ENSURE_BUILDING                                      \
         do {                                                 \
             if (!building) {                                 \
                 building = true;                             \
                 if (!sb.reserve(length))                     \
                     return false;                            \
-                sb.infallibleAppend(chars.start().get(), k); \
+                sb.infallibleAppend(chars.begin().get(), k); \
             }                                                \
         } while(false);
 
         /* Step 10-13. */
-        if (Unhex4(chars.start() + k + 2, &c)) {
+        if (Unhex4(chars.begin() + k + 2, &c)) {
             ENSURE_BUILDING;
             k += 5;
             goto step_18;
         }
 
       step_14:
         /* Step 14. */
         if (k > length - 3)
             goto step_18;
 
         /* Step 15-17. */
-        if (Unhex2(chars.start() + k + 1, &c)) {
+        if (Unhex2(chars.begin() + k + 1, &c)) {
             ENSURE_BUILDING;
             k += 2;
         }
 
       step_18:
         if (building && !sb.append(c))
             return false;
 
@@ -969,17 +969,17 @@ js::str_normalize(JSContext* cx, unsigne
 
     // Step 8.
     AutoStableStringChars stableChars(cx);
     if (!str->ensureFlat(cx) || !stableChars.initTwoByte(cx, str))
         return false;
 
     static const size_t INLINE_CAPACITY = 32;
 
-    const UChar* srcChars = Char16ToUChar(stableChars.twoByteRange().start().get());
+    const UChar* srcChars = Char16ToUChar(stableChars.twoByteRange().begin().get());
     int32_t srcLen = AssertedCast<int32_t>(str->length());
     Vector<char16_t, INLINE_CAPACITY> chars(cx);
     if (!chars.resize(INLINE_CAPACITY))
         return false;
 
     UErrorCode status = U_ZERO_ERROR;
     int32_t size = unorm_normalize(srcChars, srcLen, form, 0,
                                    Char16ToUChar(chars.begin()), INLINE_CAPACITY,
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1702,46 +1702,46 @@ Evaluate(JSContext* cx, unsigned argc, V
             }
 
             if (loadBytecode) {
                 JS::TranscodeResult rv = JS::DecodeScript(cx, loadBuffer, &script);
                 if (!ConvertTranscodeResultToJSException(cx, rv))
                     return false;
             } else {
                 mozilla::Range<const char16_t> chars = codeChars.twoByteRange();
-                (void) JS::Compile(cx, options, chars.start().get(), chars.length(), &script);
+                (void) JS::Compile(cx, options, chars.begin().get(), chars.length(), &script);
             }
 
             if (!script)
                 return false;
         }
 
         if (displayURL && !script->scriptSource()->hasDisplayURL()) {
             JSFlatString* flat = displayURL->ensureFlat(cx);
             if (!flat)
                 return false;
 
             AutoStableStringChars chars(cx);
             if (!chars.initTwoByte(cx, flat))
                 return false;
 
-            const char16_t* durl = chars.twoByteRange().start().get();
+            const char16_t* durl = chars.twoByteRange().begin().get();
             if (!script->scriptSource()->setDisplayURL(cx, durl))
                 return false;
         }
         if (sourceMapURL && !script->scriptSource()->hasSourceMapURL()) {
             JSFlatString* flat = sourceMapURL->ensureFlat(cx);
             if (!flat)
                 return false;
 
             AutoStableStringChars chars(cx);
             if (!chars.initTwoByte(cx, flat))
                 return false;
 
-            const char16_t* smurl = chars.twoByteRange().start().get();
+            const char16_t* smurl = chars.twoByteRange().begin().get();
             if (!script->scriptSource()->setSourceMapURL(cx, smurl))
                 return false;
         }
         if (!JS_ExecuteScript(cx, script, args.rval())) {
             if (catchTermination && !JS_IsExceptionPending(cx)) {
                 JSAutoCompartment ac1(cx, callerGlobal);
                 JSString* str = JS_NewStringCopyZ(cx, "terminated");
                 if (!str)
@@ -1883,17 +1883,17 @@ Run(JSContext* cx, unsigned argc, Value*
     str = FileAsString(cx, str);
     if (!str)
         return false;
 
     AutoStableStringChars chars(cx);
     if (!chars.initTwoByte(cx, str))
         return false;
 
-    const char16_t* ucbuf = chars.twoByteRange().start().get();
+    const char16_t* ucbuf = chars.twoByteRange().begin().get();
     size_t buflen = str->length();
 
     RootedScript script(cx);
     int64_t startClock = PRMJ_Now();
     {
         /* FIXME: This should use UTF-8 (bug 987069). */
         JSAutoByteString filename(cx, str);
         if (!filename)
@@ -2271,17 +2271,17 @@ ValueToScript(JSContext* cx, HandleValue
         // To convert a string to a script, compile it. Parse it as an ES6 Program.
         RootedLinearString linearStr(cx, StringToLinearString(cx, v.toString()));
         if (!linearStr)
             return nullptr;
         size_t len = GetLinearStringLength(linearStr);
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr))
             return nullptr;
-        const char16_t* chars = linearChars.twoByteRange().start().get();
+        const char16_t* chars = linearChars.twoByteRange().begin().get();
 
         RootedScript script(cx);
         CompileOptions options(cx);
         if (!JS::Compile(cx, options, chars, len, &script))
             return nullptr;
         return script;
     }
 
@@ -3005,17 +3005,17 @@ Intern(JSContext* cx, unsigned argc, Val
         return false;
 
     AutoStableStringChars strChars(cx);
     if (!strChars.initTwoByte(cx, str))
         return false;
 
     mozilla::Range<const char16_t> chars = strChars.twoByteRange();
 
-    if (!JS_AtomizeAndPinUCStringN(cx, chars.start().get(), chars.length()))
+    if (!JS_AtomizeAndPinUCStringN(cx, chars.begin().get(), chars.length()))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 Clone(JSContext* cx, unsigned argc, Value* vp)
@@ -3207,17 +3207,17 @@ EvalInContext(JSContext* cx, unsigned ar
     }
 
     AutoStableStringChars strChars(cx);
     if (!strChars.initTwoByte(cx, str))
         return false;
 
     mozilla::Range<const char16_t> chars = strChars.twoByteRange();
     size_t srclen = chars.length();
-    const char16_t* src = chars.start().get();
+    const char16_t* src = chars.begin().get();
 
     bool lazy = false;
     if (srclen == 4) {
         if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
             lazy = true;
             srclen = 0;
         }
     }
@@ -3927,17 +3927,17 @@ Compile(JSContext* cx, unsigned argc, Va
         return false;
 
     JS::CompileOptions options(cx);
     options.setIntroductionType("js shell compile")
            .setFileAndLine("<string>", 1)
            .setIsRunOnce(true)
            .setNoScriptRval(true);
     RootedScript script(cx);
-    const char16_t* chars = stableChars.twoByteRange().start().get();
+    const char16_t* chars = stableChars.twoByteRange().begin().get();
     bool ok = JS_CompileUCScript(cx, chars, scriptContents->length(), options, &script);
     args.rval().setUndefined();
     return ok;
 }
 
 static bool
 ParseModule(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -3976,17 +3976,17 @@ ParseModule(JSContext* cx, unsigned argc
     } else {
         options.setFileAndLine("<string>", 1);
     }
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, scriptContents))
         return false;
 
-    const char16_t* chars = stableChars.twoByteRange().start().get();
+    const char16_t* chars = stableChars.twoByteRange().begin().get();
     SourceBufferHolder srcBuf(chars, scriptContents->length(),
                               SourceBufferHolder::NoOwnership);
 
     RootedObject module(cx, frontend::CompileModule(cx, options, srcBuf));
     if (!module)
         return false;
 
     args.rval().setObject(*module);
@@ -4046,17 +4046,17 @@ Parse(JSContext* cx, unsigned argc, Valu
     if (!scriptContents)
         return false;
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, scriptContents))
         return false;
 
     size_t length = scriptContents->length();
-    const char16_t* chars = stableChars.twoByteRange().start().get();
+    const char16_t* chars = stableChars.twoByteRange().begin().get();
 
     CompileOptions options(cx);
     options.setIntroductionType("js shell parse")
            .setFileAndLine("<string>", 1);
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
     Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
@@ -4099,17 +4099,17 @@ SyntaxParse(JSContext* cx, unsigned argc
     CompileOptions options(cx);
     options.setIntroductionType("js shell syntaxParse")
            .setFileAndLine("<string>", 1);
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, scriptContents))
         return false;
 
-    const char16_t* chars = stableChars.twoByteRange().start().get();
+    const char16_t* chars = stableChars.twoByteRange().begin().get();
     size_t length = scriptContents->length();
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
     Parser<frontend::SyntaxParseHandler> parser(cx, cx->tempLifoAlloc(),
                                                 options, chars, length, false,
                                                 usedNames, nullptr, nullptr);
     if (!parser.checkOptions())
@@ -4184,17 +4184,17 @@ OffThreadCompileScript(JSContext* cx, un
     options.forceAsync = true;
 
     JSString* scriptContents = args[0].toString();
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, scriptContents))
         return false;
 
     size_t length = scriptContents->length();
-    const char16_t* chars = stableChars.twoByteRange().start().get();
+    const char16_t* chars = stableChars.twoByteRange().begin().get();
 
     // Make sure we own the string's chars, so that they are not freed before
     // the compilation is finished.
     ScopedJSFreePtr<char16_t> ownedChars;
     if (stableChars.maybeGiveOwnershipToCaller()) {
         ownedChars = const_cast<char16_t*>(chars);
     } else {
         char16_t* copy = cx->pod_malloc<char16_t>(length);
@@ -4271,17 +4271,17 @@ OffThreadCompileModule(JSContext* cx, un
     options.forceAsync = true;
 
     JSString* scriptContents = args[0].toString();
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, scriptContents))
         return false;
 
     size_t length = scriptContents->length();
-    const char16_t* chars = stableChars.twoByteRange().start().get();
+    const char16_t* chars = stableChars.twoByteRange().begin().get();
 
     // Make sure we own the string's chars, so that they are not freed before
     // the compilation is finished.
     ScopedJSFreePtr<char16_t> ownedChars;
     if (stableChars.maybeGiveOwnershipToCaller()) {
         ownedChars = const_cast<char16_t*>(chars);
     } else {
         char16_t* copy = cx->pod_malloc<char16_t>(length);
@@ -5554,17 +5554,17 @@ EntryPoints(JSContext* cx, unsigned argc
         if (!code.isUndefined()) {
             RootedString codeString(cx, ToString(cx, code));
             if (!codeString || !codeString->ensureFlat(cx))
                 return false;
 
             AutoStableStringChars stableChars(cx);
             if (!stableChars.initTwoByte(cx, codeString))
                 return false;
-            const char16_t* chars = stableChars.twoByteRange().start().get();
+            const char16_t* chars = stableChars.twoByteRange().begin().get();
             size_t length = codeString->length();
 
             CompileOptions options(cx);
             options.setIntroductionType("entryPoint eval")
                    .setFileAndLine("entryPoint eval", 1);
 
             js::shell::ShellAutoEntryMonitor sarep(cx);
             if (!JS::Evaluate(cx, options, chars, length, &dummy))
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -151,17 +151,17 @@ JS::DeflateStringToUTF8Buffer(JSFlatStri
                                          dstlenp, numcharsp);
 }
 
 template <typename CharT>
 UTF8CharsZ
 JS::CharsToNewUTF8CharsZ(js::ExclusiveContext* maybeCx, const mozilla::Range<CharT> chars)
 {
     /* Get required buffer size. */
-    const CharT* str = chars.start().get();
+    const CharT* str = chars.begin().get();
     size_t len = ::GetDeflatedUTF8StringLength(str, chars.length());
 
     /* Allocate buffer. */
     char* utf8;
     if (maybeCx)
         utf8 = maybeCx->pod_malloc<char>(len + 1);
     else
         utf8 = js_pod_malloc<char>(len + 1);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -7450,17 +7450,17 @@ EvaluateInEnv(JSContext* cx, Handle<Env*
     CompileOptions options(cx);
     options.setIsRunOnce(true)
            .setNoScriptRval(false)
            .setFileAndLine(filename, lineno)
            .setCanLazilyParse(false)
            .setIntroductionType("debugger eval")
            .maybeMakeStrictMode(frame ? frame.script()->strict() : false);
     RootedScript callerScript(cx, frame ? frame.script() : nullptr);
-    SourceBufferHolder srcBuf(chars.start().get(), chars.length(), SourceBufferHolder::NoOwnership);
+    SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), SourceBufferHolder::NoOwnership);
     RootedScript script(cx);
 
     ScopeKind scopeKind;
     if (IsGlobalLexicalEnvironment(env))
         scopeKind = ScopeKind::Global;
     else
         scopeKind = ScopeKind::NonSyntactic;
 
--- a/js/src/vm/JSONParser.h
+++ b/js/src/vm/JSONParser.h
@@ -200,17 +200,17 @@ class MOZ_STACK_CLASS JSONParser : publi
 
   public:
     /* Public API */
 
     /* Create a parser for the provided JSON data. */
     JSONParser(JSContext* cx, mozilla::Range<const CharT> data,
                ErrorHandling errorHandling = RaiseError)
       : JSONParserBase(cx, errorHandling),
-        current(data.start()),
+        current(data.begin()),
         begin(current),
         end(data.end())
     {
         MOZ_ASSERT(current <= end);
     }
 
     /* Allow move construction for use with Rooted. */
     JSONParser(JSONParser&& other)
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -1131,20 +1131,20 @@ RegExpShared::execute(JSContext* cx, Han
     AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
 
     AutoStableStringChars inputChars(cx);
     if (!inputChars.init(cx, input))
         return RegExpRunStatus_Error;
 
     RegExpRunStatus result;
     if (inputChars.isLatin1()) {
-        const Latin1Char* chars = inputChars.latin1Range().start().get();
+        const Latin1Char* chars = inputChars.latin1Range().begin().get();
         result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
     } else {
-        const char16_t* chars = inputChars.twoByteRange().start().get();
+        const char16_t* chars = inputChars.twoByteRange().begin().get();
         result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
     }
 
     if (result == RegExpRunStatus_Success && matches)
         matches->checkAgainst(length);
     return result;
 }
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -576,17 +576,17 @@ InvokeInterruptCallback(JSContext* cx)
     // No need to set aside any pending exception here: ComputeStackString
     // already does that.
     JSString* stack = ComputeStackString(cx);
     JSFlatString* flat = stack ? stack->ensureFlat(cx) : nullptr;
 
     const char16_t* chars;
     AutoStableStringChars stableChars(cx);
     if (flat && stableChars.initTwoByte(cx, flat))
-        chars = stableChars.twoByteRange().start().get();
+        chars = stableChars.twoByteRange().begin().get();
     else
         chars = u"(stack not available)";
     JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
                                    JSMSG_TERMINATED, chars);
 
     return false;
 }
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2849,18 +2849,18 @@ CloneString(JSContext* cx, JSFlatString*
             return clone;
     }
 
     AutoStableStringChars chars(cx);
     if (!chars.init(cx, selfHostedString))
         return nullptr;
 
     return chars.isLatin1()
-           ? NewStringCopyN<CanGC>(cx, chars.latin1Range().start().get(), len)
-           : NewStringCopyNDontDeflate<CanGC>(cx, chars.twoByteRange().start().get(), len);
+           ? NewStringCopyN<CanGC>(cx, chars.latin1Range().begin().get(), len)
+           : NewStringCopyNDontDeflate<CanGC>(cx, chars.twoByteRange().begin().get(), len);
 }
 
 static JSObject*
 CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
 {
 #ifdef DEBUG
     // Object hash identities are owned by the hashed object, which may be on a
     // different thread than the clone target. In theory, these objects are all
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -52,17 +52,17 @@ NewInlineString(ExclusiveContext* cx, mo
      */
 
     size_t len = chars.length();
     CharT* storage;
     JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage);
     if (!str)
         return nullptr;
 
-    mozilla::PodCopy(storage, chars.start().get(), len);
+    mozilla::PodCopy(storage, chars.begin().get(), len);
     storage[len] = 0;
     return str;
 }
 
 // Create a thin inline string if possible, and a fat inline string if not.
 template <typename CharT>
 static MOZ_ALWAYS_INLINE JSInlineString*
 NewInlineString(ExclusiveContext* cx, HandleLinearString base, size_t start, size_t length)
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -1321,17 +1321,17 @@ NewStringCopyN<NoGC>(ExclusiveContext* c
 
 
 template <js::AllowGC allowGC>
 JSFlatString*
 NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8)
 {
     JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
     if (encoding == JS::SmallestEncoding::ASCII)
-        return NewStringCopyN<allowGC>(cx, utf8.start().get(), utf8.length());
+        return NewStringCopyN<allowGC>(cx, utf8.begin().get(), utf8.length());
 
     size_t length;
     if (encoding == JS::SmallestEncoding::Latin1) {
         Latin1Char* latin1 = UTF8CharsToNewLatin1CharsZ(cx, utf8, &length).get();
         if (!latin1)
             return nullptr;
 
         JSFlatString* result = NewString<allowGC>(cx, latin1, length);
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -8097,17 +8097,17 @@ HandleInstantiationFailure(JSContext* cx
     // also inherited it somehow.
     if (metadata.strict)
         options.strictOption = true;
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, src))
         return false;
 
-    const char16_t* chars = stableChars.twoByteRange().start().get();
+    const char16_t* chars = stableChars.twoByteRange().begin().get();
     SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
                                               ? SourceBufferHolder::GiveOwnership
                                               : SourceBufferHolder::NoOwnership;
     SourceBufferHolder srcBuf(chars, end - begin, ownership);
     if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
         return false;
 
     // Call the function we just recompiled.
--- a/media/libcubeb/README.md
+++ b/media/libcubeb/README.md
@@ -1,5 +1,6 @@
 [![Build Status](https://travis-ci.org/kinetiknz/cubeb.svg?branch=master)](https://travis-ci.org/kinetiknz/cubeb)
+[![Build status](https://ci.appveyor.com/api/projects/status/osv2r0m1j1nt9csr/branch/master?svg=true)](https://ci.appveyor.com/project/kinetiknz/cubeb/branch/master)
 
 See INSTALL.md for build instructions.
 
 Licensed under an ISC-style license.  See LICENSE for details.
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was 352c0bed012a770ef65aee1e507704922ea80b0e.
+The git commit ID used was 98b189d2aad06bef758a5ae76fb758fa4dfa9bba.
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -379,28 +379,124 @@ int cubeb_stream_register_device_changed
 
   if (!stream->context->ops->stream_register_device_changed_callback) {
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
 
   return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback);
 }
 
+static
+void log_device(cubeb_device_info * device_info)
+{
+  char devfmts[128] = "";
+  const char * devtype, * devstate, * devdeffmt;
+
+  switch (device_info->type) {
+    case CUBEB_DEVICE_TYPE_INPUT:
+      devtype = "input";
+      break;
+    case CUBEB_DEVICE_TYPE_OUTPUT:
+      devtype = "output";
+      break;
+    case CUBEB_DEVICE_TYPE_UNKNOWN:
+    default:
+      devtype = "unknown?";
+      break;
+  };
+
+  switch (device_info->state) {
+    case CUBEB_DEVICE_STATE_DISABLED:
+      devstate = "disabled";
+      break;
+    case CUBEB_DEVICE_STATE_UNPLUGGED:
+      devstate = "unplugged";
+      break;
+    case CUBEB_DEVICE_STATE_ENABLED:
+      devstate = "enabled";
+      break;
+    default:
+      devstate = "unknown?";
+      break;
+  };
+
+  switch (device_info->default_format) {
+    case CUBEB_DEVICE_FMT_S16LE:
+      devdeffmt = "S16LE";
+      break;
+    case CUBEB_DEVICE_FMT_S16BE:
+      devdeffmt = "S16BE";
+      break;
+    case CUBEB_DEVICE_FMT_F32LE:
+      devdeffmt = "F32LE";
+      break;
+    case CUBEB_DEVICE_FMT_F32BE:
+      devdeffmt = "F32BE";
+      break;
+    default:
+      devdeffmt = "unknown?";
+      break;
+  };
+
+  if (device_info->format & CUBEB_DEVICE_FMT_S16LE) {
+    strcat(devfmts, " S16LE");
+  }
+  if (device_info->format & CUBEB_DEVICE_FMT_S16BE) {
+    strcat(devfmts, " S16BE");
+  }
+  if (device_info->format & CUBEB_DEVICE_FMT_F32LE) {
+    strcat(devfmts, " F32LE");
+  }
+  if (device_info->format & CUBEB_DEVICE_FMT_F32BE) {
+    strcat(devfmts, " F32BE");
+  }
+
+  LOG("DeviceID: \"%s\"%s\n"
+      "\tName:\t\"%s\"\n"
+      "\tGroup:\t\"%s\"\n"
+      "\tVendor:\t\"%s\"\n"
+      "\tType:\t%s\n"
+      "\tState:\t%s\n"
+      "\tMaximum channels:\t%u\n"
+      "\tFormat:\t%s (0x%x) (default: %s)\n"
+      "\tRate:\t[%u, %u] (default: %u)\n"
+      "\tLatency: lo %u frames, hi %u frames",
+      device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
+      device_info->friendly_name,
+      device_info->group_id,
+      device_info->vendor_name,
+      devtype,
+      devstate,
+      device_info->max_channels,
+      (devfmts[0] == '\0') ? devfmts : devfmts + 1, (unsigned int)device_info->format, devdeffmt,
+      device_info->min_rate, device_info->max_rate, device_info->default_rate,
+      device_info->latency_lo, device_info->latency_hi);
+}
+
 int cubeb_enumerate_devices(cubeb * context,
                             cubeb_device_type devtype,
                             cubeb_device_collection ** collection)
 {
+  int rv;
   if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
     return CUBEB_ERROR_INVALID_PARAMETER;
   if (collection == NULL)
     return CUBEB_ERROR_INVALID_PARAMETER;
   if (!context->ops->enumerate_devices)
     return CUBEB_ERROR_NOT_SUPPORTED;
 
-  return context->ops->enumerate_devices(context, devtype, collection);
+  rv = context->ops->enumerate_devices(context, devtype, collection);
+
+  if (g_log_callback) {
+    for (uint32_t i = 0; i < (*collection)->count; i++) {
+      log_device((*collection)->device[i]);
+    }
+  }
+
+  return rv;
 }
 
 int cubeb_device_collection_destroy(cubeb_device_collection * collection)
 {
   uint32_t i;
 
   if (collection == NULL)
     return CUBEB_ERROR_INVALID_PARAMETER;
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -289,18 +289,18 @@ audiounit_render_input(cubeb_stream * st
     audiounit_make_silent(&input_buffer_list.mBuffers[0]);
     return r;
   }
 
   /* Copy input data in linear buffer. */
   stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
                                  input_frames * stm->input_desc.mChannelsPerFrame);
 
-  LOGV("input:  buffers %d, size %d, channels %d, frames %d.",
-       input_buffer_list.mNumberBuffers,
+  LOGV("(%p) input:  buffers %d, size %d, channels %d, frames %d.",
+       stm, input_buffer_list.mNumberBuffers,
        input_buffer_list.mBuffers[0].mDataByteSize,
        input_buffer_list.mBuffers[0].mNumberChannels,
        input_frames);
 
   /* Advance input frame counter. */
   assert(input_frames > 0);
   stm->frames_read += input_frames;
 
@@ -308,26 +308,26 @@ audiounit_render_input(cubeb_stream * st
 }
 
 static OSStatus
 audiounit_input_callback(void * user_ptr,
                          AudioUnitRenderActionFlags * flags,
                          AudioTimeStamp const * tstamp,
                          UInt32 bus,
                          UInt32 input_frames,
-                         AudioBufferList * bufs)
+                         AudioBufferList * /* bufs */)
 {
   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
-  long outframes, frames;
+  long outframes;
 
   assert(stm->input_unit != NULL);
   assert(AU_IN_BUS == bus);
 
   if (stm->shutdown) {
-    LOG("input shutdown");
+    LOG("(%p) input shutdown", stm);
     return noErr;
   }
 
   OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
   if (r != noErr) {
     return r;
   }
 
@@ -365,38 +365,39 @@ audiounit_input_callback(void * user_ptr
     return noErr;
   }
 
   return noErr;
 }
 
 static OSStatus
 audiounit_output_callback(void * user_ptr,
-                          AudioUnitRenderActionFlags * flags,
+                          AudioUnitRenderActionFlags * /* flags */,
                           AudioTimeStamp const * tstamp,
                           UInt32 bus,
                           UInt32 output_frames,
                           AudioBufferList * outBufferList)
 {
   assert(AU_OUT_BUS == bus);
   assert(outBufferList->mNumberBuffers == 1);
 
   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
 
   stm->output_callback_in_a_row++;
 
-  LOGV("output(%p): buffers %d, size %d, channels %d, frames %d.", stm,
-       outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
+  LOGV("(%p) output: buffers %d, size %d, channels %d, frames %d.",
+       stm, outBufferList->mNumberBuffers,
+       outBufferList->mBuffers[0].mDataByteSize,
        outBufferList->mBuffers[0].mNumberChannels, output_frames);
 
   long outframes = 0, input_frames = 0;
   void * output_buffer = NULL, * input_buffer = NULL;
 
   if (stm->shutdown) {
-    LOG("output shutdown.");
+    LOG("(%p) output shutdown.", stm);
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
 
   stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
   if (stm->draining) {
     OSStatus r = AudioOutputUnitStop(stm->output_unit);
     assert(r == 0);
@@ -415,17 +416,17 @@ audiounit_output_callback(void * user_pt
     /* If the output callback came first and this is a duplex stream, we need to
      * fill in some additional silence in the resampler.
      * Otherwise, if we had more than two callback in a row, or we're currently
      * switching, we add some silence as well to compensate for the fact that
      * we're lacking some input data. */
     if (stm->frames_read == 0 ||
         (stm->input_linear_buffer->length() == 0 &&
         (stm->output_callback_in_a_row > 2 || stm->switching_device))) {
-      LOG("Output callback came first send silent.");
+      LOG("(%p) Output callback came first send silent.", stm);
       stm->input_linear_buffer->push_silence(stm->input_buffer_frames *
                                              stm->input_desc.mChannelsPerFrame);
     }
     // The input buffer
     input_buffer = stm->input_linear_buffer->data();
     // Number of input frames in the buffer
     input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
   }
@@ -468,17 +469,17 @@ audiounit_output_callback(void * user_pt
       cubeb_pan_stereo_buffer_int((short*)output_buffer, outframes, panning);
     }
   }
   return noErr;
 }
 
 extern "C" {
 int
-audiounit_init(cubeb ** context, char const * context_name)
+audiounit_init(cubeb ** context, char const * /* context_name */)
 {
   cubeb * ctx;
 
   *context = NULL;
 
   ctx = (cubeb *)calloc(1, sizeof(cubeb));
   assert(ctx);
   // Placement new to call the ctors of cubeb members.
@@ -495,17 +496,17 @@ audiounit_init(cubeb ** context, char co
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 }
 
 static char const *
-audiounit_get_backend_id(cubeb * ctx)
+audiounit_get_backend_id(cubeb * /* ctx */)
 {
   return "audiounit";
 }
 
 #if !TARGET_OS_IPHONE
 static int
 audiounit_get_output_device_id(AudioDeviceID * device_id)
 {
@@ -555,41 +556,41 @@ audiounit_get_input_device_id(AudioDevic
   if (r != noErr) {
     return CUBEB_ERROR;
   }
 
   return CUBEB_OK;
 }
 
 static OSStatus
-audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
+audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_count,
                                      const AudioObjectPropertyAddress * addresses,
                                      void * user)
 {
   cubeb_stream * stm = (cubeb_stream*) user;
   int rv;
   bool was_running = false;
 
   stm->switching_device = true;
 
   // Note if the stream was running or not
   was_running = !stm->shutdown;
 
-  LOG("Audio device changed, %d events.", address_count);
+  LOG("(%p) Audio device changed, %d events.", stm, address_count);
   if (g_log_level) {
     for (UInt32 i = 0; i < address_count; i++) {
       switch(addresses[i].mSelector) {
         case kAudioHardwarePropertyDefaultOutputDevice:
-          LOG("%d mSelector == kAudioHardwarePropertyDefaultOutputDevice", i);
+          LOG("(%p) %d mSelector == kAudioHardwarePropertyDefaultOutputDevice", stm, i);
           break;
         case kAudioHardwarePropertyDefaultInputDevice:
-          LOG("%d mSelector == kAudioHardwarePropertyDefaultInputDevice", i);
+          LOG("(%p) %d mSelector == kAudioHardwarePropertyDefaultInputDevice", stm, i);
           break;
         case kAudioDevicePropertyDataSource:
-          LOG("%d mSelector == kAudioHardwarePropertyDataSource", i);
+          LOG("(%p) %d mSelector == kAudioHardwarePropertyDataSource", stm, i);
           break;
       }
     }
   }
 
   for (UInt32 i = 0; i < address_count; i++) {
     switch(addresses[i].mSelector) {
     case kAudioHardwarePropertyDefaultOutputDevice:
@@ -608,17 +609,17 @@ audiounit_property_listener_callback(Aud
   // This means the callback won't be called again.
   audiounit_stream_stop_internal(stm);
 
   {
     auto_lock lock(stm->mutex);
     close_audiounit_stream(stm);
     rv = setup_audiounit_stream(stm);
     if (rv != CUBEB_OK) {
-      LOG("Could not reopen a stream after switching.");
+      LOG("(%p) Could not reopen a stream after switching.", stm);
       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
       return noErr;
     }
 
     stm->frames_read = 0;
 
     // If the stream was running, start it again.
     if (was_running) {
@@ -859,17 +860,19 @@ audiounit_get_max_channel_count(cubeb * 
   }
 
   *max_channels = stream_format.mChannelsPerFrame;
 #endif
   return CUBEB_OK;
 }
 
 static int
-audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
+audiounit_get_min_latency(cubeb * /* ctx */,
+                          cubeb_stream_params /* params */,
+                          uint32_t * latency_frames)
 {
 #if TARGET_OS_IPHONE
   //TODO: [[AVAudioSession sharedInstance] inputLatency]
   return CUBEB_ERROR_NOT_SUPPORTED;
 #else
   AudioValueRange latency_range;
   if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
     LOG("Could not get acceptable latency range.");
@@ -879,17 +882,17 @@ audiounit_get_min_latency(cubeb * ctx, c
   *latency_frames = std::max<uint32_t>(latency_range.mMinimum,
                                        SAFE_MIN_LATENCY_FRAMES);
 #endif
 
   return CUBEB_OK;
 }
 
 static int
-audiounit_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate)
 {
 #if TARGET_OS_IPHONE
   //TODO
   return CUBEB_ERROR_NOT_SUPPORTED;
 #else
   UInt32 size;
   OSStatus r;
   Float64 fsamplerate;
@@ -980,17 +983,17 @@ audio_stream_desc_init(AudioStreamBasicD
   ss->mReserved = 0;
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_create_unit(AudioUnit * unit,
                       bool is_input,
-                      const cubeb_stream_params * stream_params,
+                      const cubeb_stream_params * /* stream_params */,
                       cubeb_devid device)
 {
   AudioComponentDescription desc;
   AudioComponent comp;
   UInt32 enable;
   AudioDeviceID devid;
   OSStatus rv;
 
@@ -1023,55 +1026,55 @@ audiounit_create_unit(AudioUnit * unit,
   if (rv != noErr) {
     PRINT_ERROR_CODE("AudioComponentInstanceNew", rv);
     return CUBEB_ERROR;
   }
 
   if (!use_default_output) {
     enable = 1;
     rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
-			      is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output,
-			      is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32));
+                              is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output,
+                              is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32));
     if (rv != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO", rv);
       return CUBEB_ERROR;
     }
 
     enable = 0;
     rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
-			      is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input,
-			      is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32));
+                              is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input,
+                              is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32));
     if (rv != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO", rv);
       return CUBEB_ERROR;
     }
 
     if (device == NULL) {
       assert(is_input);
       devid = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT);
     } else {
       devid = reinterpret_cast<intptr_t>(device);
     }
     int err = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice,
-				   kAudioUnitScope_Global,
-				   is_input ? AU_IN_BUS : AU_OUT_BUS,
-				   &devid, sizeof(AudioDeviceID));
+                                   kAudioUnitScope_Global,
+                                   is_input ? AU_IN_BUS : AU_OUT_BUS,
+                                   &devid, sizeof(AudioDeviceID));
     if (err != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice", rv);
       return CUBEB_ERROR;
     }
   }
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
 {
-  if (stream->input_desc.mFormatFlags == kAudioFormatFlagIsSignedInteger) {
+  if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) {
     stream->input_linear_buffer = new auto_array_wrapper(
         new auto_array<short>(capacity *
                               stream->input_buffer_frames *
                               stream->input_desc.mChannelsPerFrame) );
   } else {
     stream->input_linear_buffer = new auto_array_wrapper(
         new auto_array<float>(capacity *
                               stream->input_buffer_frames *
@@ -1167,39 +1170,39 @@ audiounit_clamp_latency(cubeb_stream * s
                   SAFE_MIN_LATENCY_FRAMES);
 }
 
 static int
 setup_audiounit_stream(cubeb_stream * stm)
 {
   stm->mutex.assert_current_thread_owns();
 
-  AudioUnit input_unit;
-  AudioUnit output_unit;
   int r;
   AURenderCallbackStruct aurcbs_in;
   AURenderCallbackStruct aurcbs_out;
   UInt32 size;
 
   if (has_input(stm)) {
+    LOG("(%p) Opening input side, rate: %u", stm, stm->input_stream_params.rate);
     r = audiounit_create_unit(&stm->input_unit, true,
                               &stm->input_stream_params,
                               stm->input_device);
     if (r != CUBEB_OK) {
-      LOG("AudioUnit creation for input failed.");
+      LOG("(%p) AudioUnit creation for input failed.", stm);
       return r;
     }
   }
 
   if (has_output(stm)) {
+    LOG("(%p) Opening output side, rate: %u", stm, stm->input_stream_params.rate);
     r = audiounit_create_unit(&stm->output_unit, false,
                               &stm->output_stream_params,
                               stm->output_device);
     if (r != CUBEB_OK) {
-      LOG("AudioUnit creation for output failed.");
+      LOG("(%p) AudioUnit creation for output failed.", stm);
       return r;
     }
   }
 
   /* Setup Input Stream! */
   if (has_input(stm)) {
     /* Get input device sample rate. */
     AudioStreamBasicDescription input_hw_desc;
@@ -1214,23 +1217,23 @@ setup_audiounit_stream(cubeb_stream * st
       PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
       return CUBEB_ERROR;
     }
     stm->input_hw_rate = input_hw_desc.mSampleRate;
 
     /* Set format description according to the input params. */
     r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
     if (r != CUBEB_OK) {
-      LOG("Setting format description for input failed.");
+      LOG("(%p) Setting format description for input failed.", stm);
       return r;
     }
 
     // Use latency to set buffer size
     stm->input_buffer_frames = stm->latency_frames;
-    LOG("Input buffer frame count %u.", unsigned(stm->input_buffer_frames));
+    LOG("(%p) Input buffer frame count %u.", stm, unsigned(stm->input_buffer_frames));
     r = AudioUnitSetProperty(stm->input_unit,
                              kAudioDevicePropertyBufferFrameSize,
                              kAudioUnitScope_Output,
                              AU_IN_BUS,
                              &stm->input_buffer_frames,
                              sizeof(UInt32));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
@@ -1284,24 +1287,24 @@ setup_audiounit_stream(cubeb_stream * st
                              kAudioUnitScope_Global,
                              AU_OUT_BUS,
                              &aurcbs_in,
                              sizeof(aurcbs_in));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
       return CUBEB_ERROR;
     }
-    LOG("Input audiounit init successfully.");
+    LOG("(%p) Input audiounit init successfully.", stm);
   }
 
   /* Setup Output Stream! */
   if (has_output(stm)) {
     r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
     if (r != CUBEB_OK) {
-      LOG("Could not initialize the audio stream description.");
+      LOG("(%p) Could not initialize the audio stream description.", stm);
       return r;
     }
 
     /* Get output device sample rate. */
     AudioStreamBasicDescription output_hw_desc;
     size = sizeof(AudioStreamBasicDescription);
     memset(&output_hw_desc, 0, size);
     r = AudioUnitGetProperty(stm->output_unit,
@@ -1323,17 +1326,17 @@ setup_audiounit_stream(cubeb_stream * st
                              sizeof(AudioStreamBasicDescription));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
       return CUBEB_ERROR;
     }
 
     // Use latency to calculate buffer size
     uint32_t output_buffer_frames = stm->latency_frames;
-    LOG("Output buffer frame count %u.", output_buffer_frames);
+    LOG("(%p) Output buffer frame count %u.", stm, output_buffer_frames);
     r = AudioUnitSetProperty(stm->output_unit,
                              kAudioDevicePropertyBufferFrameSize,
                              kAudioUnitScope_Input,
                              AU_OUT_BUS,
                              &output_buffer_frames,
                              sizeof(output_buffer_frames));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
@@ -1348,17 +1351,17 @@ setup_audiounit_stream(cubeb_stream * st
                              kAudioUnitScope_Global,
                              AU_OUT_BUS,
                              &aurcbs_out,
                              sizeof(aurcbs_out));
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
       return CUBEB_ERROR;
     }
-    LOG("Output audiounit init successfully.");
+    LOG("(%p) Output audiounit init successfully.", stm);
   }
 
   // Setting the latency doesn't work well for USB headsets (eg. plantronics).
   // Keep the default latency for now.
 #if 0
   buffer_size = latency;
 
   /* Get the range of latency this particular device can work with, and clamp
@@ -1422,17 +1425,17 @@ setup_audiounit_stream(cubeb_stream * st
   stm->resampler = cubeb_resampler_create(stm,
                                           has_input(stm) ? &input_unconverted_params : NULL,
                                           has_output(stm) ? &stm->output_stream_params : NULL,
                                           target_sample_rate,
                                           stm->data_callback,
                                           stm->user_ptr,
                                           CUBEB_RESAMPLER_QUALITY_DESKTOP);
   if (!stm->resampler) {
-    LOG("Could not create resampler.");
+    LOG("(%p) Could not create resampler.", stm);
     return CUBEB_ERROR;
   }
 
   if (stm->input_unit != NULL) {
     r = AudioUnitInitialize(stm->input_unit);
     if (r != noErr) {
       PRINT_ERROR_CODE("AudioUnitInitialize/input", r);
       return CUBEB_ERROR;
@@ -1447,17 +1450,17 @@ setup_audiounit_stream(cubeb_stream * st
     }
   }
   return CUBEB_OK;
 }
 
 static int
 audiounit_stream_init(cubeb * context,
                       cubeb_stream ** stream,
-                      char const * stream_name,
+                      char const * /* stream_name */,
                       cubeb_devid input_device,
                       cubeb_stream_params * input_stream_params,
                       cubeb_devid output_device,
                       cubeb_stream_params * output_stream_params,
                       unsigned int latency_frames,
                       cubeb_data_callback data_callback,
                       cubeb_state_callback state_callback,
                       void * user_ptr)
@@ -1510,24 +1513,24 @@ audiounit_stream_init(cubeb * context,
     // It's not critical to lock here, because no other thread has been started
     // yet, but it allows to assert that the lock has been taken in
     // `setup_audiounit_stream`.
     auto_lock lock(stm->mutex);
     r = setup_audiounit_stream(stm);
   }
 
   if (r != CUBEB_OK) {
-    LOG("Could not setup the audiounit stream.");
+    LOG("(%p) Could not setup the audiounit stream.", stm);
     audiounit_stream_destroy(stm);
     return r;
   }
 
   r = audiounit_install_device_changed_callback(stm);
   if (r != CUBEB_OK) {
-    LOG("Could not install the device change callback.");
+    LOG("(%p) Could not install the device change callback.", stm);
     return r;
   }
 
   *stream = stm;
   LOG("Cubeb stream (%p) init successful.", stm);
   return CUBEB_OK;
 }
 
@@ -1560,17 +1563,17 @@ audiounit_stream_destroy(cubeb_stream * 
   {
     auto_lock lock(stm->mutex);
     close_audiounit_stream(stm);
   }
 
 #if !TARGET_OS_IPHONE
   int r = audiounit_uninstall_device_changed_callback(stm);
   if (r != CUBEB_OK) {
-    LOG("Could not uninstall the device changed callback");
+    LOG("(%p) Could not uninstall the device changed callback", stm);
   }
 #endif
 
   assert(stm->context->active_streams >= 1);
   stm->context->active_streams -= 1;
 
   stm->~cubeb_stream();
   free(stm);
@@ -1809,17 +1812,17 @@ int audiounit_stream_get_current_device(
 
   if (audiounit_get_input_device_id(&input_device_id) != CUBEB_OK) {
     return CUBEB_ERROR;
   }
 
   size = sizeof(UInt32);
   r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data);
   if (r != noErr) {
-    LOG("Error when getting device !");
+    LOG("(%p) Error when getting device !", stm);
     size = 0;
     data = 0;
   }
 
   (*device)->input_name = new char[size + 1];
   if (!(*device)->input_name) {
     return CUBEB_ERROR;
   }
@@ -1832,17 +1835,17 @@ int audiounit_stream_get_current_device(
 
   memcpy((*device)->input_name, strdata, size);
   (*device)->input_name[size] = '\0';
 
   return CUBEB_OK;
 #endif
 }
 
-int audiounit_stream_device_destroy(cubeb_stream * stream,
+int audiounit_stream_device_destroy(cubeb_stream * /* stream */,
                                     cubeb_device * device)
 {
   delete [] device->output_name;
   delete [] device->input_name;
   delete device;
   return CUBEB_OK;
 }
 
@@ -1901,20 +1904,20 @@ audiounit_strref_to_cstr_utf8(CFStringRe
   CFIndex len, size;
   char * ret;
   if (strref == NULL) {
     return NULL;
   }
 
   len = CFStringGetLength(strref);
   size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
-  ret = new char[size];
+  ret = static_cast<char *>(malloc(size));
 
   if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
-    delete [] ret;
+    free(ret);
     ret = NULL;
   }
 
   return ret;
 }
 
 static uint32_t
 audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
@@ -2094,17 +2097,17 @@ audiounit_create_device_from_hwdev(Audio
     ret->latency_lo = 10 * ret->default_rate / 1000;  /* Default to  10ms */
     ret->latency_hi = 100 * ret->default_rate / 1000; /* Default to 100ms */
   }
 
   return ret;
 }
 
 static int
-audiounit_enumerate_devices(cubeb * context, cubeb_device_type type,
+audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
                             cubeb_device_collection ** collection)
 {
   AudioObjectID * hwdevs = NULL;
   uint32_t i, hwdevcount = 0;
   OSStatus err;
 
   if ((err = audiounit_get_devices(&hwdevs, &hwdevcount)) != noErr) {
     return CUBEB_ERROR;
@@ -2186,17 +2189,17 @@ audiounit_get_devices_of_type(cubeb_devi
     /* For device in the given scope channel must be > 0. */
     if (audiounit_get_channel_count(devices[i], scope) > 0) {
       devices_in_scope[dev_count] = devices[i];
       ++dev_count;
     }
   }
 
   if (devid_array && dev_count > 0) {
-    *devid_array = static_cast<AudioObjectID *>(calloc(dev_count, sizeof(AudioObjectID)));
+    *devid_array = new AudioObjectID[dev_count];
     assert(*devid_array);
     memcpy(*devid_array, &devices_in_scope, dev_count * sizeof(AudioObjectID));
   }
   return dev_count;
 }
 
 static uint32_t
 audiounit_equal_arrays(AudioObjectID * left, AudioObjectID * right, uint32_t size)
@@ -2206,19 +2209,19 @@ audiounit_equal_arrays(AudioObjectID * l
     if (left[i] != right[i]) {
       return 0;
     }
   }
   return 1;
 }
 
 static OSStatus
-audiounit_collection_changed_callback(AudioObjectID inObjectID,
-                                      UInt32 inNumberAddresses,
-                                      const AudioObjectPropertyAddress * inAddresses,
+audiounit_collection_changed_callback(AudioObjectID /* inObjectID */,
+                                      UInt32 /* inNumberAddresses */,
+                                      const AudioObjectPropertyAddress * /* inAddresses */,
                                       void * inClientData)
 {
   cubeb * context = static_cast<cubeb *>(inClientData);
   auto_lock lock(context->mutex);
 
   if (context->collection_changed_callback == NULL) {
     /* Listener removed while waiting in mutex, abort. */
     return noErr;
--- a/media/libcubeb/src/cubeb_log.h
+++ b/media/libcubeb/src/cubeb_log.h
@@ -7,18 +7,24 @@
 
 #ifndef CUBEB_LOG
 #define CUBEB_LOG
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#if defined(__GNUC__) || defined(__clang__)
+#define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
+#else
+#define PRINTF_FORMAT(fmt, args)
+#endif
+
 extern cubeb_log_level g_log_level;
-extern cubeb_log_callback g_log_callback;
+extern cubeb_log_callback g_log_callback PRINTF_FORMAT(1, 2);
 
 #ifdef __cplusplus
 }
 #endif
 
 #define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
 #define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
 
--- a/media/libcubeb/src/cubeb_utils.h
+++ b/media/libcubeb/src/cubeb_utils.h
@@ -207,23 +207,9 @@ struct auto_lock {
   ~auto_lock()
   {
     lock.leave();
   }
 private:
   owned_critical_section & lock;
 };
 
-struct auto_unlock {
-  explicit auto_unlock(owned_critical_section & lock)
-    : lock(lock)
-  {
-    lock.leave();
-  }
-  ~auto_unlock()
-  {
-    lock.enter();
-  }
-private:
-  owned_critical_section & lock;
-};
-
 #endif /* CUBEB_UTILS */
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -101,17 +101,17 @@ typedef BOOL (WINAPI *revert_mm_thread_c
 
 extern cubeb_ops const wasapi_ops;
 
 int wasapi_stream_stop(cubeb_stream * stm);
 int wasapi_stream_start(cubeb_stream * stm);
 void close_wasapi_stream(cubeb_stream * stm);
 int setup_wasapi_stream(cubeb_stream * stm);
 static char * wstr_to_utf8(const wchar_t * str);
-static const wchar_t * utf8_to_wstr(char* str);
+static std::unique_ptr<const wchar_t[]> utf8_to_wstr(char* str);
 
 }
 
 struct cubeb
 {
   cubeb_ops const * ops;
   /* Library dynamically opened to increase the render thread priority, and
      the two function pointers we need. */
@@ -614,19 +614,21 @@ bool get_output_buffer(cubeb_stream * st
   if (FAILED(hr)) {
     LOG("Failed to get padding: %x", hr);
     return false;
   }
   XASSERT(padding_out <= stm->output_buffer_frame_count);
 
   if (stm->draining) {
     if (padding_out == 0) {
+      LOG("Draining finished.");
       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
       return false;
     }
+    LOG("Draining.");
     return true;
   }
 
   frame_count = stm->output_buffer_frame_count - padding_out;
   BYTE * output_buffer;
 
   hr = stm->render_client->GetBuffer(frame_count, &output_buffer);
   if (FAILED(hr)) {
@@ -679,16 +681,19 @@ refill_callback_duplex(cubeb_stream * st
   double output_duration = double(output_frames) / stm->output_mix_params.rate;
   double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
   if (input_duration < output_duration) {
     size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
     LOG("padding silence: out=%f in=%f pad=%u", output_duration, input_duration, padding);
     stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
   }
 
+  LOGV("Duplex callback: input frames: %zu, output frames: %zu",
+       stm->linear_input_buffer.length(), output_frames);
+
   refill(stm,
          stm->linear_input_buffer.data(),
          stm->linear_input_buffer.length(),
          output_buffer,
          output_frames);
 
   stm->linear_input_buffer.clear();
 
@@ -712,16 +717,18 @@ refill_callback_input(cubeb_stream * stm
     return rv;
   }
 
   // This can happen at the very beginning of the stream.
   if (!stm->linear_input_buffer.length()) {
     return true;
   }
 
+  LOGV("Input callback: input frames: %zu", stm->linear_input_buffer.length());
+
   long read = refill(stm,
                      stm->linear_input_buffer.data(),
                      stm->linear_input_buffer.length(),
                      nullptr,
                      0);
 
   consumed_all_buffer = read == stm->linear_input_buffer.length();
 
@@ -749,16 +756,20 @@ refill_callback_output(cubeb_stream * st
     return true;
   }
 
   long got = refill(stm,
                     nullptr,
                     0,
                     output_buffer,
                     output_frames);
+
+  LOGV("Output callback: output frames requested: %zu, got %ld",
+       output_frames, got);
+
   XASSERT(got >= 0);
   XASSERT(got == output_frames || stm->draining);
 
   hr = stm->render_client->ReleaseBuffer(got, 0);
   if (FAILED(hr)) {
     LOG("failed to release buffer: %x", hr);
     return false;
   }
@@ -819,58 +830,67 @@ wasapi_stream_render_loop(LPVOID stream)
          shutdown. */
       if (stm->draining) {
         stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
       }
       continue;
     }
     case WAIT_OBJECT_0 + 1: { /* reconfigure */
       XASSERT(stm->output_client || stm->input_client);
+      LOG("Reconfiguring the stream");
       /* Close the stream */
       if (stm->output_client) {
         stm->output_client->Stop();
+        LOG("Output stopped.");
       }
       if (stm->input_client) {
         stm->input_client->Stop();
+        LOG("Input stopped.");
       }
       {
         auto_lock lock(stm->stream_reset_lock);
         close_wasapi_stream(stm);
+        LOG("Stream closed.");
         /* Reopen a stream and start it immediately. This will automatically pick the
            new default device for this role. */
         int r = setup_wasapi_stream(stm);
         if (r != CUBEB_OK) {
+          LOG("Error setting up the stream during reconfigure.");
           /* Don't destroy the stream here, since we expect the caller to do
              so after the error has propagated via the state callback. */
           is_playing = false;
           hr = E_FAIL;
           continue;
         }
+        LOG("Stream setup successfuly.");
       }
       XASSERT(stm->output_client || stm->input_client);
       if (stm->output_client) {
         stm->output_client->Start();
+        LOG("Output started after reconfigure.");
       }
       if (stm->input_client) {
         stm->input_client->Start();
+        LOG("Input started after reconfigure.");
       }
       break;
     }
     case WAIT_OBJECT_0 + 2:  /* refill */
       XASSERT(has_input(stm) && has_output(stm) ||
               !has_input(stm) && has_output(stm));
       is_playing = stm->refill_callback(stm);
       break;
     case WAIT_OBJECT_0 + 3: /* input available */
       if (has_input(stm) && has_output(stm)) { continue; }
       is_playing = stm->refill_callback(stm);
       break;
     case WAIT_TIMEOUT:
       XASSERT(stm->shutdown_event == wait_array[0]);
       if (++timeout_count >= timeout_limit) {
+        LOG("Render loop reached the timeout limit.");
         is_playing = false;
         hr = E_FAIL;
       }
       break;
     default:
       LOG("case %d not handled in render loop.", waitResult);
       abort();
     }
@@ -1111,17 +1131,19 @@ int wasapi_init(cubeb ** context, char c
 
   return CUBEB_OK;
 }
 }
 
 namespace {
 void stop_and_join_render_thread(cubeb_stream * stm)
 {
+  LOG("Stop and join render thread.");
   if (!stm->thread) {
+    LOG("No thread present.");
     return;
   }
 
   BOOL ok = SetEvent(stm->shutdown_event);
   if (!ok) {
     LOG("Destroy SetEvent failed: %d", GetLastError());
   }
 
@@ -1133,16 +1155,18 @@ void stop_and_join_render_thread(cubeb_s
      * process. */
     LOG("Destroy WaitForSingleObject on thread timed out,"
         " leaking the thread: %d", GetLastError());
   }
   if (r == WAIT_FAILED) {
     LOG("Destroy WaitForSingleObject on thread failed: %d", GetLastError());
   }
 
+  LOG("Closing thread.");
+
   CloseHandle(stm->thread);
   stm->thread = NULL;
 
   CloseHandle(stm->shutdown_event);
   stm->shutdown_event = 0;
 }
 
 void wasapi_destroy(cubeb * context)
@@ -1241,16 +1265,18 @@ wasapi_get_min_latency(cubeb * ctx, cube
   LOG("default device period: %lld", default_period);
 
   /* According to the docs, the best latency we can achieve is by synchronizing
      the stream and the engine.
      http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
 
   *latency_frames = hns_to_frames(params.rate, default_period);
 
+  LOG("Minimum latency in frames: %u", *latency_frames);
+
   SafeRelease(client);
 
   return CUBEB_OK;
 }
 
 int
 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
@@ -1279,16 +1305,18 @@ wasapi_get_preferred_sample_rate(cubeb *
   hr = client->GetMixFormat(&mix_format);
   if (FAILED(hr)) {
     SafeRelease(client);
     return CUBEB_ERROR;
   }
 
   *rate = mix_format->nSamplesPerSec;
 
+  LOG("Preferred sample rate for output: %u", *rate);
+
   CoTaskMemFree(mix_format);
   SafeRelease(client);
 
   return CUBEB_OK;
 }
 
 void wasapi_stream_destroy(cubeb_stream * stm);
 
@@ -1381,18 +1409,17 @@ int setup_wasapi_stream_one_side(cubeb_s
   HRESULT hr;
 
   stm->stream_reset_lock.assert_current_thread_owns();
   bool try_again = false;
   // This loops until we find a device that works, or we've exhausted all
   // possibilities.
   do {
     if (devid) {
-      std::unique_ptr<const wchar_t> id;
-      id.reset(utf8_to_wstr(reinterpret_cast<char*>(devid)));
+      std::unique_ptr<const wchar_t[]> id(utf8_to_wstr(reinterpret_cast<char*>(devid)));
       hr = get_endpoint(&device, id.get());
       if (FAILED(hr)) {
         LOG("Could not get %s endpoint, error: %x\n", DIRECTION_NAME, hr);
         return CUBEB_ERROR;
       }
     }
     else {
       hr = get_default_endpoint(&device, direction);
@@ -1495,16 +1522,17 @@ int setup_wasapi_stream(cubeb_stream * s
 {
   HRESULT hr;
   int rv;
 
   stm->stream_reset_lock.assert_current_thread_owns();
 
   auto_com com;
   if (!com.ok()) {
+    LOG("Failure to initialize COM.");
     return CUBEB_ERROR;
   }
 
   XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first.");
 
   if (has_input(stm)) {
     LOG("Setup capture: device=%x", (int)stm->input_device);
     rv = setup_wasapi_stream_one_side(stm,
@@ -1513,16 +1541,17 @@ int setup_wasapi_stream(cubeb_stream * s
                                       eCapture,
                                       __uuidof(IAudioCaptureClient),
                                       &stm->input_client,
                                       &stm->input_buffer_frame_count,
                                       stm->input_available_event,
                                       &stm->capture_client,
                                       &stm->input_mix_params);
     if (rv != CUBEB_OK) {
+      LOG("Failure to open the input side.");
       return rv;
     }
   }
 
   if (has_output(stm)) {
     LOG("Setup render: device=%x", (int)stm->output_device);
     rv = setup_wasapi_stream_one_side(stm,
                                       &stm->output_stream_params,
@@ -1530,16 +1559,17 @@ int setup_wasapi_stream(cubeb_stream * s
                                       eRender,
                                       __uuidof(IAudioRenderClient),
                                       &stm->output_client,
                                       &stm->output_buffer_frame_count,
                                       stm->refill_event,
                                       &stm->render_client,
                                       &stm->output_mix_params);
     if (rv != CUBEB_OK) {
+      LOG("Failure to open the output side.");
       return rv;
     }
 
     hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
                                         (void **)&stm->audio_stream_volume);
     if (FAILED(hr)) {
       LOG("Could not get the IAudioStreamVolume: %x", hr);
       return CUBEB_ERROR;
@@ -1550,16 +1580,17 @@ int setup_wasapi_stream(cubeb_stream * s
                                         (void **)&stm->audio_clock);
     if (FAILED(hr)) {
       LOG("Could not get the IAudioClock: %x", hr);
       return CUBEB_ERROR;
     }
 
     /* Restore the stream volume over a device change. */
     if (stream_set_volume(stm, stm->volume) != CUBEB_OK) {
+      LOG("Could not set the volume.");
       return CUBEB_ERROR;
     }
   }
 
   /* If we have both input and output, we resample to
    * the highest sample rate available. */
   int32_t target_sample_rate;
   if (has_input(stm) && has_output(stm)) {
@@ -1567,16 +1598,18 @@ int setup_wasapi_stream(cubeb_stream * s
     target_sample_rate = stm->input_stream_params.rate;
   } else if (has_input(stm)) {
     target_sample_rate = stm->input_stream_params.rate;
   } else {
     XASSERT(has_output(stm));
     target_sample_rate = stm->output_stream_params.rate;
   }
 
+  LOG("Target sample rate: %d", target_sample_rate);
+
   /* If we are playing/capturing a mono stream, we only resample one channel,
    and copy it over, so we are always resampling the number
    of channels of the stream, not the number of channels
    that WASAPI wants. */
   cubeb_stream_params input_params = stm->input_mix_params;
   input_params.channels = stm->input_stream_params.channels;
   cubeb_stream_params output_params = stm->output_mix_params;
   output_params.channels = stm->output_stream_params.channels;
@@ -1623,16 +1656,20 @@ wasapi_stream_init(cubeb * context, cube
   if (!com.ok()) {
     return CUBEB_ERROR;
   }
 
   XASSERT(context && stream && (input_stream_params || output_stream_params));
 
   if (output_stream_params && output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE ||
       input_stream_params && input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE) {
+    LOG("Invalid format, %p %p %d %d",
+        output_stream_params, input_stream_params,
+        output_stream_params && output_stream_params->format,
+        input_stream_params && input_stream_params->format);
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
   cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream));
 
   XASSERT(stm);
 
   stm->context = context;
@@ -1706,21 +1743,24 @@ void close_wasapi_stream(cubeb_stream * 
 {
   XASSERT(stm);
 
   stm->stream_reset_lock.assert_current_thread_owns();
 
   SafeRelease(stm->output_client);
   stm->output_client = NULL;
   SafeRelease(stm->input_client);
-  stm->capture_client = NULL;
+  stm->input_client = NULL;
 
   SafeRelease(stm->render_client);
   stm->render_client = NULL;
 
+  SafeRelease(stm->capture_client);
+  stm->capture_client = NULL;
+
   SafeRelease(stm->audio_stream_volume);
   stm->audio_stream_volume = NULL;
 
   SafeRelease(stm->audio_clock);
   stm->audio_clock = NULL;
   stm->total_frames_written += static_cast<UINT64>(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params)));
   stm->frames_written = 0;
 
@@ -1945,36 +1985,36 @@ int wasapi_stream_set_volume(cubeb_strea
 static char *
 wstr_to_utf8(LPCWSTR str)
 {
   char * ret = NULL;
   int size;
 
   size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, 0, NULL, NULL);
   if (size > 0) {
-    ret =  new char[size];
+    ret = static_cast<char *>(malloc(size));
     ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
   }
 
   return ret;
 }
 
-static const wchar_t *
+static std::unique_ptr<const wchar_t[]>
 utf8_to_wstr(char* str)
 {
-  wchar_t * ret = nullptr;
+  std::unique_ptr<wchar_t[]> ret;
   int size;
 
-  size = ::MultiByteToWideChar(CP_UTF8, 0, str, -1, ret, 0);
+  size = ::MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
   if (size > 0) {
-    ret = new wchar_t[size];
-    ::MultiByteToWideChar(CP_UTF8, 0, str, -1, ret, size);
+    ret.reset(new wchar_t[size]);
+    ::MultiByteToWideChar(CP_UTF8, 0, str, -1, ret.get(), size);
   }
 
-  return ret;
+  return std::move(ret);
 }
 
 static IMMDevice *
 wasapi_get_device_node(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
 {
   IMMDevice * ret = NULL;
   IDeviceTopology * devtopo = NULL;
   IConnector * connector = NULL;
--- a/media/libcubeb/tests/test_sanity.cpp
+++ b/media/libcubeb/tests/test_sanity.cpp
@@ -466,16 +466,31 @@ test_stream_position(void)
     r = cubeb_stream_get_position(stream, &position);
     assert(r == 0);
     assert(position >= last_position);
     assert(position <= total_frames_written);
     last_position = position;
     delay(500);
   }
 
+  /* test that the position is valid even when starting and
+   * stopping the stream.  */
+  for (i = 0; i < 5; ++i) {
+    r = cubeb_stream_stop(stream);
+    assert(r == 0);
+    r = cubeb_stream_get_position(stream, &position);
+    assert(r == 0);
+    assert(last_position < position);
+    last_position = position;
+    delay(500);
+    r = cubeb_stream_start(stream);
+    assert(r == 0);
+    delay(500);
+  }
+
   assert(last_position != 0);
 
   /* stream position should not advance after stopping playback */
   r = cubeb_stream_stop(stream);
   assert(r == 0);
 
   /* XXX allow stream to settle */
   delay(500);
--- a/mfbt/Range.h
+++ b/mfbt/Range.h
@@ -30,17 +30,17 @@ public:
     : mStart(aStart.get(), aStart.get(), aEnd.get()),
       mEnd(aEnd.get(), aStart.get(), aEnd.get())
   {
     // Only accept two RangedPtrs within the same range.
     aStart.checkIdenticalRange(aEnd);
     MOZ_ASSERT(aStart <= aEnd);
   }
 
-  RangedPtr<T> start() const { return mStart; }
+  RangedPtr<T> begin() const { return mStart; }
   RangedPtr<T> end() const { return mEnd; }
   size_t length() const { return mEnd - mStart; }
 
   T& operator[](size_t aOffset) const { return mStart[aOffset]; }
 
   explicit operator bool() const { return mStart != nullptr; }
 };
 
--- a/taskcluster/docs/parameters.rst
+++ b/taskcluster/docs/parameters.rst
@@ -44,16 +44,24 @@ Push Information
 
 ``pushlog_id``
    The ID from the ``hg.mozilla.org`` pushlog
 
 ``pushdate``
    The timestamp of the push to the repository that triggered this decision
    task.  Expressed as an integer seconds since the UNIX epoch.
 
+``build_date``
+   The timestamp of the build date. Defaults to ``pushdate`` and falls back to present time of
+   taskgraph invocation. Expressed as an integer seconds since the UNIX epoch.
+
+``moz_build_date``
+   A formatted timestamp of ``build_date``. Expressed as a string with the following
+   format: %Y%m%d%H%M%S
+
 Tree Information
 ----------------
 
 ``project``
    Another name for what may otherwise be called tree or branch or
    repository.  This is the unqualified name, such as ``mozilla-central`` or
    ``cedar``.
 
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -4,16 +4,18 @@
 # 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/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import os
 import json
 import logging
+
+import time
 import yaml
 
 from .generator import TaskGraphGenerator
 from .create import create_tasks
 from .parameters import Parameters
 from .target_tasks import get_method
 from .taskgraph import TaskGraph
 
@@ -120,16 +122,22 @@ def get_decision_parameters(options):
         'target_tasks_method',
     ] if n in options}
 
     # owner must be an email, but sometimes (e.g., for ffxbld) it is not, in which
     # case, fake it
     if '@' not in parameters['owner']:
         parameters['owner'] += '@noreply.mozilla.org'
 
+    # use the pushdate as build_date if given, else use current time
+    parameters['build_date'] = parameters['pushdate'] or int(time.time())
+    # moz_build_date is the build identifier based on build_date
+    parameters['moz_build_date'] = time.strftime("%Y%m%d%H%M%S",
+                                                 time.gmtime(parameters['build_date']))
+
     project = parameters['project']
     try:
         parameters.update(PER_PROJECT_PARAMETERS[project])
     except KeyError:
         logger.warning("using default project parameters; add {} to "
                        "PER_PROJECT_PARAMETERS in {} to customize behavior "
                        "for this project".format(project, __file__))
         parameters.update(PER_PROJECT_PARAMETERS['default'])
--- a/taskcluster/taskgraph/task/docker_image.py
+++ b/taskcluster/taskgraph/task/docker_image.py
@@ -4,17 +4,16 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import logging
 import json
 import os
 import re
 import urllib2
-import time
 
 from . import base
 from taskgraph.util.docker import (
     create_context_tar,
     docker_image,
     generate_context_hash,
 )
 from taskgraph.util.templates import Templates
@@ -33,25 +32,23 @@ class DockerImageTask(base.Task):
         super(DockerImageTask, self).__init__(*args, **kwargs)
 
     def __eq__(self, other):
         return super(DockerImageTask, self).__eq__(other) and \
                self.index_paths == other.index_paths
 
     @classmethod
     def load_tasks(cls, kind, path, config, params, loaded_tasks):
-        pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(params['pushdate']))
-
         parameters = {
             'pushlog_id': params.get('pushlog_id', 0),
-            'pushdate': pushdate,
-            'pushtime': pushdate[8:],
-            'year': pushdate[0:4],
-            'month': pushdate[4:6],
-            'day': pushdate[6:8],
+            'pushdate': params['moz_build_date'],
+            'pushtime': params['moz_build_date'][8:],
+            'year': params['moz_build_date'][0:4],
+            'month': params['moz_build_date'][4:6],
+            'day': params['moz_build_date'][6:8],
             'project': params['project'],
             'docker_image': docker_image,
             'base_repository': params['base_repository'] or params['head_repository'],
             'head_repository': params['head_repository'],
             'head_ref': params['head_ref'] or params['head_rev'],
             'head_rev': params['head_rev'],
             'owner': params['owner'],
             'level': params['level'],
--- a/taskcluster/taskgraph/transforms/android_stuff.py
+++ b/taskcluster/taskgraph/transforms/android_stuff.py
@@ -2,34 +2,32 @@
 # 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/.
 """
 Set dynamic task description properties of the android stuff.  Temporary!
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-import time
 from taskgraph.transforms.base import TransformSequence
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def setup_task(config, tasks):
     for task in tasks:
         task['label'] = task['name']
         env = task['worker'].setdefault('env', {})
         env.update({
             'GECKO_BASE_REPOSITORY': config.params['base_repository'],
             'GECKO_HEAD_REF': config.params['head_rev'],
             'GECKO_HEAD_REPOSITORY': config.params['head_repository'],
             'GECKO_HEAD_REV': config.params['head_rev'],
-            'MOZ_BUILD_DATE': time.strftime("%Y%m%d%H%M%S",
-                                            time.gmtime(config.params['pushdate'])),
+            'MOZ_BUILD_DATE': config.params['moz_build_date'],
             'MOZ_SCM_LEVEL': config.params['level'],
             'MH_BRANCH': config.params['project'],
         })
 
         task['worker'].setdefault('caches', []).append({
             'type': 'persistent',
             'name': 'level-{}-{}-tc-vcs'.format(
                 config.params['level'], config.params['project']),
--- a/taskcluster/taskgraph/transforms/job/hazard.py
+++ b/taskcluster/taskgraph/transforms/job/hazard.py
@@ -2,17 +2,16 @@
 # 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/.
 """
 Support for running hazard jobs via dedicated scripts
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-import time
 from voluptuous import Schema, Required, Optional, Any
 
 from taskgraph.transforms.job import run_job_using
 from taskgraph.transforms.job.common import (
     docker_worker_add_workspace_cache,
     docker_worker_setup_secrets,
     docker_worker_add_public_artifacts,
     docker_worker_support_vcs_checkout,
@@ -49,17 +48,17 @@ def docker_worker_hazard(config, job, ta
 
     docker_worker_add_public_artifacts(config, job, taskdesc)
     docker_worker_add_workspace_cache(config, job, taskdesc)
     docker_worker_setup_secrets(config, job, taskdesc)
     docker_worker_support_vcs_checkout(config, job, taskdesc)
 
     env = worker['env']
     env.update({
-        'MOZ_BUILD_DATE': time.strftime("%Y%m%d%H%M%S", time.gmtime(config.params['pushdate'])),
+        'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
     })
 
     # script parameters
     if run.get('tooltool-manifest'):
         env['TOOLTOOL_MANIFEST'] = run['tooltool-manifest']
     if run.get('mozconfig'):
         env['MOZCONFIG'] = run['mozconfig']
--- a/taskcluster/taskgraph/transforms/job/mozharness.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness.py
@@ -5,17 +5,16 @@
 
 Support for running jobs via mozharness.  Ideally, most stuff gets run this
 way, and certainly anything using mozharness should use this approach.
 
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-import time
 from voluptuous import Schema, Required, Optional, Any
 
 from taskgraph.transforms.job import run_job_using
 from taskgraph.transforms.job.common import (
     docker_worker_add_workspace_cache,
     docker_worker_add_gecko_vcs_env_vars,
     docker_worker_setup_secrets,
     docker_worker_add_public_artifacts,
@@ -95,17 +94,17 @@ def mozharness_on_docker_worker_setup(co
     docker_worker_support_vcs_checkout(config, job, taskdesc)
 
     env = worker.setdefault('env', {})
     env.update({
         'MOZHARNESS_CONFIG': ' '.join(run['config']),
         'MOZHARNESS_SCRIPT': run['script'],
         'MH_BRANCH': config.params['project'],
         'MH_BUILD_POOL': 'taskcluster',
-        'MOZ_BUILD_DATE': time.strftime("%Y%m%d%H%M%S", time.gmtime(config.params['pushdate'])),
+        'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
     })
 
     if 'actions' in run:
         env['MOZHARNESS_ACTIONS'] = ' '.join(run['actions'])
 
     if 'options' in run:
         env['MOZHARNESS_OPTIONS'] = ' '.join(run['options'])
@@ -192,17 +191,17 @@ def mozharness_on_windows(config, job, t
         'path': r'public\build',
         'type': 'directory',
     }]
 
     docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc)
 
     env = worker['env']
     env.update({
-        'MOZ_BUILD_DATE': time.strftime("%Y%m%d%H%M%S", time.gmtime(config.params['pushdate'])),
+        'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
         'TOOLTOOL_REPO': 'https://github.com/mozilla/build-tooltool',
         'TOOLTOOL_REV': 'master',
     })
 
     mh_command = [r'c:\mozilla-build\python\python.exe']
     mh_command.append('\\'.join([r'.\build\src\testing', run['script'].replace('/', '\\')]))
     for cfg in run['config']:
--- a/taskcluster/taskgraph/transforms/job/spidermonkey.py
+++ b/taskcluster/taskgraph/transforms/job/spidermonkey.py
@@ -2,17 +2,16 @@
 # 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/.
 """
 Support for running spidermonkey jobs via dedicated scripts
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-import time
 from voluptuous import Schema, Required, Optional, Any
 
 from taskgraph.transforms.job import run_job_using
 from taskgraph.transforms.job.common import (
     docker_worker_add_public_artifacts,
     docker_worker_support_vcs_checkout,
 )
 
@@ -47,17 +46,17 @@ def docker_worker_spidermonkey(config, j
         })
 
     docker_worker_add_public_artifacts(config, job, taskdesc)
 
     env = worker['env']
     env.update({
         'MOZHARNESS_DISABLE': 'true',
         'SPIDERMONKEY_VARIANT': run['spidermonkey-variant'],
-        'MOZ_BUILD_DATE': time.strftime("%Y%m%d%H%M%S", time.gmtime(config.params['pushdate'])),
+        'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
     })
 
     # tooltool downloads; note that this script downloads using the API
     # endpoiint directly, rather than via relengapi-proxy
     worker['caches'].append({
         'type': 'persistent',
         'name': 'tooltool-cache',
--- a/taskcluster/taskgraph/transforms/job/toolchain.py
+++ b/taskcluster/taskgraph/transforms/job/toolchain.py
@@ -2,17 +2,16 @@
 # 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/.
 """
 Support for running toolchain-building jobs via dedicated scripts
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-import time
 from voluptuous import Schema, Required
 
 from taskgraph.transforms.job import run_job_using
 from taskgraph.transforms.job.common import (
     docker_worker_add_tc_vcs_cache,
     docker_worker_add_gecko_vcs_env_vars
 )
 
@@ -38,17 +37,17 @@ def docker_worker_toolchain(config, job,
         'type': 'directory',
     })
 
     docker_worker_add_tc_vcs_cache(config, job, taskdesc)
     docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc)
 
     env = worker['env']
     env.update({
-        'MOZ_BUILD_DATE': time.strftime("%Y%m%d%H%M%S", time.gmtime(config.params['pushdate'])),
+        'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
         'TOOLS_DISABLE': 'true',
     })
 
     # tooltool downloads; note that this downloads using the API endpoint directly,
     # rather than via relengapi-proxy
     worker['caches'].append({
         'type': 'persistent',
@@ -87,17 +86,17 @@ def windows_toolchain(config, job, taskd
         'path': r'llvm-sources',
     }]
     taskdesc['scopes'].extend([
         'generic-worker:cache:' + svn_cache,
     ])
 
     env = worker['env']
     env.update({
-        'MOZ_BUILD_DATE': time.strftime("%Y%m%d%H%M%S", time.gmtime(config.params['pushdate'])),
+        'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
         'TOOLTOOL_REPO': 'https://github.com/mozilla/build-tooltool',
         'TOOLTOOL_REV': 'master',
     })
 
     hg = r'c:\Program Files\Mercurial\hg.exe'
     hg_command = ['"{}"'.format(hg)]
     hg_command.append('robustcheckout')
--- a/taskcluster/taskgraph/transforms/marionette_harness.py
+++ b/taskcluster/taskgraph/transforms/marionette_harness.py
@@ -2,35 +2,33 @@
 # 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/.
 """
 Set dynamic task description properties of the marionette-harness task.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-import time
 from taskgraph.transforms.base import TransformSequence
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def setup_task(config, tasks):
     for task in tasks:
         del task['name']
         task['label'] = 'marionette-harness'
         env = task['worker'].setdefault('env', {})
         env.update({
             'GECKO_BASE_REPOSITORY': config.params['base_repository'],
             'GECKO_HEAD_REF': config.params['head_rev'],
             'GECKO_HEAD_REPOSITORY': config.params['head_repository'],
             'GECKO_HEAD_REV': config.params['head_rev'],
-            'MOZ_BUILD_DATE': time.strftime("%Y%m%d%H%M%S",
-                                            time.gmtime(config.params['pushdate'])),
+            'MOZ_BUILD_DATE': config.params['moz_build_date'],
             'MOZ_SCM_LEVEL': config.params['level'],
         })
 
         task['worker']['caches'] = [{
             'type': 'persistent',
             'name': 'level-{}-{}-tc-vcs'.format(
                 config.params['level'], config.params['project']),
             'mount-point': "/home/worker/.tc-vcs",
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -104,29 +104,29 @@ task_description_schema = Schema({
             }
         ),
 
         # The rank that the task will receive in the TaskCluster
         # index.  A newly completed task supercedes the currently
         # indexed task iff it has a higher rank.  If unspecified,
         # 'by-tier' behavior will be used.
         'rank': Any(
-            # Rank is equal the timestamp of the pushdate for tier-1
+            # Rank is equal the timestamp of the build_date for tier-1
             # tasks, and zero for non-tier-1.  This sorts tier-{2,3}
             # builds below tier-1 in the index.
             'by-tier',
 
             # Rank is given as an integer constant (e.g. zero to make
             # sure a task is last in the index).
             int,
 
-            # Rank is equal to the timestamp of the pushdate.  This
+            # Rank is equal to the timestamp of the build_date.  This
             # option can be used to override the 'by-tier' behavior
             # for non-tier-1 tasks.
-            'pushdate',
+            'build_date',
         ),
     },
 
     # The `run_on_projects` attribute, defaulting to "all".  This dictates the
     # projects on which this task should be included in the target task set.
     # See the attributes documentation for details.
     Optional('run-on-projects'): [basestring],
 
@@ -288,17 +288,17 @@ UNKNOWN_GROUP_NAME = "Treeherder group {
 
 BUILDBOT_ROUTE_TEMPLATES = [
     "index.buildbot.branches.{project}.{job-name-buildbot}",
     "index.buildbot.revisions.{head_rev}.{project}.{job-name-buildbot}",
 ]
 
 V2_ROUTE_TEMPLATES = [
     "index.gecko.v2.{project}.latest.{product}.{job-name-gecko-v2}",
-    "index.gecko.v2.{project}.pushdate.{pushdate_long}.{product}.{job-name-gecko-v2}",
+    "index.gecko.v2.{project}.pushdate.{build_date_long}.{product}.{job-name-gecko-v2}",
     "index.gecko.v2.{project}.revision.{head_rev}.{product}.{job-name-gecko-v2}",
 ]
 
 # the roots of the treeherder routes, keyed by treeherder environment
 TREEHERDER_ROUTE_ROOTS = {
     'production': 'tc-treeherder',
     'staging': 'tc-treeherder-stage',
 }
@@ -461,19 +461,18 @@ def add_index_routes(config, tasks):
             }
 
         if job_name['gecko-v2'] not in JOB_NAME_WHITELIST:
             raise Exception(JOB_NAME_WHITELIST_ERROR.format(job_name['gecko-v2']))
 
         subs = config.params.copy()
         for n in job_name:
             subs['job-name-' + n] = job_name[n]
-        subs['pushdate_long'] = time.strftime(
-            "%Y.%m.%d.%Y%m%d%H%M%S",
-            time.gmtime(config.params['pushdate']))
+        subs['build_date_long'] = time.strftime("%Y.%m.%d.%Y%m%d%H%M%S",
+                                                time.gmtime(config.params['build_date']))
         subs['product'] = index['product']
 
         if 'buildbot' in job_name:
             for tpl in BUILDBOT_ROUTE_TEMPLATES:
                 routes.append(tpl.format(**subs))
         if 'gecko-v2' in job_name:
             for tpl in V2_ROUTE_TEMPLATES:
                 routes.append(tpl.format(**subs))
@@ -481,19 +480,19 @@ def add_index_routes(config, tasks):
         # The default behavior is to rank tasks according to their tier
         extra_index = task.setdefault('extra', {}).setdefault('index', {})
         rank = index.get('rank', 'by-tier')
 
         if rank == 'by-tier':
             # rank is zero for non-tier-1 tasks and based on pushid for others;
             # this sorts tier-{2,3} builds below tier-1 in the index
             tier = task.get('treeherder', {}).get('tier', 3)
-            extra_index['rank'] = 0 if tier > 1 else int(config.params['pushdate'])
-        elif rank == 'pushdate':
-            extra_index['rank'] = int(config.params['pushdate'])
+            extra_index['rank'] = 0 if tier > 1 else int(config.params['build_date'])
+        elif rank == 'build_date':
+            extra_index['rank'] = int(config.params['build_date'])
         else:
             extra_index['rank'] = rank
 
         del task['index']
         yield task
 
 
 @transforms.add
@@ -593,16 +592,16 @@ def check_v2_routes():
     # we only deal with the 'routes' key here
     routes = routes_json['routes']
 
     # we use different variables than mozharness
     for mh, tg in [
             ('{index}', 'index'),
             ('{build_product}', '{product}'),
             ('{build_name}-{build_type}', '{job-name-gecko-v2}'),
-            ('{year}.{month}.{day}.{pushdate}', '{pushdate_long}')]:
+            ('{year}.{month}.{day}.{pushdate}', '{build_date_long}')]:
         routes = [r.replace(mh, tg) for r in routes]
 
     if sorted(routes) != sorted(V2_ROUTE_TEMPLATES):
         raise Exception("V2_ROUTE_TEMPLATES does not match Mozharness's routes.json: "
                         "%s vs %s" % (V2_ROUTE_TEMPLATES, routes))
 
 check_v2_routes()