Merge autoland to m-c on a CLOSED TREE. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 21 Oct 2016 17:12:27 -0400
changeset 364063 5639a9f476d08f300c079117e61697f5026b6367
parent 364040 806054dd12bdcbdee81dbd75f1583156cef9b649 (current diff)
parent 364062 619ccb0d81f1adc30894807a13196d63ddbe7966 (diff)
child 364064 60dd82380d43a2b681f50842238f829204486290
child 364076 4b2363bd94e72797913817ae8bb9195afd978169
child 364124 32f298a71ce9f6d52387a929dd82b66d6cf13af8
push id1369
push userjlorenzo@mozilla.com
push dateMon, 27 Feb 2017 14:59:41 +0000
treeherdermozilla-release@d75a1dba431f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
5639a9f476d0 / 52.0a1 / 20161022030204 / files
nightly linux64
5639a9f476d0 / 52.0a1 / 20161022030204 / files
nightly mac
5639a9f476d0 / 52.0a1 / 20161022030204 / files
nightly win32
5639a9f476d0 / 52.0a1 / 20161022030204 / files
nightly win64
5639a9f476d0 / 52.0a1 / 20161022030204 / 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 autoland to m-c on a CLOSED TREE. a=merge
devtools/client/animationinspector/test/browser_animation_timeline_pause_button.js
js/src/jit/CodeGenerator.cpp
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/MIR.cpp
js/src/jit/Recover.cpp
js/src/jit/ValueNumbering.cpp
js/src/jsapi.h
js/src/moz.build
js/src/shell/js.cpp
layout/base/nsCSSFrameConstructor.cpp
security/manager/pki/resources/content/viewCertDetails.js
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -34,22 +34,26 @@ extensions.registerSchemaAPI("windows", 
         fire(WindowManager.getId(window));
       }).api(),
 
       onFocusChanged: new EventManager(context, "windows.onFocusChanged", fire => {
         // Keep track of the last windowId used to fire an onFocusChanged event
         let lastOnFocusChangedWindowId;
 
         let listener = event => {
-          let window = WindowManager.topWindow;
-          let windowId = window ? WindowManager.getId(window) : WindowManager.WINDOW_ID_NONE;
-          if (windowId !== lastOnFocusChangedWindowId) {
-            fire(windowId);
-            lastOnFocusChangedWindowId = windowId;
-          }
+          // Wait a tick to avoid firing a superfluous WINDOW_ID_NONE
+          // event when switching focus between two Firefox windows.
+          Promise.resolve().then(() => {
+            let window = Services.focus.activeWindow;
+            let windowId = window ? WindowManager.getId(window) : WindowManager.WINDOW_ID_NONE;
+            if (windowId !== lastOnFocusChangedWindowId) {
+              fire(windowId);
+              lastOnFocusChangedWindowId = windowId;
+            }
+          });
         };
         AllWindowEvents.addListener("focus", listener);
         AllWindowEvents.addListener("blur", listener);
         return () => {
           AllWindowEvents.removeListener("focus", listener);
           AllWindowEvents.removeListener("blur", listener);
         };
       }).api(),
--- a/browser/components/extensions/test/browser/browser_ext_windows_events.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_events.js
@@ -11,20 +11,25 @@ add_task(function* testWindowsEvents() {
 
       browser.test.assertTrue(Number.isInteger(window.id),
                               "Window object's id is an integer");
       browser.test.assertEq("normal", window.type,
                             "Window object returned with the correct type");
       browser.test.sendMessage("window-created", window.id);
     });
 
-    let lastWindowId;
+    let lastWindowId, os;
     browser.windows.onFocusChanged.addListener(function listener(windowId) {
       browser.test.log(`onFocusChange: windowId=${windowId} lastWindowId=${lastWindowId}`);
 
+      if (windowId === browser.windows.WINDOW_ID_NONE && os === "linux") {
+        browser.test.log("Ignoring a superfluous WINDOW_ID_NONE (blur) event on Linux");
+        return;
+      }
+
       browser.test.assertTrue(lastWindowId !== windowId,
                               "onFocusChanged fired once for the given window");
       lastWindowId = windowId;
 
       browser.test.assertTrue(Number.isInteger(windowId),
                               "windowId is an integer");
 
       browser.windows.getLastFocused().then(window => {
@@ -38,17 +43,20 @@ add_task(function* testWindowsEvents() {
       browser.test.log(`onRemoved: windowId=${windowId}`);
 
       browser.test.assertTrue(Number.isInteger(windowId),
                               "windowId is an integer");
       browser.test.sendMessage(`window-removed`, windowId);
       browser.test.notifyPass("windows.events");
     });
 
-    browser.test.sendMessage("ready");
+    browser.runtime.getPlatformInfo(info => {
+      os = info.os;
+      browser.test.sendMessage("ready");
+    });
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     background: `(${background})()`,
   });
 
   yield extension.startup();
   yield extension.awaitMessage("ready");
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -149,18 +149,18 @@ GetPrincipalDomainOrigin(nsIPrincipal* a
 
 inline void SetPendingExceptionASCII(JSContext *cx, const char *aMsg)
 {
     JS_ReportErrorASCII(cx, "%s", aMsg);
 }
 
 inline void SetPendingException(JSContext *cx, const char16_t *aMsg)
 {
-    // FIXME: Need to convert to UTF-8 (bug XXX).
-    JS_ReportErrorLatin1(cx, "%hs", aMsg);
+    NS_ConvertUTF16toUTF8 msg(aMsg);
+    JS_ReportErrorUTF8(cx, "%s", msg.get());
 }
 
 // Helper class to get stuff from the ClassInfo and not waste extra time with
 // virtual method calls for things it has already gotten
 class ClassInfoData
 {
 public:
     ClassInfoData(nsIClassInfo *aClassInfo, const char *aName)
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -43,18 +43,19 @@ skip-if = os == "linux" && !debug # Bug 
 [browser_animation_shows_player_on_valid_node.js]
 [browser_animation_spacebar_toggles_animations.js]
 [browser_animation_spacebar_toggles_node_animations.js]
 [browser_animation_target_highlight_select.js]
 [browser_animation_target_highlighter_lock.js]
 [browser_animation_timeline_currentTime.js]
 [browser_animation_timeline_header.js]
 [browser_animation_timeline_iterationStart.js]
-[browser_animation_timeline_pause_button.js]
-skip-if = os == "linux" && bits == 32 # Bug 1220974
+[browser_animation_timeline_pause_button_01.js]
+[browser_animation_timeline_pause_button_02.js]
+[browser_animation_timeline_pause_button_03.js]
 [browser_animation_timeline_rate_selector.js]
 [browser_animation_timeline_rewind_button.js]
 [browser_animation_timeline_scrubber_exists.js]
 [browser_animation_timeline_scrubber_movable.js]
 [browser_animation_timeline_scrubber_moves.js]
 [browser_animation_timeline_shows_delay.js]
 [browser_animation_timeline_shows_endDelay.js]
 [browser_animation_timeline_shows_iterations.js]
rename from devtools/client/animationinspector/test/browser_animation_timeline_pause_button.js
rename to devtools/client/animationinspector/test/browser_animation_timeline_pause_button_01.js
--- a/devtools/client/animationinspector/test/browser_animation_timeline_pause_button.js
+++ b/devtools/client/animationinspector/test/browser_animation_timeline_pause_button_01.js
@@ -1,110 +1,34 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 requestLongerTimeout(2);
 
-// Check that the timeline toolbar contains a pause button and that this pause
-// button can be clicked. Check that when it is, the current animations
-// displayed in the timeline get their playstates changed accordingly, and check
-// that the scrubber resumes/stops moving.
-// Also checks that the button goes to the right state when the scrubber has
-// reached the end of the timeline: continues to be in playing mode for infinite
-// animations, goes to paused mode otherwise.
-// And test that clicking the button once the scrubber has reached the end of
-// the timeline does the right thing.
+// Check that the timeline toolbar contains a pause button and that this pause button can
+// be clicked. Check that when it is, the button changes state and the scrubber stops and
+// resumes.
 
 add_task(function* () {
   yield addTab(URL_ROOT + "doc_simple_animation.html");
 
-  let {panel, inspector} = yield openAnimationInspector();
-  let timeline = panel.animationsTimelineComponent;
+  let {panel} = yield openAnimationInspector();
   let btn = panel.playTimelineButtonEl;
 
   ok(btn, "The play/pause button exists");
-  ok(!btn.classList.contains("paused"),
-     "The play/pause button is in its playing state");
+  ok(!btn.classList.contains("paused"), "The play/pause button is in its playing state");
 
   info("Click on the button to pause all timeline animations");
   yield clickTimelinePlayPauseButton(panel);
 
-  ok(btn.classList.contains("paused"),
-     "The play/pause button is in its paused state");
+  ok(btn.classList.contains("paused"), "The play/pause button is in its paused state");
   yield assertScrubberMoving(panel, false);
 
   info("Click again on the button to play all timeline animations");
   yield clickTimelinePlayPauseButton(panel);
 
   ok(!btn.classList.contains("paused"),
      "The play/pause button is in its playing state again");
   yield assertScrubberMoving(panel, true);
-
-  // Some animations on the test page are infinite, so the scrubber won't stop
-  // at the end of the timeline, and the button should remain in play mode.
-  info("Select an infinite animation, reload the page and wait for the " +
-       "animation to complete");
-  yield selectNodeAndWaitForAnimations(".multi", inspector);
-  yield reloadTab(inspector);
-  yield waitForOutOfBoundScrubber(timeline);
-
-  ok(!btn.classList.contains("paused"),
-     "The button is in its playing state still, animations are infinite.");
-  yield assertScrubberMoving(panel, true);
-
-  info("Click on the button after the scrubber has moved out of bounds");
-  yield clickTimelinePlayPauseButton(panel);
-
-  ok(btn.classList.contains("paused"),
-     "The button can be paused after the scrubber has moved out of bounds");
-  yield assertScrubberMoving(panel, false);
-
-  // For a finite animation though, once the scrubber reaches the end of the
-  // timeline, it should go back to paused mode.
-  info("Select a finite animation, reload the page and wait for the " +
-       "animation to complete");
-  yield selectNodeAndWaitForAnimations(".negative-delay", inspector);
-
-  let onScrubberStopped = waitForScrubberStopped(timeline);
-  yield reloadTab(inspector);
-  yield onScrubberStopped;
-
-  ok(btn.classList.contains("paused"),
-     "The button is in paused state once finite animations are done");
-  yield assertScrubberMoving(panel, false);
-
-  info("Click again on the button to play the animation from the start again");
-  yield clickTimelinePlayPauseButton(panel);
-
-  ok(!btn.classList.contains("paused"),
-     "Clicking the button once finite animations are done should restart them");
-  yield assertScrubberMoving(panel, true);
 });
-
-function waitForOutOfBoundScrubber({win, scrubberEl}) {
-  return new Promise(resolve => {
-    function check() {
-      let pos = scrubberEl.getBoxQuads()[0].bounds.right;
-      let width = win.document.documentElement.offsetWidth;
-      if (pos >= width) {
-        setTimeout(resolve, 50);
-      } else {
-        setTimeout(check, 50);
-      }
-    }
-    check();
-  });
-}
-
-function waitForScrubberStopped(timeline) {
-  return new Promise(resolve => {
-    timeline.on("timeline-data-changed",
-      function onTimelineData(e, {isMoving}) {
-        if (!isMoving) {
-          timeline.off("timeline-data-changed", onTimelineData);
-          resolve();
-        }
-      });
-  });
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_timeline_pause_button_02.js
@@ -0,0 +1,48 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Checks that the play/pause button goes to the right state when the scrubber has reached
+// the end of the timeline but there are infinite animations playing.
+
+add_task(function* () {
+  yield addTab(URL_ROOT + "doc_simple_animation.html");
+
+  let {panel, inspector} = yield openAnimationInspector();
+  let timeline = panel.animationsTimelineComponent;
+  let btn = panel.playTimelineButtonEl;
+
+  info("Select an infinite animation and wait for the scrubber to reach the end");
+  yield selectNodeAndWaitForAnimations(".multi", inspector);
+  yield waitForOutOfBoundScrubber(timeline);
+
+  ok(!btn.classList.contains("paused"),
+     "The button is in its playing state still, animations are infinite.");
+  yield assertScrubberMoving(panel, true);
+
+  info("Click on the button after the scrubber has moved out of bounds");
+  yield clickTimelinePlayPauseButton(panel);
+
+  ok(btn.classList.contains("paused"),
+     "The button can be paused after the scrubber has moved out of bounds");
+  yield assertScrubberMoving(panel, false);
+});
+
+function waitForOutOfBoundScrubber({win, scrubberEl}) {
+  return new Promise(resolve => {
+    function check() {
+      let pos = scrubberEl.getBoxQuads()[0].bounds.right;
+      let width = win.document.documentElement.offsetWidth;
+      if (pos >= width) {
+        setTimeout(resolve, 50);
+      } else {
+        setTimeout(check, 50);
+      }
+    }
+    check();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_timeline_pause_button_03.js
@@ -0,0 +1,54 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Also checks that the button goes to the right state when the scrubber has
+// reached the end of the timeline: continues to be in playing mode for infinite
+// animations, goes to paused mode otherwise.
+// And test that clicking the button once the scrubber has reached the end of
+// the timeline does the right thing.
+
+add_task(function* () {
+  yield addTab(URL_ROOT + "doc_simple_animation.html");
+
+  let {panel, inspector} = yield openAnimationInspector();
+  let timeline = panel.animationsTimelineComponent;
+  let btn = panel.playTimelineButtonEl;
+
+  // For a finite animation, once the scrubber reaches the end of the timeline, the pause
+  // button should go back to paused mode.
+  info("Select a finite animation and wait for the animation to complete");
+  yield selectNodeAndWaitForAnimations(".negative-delay", inspector);
+
+  let onScrubberStopped = waitForScrubberStopped(timeline);
+  // The page is reloaded to avoid missing the animation.
+  yield reloadTab(inspector);
+  yield onScrubberStopped;
+
+  ok(btn.classList.contains("paused"),
+     "The button is in paused state once finite animations are done");
+  yield assertScrubberMoving(panel, false);
+
+  info("Click again on the button to play the animation from the start again");
+  yield clickTimelinePlayPauseButton(panel);
+
+  ok(!btn.classList.contains("paused"),
+     "Clicking the button once finite animations are done should restart them");
+  yield assertScrubberMoving(panel, true);
+});
+
+function waitForScrubberStopped(timeline) {
+  return new Promise(resolve => {
+    timeline.on("timeline-data-changed",
+      function onTimelineData(e, {isMoving}) {
+        if (!isMoving) {
+          timeline.off("timeline-data-changed", onTimelineData);
+          resolve();
+        }
+      });
+  });
+}
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -100,34 +100,16 @@ window {
   overflow: hidden;
   margin-inline-end: 2px;
 }
 
 #inspector-search {
   flex: unset;
 }
 
-/* TODO: bug 1265759: should apply to .devtools-searchinput once all searchbox
-   is converted to html*/
-#inspector-searchbox {
-  width: 100%;
-}
-
-/* Make sure the text is vertically centered in Inspector's
-   search box. This can be removed when the search box is
-   switched to HTML.
-   See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1265759 */
-.theme-dark #inspector-searchbox,
-.theme-light #inspector-searchbox {
-  line-height: 19px;
-}
-.theme-firebug #inspector-searchbox {
-  line-height: 17px;
-}
-
 /* Eyedropper toolbar button */
 
 #inspector-eyedropper-toggle {
   /* hidden by default, until we can check that the required highlighter exists */
   display: none;
 }
 
 #inspector-eyedropper-toggle::before {
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -633,17 +633,17 @@ ThrowJSExceptionASCII(JSContext *cx, con
 
     if (str) {
       JS::Rooted<JS::Value> exn(cx, JS::StringValue(str));
       ::JS_SetPendingException(cx, exn);
     }
 
     PopException();
   } else {
-    ::JS_ReportErrorASCII(cx, message);
+    ::JS_ReportErrorASCII(cx, "%s", message);
   }
 }
 
 static bool
 ReportExceptionIfPending(JSContext *cx)
 {
   const char *ex = PeekException();
 
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -2245,25 +2245,25 @@ class MOZ_STACK_CLASS ModuleValidator
         MOZ_ASSERT(!hasAlreadyFailed());
         MOZ_ASSERT(errorOffset_ == UINT32_MAX);
         MOZ_ASSERT(fmt);
         errorOffset_ = offset;
         errorString_.reset(JS_vsmprintf(fmt, ap));
         return false;
     }
 
-    bool failfOffset(uint32_t offset, const char* fmt, ...) {
+    bool failfOffset(uint32_t offset, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
         va_list ap;
         va_start(ap, fmt);
         failfVAOffset(offset, fmt, ap);
         va_end(ap);
         return false;
     }
 
-    bool failf(ParseNode* pn, const char* fmt, ...) {
+    bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
         va_list ap;
         va_start(ap, fmt);
         failfVAOffset(pn->pn_pos.begin, fmt, ap);
         va_end(ap);
         return false;
     }
 
     bool failNameOffset(uint32_t offset, const char* fmt, PropertyName* name) {
@@ -2939,17 +2939,17 @@ class MOZ_STACK_CLASS FunctionValidator
 
         return m_.mg().finishFuncDef(funcIndex, &fg_);
     }
 
     bool fail(ParseNode* pn, const char* str) {
         return m_.fail(pn, str);
     }
 
-    bool failf(ParseNode* pn, const char* fmt, ...) {
+    bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
         va_list ap;
         va_start(ap, fmt);
         m_.failfVAOffset(pn->pn_pos.begin, fmt, ap);
         va_end(ap);
         return false;
     }
 
     bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
@@ -4715,17 +4715,18 @@ CheckCallArgs(FunctionValidator& f, Pars
     }
     return true;
 }
 
 static bool
 CheckSignatureAgainstExisting(ModuleValidator& m, ParseNode* usepn, const Sig& sig, const Sig& existing)
 {
     if (sig.args().length() != existing.args().length()) {
-        return m.failf(usepn, "incompatible number of arguments (%u here vs. %u before)",
+        return m.failf(usepn, "incompatible number of arguments (%" PRIuSIZE
+                       " here vs. %" PRIuSIZE " before)",
                        sig.args().length(), existing.args().length());
     }
 
     for (unsigned i = 0; i < sig.args().length(); i++) {
         if (sig.arg(i) != existing.arg(i)) {
             return m.failf(usepn, "incompatible type for argument %u: (%s here vs. %s before)", i,
                            ToCString(sig.arg(i)), ToCString(existing.arg(i)));
         }
--- a/js/src/asmjs/WasmBinary.cpp
+++ b/js/src/asmjs/WasmBinary.cpp
@@ -36,33 +36,34 @@ Decoder::fail(const char* msg, ...) {
         return false;
 
     return fail(Move(str));
 }
 
 bool
 Decoder::fail(UniqueChars msg) {
     MOZ_ASSERT(error_);
-    UniqueChars strWithOffset(JS_smprintf("at offset %zu: %s", currentOffset(), msg.get()));
+    UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s",
+                                          currentOffset(), msg.get()));
     if (!strWithOffset)
         return false;
 
     *error_ = Move(strWithOffset);
     return false;
 }
 
 bool
 wasm::DecodePreamble(Decoder& d)
 {
     uint32_t u32;
     if (!d.readFixedU32(&u32) || u32 != MagicNumber)
         return d.fail("failed to match magic number");
 
     if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
-        return d.fail("binary version 0x%lx does not match expected version 0x%lx",
+        return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
                       u32, EncodingVersion);
 
     return true;
 }
 
 bool
 wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals)
 {
@@ -146,29 +147,29 @@ wasm::DecodeLimits(Decoder& d, Limits* l
 {
     uint32_t flags;
     if (!d.readVarU32(&flags))
         return d.fail("expected flags");
 
     // TODO (bug 1310149): tighten this check (s/3/1) when the AngryBots demo
     // gets updated.
     if (flags & ~uint32_t(0x3))
-        return d.fail("unexpected bits set in flags: %lu", (flags & ~uint32_t(0x3)));
+        return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x3)));
 
     if (!d.readVarU32(&limits->initial))
         return d.fail("expected initial length");
 
     if (flags & 0x1) {
         uint32_t maximum;
         if (!d.readVarU32(&maximum))
             return d.fail("expected maximum length");
 
         if (limits->initial > maximum) {
             return d.fail("memory size minimum must not be greater than maximum; "
-                          "maximum length %lu is less than initial length %lu",
+                          "maximum length %" PRIu32 " is less than initial length %" PRIu32 ,
                           maximum, limits->initial);
         }
 
         limits->maximum.emplace(maximum);
     }
 
     return true;
 }
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -773,17 +773,17 @@ class Decoder
     }
     explicit Decoder(const Bytes& bytes, UniqueChars* error = nullptr)
       : beg_(bytes.begin()),
         end_(bytes.end()),
         cur_(bytes.begin()),
         error_(error)
     {}
 
-    bool fail(const char* msg, ...);
+    bool fail(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
     bool fail(UniqueChars msg);
     void clearError() {
         if (error_)
             error_->reset();
     }
 
     bool done() const {
         MOZ_ASSERT(cur_ <= end_);
--- a/js/src/asmjs/WasmBinaryIterator.h
+++ b/js/src/asmjs/WasmBinaryIterator.h
@@ -720,17 +720,17 @@ ExprIter<Policy>::unrecognizedOpcode(Exp
 
     return fail(error.get());
 }
 
 template <typename Policy>
 bool
 ExprIter<Policy>::fail(const char* msg)
 {
-    return d_.fail(msg);
+    return d_.fail("%s", msg);
 }
 
 template <typename Policy>
 inline bool
 ExprIter<Policy>::pushControl(LabelKind kind, ExprType type, bool reachable)
 {
     return controlStack_.emplaceBack(kind, type, reachable, valueStack_.length());
 }
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -791,18 +791,20 @@ Code::ensureProfilingState(JSContext* cx
                 continue;
 
             TwoByteName name(cx);
             if (!getFuncDefName(cx, codeRange.funcDefIndex(), &name))
                 return false;
             if (!name.append('\0'))
                 return false;
 
-            UniqueChars label(JS_smprintf("%hs (%s:%u)",
-                                          name.begin(),
+            TwoByteChars chars(name.begin(), name.length());
+            UniqueChars utf8Name(JS::CharsToNewUTF8CharsZ(nullptr, chars).c_str());
+            UniqueChars label(JS_smprintf("%s (%s:%u)",
+                                          utf8Name.get(),
                                           metadata_->filename.get(),
                                           codeRange.funcLineOrBytecode()));
             if (!label) {
                 ReportOutOfMemory(cx);
                 return false;
             }
 
             if (codeRange.funcDefIndex() >= funcLabels_.length()) {
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -1483,17 +1483,17 @@ struct WasmParseContext
     WasmParseContext(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
       : ts(text, error),
         lifo(lifo),
         error(error),
         dtoaState(NewDtoaState())
     {}
 
     bool fail(const char* message) {
-        error->reset(JS_smprintf(message));
+        error->reset(js_strdup(message));
         return false;
     }
     ~WasmParseContext() {
         DestroyDtoaState(dtoaState);
     }
 };
 
 } // end anonymous namespace
@@ -3331,22 +3331,19 @@ class Resolver
         AstNameMap::Ptr p = map.lookup(ref.name());
         if (p) {
             ref.setIndex(p->value());
             return true;
         }
         return false;
     }
     bool failResolveLabel(const char* kind, AstName name) {
-        Vector<char16_t, 0, SystemAllocPolicy> nameWithNull;
-        if (!nameWithNull.append(name.begin(), name.length()))
-            return false;
-        if (!nameWithNull.append(0))
-            return false;
-        error_->reset(JS_smprintf("%s label '%hs' not found", kind, nameWithNull.begin()));
+        TwoByteChars chars(name.begin(), name.length());
+        UniqueChars utf8Chars(CharsToNewUTF8CharsZ(nullptr, chars).c_str());
+        error_->reset(JS_smprintf("%s label '%s' not found", kind, utf8Chars.get()));
         return false;
     }
 
   public:
     explicit Resolver(LifoAlloc& lifo, UniqueChars* error)
       : error_(error),
         varMap_(lifo),
         globalMap_(lifo),
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -36,19 +36,17 @@ using namespace js;
 using mozilla::ArrayLength;
 
 /* Thread-unsafe error management */
 
 static char gLastError[2000];
 
 #if defined(__APPLE__) || defined(__linux__) || defined(MOZ_CALLGRIND)
 static void
-#ifdef __GNUC__
-__attribute__((format(printf,1,2)))
-#endif
+MOZ_FORMAT_PRINTF(1, 2)
 UnsafeError(const char* format, ...)
 {
     va_list args;
     va_start(args, format);
     (void) VsprintfLiteral(gLastError, format, args);
     va_end(args);
 }
 #endif
--- a/js/src/devtools/gctrace/gcstats.cpp
+++ b/js/src/devtools/gctrace/gcstats.cpp
@@ -160,16 +160,17 @@ Array<uint64_t, MaxClasses> objectCountB
 std::vector<uint64_t> objectCountByType;
 Array<Array<uint64_t, MaxClasses>, HeapKinds> objectCountByHeapAndClass;
 Array<Array<Array<uint64_t, MaxLifetimeBins>, MaxClasses>, HeapKinds> objectCountByHeapClassAndLifetime;
 Array<Array<uint64_t, MaxLifetimeBins>, FinalizerKinds> heapObjectCountByFinalizerAndLifetime;
 Array<Array<uint64_t, MaxLifetimeBins>, MaxClasses> finalizedHeapObjectCountByClassAndLifetime;
 std::vector<Array<Array<uint64_t, MaxLifetimeBins>, HeapKinds> > objectCountByTypeHeapAndLifetime;
 
 static void
+MOZ_FORMAT_PRINTF(1, 2)
 die(const char* format, ...)
 {
     va_list va;
     va_start(va, format);
     vfprintf(stderr, format, va);
     fprintf(stderr, "\n");
     va_end(va);
     exit(1);
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -530,17 +530,17 @@ CheckHeapTracer::check(AutoLockForExclus
             stack.back().processed = true;
         }
     }
 
     if (oom)
         return;
 
     if (failures) {
-        fprintf(stderr, "Heap check: %zu failure(s) out of %" PRIu32 " pointers checked\n",
+        fprintf(stderr, "Heap check: %" PRIuSIZE " failure(s) out of %" PRIu32 " pointers checked\n",
                 failures, visited.count());
     }
     MOZ_RELEASE_ASSERT(failures == 0);
 }
 
 void
 js::gc::CheckHeapAfterGC(JSRuntime* rt)
 {
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1867,17 +1867,17 @@ irregexp::CompilePattern(JSContext* cx, 
     }
 
     if (node == nullptr)
         node = alloc.newInfallible<EndNode>(&alloc, EndNode::BACKTRACK);
 
     Analysis analysis(cx, ignore_case, is_ascii, unicode);
     analysis.EnsureAnalyzed(node);
     if (analysis.has_failed()) {
-        JS_ReportErrorASCII(cx, analysis.errorMessage());
+        JS_ReportErrorASCII(cx, "%s", analysis.errorMessage());
         return RegExpCode();
     }
 
     Maybe<jit::JitContext> ctx;
     Maybe<NativeRegExpMacroAssembler> native_assembler;
     Maybe<InterpretedRegExpMacroAssembler> interpreted_assembler;
 
     RegExpMacroAssembler* assembler;
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -1240,17 +1240,17 @@ BacktrackingAllocator::tryAllocateNonFix
     MOZ_ASSERT(!*success);
     return true;
 }
 
 bool
 BacktrackingAllocator::processBundle(MIRGenerator* mir, LiveBundle* bundle)
 {
     if (JitSpewEnabled(JitSpew_RegAlloc)) {
-        JitSpew(JitSpew_RegAlloc, "Allocating %s [priority %lu] [weight %lu]",
+        JitSpew(JitSpew_RegAlloc, "Allocating %s [priority %" PRIuSIZE "] [weight %" PRIuSIZE "]",
                 bundle->toString().get(), computePriority(bundle), computeSpillWeight(bundle));
     }
 
     // A bundle can be processed by doing any of the following:
     //
     // - Assigning the bundle a register. The bundle cannot overlap any other
     //   bundle allocated for that physical register.
     //
@@ -1432,23 +1432,23 @@ BacktrackingAllocator::tryAllocateRegist
         // case of multiple conflicting sets keep track of the set with the
         // lowest maximum spill weight.
 
         // The #ifdef guards against "unused variable 'existing'" bustage.
 #ifdef JS_JITSPEW
         if (JitSpewEnabled(JitSpew_RegAlloc)) {
             if (aliasedConflicting.length() == 1) {
                 LiveBundle* existing = aliasedConflicting[0];
-                JitSpew(JitSpew_RegAlloc, "  %s collides with %s [weight %lu]",
+                JitSpew(JitSpew_RegAlloc, "  %s collides with %s [weight %" PRIuSIZE "]",
                         r.reg.name(), existing->toString().get(), computeSpillWeight(existing));
             } else {
                 JitSpew(JitSpew_RegAlloc, "  %s collides with the following", r.reg.name());
                 for (size_t i = 0; i < aliasedConflicting.length(); i++) {
                     LiveBundle* existing = aliasedConflicting[i];
-                    JitSpew(JitSpew_RegAlloc, "      %s [weight %lu]",
+                    JitSpew(JitSpew_RegAlloc, "      %s [weight %" PRIuSIZE "]",
                             existing->toString().get(), computeSpillWeight(existing));
                 }
             }
         }
 #endif
 
         if (conflicting.empty()) {
             if (!conflicting.appendAll(aliasedConflicting))
@@ -1477,17 +1477,17 @@ BacktrackingAllocator::tryAllocateRegist
     *success = true;
     return true;
 }
 
 bool
 BacktrackingAllocator::evictBundle(LiveBundle* bundle)
 {
     if (JitSpewEnabled(JitSpew_RegAlloc)) {
-        JitSpew(JitSpew_RegAlloc, "  Evicting %s [priority %lu] [weight %lu]",
+        JitSpew(JitSpew_RegAlloc, "  Evicting %s [priority %" PRIuSIZE "] [weight %" PRIuSIZE "]",
                 bundle->toString().get(), computePriority(bundle), computeSpillWeight(bundle));
     }
 
     AnyRegister reg(bundle->allocation().toRegister());
     PhysicalRegister& physical = registers[reg.code()];
     MOZ_ASSERT(physical.reg == reg && physical.allocatable);
 
     for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) {
@@ -2291,17 +2291,18 @@ LiveRange::toString() const
     return UniqueChars(buf);
 }
 
 UniqueChars
 LiveBundle::toString() const
 {
     AutoEnterOOMUnsafeRegion oomUnsafe;
 
-    char *buf = JS_smprintf("");
+    // Suppress -Wformat warning.
+    char *buf = JS_smprintf("%s", "");
 
     for (LiveRange::BundleLinkIterator iter = rangesBegin(); buf && iter; iter++) {
         buf = JS_sprintf_append(buf, "%s %s",
                                 (iter == rangesBegin()) ? "" : " ##",
                                 LiveRange::get(*iter)->toString().get());
     }
 
     if (!buf)
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -225,33 +225,33 @@ struct BaselineStackBuilder
     }
 
     MOZ_MUST_USE bool writeWord(size_t w, const char* info) {
         if (!write<size_t>(w))
             return false;
         if (info) {
             if (sizeof(size_t) == 4) {
                 JitSpew(JitSpew_BaselineBailouts,
-                        "      WRITE_WRD %p/%p %-15s %08x",
+                        "      WRITE_WRD %p/%p %-15s %08" PRIxSIZE,
                         header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
             } else {
                 JitSpew(JitSpew_BaselineBailouts,
-                        "      WRITE_WRD %p/%p %-15s %016llx",
+                        "      WRITE_WRD %p/%p %-15s %016" PRIxSIZE,
                         header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
             }
         }
         return true;
     }
 
     MOZ_MUST_USE bool writeValue(const Value& val, const char* info) {
         if (!write<Value>(val))
             return false;
         if (info) {
             JitSpew(JitSpew_BaselineBailouts,
-                    "      WRITE_VAL %p/%p %-15s %016llx",
+                    "      WRITE_VAL %p/%p %-15s %016" PRIx64,
                     header_->copyStackBottom, virtualPointerAtStackOffset(0), info,
                     *((uint64_t*) &val));
         }
         return true;
     }
 
     MOZ_MUST_USE bool maybeWritePadding(size_t alignment, size_t after, const char* info) {
         MOZ_ASSERT(framePushed_ % sizeof(Value) == 0);
@@ -632,17 +632,17 @@ InitFromBailout(JSContext* cx, HandleScr
     // +---------------+
     // |    StackS     |
     // +---------------+  --- IF NOT LAST INLINE FRAME,
     // |  Descr(BLJS)  |  --- CALLING INFO STARTS HERE
     // +---------------+
     // |  ReturnAddr   | <-- return into main jitcode after IC
     // +===============+
 
-    JitSpew(JitSpew_BaselineBailouts, "      Unpacking %s:%d", script->filename(), script->lineno());
+    JitSpew(JitSpew_BaselineBailouts, "      Unpacking %s:%" PRIuSIZE, script->filename(), script->lineno());
     JitSpew(JitSpew_BaselineBailouts, "      [BASELINE-JS FRAME]");
 
     // Calculate and write the previous frame pointer value.
     // Record the virtual stack offset at this location.  Later on, if we end up
     // writing out a BaselineStub frame for the next callee, we'll need to save the
     // address.
     void* prevFramePtr = builder.calculatePrevFramePtr();
     if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
@@ -744,54 +744,54 @@ InitFromBailout(JSContext* cx, HandleScr
             v = iter.read();
             MOZ_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
             if (v.isObject())
                 argsObj = &v.toObject().as<ArgumentsObject>();
         }
     }
     JitSpew(JitSpew_BaselineBailouts, "      EnvChain=%p", envChain);
     blFrame->setEnvironmentChain(envChain);
-    JitSpew(JitSpew_BaselineBailouts, "      ReturnValue=%016llx", *((uint64_t*) &returnValue));
+    JitSpew(JitSpew_BaselineBailouts, "      ReturnValue=%016" PRIx64, *((uint64_t*) &returnValue));
     blFrame->setReturnValue(returnValue);
 
     // Do not need to initialize scratchValue field in BaselineFrame.
     blFrame->setFlags(flags);
 
     // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags.
     if (argsObj)
         blFrame->initArgsObjUnchecked(*argsObj);
 
     if (fun) {
         // The unpacked thisv and arguments should overwrite the pushed args present
         // in the calling frame.
         Value thisv = iter.read();
         JitSpew(JitSpew_BaselineBailouts, "      Is function!");
-        JitSpew(JitSpew_BaselineBailouts, "      thisv=%016llx", *((uint64_t*) &thisv));
+        JitSpew(JitSpew_BaselineBailouts, "      thisv=%016" PRIx64, *((uint64_t*) &thisv));
 
         size_t thisvOffset = builder.framePushed() + JitFrameLayout::offsetOfThis();
         builder.valuePointerAtStackOffset(thisvOffset).set(thisv);
 
         MOZ_ASSERT(iter.numAllocations() >= CountArgSlots(script, fun));
-        JitSpew(JitSpew_BaselineBailouts, "      frame slots %u, nargs %u, nfixed %u",
+        JitSpew(JitSpew_BaselineBailouts, "      frame slots %u, nargs %" PRIuSIZE ", nfixed %" PRIuSIZE,
                 iter.numAllocations(), fun->nargs(), script->nfixed());
 
         if (!callerPC) {
             // This is the first frame. Store the formals in a Vector until we
             // are done. Due to UCE and phi elimination, we could store an
             // UndefinedValue() here for formals we think are unused, but
             // locals may still reference the original argument slot
             // (MParameter/LArgument) and expect the original Value.
             MOZ_ASSERT(startFrameFormals.empty());
             if (!startFrameFormals.resize(fun->nargs()))
                 return false;
         }
 
         for (uint32_t i = 0; i < fun->nargs(); i++) {
             Value arg = iter.read();
-            JitSpew(JitSpew_BaselineBailouts, "      arg %d = %016llx",
+            JitSpew(JitSpew_BaselineBailouts, "      arg %d = %016" PRIx64,
                         (int) i, *((uint64_t*) &arg));
             if (callerPC) {
                 size_t argOffset = builder.framePushed() + JitFrameLayout::offsetOfActualArg(i);
                 builder.valuePointerAtStackOffset(argOffset).set(arg);
             } else {
                 startFrameFormals[i].set(arg);
             }
         }
@@ -1309,17 +1309,17 @@ InitFromBailout(JSContext* cx, HandleScr
                                                         JitFrame_BaselineStub,
                                                         JitFrameLayout::Size());
 
     // Push actual argc
     if (!builder.writeWord(actualArgc, "ActualArgc"))
         return false;
 
     // Push callee token (must be a JS Function)
-    JitSpew(JitSpew_BaselineBailouts, "      Callee = %016llx", callee.asRawBits());
+    JitSpew(JitSpew_BaselineBailouts, "      Callee = %016" PRIx64, callee.asRawBits());
 
     JSFunction* calleeFun = &callee.toObject().as<JSFunction>();
     if (!builder.writePtr(CalleeToToken(calleeFun, JSOp(*pc) == JSOP_NEW), "CalleeToken"))
         return false;
     nextCallee.set(calleeFun);
 
     // Push BaselineStub frame descriptor
     if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
@@ -1489,17 +1489,17 @@ jit::BailoutIonToBaseline(JSContext* cx,
     //      +---------------+
     //      |  ReturnAddr   |
     //      +---------------+
     //      |    |||||      | <---- Overwrite starting here.
     //      |    |||||      |
     //      |    |||||      |
     //      +---------------+
 
-    JitSpew(JitSpew_BaselineBailouts, "Bailing to baseline %s:%u (IonScript=%p) (FrameType=%d)",
+    JitSpew(JitSpew_BaselineBailouts, "Bailing to baseline %s:%" PRIuSIZE " (IonScript=%p) (FrameType=%d)",
             iter.script()->filename(), iter.script()->lineno(), (void*) iter.ionScript(),
             (int) prevFrameType);
 
     bool catchingException;
     bool propagatingExceptionForDebugMode;
     if (excInfo) {
         catchingException = excInfo->catchingException();
         propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode();
@@ -1509,17 +1509,17 @@ jit::BailoutIonToBaseline(JSContext* cx,
 
         if (propagatingExceptionForDebugMode)
             JitSpew(JitSpew_BaselineBailouts, "Resuming in-place for debug mode");
     } else {
         catchingException = false;
         propagatingExceptionForDebugMode = false;
     }
 
-    JitSpew(JitSpew_BaselineBailouts, "  Reading from snapshot offset %u size %u",
+    JitSpew(JitSpew_BaselineBailouts, "  Reading from snapshot offset %u size %" PRIuSIZE,
             iter.snapshotOffset(), iter.ionScript()->snapshotsListSize());
 
     if (!excInfo)
         iter.ionScript()->incNumBailouts();
     iter.script()->updateBaselineOrIonRaw(cx->runtime());
 
     // Allocate buffer to hold stack replacement data.
     BaselineStackBuilder builder(iter, 1024);
@@ -1535,17 +1535,17 @@ jit::BailoutIonToBaseline(JSContext* cx,
 
 #ifdef TRACK_SNAPSHOTS
     snapIter.spewBailingFrom();
 #endif
 
     RootedFunction callee(cx, iter.maybeCallee());
     RootedScript scr(cx, iter.script());
     if (callee) {
-        JitSpew(JitSpew_BaselineBailouts, "  Callee function (%s:%u)",
+        JitSpew(JitSpew_BaselineBailouts, "  Callee function (%s:%" PRIuSIZE ")",
                 scr->filename(), scr->lineno());
     } else {
         JitSpew(JitSpew_BaselineBailouts, "  No callee!");
     }
 
     if (iter.isConstructing())
         JitSpew(JitSpew_BaselineBailouts, "  Constructing!");
     else
@@ -1570,17 +1570,17 @@ jit::BailoutIonToBaseline(JSContext* cx,
             // TraceLogger doesn't create entries for inlined frames. But we
             // see them in Baseline. Here we create the start events of those
             // entries. So they correspond to what we will see in Baseline.
             TraceLoggerEvent scriptEvent(logger, TraceLogger_Scripts, scr);
             TraceLogStartEvent(logger, scriptEvent);
             TraceLogStartEvent(logger, TraceLogger_Baseline);
         }
 
-        JitSpew(JitSpew_BaselineBailouts, "    FrameNo %d", frameNo);
+        JitSpew(JitSpew_BaselineBailouts, "    FrameNo %" PRIuSIZE, frameNo);
 
         // If we are bailing out to a catch or finally block in this frame,
         // pass excInfo to InitFromBailout and don't unpack any other frames.
         bool handleException = (catchingException && excInfo->frameNo() == frameNo);
 
         // We also need to pass excInfo if we're bailing out in place for
         // debug mode.
         bool passExcInfo = handleException || propagatingExceptionForDebugMode;
@@ -1666,57 +1666,57 @@ InvalidateAfterBailout(JSContext* cx, Ha
 
     JitSpew(JitSpew_BaselineBailouts, "Invalidating due to %s", reason);
     Invalidate(cx, outerScript);
 }
 
 static void
 HandleBoundsCheckFailure(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
 {
-    JitSpew(JitSpew_IonBailouts, "Bounds check failure %s:%d, inlined into %s:%d",
+    JitSpew(JitSpew_IonBailouts, "Bounds check failure %s:%" PRIuSIZE ", inlined into %s:%" PRIuSIZE,
             innerScript->filename(), innerScript->lineno(),
             outerScript->filename(), outerScript->lineno());
 
     if (!innerScript->failedBoundsCheck())
         innerScript->setFailedBoundsCheck();
 
     InvalidateAfterBailout(cx, outerScript, "bounds check failure");
     if (innerScript->hasIonScript())
         Invalidate(cx, innerScript);
 }
 
 static void
 HandleShapeGuardFailure(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
 {
-    JitSpew(JitSpew_IonBailouts, "Shape guard failure %s:%d, inlined into %s:%d",
+    JitSpew(JitSpew_IonBailouts, "Shape guard failure %s:%" PRIuSIZE ", inlined into %s:%" PRIuSIZE,
             innerScript->filename(), innerScript->lineno(),
             outerScript->filename(), outerScript->lineno());
 
     // TODO: Currently this mimic's Ion's handling of this case.  Investigate setting
     // the flag on innerScript as opposed to outerScript, and maybe invalidating both
     // inner and outer scripts, instead of just the outer one.
     outerScript->setFailedShapeGuard();
 
     InvalidateAfterBailout(cx, outerScript, "shape guard failure");
 }
 
 static void
 HandleBaselineInfoBailout(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
 {
-    JitSpew(JitSpew_IonBailouts, "Baseline info failure %s:%d, inlined into %s:%d",
+    JitSpew(JitSpew_IonBailouts, "Baseline info failure %s:%" PRIuSIZE ", inlined into %s:%" PRIuSIZE,
             innerScript->filename(), innerScript->lineno(),
             outerScript->filename(), outerScript->lineno());
 
     InvalidateAfterBailout(cx, outerScript, "invalid baseline info");
 }
 
 static void
 HandleLexicalCheckFailure(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
 {
-    JitSpew(JitSpew_IonBailouts, "Lexical check failure %s:%d, inlined into %s:%d",
+    JitSpew(JitSpew_IonBailouts, "Lexical check failure %s:%" PRIuSIZE ", inlined into %s:%" PRIuSIZE,
             innerScript->filename(), innerScript->lineno(),
             outerScript->filename(), outerScript->lineno());
 
     if (!innerScript->failedLexicalCheck())
         innerScript->setFailedLexicalCheck();
 
     InvalidateAfterBailout(cx, outerScript, "lexical check failure");
     if (innerScript->hasIonScript())
@@ -1749,17 +1749,17 @@ CopyFromRematerializedFrame(JSContext* c
         *frame->valueSlot(i) = rematFrame->locals()[i];
 
     frame->setReturnValue(rematFrame->returnValue());
 
     if (rematFrame->hasCachedSavedFrame())
         frame->setHasCachedSavedFrame();
 
     JitSpew(JitSpew_BaselineBailouts,
-            "  Copied from rematerialized frame at (%p,%u)",
+            "  Copied from rematerialized frame at (%p,%" PRIuSIZE ")",
             fp, inlineDepth);
 
     // Propagate the debuggee frame flag. For the case where the Debugger did
     // not rematerialize an Ion frame, the baseline frame has its debuggee
     // flag set iff its script is considered a debuggee. See the debuggee case
     // in InitFromBailout.
     if (rematFrame->isDebuggee()) {
         frame->setIsDebuggee();
@@ -1886,17 +1886,17 @@ jit::FinishBailoutToBaseline(BaselineBai
         // the table to keep the table up to date.
         act->removeRematerializedFrame(outerFp);
 
         if (!ok)
             return false;
     }
 
     JitSpew(JitSpew_BaselineBailouts,
-            "  Restored outerScript=(%s:%u,%u) innerScript=(%s:%u,%u) (bailoutKind=%u)",
+            "  Restored outerScript=(%s:%" PRIuSIZE ",%u) innerScript=(%s:%" PRIuSIZE ",%u) (bailoutKind=%u)",
             outerScript->filename(), outerScript->lineno(), outerScript->getWarmUpCount(),
             innerScript->filename(), innerScript->lineno(), innerScript->getWarmUpCount(),
             (unsigned) bailoutKind);
 
     switch (bailoutKind) {
       // Normal bailouts.
       case Bailout_Inevitable:
       case Bailout_DuringVMCall:
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/BaselineCompiler.h"
 
 #include "mozilla/Casting.h"
+#include "mozilla/SizePrintfMacros.h"
 
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/FixedList.h"
 #include "jit/IonAnalysis.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
@@ -77,20 +78,20 @@ BaselineCompiler::addPCMappingEntry(bool
     entry.addIndexEntry = addIndexEntry;
 
     return pcMappingEntries_.append(entry);
 }
 
 MethodStatus
 BaselineCompiler::compile()
 {
-    JitSpew(JitSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
+    JitSpew(JitSpew_BaselineScripts, "Baseline compiling script %s:%" PRIuSIZE " (%p)",
             script->filename(), script->lineno(), script);
 
-    JitSpew(JitSpew_Codegen, "# Emitting baseline code for script %s:%d",
+    JitSpew(JitSpew_Codegen, "# Emitting baseline code for script %s:%" PRIuSIZE,
             script->filename(), script->lineno());
 
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
     TraceLoggerEvent scriptEvent(logger, TraceLogger_AnnotateScripts, script);
     AutoTraceLog logScript(logger, scriptEvent);
     AutoTraceLog logCompile(logger, TraceLogger_BaselineCompilation);
 
     if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
@@ -212,17 +213,17 @@ BaselineCompiler::compile()
     if (!baselineScript) {
         ReportOutOfMemory(cx);
         return Method_Error;
     }
 
     baselineScript->setMethod(code);
     baselineScript->setTemplateEnvironment(templateEnv);
 
-    JitSpew(JitSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d",
+    JitSpew(JitSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%" PRIuSIZE,
             (void*) baselineScript.get(), (void*) code->raw(),
             script->filename(), script->lineno());
 
 #ifdef JS_ION_PERF
     writePerfSpewerBaselineProfile(script, code);
 #endif
 
     MOZ_ASSERT(pcMappingIndexEntries.length() > 0);
@@ -274,17 +275,17 @@ BaselineCompiler::compile()
     baselineScript->copyYieldEntries(script, yieldOffsets_);
 
     if (compileDebugInstrumentation_)
         baselineScript->setHasDebugInstrumentation();
 
     // Always register a native => bytecode mapping entry, since profiler can be
     // turned on with baseline jitcode on stack, and baseline jitcode cannot be invalidated.
     {
-        JitSpew(JitSpew_Profiling, "Added JitcodeGlobalEntry for baseline script %s:%d (%p)",
+        JitSpew(JitSpew_Profiling, "Added JitcodeGlobalEntry for baseline script %s:%" PRIuSIZE " (%p)",
                     script->filename(), script->lineno(), baselineScript.get());
 
         // Generate profiling string.
         char* str = JitcodeGlobalEntry::createScriptString(cx, script);
         if (!str)
             return Method_Error;
 
         JitcodeGlobalEntry::BaselineEntry entry;
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/BaselineDebugModeOSR.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/SizePrintfMacros.h"
 
 #include "jit/BaselineIC.h"
 #include "jit/JitcodeMap.h"
 #include "jit/Linker.h"
 #include "jit/PerfSpewer.h"
 
 #include "jit/JitFrames-inl.h"
 #include "jit/MacroAssembler-inl.h"
@@ -317,27 +318,27 @@ ICEntryKindToString(ICEntry::Kind kind)
 }
 #endif // JS_JITSPEW
 
 static void
 SpewPatchBaselineFrame(uint8_t* oldReturnAddress, uint8_t* newReturnAddress,
                        JSScript* script, ICEntry::Kind frameKind, jsbytecode* pc)
 {
     JitSpew(JitSpew_BaselineDebugModeOSR,
-            "Patch return %p -> %p on BaselineJS frame (%s:%d) from %s at %s",
+            "Patch return %p -> %p on BaselineJS frame (%s:%" PRIuSIZE ") from %s at %s",
             oldReturnAddress, newReturnAddress, script->filename(), script->lineno(),
             ICEntryKindToString(frameKind), CodeName[(JSOp)*pc]);
 }
 
 static void
 SpewPatchBaselineFrameFromExceptionHandler(uint8_t* oldReturnAddress, uint8_t* newReturnAddress,
                                            JSScript* script, jsbytecode* pc)
 {
     JitSpew(JitSpew_BaselineDebugModeOSR,
-            "Patch return %p -> %p on BaselineJS frame (%s:%d) from exception handler at %s",
+            "Patch return %p -> %p on BaselineJS frame (%s:%" PRIuSIZE ") from exception handler at %s",
             oldReturnAddress, newReturnAddress, script->filename(), script->lineno(),
             CodeName[(JSOp)*pc]);
 }
 
 static void
 SpewPatchStubFrame(ICStub* oldStub, ICStub* newStub)
 {
     JitSpew(JitSpew_BaselineDebugModeOSR,
@@ -662,17 +663,17 @@ RecompileBaselineScriptForDebugMode(JSCo
 {
     BaselineScript* oldBaselineScript = script->baselineScript();
 
     // If a script is on the stack multiple times, it may have already
     // been recompiled.
     if (oldBaselineScript->hasDebugInstrumentation() == observing)
         return true;
 
-    JitSpew(JitSpew_BaselineDebugModeOSR, "Recompiling (%s:%d) for %s",
+    JitSpew(JitSpew_BaselineDebugModeOSR, "Recompiling (%s:%" PRIuSIZE ") for %s",
             script->filename(), script->lineno(), observing ? "DEBUGGING" : "NORMAL EXECUTION");
 
     script->setBaselineScript(cx->runtime(), nullptr);
 
     MethodStatus status = BaselineCompile(cx, script, /* forceDebugMode = */ observing);
     if (status != Method_Compiled) {
         // We will only fail to recompile for debug mode due to OOM. Restore
         // the old baseline script in case something doesn't properly
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -2581,17 +2581,17 @@ DoSetElemFallback(JSContext* cx, Baselin
                 return false;
 
             if (addingCase &&
                 !DenseOrUnboxedArraySetElemStubExists(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd,
                                                       stub, obj))
             {
                 JitSpew(JitSpew_BaselineIC,
                         "  Generating SetElem_DenseOrUnboxedArrayAdd stub "
-                        "(shape=%p, group=%p, protoDepth=%u)",
+                        "(shape=%p, group=%p, protoDepth=%" PRIuSIZE ")",
                         shape.get(), group.get(), protoDepth);
                 ICSetElemDenseOrUnboxedArrayAddCompiler compiler(cx, obj, protoDepth);
                 ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(outerScript));
                 if (!newStub)
                     return false;
                 if (compiler.needsUpdateStubs() &&
                     !newStub->addUpdateStubForValue(cx, outerScript, obj, JSID_VOIDHANDLE, rhs))
                 {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -9239,17 +9239,17 @@ CodeGenerator::generateWasm(wasm::SigIdD
     MOZ_ASSERT(safepoints_.size() == 0);
     MOZ_ASSERT(!scriptCounts_);
     return true;
 }
 
 bool
 CodeGenerator::generate()
 {
-    JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%d",
+    JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%" PRIuSIZE,
             gen->info().script()->filename(),
             gen->info().script()->lineno());
 
     // Initialize native code table with an entry to the start of
     // top-level script.
     InlineScriptTree* tree = gen->info().inlineScriptTree();
     jsbytecode* startPC = tree->script()->code();
     BytecodeSite* startSite = new(gen->alloc()) BytecodeSite(tree, startPC);
--- a/js/src/jit/FlowAliasAnalysis.cpp
+++ b/js/src/jit/FlowAliasAnalysis.cpp
@@ -397,17 +397,17 @@ DumpLoopInvariant(MDefinition* load, MBa
 
 static void
 DumpImprovement(MDefinition *load, MDefinitionVector& input, MDefinitionVector& output)
 {
 #ifdef JS_JITSPEW
     if (JitSpewEnabled(JitSpew_Alias)) {
         Fprinter &print = JitSpewPrinter();
         JitSpewHeader(JitSpew_Alias);
-        print.printf("   Improve dependency from ", load->id());
+        print.printf("   Improve dependency from %d", load->id());
         DumpStoreList(input);
         print.printf(" to ");
         DumpStoreList(output);
         print.printf("\n");
     }
 #endif
 }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/Ion.h"
 
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/ThreadLocal.h"
 
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsprf.h"
 
@@ -2131,17 +2132,17 @@ TrackIonAbort(JSContext* cx, JSScript* s
     void* ptr = script->baselineScript()->method()->raw();
     JitcodeGlobalEntry& entry = table->lookupInfallible(ptr);
     entry.baselineEntry().trackIonAbort(pc, message);
 }
 
 static void
 TrackAndSpewIonAbort(JSContext* cx, JSScript* script, const char* message)
 {
-    JitSpew(JitSpew_IonAbort, message);
+    JitSpew(JitSpew_IonAbort, "%s", message);
     TrackIonAbort(cx, script, script->code(), message);
 }
 
 static AbortReason
 IonCompile(JSContext* cx, JSScript* script,
            BaselineFrame* baselineFrame, jsbytecode* osrPc,
            bool recompile, OptimizationLevel optimizationLevel)
 {
@@ -2390,17 +2391,17 @@ CheckScriptSize(JSContext* cx, JSScript*
         return Method_Compiled;
 
     uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
 
     if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
         numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
     {
         if (!OffThreadCompilationAvailable(cx)) {
-            JitSpew(JitSpew_IonAbort, "Script too large (%u bytes) (%u locals/args)",
+            JitSpew(JitSpew_IonAbort, "Script too large (%" PRIuSIZE " bytes) (%u locals/args)",
                     script->length(), numLocalsAndArgs);
             TrackIonAbort(cx, script, script->code(), "too large");
             return Method_CantCompile;
         }
     }
 
     return Method_Compiled;
 }
@@ -3007,51 +3008,51 @@ InvalidateActivation(FreeOp* fop, const 
     size_t frameno = 1;
 
     for (JitFrameIterator it(activations); !it.done(); ++it, ++frameno) {
         MOZ_ASSERT_IF(frameno == 1, it.isExitFrame() || it.type() == JitFrame_Bailout);
 
 #ifdef JS_JITSPEW
         switch (it.type()) {
           case JitFrame_Exit:
-            JitSpew(JitSpew_IonInvalidate, "#%d exit frame @ %p", frameno, it.fp());
+            JitSpew(JitSpew_IonInvalidate, "#%" PRIuSIZE " exit frame @ %p", frameno, it.fp());
             break;
           case JitFrame_BaselineJS:
           case JitFrame_IonJS:
           case JitFrame_Bailout:
           {
             MOZ_ASSERT(it.isScripted());
             const char* type = "Unknown";
             if (it.isIonJS())
                 type = "Optimized";
             else if (it.isBaselineJS())
                 type = "Baseline";
             else if (it.isBailoutJS())
                 type = "Bailing";
             JitSpew(JitSpew_IonInvalidate,
-                    "#%d %s JS frame @ %p, %s:%" PRIuSIZE " (fun: %p, script: %p, pc %p)",
+                    "#%" PRIuSIZE " %s JS frame @ %p, %s:%" PRIuSIZE " (fun: %p, script: %p, pc %p)",
                     frameno, type, it.fp(), it.script()->maybeForwardedFilename(),
                     it.script()->lineno(), it.maybeCallee(), (JSScript*)it.script(),
                     it.returnAddressToFp());
             break;
           }
           case JitFrame_IonStub:
-            JitSpew(JitSpew_IonInvalidate, "#%d ion stub frame @ %p", frameno, it.fp());
+            JitSpew(JitSpew_IonInvalidate, "#%" PRIuSIZE " ion stub frame @ %p", frameno, it.fp());
             break;
           case JitFrame_BaselineStub:
-            JitSpew(JitSpew_IonInvalidate, "#%d baseline stub frame @ %p", frameno, it.fp());
+            JitSpew(JitSpew_IonInvalidate, "#%" PRIuSIZE " baseline stub frame @ %p", frameno, it.fp());
             break;
           case JitFrame_Rectifier:
-            JitSpew(JitSpew_IonInvalidate, "#%d rectifier frame @ %p", frameno, it.fp());
+            JitSpew(JitSpew_IonInvalidate, "#%" PRIuSIZE " rectifier frame @ %p", frameno, it.fp());
             break;
           case JitFrame_IonAccessorIC:
-            JitSpew(JitSpew_IonInvalidate, "#%d ion IC getter/setter frame @ %p", frameno, it.fp());
+            JitSpew(JitSpew_IonInvalidate, "#%" PRIuSIZE " ion IC getter/setter frame @ %p", frameno, it.fp());
             break;
           case JitFrame_Entry:
-            JitSpew(JitSpew_IonInvalidate, "#%d entry frame @ %p", frameno, it.fp());
+            JitSpew(JitSpew_IonInvalidate, "#%" PRIuSIZE " entry frame @ %p", frameno, it.fp());
             break;
         }
 #endif // JS_JITSPEW
 
         if (!it.isIonScripted())
             continue;
 
         bool calledFromLinkStub = false;
@@ -3136,17 +3137,17 @@ InvalidateActivation(FreeOp* fop, const 
         CodeLocationLabel dataLabelToMunge(it.returnAddressToFp());
         ptrdiff_t delta = ionScript->invalidateEpilogueDataOffset() -
                           (it.returnAddressToFp() - ionCode->raw());
         Assembler::PatchWrite_Imm32(dataLabelToMunge, Imm32(delta));
 
         CodeLocationLabel osiPatchPoint = SafepointReader::InvalidationPatchPoint(ionScript, si);
         CodeLocationLabel invalidateEpilogue(ionCode, CodeOffset(ionScript->invalidateEpilogueOffset()));
 
-        JitSpew(JitSpew_IonInvalidate, "   ! Invalidate ionScript %p (inv count %u) -> patching osipoint %p",
+        JitSpew(JitSpew_IonInvalidate, "   ! Invalidate ionScript %p (inv count %" PRIuSIZE ") -> patching osipoint %p",
                 ionScript, ionScript->invalidationCount(), (void*) osiPatchPoint.raw());
         Assembler::PatchWrite_NearCall(osiPatchPoint, invalidateEpilogue);
     }
 
     JitSpew(JitSpew_IonInvalidate, "END invalidating activation");
 }
 
 void
@@ -3353,17 +3354,17 @@ PerThreadData::setAutoFlushICache(AutoFl
 // AutoFlushICache context.
 void
 AutoFlushICache::setRange(uintptr_t start, size_t len)
 {
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     AutoFlushICache* afc = TlsPerThreadData.get()->PerThreadData::autoFlushICache();
     MOZ_ASSERT(afc);
     MOZ_ASSERT(!afc->start_);
-    JitSpewCont(JitSpew_CacheFlush, "(%x %x):", start, len);
+    JitSpewCont(JitSpew_CacheFlush, "(%" PRIxPTR " %" PRIxSIZE "):", start, len);
 
     uintptr_t stop = start + len;
     afc->start_ = start;
     afc->stop_ = stop;
 #endif
 }
 
 // Flush the instruction cache.
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/IonAnalysis.h"
 
+#include "mozilla/SizePrintfMacros.h"
+
 #include "jit/AliasAnalysis.h"
 #include "jit/BaselineInspector.h"
 #include "jit/BaselineJIT.h"
 #include "jit/FlowAliasAnalysis.h"
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/LIR.h"
@@ -440,19 +442,20 @@ jit::PruneUnusedBranches(MIRGenerator* m
             // Interpreters are often implemented as a table switch within a for
             // loop. What might happen is that the interpreter heats up in a
             // subset of instructions, but might need other instructions for the
             // rest of the evaluation.
             if (numSuccessorsOfPreds > 8)
                 shouldBailout = false;
 
             JitSpew(JitSpew_Prune, "info: block %d,"
-                    " predCount: %lu, domInst: %lu, span: %lu, effectful: %lu, "
-                    " isLoopExit: %s, numSuccessorsOfPred: %lu."
-                    " (score: %lu, shouldBailout: %s)",
+                    " predCount: %" PRIuSIZE ", domInst: %" PRIuSIZE
+                    ", span: %" PRIuSIZE ", effectful: %" PRIuSIZE ", "
+                    " isLoopExit: %s, numSuccessorsOfPred: %" PRIuSIZE "."
+                    " (score: %" PRIuSIZE ", shouldBailout: %s)",
                     block->id(), predCount, numDominatedInst, branchSpan, numEffectfulInst,
                     isLoopExit ? "true" : "false", numSuccessorsOfPreds,
                     score, shouldBailout ? "true" : "false");
         }
 
         // Continue to the next basic block if the current basic block can
         // remain unchanged.
         if (!isUnreachable && !shouldBailout)
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -224,17 +224,17 @@ class IonBuilder
 
   private:
     MOZ_MUST_USE bool traverseBytecode();
     ControlStatus snoopControlFlow(JSOp op);
     MOZ_MUST_USE bool processIterators();
     MOZ_MUST_USE bool inspectOpcode(JSOp op);
     uint32_t readIndex(jsbytecode* pc);
     JSAtom* readAtom(jsbytecode* pc);
-    bool abort(const char* message, ...);
+    bool abort(const char* message, ...) MOZ_FORMAT_PRINTF(2, 3);
     void trackActionableAbort(const char* message);
     void spew(const char* message);
 
     JSFunction* getSingleCallTarget(TemporaryTypeSet* calleeTypes);
     MOZ_MUST_USE bool getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
                                          ObjectVector& targets, uint32_t maxTargets);
 
     void popCfgStack();
--- a/js/src/jit/JSONSpewer.cpp
+++ b/js/src/jit/JSONSpewer.cpp
@@ -3,16 +3,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef JS_JITSPEW
 
 #include "jit/JSONSpewer.h"
 
+#include "mozilla/SizePrintfMacros.h"
+
 #include <stdarg.h>
 
 #include "jit/BacktrackingAllocator.h"
 #include "jit/LIR.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "jit/RangeAnalysis.h"
 
@@ -141,27 +143,27 @@ JSONSpewer::endList()
     first_ = false;
 }
 
 void
 JSONSpewer::beginFunction(JSScript* script)
 {
     beginObject();
     if (script)
-        stringProperty("name", "%s:%d", script->filename(), script->lineno());
+        stringProperty("name", "%s:%" PRIuSIZE, script->filename(), script->lineno());
     else
         stringProperty("name", "asm.js compilation");
     beginListProperty("passes");
 }
 
 void
 JSONSpewer::beginPass(const char* pass)
 {
     beginObject();
-    stringProperty("name", pass);
+    stringProperty("name", "%s", pass);
 }
 
 void
 JSONSpewer::spewMResumePoint(MResumePoint* rp)
 {
     if (!rp)
         return;
 
--- a/js/src/jit/JSONSpewer.h
+++ b/js/src/jit/JSONSpewer.h
@@ -31,18 +31,18 @@ class JSONSpewer
     GenericPrinter& out_;
 
     void indent();
 
     void property(const char* name);
     void beginObject();
     void beginObjectProperty(const char* name);
     void beginListProperty(const char* name);
-    void stringValue(const char* format, ...);
-    void stringProperty(const char* name, const char* format, ...);
+    void stringValue(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3);
+    void stringProperty(const char* name, const char* format, ...) MOZ_FORMAT_PRINTF(3, 4);
     void beginStringProperty(const char* name);
     void endStringProperty();
     void integerValue(int value);
     void integerProperty(const char* name, int value);
     void endObject();
     void endList();
 
   public:
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -167,19 +167,19 @@ class JitSpewIndent
 {
     JitSpewChannel channel_;
 
   public:
     explicit JitSpewIndent(JitSpewChannel channel);
     ~JitSpewIndent();
 };
 
-void JitSpew(JitSpewChannel channel, const char* fmt, ...);
-void JitSpewStart(JitSpewChannel channel, const char* fmt, ...);
-void JitSpewCont(JitSpewChannel channel, const char* fmt, ...);
+void JitSpew(JitSpewChannel channel, const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
+void JitSpewStart(JitSpewChannel channel, const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
+void JitSpewCont(JitSpewChannel channel, const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
 void JitSpewFin(JitSpewChannel channel);
 void JitSpewHeader(JitSpewChannel channel);
 bool JitSpewEnabled(JitSpewChannel channel);
 void JitSpewVA(JitSpewChannel channel, const char* fmt, va_list ap);
 void JitSpewStartVA(JitSpewChannel channel, const char* fmt, va_list ap);
 void JitSpewContVA(JitSpewChannel channel, const char* fmt, va_list ap);
 void JitSpewDef(JitSpewChannel channel, const char* str, MDefinition* def);
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5157,17 +5157,17 @@ MLoadSlot::printOpcode(GenericPrinter& o
     MDefinition::printOpcode(out);
     out.printf(" %d", slot());
 }
 
 void
 MStoreSlot::printOpcode(GenericPrinter& out) const
 {
     PrintOpcodeName(out, op());
-    out.printf(" ", slot());
+    out.printf(" ");
     getOperand(0)->printName(out);
     out.printf(" %d ", slot());
     getOperand(1)->printName(out);
 }
 
 MDefinition*
 MFunctionEnvironment::foldsTo(TempAllocator& alloc)
 {
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -67,17 +67,17 @@ class MIRGenerator
         size_t bytes;
         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(count, &bytes)))
             return nullptr;
         return static_cast<T*>(alloc().allocate(bytes));
     }
 
     // Set an error state and prints a message. Returns false so errors can be
     // propagated up.
-    bool abort(const char* message, ...);           // always returns false
+    bool abort(const char* message, ...) MOZ_FORMAT_PRINTF(2, 3); // always returns false
     bool abortFmt(const char* message, va_list ap); // always returns false
 
     bool errored() const {
         return error_;
     }
 
     MOZ_MUST_USE bool instrumentedProfiling() {
         if (!instrumentedProfilingIsCached_) {
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -321,17 +321,17 @@ UniqueTrackedOptimizations::sortByFreque
     for (size_t i = 0; i < entries.length(); i++) {
         Key key;
         key.types = entries[i].types;
         key.attempts = entries[i].attempts;
         AttemptsMap::Ptr p = map_.lookup(key);
         MOZ_ASSERT(p);
         p->value().index = sorted_.length();
 
-        JitSpew(JitSpew_OptimizationTracking, "   Entry %u has frequency %u",
+        JitSpew(JitSpew_OptimizationTracking, "   Entry %" PRIuSIZE " has frequency %" PRIu32,
                 sorted_.length(), p->value().frequency);
 
         if (!sorted_.append(entries[i]))
             return false;
     }
 
     return true;
 }
@@ -760,41 +760,41 @@ IonTrackedOptimizationsRegion::WriteDelt
 
 /* static */ bool
 IonTrackedOptimizationsRegion::WriteRun(CompactBufferWriter& writer,
                                         const NativeToTrackedOptimizations* start,
                                         const NativeToTrackedOptimizations* end,
                                         const UniqueTrackedOptimizations& unique)
 {
     // Write the header, which is the range that this whole run encompasses.
-    JitSpew(JitSpew_OptimizationTracking, "     Header: [%u, %u]",
+    JitSpew(JitSpew_OptimizationTracking, "     Header: [%" PRIuSIZE ", %" PRIuSIZE "]",
             start->startOffset.offset(), (end - 1)->endOffset.offset());
     writer.writeUnsigned(start->startOffset.offset());
     writer.writeUnsigned((end - 1)->endOffset.offset());
 
     // Write the first entry of the run, which is not delta-encoded.
     JitSpew(JitSpew_OptimizationTracking,
-            "     [%6u, %6u]                        vector %3u, offset %4u",
+            "     [%6" PRIuSIZE ", %6" PRIuSIZE "]                        vector %3u, offset %4" PRIuSIZE,
             start->startOffset.offset(), start->endOffset.offset(),
             unique.indexOf(start->optimizations), writer.length());
     uint32_t prevEndOffset = start->endOffset.offset();
     writer.writeUnsigned(prevEndOffset);
     writer.writeByte(unique.indexOf(start->optimizations));
 
     // Delta encode the run.
     for (const NativeToTrackedOptimizations* entry = start + 1; entry != end; entry++) {
         uint32_t startOffset = entry->startOffset.offset();
         uint32_t endOffset = entry->endOffset.offset();
 
         uint32_t startDelta = startOffset - prevEndOffset;
         uint32_t length = endOffset - startOffset;
         uint8_t index = unique.indexOf(entry->optimizations);
 
         JitSpew(JitSpew_OptimizationTracking,
-                "     [%6u, %6u] delta [+%5u, +%5u] vector %3u, offset %4u",
+                "     [%6u, %6u] delta [+%5u, +%5u] vector %3u, offset %4" PRIuSIZE,
                 startOffset, endOffset, startDelta, length, index, writer.length());
 
         WriteDelta(writer, startDelta, length, index);
 
         prevEndOffset = endOffset;
     }
 
     if (writer.oom())
@@ -820,17 +820,17 @@ WriteOffsetsTable(CompactBufferWriter& w
     uint32_t tableOffset = writer.length();
 
     // Write how many bytes were padded and numEntries.
     writer.writeNativeEndianUint32_t(padding);
     writer.writeNativeEndianUint32_t(offsets.length());
 
     // Write entry offset table.
     for (size_t i = 0; i < offsets.length(); i++) {
-        JitSpew(JitSpew_OptimizationTracking, "   Entry %u reverse offset %u",
+        JitSpew(JitSpew_OptimizationTracking, "   Entry %" PRIuSIZE " reverse offset %u",
                 i, tableOffset - padding - offsets[i]);
         writer.writeNativeEndianUint32_t(tableOffset - padding - offsets[i]);
     }
 
     if (writer.oom())
         return false;
 
     *tableOffsetp = tableOffset;
@@ -880,17 +880,17 @@ SpewConstructor(TypeSet::Type ty, JSFunc
         PutEscapedString(buf, 512, constructor->displayAtom(), 0);
     else
         snprintf(buf, mozilla::ArrayLength(buf), "??");
 
     const char* filename;
     Maybe<unsigned> lineno;
     InterpretedFunctionFilenameAndLineNumber(constructor, &filename, &lineno);
 
-    JitSpew(JitSpew_OptimizationTracking, "   Unique type %s has constructor %s (%s:%" PRIuSIZE ")",
+    JitSpew(JitSpew_OptimizationTracking, "   Unique type %s has constructor %s (%s:%u)",
             TypeSet::TypeString(ty), buf, filename, lineno.isSome() ? *lineno : 0);
 #endif
 }
 
 static void
 SpewAllocationSite(TypeSet::Type ty, JSScript* script, uint32_t offset)
 {
 #ifdef JS_JITSPEW
@@ -914,32 +914,33 @@ jit::WriteIonTrackedOptimizationsTable(J
     MOZ_ASSERT(unique.sorted());
 
 #ifdef JS_JITSPEW
     // Spew training data, which may be fed into a script to determine a good
     // encoding strategy.
     if (JitSpewEnabled(JitSpew_OptimizationTracking)) {
         JitSpewStart(JitSpew_OptimizationTracking, "=> Training data: ");
         for (const NativeToTrackedOptimizations* entry = start; entry != end; entry++) {
-            JitSpewCont(JitSpew_OptimizationTracking, "%u,%u,%u ",
+            JitSpewCont(JitSpew_OptimizationTracking, "%" PRIuSIZE ",%" PRIuSIZE ",%u ",
                         entry->startOffset.offset(), entry->endOffset.offset(),
                         unique.indexOf(entry->optimizations));
         }
         JitSpewFin(JitSpew_OptimizationTracking);
     }
 #endif
 
     Vector<uint32_t, 16> offsets(cx);
     const NativeToTrackedOptimizations* entry = start;
 
     // Write out region offloads, partitioned into runs.
     JitSpew(JitSpew_Profiling, "=> Writing regions");
     while (entry != end) {
         uint32_t runLength = IonTrackedOptimizationsRegion::ExpectedRunLength(entry, end);
-        JitSpew(JitSpew_OptimizationTracking, "   Run at entry %u, length %u, offset %u",
+        JitSpew(JitSpew_OptimizationTracking,
+                "   Run at entry %" PRIuSIZE ", length %" PRIu32 ", offset %" PRIuSIZE,
                 entry - start, runLength, writer.length());
 
         if (!offsets.append(writer.length()))
             return false;
 
         if (!IonTrackedOptimizationsRegion::WriteRun(writer, entry, entry + runLength, unique))
             return false;
 
@@ -952,27 +953,28 @@ jit::WriteIonTrackedOptimizationsTable(J
 
     *numRegions = offsets.length();
 
     // Clear offsets so that it may be reused below for the unique
     // optimizations table.
     offsets.clear();
 
     const UniqueTrackedOptimizations::SortedVector& vec = unique.sortedVector();
-    JitSpew(JitSpew_OptimizationTracking, "=> Writing unique optimizations table with %u entr%s",
+    JitSpew(JitSpew_OptimizationTracking, "=> Writing unique optimizations table with %" PRIuSIZE " entr%s",
             vec.length(), vec.length() == 1 ? "y" : "ies");
 
     // Write out type info payloads.
     UniqueTrackedTypes uniqueTypes(cx);
     if (!uniqueTypes.init())
         return false;
 
     for (const UniqueTrackedOptimizations::SortEntry* p = vec.begin(); p != vec.end(); p++) {
         const TempOptimizationTypeInfoVector* v = p->types;
-        JitSpew(JitSpew_OptimizationTracking, "   Type info entry %u of length %u, offset %u",
+        JitSpew(JitSpew_OptimizationTracking,
+                "   Type info entry %" PRIuSIZE " of length %" PRIuSIZE ", offset %" PRIuSIZE,
                 p - vec.begin(), v->length(), writer.length());
         SpewTempOptimizationTypeInfoVector(v, "  ");
 
         if (!offsets.append(writer.length()))
             return false;
 
         for (const OptimizationTypeInfo* t = v->begin(); t != v->end(); t++) {
             if (!t->writeCompact(cx, writer, uniqueTypes))
@@ -1012,17 +1014,18 @@ jit::WriteIonTrackedOptimizationsTable(J
 
     if (!WriteOffsetsTable(writer, offsets, typesTableOffsetp))
         return false;
     offsets.clear();
 
     // Write out attempts payloads.
     for (const UniqueTrackedOptimizations::SortEntry* p = vec.begin(); p != vec.end(); p++) {
         const TempOptimizationAttemptsVector* v = p->attempts;
-        JitSpew(JitSpew_OptimizationTracking, "   Attempts entry %u of length %u, offset %u",
+        JitSpew(JitSpew_OptimizationTracking,
+                "   Attempts entry %" PRIuSIZE " of length %" PRIuSIZE ", offset %" PRIuSIZE,
                 p - vec.begin(), v->length(), writer.length());
         SpewTempOptimizationAttemptsVector(v, "  ");
 
         if (!offsets.append(writer.length()))
             return false;
 
         for (const OptimizationAttempt* a = v->begin(); a != v->end(); a++)
             a->writeCompact(writer);
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/Recover.h"
 
+#include "mozilla/SizePrintfMacros.h"
+
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsmath.h"
 #include "jsobj.h"
 #include "jsstr.h"
 
 #include "builtin/RegExp.h"
 #include "builtin/SIMD.h"
@@ -118,17 +120,17 @@ MResumePoint::writeRecoverData(CompactBu
     MOZ_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4);
 
 #ifdef JS_JITSPEW
     uint32_t implicit = StartArgSlot(script);
 #endif
     uint32_t formalArgs = CountArgSlots(script, fun);
     uint32_t nallocs = formalArgs + script->nfixed() + exprStack;
 
-    JitSpew(JitSpew_IonSnapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u",
+    JitSpew(JitSpew_IonSnapshots, "Starting frame; implicit %u, formals %u, fixed %" PRIuSIZE ", exprs %u",
             implicit, formalArgs - implicit, script->nfixed(), exprStack);
 
     uint32_t pcoff = script->pcToOffset(pc());
     JitSpew(JitSpew_IonSnapshots, "Writing pc offset %u, nslots %u", pcoff, nallocs);
     writer.writeUnsigned(pcoff);
     writer.writeUnsigned(nallocs);
     return true;
 }
--- a/js/src/jit/Safepoints.cpp
+++ b/js/src/jit/Safepoints.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/Safepoints.h"
 
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/SizePrintfMacros.h"
 
 #include "jit/BitSet.h"
 #include "jit/JitSpewer.h"
 #include "jit/LIR.h"
 
 using namespace js;
 using namespace jit;
 
@@ -26,17 +27,17 @@ bool
 SafepointWriter::init(TempAllocator& alloc)
 {
     return frameSlots_.init(alloc) && argumentSlots_.init(alloc);
 }
 
 uint32_t
 SafepointWriter::startEntry()
 {
-    JitSpew(JitSpew_Safepoints, "Encoding safepoint (position %d):", stream_.length());
+    JitSpew(JitSpew_Safepoints, "Encoding safepoint (position %" PRIuSIZE "):", stream_.length());
     return uint32_t(stream_.length());
 }
 
 void
 SafepointWriter::writeOsiCallPointOffset(uint32_t osiCallPointOffset)
 {
     stream_.writeUnsigned(osiCallPointOffset);
 }
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -2127,17 +2127,17 @@ UpdateExistingGenerationalDOMProxyStub(I
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (iter->isGetProp_CallDOMProxyWithGenerationNative()) {
             ICGetProp_CallDOMProxyWithGenerationNative* updateStub =
                 iter->toGetProp_CallDOMProxyWithGenerationNative();
             if (updateStub->expandoAndGeneration() == expandoAndGeneration) {
                 // Update generation
                 uint64_t generation = expandoAndGeneration->generation;
                 JitSpew(JitSpew_BaselineIC,
-                        "  Updating existing stub with generation, old value: %i, "
+                        "  Updating existing stub with generation, old value: %" PRIu64 ", "
                         "new value: %" PRIu64 "", updateStub->generation(),
                         generation);
                 updateStub->setGeneration(generation);
                 return true;
             }
         }
     }
     return false;
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -197,18 +197,20 @@ class ICStub;
 class ICFallbackStub;
 
 #define FORWARD_DECLARE_STUBS(kindName) class IC##kindName;
     IC_BASELINE_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
     IC_SHARED_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
 #undef FORWARD_DECLARE_STUBS
 
 #ifdef JS_JITSPEW
-void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...);
-void TypeFallbackICSpew(JSContext* cx, ICTypeMonitor_Fallback* stub, const char* fmt, ...);
+void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...)
+    MOZ_FORMAT_PRINTF(3, 4);
+void TypeFallbackICSpew(JSContext* cx, ICTypeMonitor_Fallback* stub, const char* fmt, ...)
+    MOZ_FORMAT_PRINTF(3, 4);
 #else
 #define FallbackICSpew(...)
 #define TypeFallbackICSpew(...)
 #endif
 
 //
 // An entry in the JIT IC descriptor table.
 //
--- a/js/src/jit/ValueNumbering.cpp
+++ b/js/src/jit/ValueNumbering.cpp
@@ -997,17 +997,17 @@ ValueNumberer::visitBlock(MBasicBlock* b
 
     return visitControlInstruction(block, dominatorRoot);
 }
 
 // Visit all the blocks dominated by dominatorRoot.
 bool
 ValueNumberer::visitDominatorTree(MBasicBlock* dominatorRoot)
 {
-    JitSpew(JitSpew_GVN, "  Visiting dominator tree (with %llu blocks) rooted at block%u%s",
+    JitSpew(JitSpew_GVN, "  Visiting dominator tree (with %" PRIu64 " blocks) rooted at block%u%s",
             uint64_t(dominatorRoot->numDominated()), dominatorRoot->id(),
             dominatorRoot == graph_.entryBlock() ? " (normal entry block)" :
             dominatorRoot == graph_.osrBlock() ? " (OSR entry block)" :
             dominatorRoot->numPredecessors() == 0 ? " (odd unreachable block)" :
             " (merge point from normal entry and OSR entry)");
     MOZ_ASSERT(dominatorRoot->immediateDominator() == dominatorRoot,
             "root is not a dominator tree root");
 
@@ -1223,17 +1223,17 @@ ValueNumberer::init()
     return values_.init();
 }
 
 bool
 ValueNumberer::run(UpdateAliasAnalysisFlag updateAliasAnalysis)
 {
     updateAliasAnalysis_ = updateAliasAnalysis == UpdateAliasAnalysis;
 
-    JitSpew(JitSpew_GVN, "Running GVN on graph (with %llu blocks)",
+    JitSpew(JitSpew_GVN, "Running GVN on graph (with %" PRIu64 " blocks)",
             uint64_t(graph_.numBlocks()));
 
     // Adding fixup blocks only make sense iff we have a second entry point into
     // the graph which cannot be reached any more from the entry point.
     if (graph_.osrBlock()) {
         if (!insertOSRFixups())
             return false;
     }
@@ -1283,17 +1283,17 @@ ValueNumberer::run(UpdateAliasAnalysisFl
         // re-run we discard the construct which triggered the re-run), but it
         // does help avoid slow compile times on pathological code.
         ++runs;
         if (runs == 6) {
             JitSpew(JitSpew_GVN, "Re-run cutoff of %d reached. Terminating GVN!", runs);
             break;
         }
 
-        JitSpew(JitSpew_GVN, "Re-running GVN on graph (run %d, now with %llu blocks)",
+        JitSpew(JitSpew_GVN, "Re-running GVN on graph (run %d, now with %" PRIu64 " blocks)",
                 runs, uint64_t(graph_.numBlocks()));
     }
 
     if (MOZ_UNLIKELY(hasOSRFixups_)) {
         if (!cleanupOSRFixups())
             return false;
         hasOSRFixups_ = false;
     }
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1358,17 +1358,17 @@ class Assembler : public AssemblerShared
     SpewNodes spewNodes_;
     uint32_t spewNext_;
     Sprinter* printer_;
 
     bool spewDisabled();
     uint32_t spewResolve(Label* l);
     uint32_t spewProbe(Label* l);
     uint32_t spewDefine(Label* l);
-    void spew(const char* fmt, ...);
+    void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
     void spew(const char* fmt, va_list args);
 #endif
 
   public:
     // For the alignment fill use NOP: 0x0320f000 or (Always | InstNOP::NopInst).
     // For the nopFill use a branch to the next instruction: 0xeaffffff.
     Assembler()
       : m_buffer(1, 1, 8, GetPoolMaxOffset(), 8, 0xe320f000, 0xeaffffff, GetNopFill()),
--- a/js/src/jit/arm/disasm/Disasm-arm.cpp
+++ b/js/src/jit/arm/disasm/Disasm-arm.cpp
@@ -38,16 +38,17 @@
 
 namespace js {
 namespace jit {
 namespace disasm {
 
 
 // Helper function for printing to a Vector.
 static int
+MOZ_FORMAT_PRINTF(2, 3)
 SNPrintF(V8Vector<char> str, const char* format, ...)
 {
     va_list args;
     va_start(args, format);
     int result = vsnprintf(str.start(), str.length(), format, args);
     va_end(args);
     return result;
 }
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/shared/CodeGenerator-shared-inl.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/SizePrintfMacros.h"
 
 #include "jit/CompactBuffer.h"
 #include "jit/IonCaches.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
@@ -228,17 +229,17 @@ CodeGeneratorShared::addNativeToBytecode
         NativeToBytecode& lastEntry = nativeToBytecodeList_[lastIdx];
 
         MOZ_ASSERT(nativeOffset >= lastEntry.nativeOffset.offset());
 
         // If the new entry is for the same inlineScriptTree and same
         // bytecodeOffset, but the nativeOffset has changed, do nothing.
         // The same site just generated some more code.
         if (lastEntry.tree == tree && lastEntry.pc == pc) {
-            JitSpew(JitSpew_Profiling, " => In-place update [%u-%u]",
+            JitSpew(JitSpew_Profiling, " => In-place update [%" PRIuSIZE "-%" PRIu32 "]",
                     lastEntry.nativeOffset.offset(), nativeOffset);
             return true;
         }
 
         // If the new entry is for the same native offset, then update the
         // previous entry with the new bytecode site, since the previous
         // bytecode site did not generate any native code.
         if (lastEntry.nativeOffset.offset() == nativeOffset) {
@@ -275,17 +276,17 @@ CodeGeneratorShared::addNativeToBytecode
     return true;
 }
 
 void
 CodeGeneratorShared::dumpNativeToBytecodeEntries()
 {
 #ifdef JS_JITSPEW
     InlineScriptTree* topTree = gen->info().inlineScriptTree();
-    JitSpewStart(JitSpew_Profiling, "Native To Bytecode Entries for %s:%d\n",
+    JitSpewStart(JitSpew_Profiling, "Native To Bytecode Entries for %s:%" PRIuSIZE "\n",
                  topTree->script()->filename(), topTree->script()->lineno());
     for (unsigned i = 0; i < nativeToBytecodeList_.length(); i++)
         dumpNativeToBytecodeEntry(i);
 #endif
 }
 
 void
 CodeGeneratorShared::dumpNativeToBytecodeEntry(uint32_t idx)
@@ -298,26 +299,26 @@ CodeGeneratorShared::dumpNativeToBytecod
     unsigned nativeDelta = 0;
     unsigned pcDelta = 0;
     if (idx + 1 < nativeToBytecodeList_.length()) {
         NativeToBytecode* nextRef = &ref + 1;
         nativeDelta = nextRef->nativeOffset.offset() - nativeOffset;
         if (nextRef->tree == ref.tree)
             pcDelta = nextRef->pc - ref.pc;
     }
-    JitSpewStart(JitSpew_Profiling, "    %08x [+%-6d] => %-6d [%-4d] {%-10s} (%s:%d",
+    JitSpewStart(JitSpew_Profiling, "    %08" PRIxSIZE " [+%-6d] => %-6ld [%-4d] {%-10s} (%s:%" PRIuSIZE,
                  ref.nativeOffset.offset(),
                  nativeDelta,
-                 ref.pc - script->code(),
+                 (long) (ref.pc - script->code()),
                  pcDelta,
                  CodeName[JSOp(*ref.pc)],
                  script->filename(), script->lineno());
 
     for (tree = tree->caller(); tree; tree = tree->caller()) {
-        JitSpewCont(JitSpew_Profiling, " <= %s:%d", tree->script()->filename(),
+        JitSpewCont(JitSpew_Profiling, " <= %s:%" PRIuSIZE, tree->script()->filename(),
                                                     tree->script()->lineno());
     }
     JitSpewCont(JitSpew_Profiling, ")");
     JitSpewFin(JitSpew_Profiling);
 #endif
 }
 
 bool
@@ -922,17 +923,17 @@ CodeGeneratorShared::generateCompactTrac
     trackedOptimizationsAttemptsTableOffset_ = attemptsTableOffset;
 
     verifyCompactTrackedOptimizationsMap(code, numRegions, unique, allTypes);
 
     JitSpew(JitSpew_OptimizationTracking,
             "== Compact Native To Optimizations Map [%p-%p] size %u",
             data, data + trackedOptimizationsMapSize_, trackedOptimizationsMapSize_);
     JitSpew(JitSpew_OptimizationTracking,
-            "     with type list of length %u, size %u",
+            "     with type list of length %" PRIuSIZE ", size %" PRIuSIZE,
             allTypes->length(), allTypes->length() * sizeof(IonTrackedTypeWithAddendum));
 
     return true;
 }
 
 #ifdef DEBUG
 class ReadTempAttemptsVectorOp : public JS::ForEachTrackedOptimizationAttemptOp
 {
--- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
+++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_shared_IonAssemblerBufferWithConstantPools_h
 #define jit_shared_IonAssemblerBufferWithConstantPools_h
 
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/SizePrintfMacros.h"
 
 #include <algorithm>
 
 #include "jit/JitSpewer.h"
 #include "jit/shared/IonAssemblerBuffer.h"
 
 // This code extends the AssemblerBuffer to support the pooling of values loaded
 // using program-counter relative addressing modes. This is necessary with the
@@ -787,17 +788,17 @@ struct AssemblerBufferWithConstantPools 
         // range check.
         if (numPoolEntries)
             pool_.updateLimiter(BufferOffset(sizeExcludingCurrentPool()));
 
         if (!hasSpaceForInsts(numInst, numPoolEntries)) {
             if (numPoolEntries)
                 JitSpew(JitSpew_Pools, "[%d] Inserting pool entry caused a spill", id);
             else
-                JitSpew(JitSpew_Pools, "[%d] Inserting instruction(%d) caused a spill", id,
+                JitSpew(JitSpew_Pools, "[%d] Inserting instruction(%" PRIuSIZE ") caused a spill", id,
                         sizeExcludingCurrentPool());
 
             finishPool();
             if (this->oom())
                 return OOM_FAIL;
             return insertEntryForwards(numInst, numPoolEntries, inst, data);
         }
         if (numPoolEntries) {
@@ -858,17 +859,17 @@ struct AssemblerBufferWithConstantPools 
         // Insert the pool value.
         unsigned index = insertEntryForwards(numInst, numPoolEntries, inst, data);
         if (this->oom())
             return BufferOffset();
 
         // Now to get an instruction to write.
         PoolEntry retPE;
         if (numPoolEntries) {
-            JitSpew(JitSpew_Pools, "[%d] Entry has index %u, offset %u", id, index,
+            JitSpew(JitSpew_Pools, "[%d] Entry has index %u, offset %" PRIuSIZE, id, index,
                     sizeExcludingCurrentPool());
             Asm::InsertIndexIntoTag(inst, index);
             // Figure out the offset within the pool entries.
             retPE = PoolEntry(poolEntryCount);
             poolEntryCount += numPoolEntries;
         }
         // Now inst is a valid thing to insert into the instruction stream.
         if (pe != nullptr)
@@ -930,18 +931,18 @@ struct AssemblerBufferWithConstantPools 
         // Include branches that would expire in the next N bytes.
         // The hysteresis avoids the needless creation of many tiny constant
         // pools.
         return this->nextOffset().getOffset() + ShortRangeBranchHysteresis >
                size_t(branchDeadlines_.earliestDeadline().getOffset());
     }
 
     void finishPool() {
-        JitSpew(JitSpew_Pools, "[%d] Attempting to finish pool %d with %d entries.", id,
-                poolInfo_.length(), pool_.numEntries());
+        JitSpew(JitSpew_Pools, "[%d] Attempting to finish pool %" PRIuSIZE " with %u entries.",
+                id, poolInfo_.length(), pool_.numEntries());
 
         if (pool_.numEntries() == 0 && !hasExpirableShortRangeBranches()) {
             // If there is no data in the pool being dumped, don't dump anything.
             JitSpew(JitSpew_Pools, "[%d] Aborting because the pool is empty", id);
             return;
         }
 
         // Should not be placing a pool in a no-pool region, check.
@@ -999,17 +1000,17 @@ struct AssemblerBufferWithConstantPools 
             // substitutions.
             Inst* inst = this->getInst(*iter);
             size_t codeOffset = poolOffset - iter->getOffset();
 
             // That is, PatchConstantPoolLoad wants to be handed the address of
             // the pool entry that is being loaded.  We need to do a non-trivial
             // amount of math here, since the pool that we've made does not
             // actually reside there in memory.
-            JitSpew(JitSpew_Pools, "[%d] Fixing entry %d offset to %u", id, idx, codeOffset);
+            JitSpew(JitSpew_Pools, "[%d] Fixing entry %d offset to %" PRIuSIZE, id, idx, codeOffset);
             Asm::PatchConstantPoolLoad(inst, (uint8_t*)inst + codeOffset);
         }
 
         // Record the pool info.
         unsigned firstEntry = poolEntryCount - pool_.numEntries();
         if (!poolInfo_.append(PoolInfo(firstEntry, data))) {
             this->fail_oom();
             return;
@@ -1032,17 +1033,17 @@ struct AssemblerBufferWithConstantPools 
         MOZ_ASSERT(!canNotPlacePool_);
         insertNopFill();
 
         // Check if the pool will spill by adding maxInst instructions, and if
         // so then finish the pool before entering the no-pool region. It is
         // assumed that no pool entries are allocated in a no-pool region and
         // this is asserted when allocating entries.
         if (!hasSpaceForInsts(maxInst, 0)) {
-            JitSpew(JitSpew_Pools, "[%d] No-Pool instruction(%d) caused a spill.", id,
+            JitSpew(JitSpew_Pools, "[%d] No-Pool instruction(%" PRIuSIZE ") caused a spill.", id,
                     sizeExcludingCurrentPool());
             finishPool();
         }
 
 #ifdef DEBUG
         // Record the buffer position to allow validating maxInst when leaving
         // the region.
         canNotPlacePoolStartOffset_ = this->nextOffset().getOffset();
@@ -1072,18 +1073,18 @@ struct AssemblerBufferWithConstantPools 
         if (requiredFill == 0)
             return;
         requiredFill = alignment - requiredFill;
 
         // Add an InstSize because it is probably not useful for a pool to be
         // dumped at the aligned code position.
         if (!hasSpaceForInsts(requiredFill / InstSize + 1, 0)) {
             // Alignment would cause a pool dump, so dump the pool now.
-            JitSpew(JitSpew_Pools, "[%d] Alignment of %d at %d caused a spill.", id, alignment,
-                    sizeExcludingCurrentPool());
+            JitSpew(JitSpew_Pools, "[%d] Alignment of %d at %" PRIuSIZE " caused a spill.",
+                    id, alignment, sizeExcludingCurrentPool());
             finishPool();
         }
 
         inhibitNops_ = true;
         while ((sizeExcludingCurrentPool() & (alignment - 1)) && !this->oom())
             putInt(alignFillInst_);
         inhibitNops_ = false;
     }
--- a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
+++ b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
@@ -181,20 +181,17 @@ namespace jit {
         GenericAssembler()
           : printer(NULL)
         {}
 
         void setPrinter(Sprinter* sp) {
             printer = sp;
         }
 
-        void spew(const char* fmt, ...)
-#ifdef __GNUC__
-            __attribute__ ((format (printf, 2, 3)))
-#endif
+        void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3)
         {
             if (MOZ_UNLIKELY(printer || JitSpewEnabled(JitSpew_Codegen))) {
                 va_list va;
                 va_start(va, fmt);
                 spew(fmt, va);
                 va_end(va);
             }
         }
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -68,16 +68,17 @@ UNIFIED_SOURCES += [
     'testNewObject.cpp',
     'testNewTargetInvokeConstructor.cpp',
     'testNullRoot.cpp',
     'testObjectEmulatingUndefined.cpp',
     'testOOM.cpp',
     'testParseJSON.cpp',
     'testPersistentRooted.cpp',
     'testPreserveJitCode.cpp',
+    'testPrintf.cpp',
     'testPrivateGCThingValue.cpp',
     'testProfileStrings.cpp',
     'testPropCache.cpp',
     'testRegExp.cpp',
     'testResolveRecursion.cpp',
     'tests.cpp',
     'testSameValue.cpp',
     'testSavedStacks.cpp',
@@ -141,16 +142,16 @@ if CONFIG['ENABLE_INTL_API'] and CONFIG[
 
 USE_LIBS += [
     'static:js',
 ]
 
 OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
 
 if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wno-shadow']
+    CXXFLAGS += ['-Wno-shadow', '-Werror=format']
 
 # This is intended as a temporary workaround to enable VS2015.
 if CONFIG['_MSC_VER']:
     CXXFLAGS += ['-wd4312']
 
 DEFINES['topsrcdir'] = '%s/js/src' % TOPSRCDIR
 OBJDIR_PP_FILES.js.src['jsapi-tests'] += ['jsapi-tests-gdb.py.in']
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testPrintf.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/SizePrintfMacros.h"
+
+#include <stdarg.h>
+
+#include "jsprf.h"
+
+#include "jsapi-tests/tests.h"
+
+static bool
+MOZ_FORMAT_PRINTF(2, 3)
+print_one (const char *expect, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    char *output = JS_vsmprintf (fmt, ap);
+    va_end(ap);
+
+    bool result = output && !strcmp(output, expect);
+    JS_smprintf_free(output);
+
+    return result;
+}
+
+static const char *
+zero()
+{
+    return nullptr;
+}
+
+BEGIN_TEST(testPrintf)
+{
+    CHECK(print_one("23", "%d", 23));
+    CHECK(print_one("-1", "%d", -1));
+    CHECK(print_one("23", "%u", 23u));
+    CHECK(print_one("0x17", "0x%x", 23u));
+    CHECK(print_one("0xFF", "0x%X", 255u));
+    CHECK(print_one("027", "0%o", 23u));
+    CHECK(print_one("-1", "%hd", (short) -1));
+    // This could be expanded if need be, it's just convenient to do
+    // it this way.
+    if (sizeof(short) == 2) {
+        CHECK(print_one("8000", "%hx", (unsigned short) 0x8000));
+    }
+    CHECK(print_one("0xf0f0", "0x%lx", 0xf0f0ul));
+    CHECK(print_one("0xF0F0", "0x%llX", 0xf0f0ull));
+    CHECK(print_one("27270", "%zu", (size_t) 27270));
+    CHECK(print_one("27270", "%" PRIuSIZE, (size_t) 27270));
+    CHECK(print_one("hello", "he%so", "ll"));
+    CHECK(print_one("(null)", "%s", zero()));
+    CHECK(print_one("0", "%p", (char *) 0));
+    CHECK(print_one("h", "%c", 'h'));
+    CHECK(print_one("1.500000", "%f", 1.5f));
+    CHECK(print_one("1.5", "%g", 1.5));
+
+    CHECK(print_one("2727", "%" PRIu32, (uint32_t) 2727));
+    CHECK(print_one("aa7", "%" PRIx32, (uint32_t) 2727));
+    CHECK(print_one("2727", "%" PRIu64, (uint64_t) 2727));
+    CHECK(print_one("aa7", "%" PRIx64, (uint64_t) 2727));
+
+    return true;
+}
+END_TEST(testPrintf)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5194,23 +5194,26 @@ namespace JS {
 const uint16_t MaxNumErrorArguments = 10;
 };
 
 /**
  * Report an exception represented by the sprintf-like conversion of format
  * and its arguments.
  */
 extern JS_PUBLIC_API(void)
-JS_ReportErrorASCII(JSContext* cx, const char* format, ...);
+JS_ReportErrorASCII(JSContext* cx, const char* format, ...)
+    MOZ_FORMAT_PRINTF(2, 3);
 
 extern JS_PUBLIC_API(void)
-JS_ReportErrorLatin1(JSContext* cx, const char* format, ...);
+JS_ReportErrorLatin1(JSContext* cx, const char* format, ...)
+    MOZ_FORMAT_PRINTF(2, 3);
 
 extern JS_PUBLIC_API(void)
-JS_ReportErrorUTF8(JSContext* cx, const char* format, ...);
+JS_ReportErrorUTF8(JSContext* cx, const char* format, ...)
+    MOZ_FORMAT_PRINTF(2, 3);
 
 /*
  * Use an errorNumber to retrieve the format string, args are char*
  */
 extern JS_PUBLIC_API(void)
 JS_ReportErrorNumberASCII(JSContext* cx, JSErrorCallback errorCallback,
                           void* userRef, const unsigned errorNumber, ...);
 
@@ -5252,23 +5255,26 @@ JS_ReportErrorNumberUCArray(JSContext* c
 
 /**
  * As above, but report a warning instead (JSREPORT_IS_WARNING(report.flags)).
  * Return true if there was no error trying to issue the warning, and if the
  * warning was not converted into an error due to the JSOPTION_WERROR option
  * being set, false otherwise.
  */
 extern JS_PUBLIC_API(bool)
-JS_ReportWarningASCII(JSContext* cx, const char* format, ...);
-
-extern JS_PUBLIC_API(bool)
-JS_ReportWarningLatin1(JSContext* cx, const char* format, ...);
-
-extern JS_PUBLIC_API(bool)
-JS_ReportWarningUTF8(JSContext* cx, const char* format, ...);
+JS_ReportWarningASCII(JSContext* cx, const char* format, ...)
+    MOZ_FORMAT_PRINTF(2, 3);
+
+extern JS_PUBLIC_API(bool)
+JS_ReportWarningLatin1(JSContext* cx, const char* format, ...)
+    MOZ_FORMAT_PRINTF(2, 3);
+
+extern JS_PUBLIC_API(bool)
+JS_ReportWarningUTF8(JSContext* cx, const char* format, ...)
+    MOZ_FORMAT_PRINTF(2, 3);
 
 extern JS_PUBLIC_API(bool)
 JS_ReportErrorFlagsAndNumberASCII(JSContext* cx, unsigned flags,
                                   JSErrorCallback errorCallback, void* userRef,
                                   const unsigned errorNumber, ...);
 
 extern JS_PUBLIC_API(bool)
 JS_ReportErrorFlagsAndNumberLatin1(JSContext* cx, unsigned flags,
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -758,21 +758,26 @@ FormatValue(JSContext* cx, const Value& 
     const char* found = strstr(buf, "function ");
     if (found && (found - buf <= 2))
         return "[function]";
     return buf;
 }
 
 // Wrapper for JS_sprintf_append() that reports allocation failure to the
 // context.
-template <typename... Args>
 static char*
-sprintf_append(JSContext* cx, char* buf, Args&&... args)
+MOZ_FORMAT_PRINTF(3, 4)
+sprintf_append(JSContext* cx, char* buf, const char* fmt, ...)
 {
-    char* result = JS_sprintf_append(buf, mozilla::Forward<Args>(args)...);
+    va_list ap;
+
+    va_start(ap, fmt);
+    char* result = JS_vsprintf_append(buf, fmt, ap);
+    va_end(ap);
+
     if (!result) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     return result;
 }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3625,21 +3625,21 @@ js::DumpBacktrace(JSContext* cx, FILE* f
         }
         char frameType =
             i.isInterp() ? 'i' :
             i.isBaseline() ? 'b' :
             i.isIon() ? 'I' :
             i.isWasm() ? 'W' :
             '?';
 
-        sprinter.printf("#%d %14p %c   %s:%d",
+        sprinter.printf("#%" PRIuSIZE " %14p %c   %s:%d",
                         depth, i.rawFramePtr(), frameType, filename, line);
 
         if (i.hasScript()) {
-            sprinter.printf(" (%p @ %d)\n",
+            sprinter.printf(" (%p @ %" PRIuSIZE ")\n",
                             i.script(), i.script()->pcToOffset(i.pc()));
         } else {
             sprinter.printf(" (%p)\n", i.pc());
         }
     }
     fprintf(fp, "%s", sprinter.string());
 #ifdef XP_WIN32
     if (IsDebuggerPresent())
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -146,37 +146,37 @@ js::StackDefs(JSScript* script, jsbyteco
     return cs.ndefs;
 }
 
 const char * PCCounts::numExecName = "interp";
 
 static MOZ_MUST_USE bool
 DumpIonScriptCounts(Sprinter* sp, HandleScript script, jit::IonScriptCounts* ionCounts)
 {
-    if (!sp->jsprintf("IonScript [%lu blocks]:\n", ionCounts->numBlocks()))
+    if (!sp->jsprintf("IonScript [%" PRIuSIZE " blocks]:\n", ionCounts->numBlocks()))
         return false;
 
     for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
         const jit::IonBlockCounts& block = ionCounts->block(i);
         unsigned lineNumber = 0, columnNumber = 0;
         lineNumber = PCToLineNumber(script, script->offsetToPC(block.offset()), &columnNumber);
-        if (!sp->jsprintf("BB #%lu [%05u,%u,%u]",
+        if (!sp->jsprintf("BB #%" PRIu32 " [%05u,%u,%u]",
                           block.id(), block.offset(), lineNumber, columnNumber))
         {
             return false;
         }
         if (block.description()) {
             if (!sp->jsprintf(" [inlined %s]", block.description()))
                 return false;
         }
         for (size_t j = 0; j < block.numSuccessors(); j++) {
-            if (!sp->jsprintf(" -> #%lu", block.successor(j)))
+            if (!sp->jsprintf(" -> #%" PRIu32, block.successor(j)))
                 return false;
         }
-        if (!sp->jsprintf(" :: %llu hits\n", block.hitCount()))
+        if (!sp->jsprintf(" :: %" PRIu64 " hits\n", block.hitCount()))
             return false;
         if (!sp->jsprintf("%s\n", block.code()))
             return false;
     }
 
     return true;
 }
 
--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -58,67 +58,45 @@ struct NumArgState
 {
     int type;       // type of the current ap
     va_list ap;     // point to the corresponding position on ap
 };
 
 typedef mozilla::Vector<NumArgState, 20, js::SystemAllocPolicy> NumArgStateVector;
 
 
-#define TYPE_INT16      0
-#define TYPE_UINT16     1
+#define TYPE_SHORT      0
+#define TYPE_USHORT     1
 #define TYPE_INTN       2
 #define TYPE_UINTN      3
-#define TYPE_INT32      4
-#define TYPE_UINT32     5
-#define TYPE_INT64      6
-#define TYPE_UINT64     7
+#define TYPE_LONG       4
+#define TYPE_ULONG      5
+#define TYPE_LONGLONG   6
+#define TYPE_ULONGLONG  7
 #define TYPE_STRING     8
 #define TYPE_DOUBLE     9
 #define TYPE_INTSTR     10
-#define TYPE_WSTRING    11
+#define TYPE_POINTER    11
 #define TYPE_UNKNOWN    20
 
 #define FLAG_LEFT       0x1
 #define FLAG_SIGNED     0x2
 #define FLAG_SPACED     0x4
 #define FLAG_ZEROS      0x8
 #define FLAG_NEG        0x10
 
 inline bool
 generic_write(SprintfState* ss, const char* src, size_t srclen)
 {
     return (*ss->stuff)(ss, src, srclen);
 }
 
-inline bool
-generic_write(SprintfState* ss, const char16_t* src, size_t srclen)
-{
-    const size_t CHUNK_SIZE = 64;
-    char chunk[CHUNK_SIZE];
-
-    size_t j = 0;
-    size_t i = 0;
-    while (i < srclen) {
-        // FIXME: truncates characters to 8 bits
-        chunk[j++] = char(src[i++]);
-
-        if (j == CHUNK_SIZE || i == srclen) {
-            if (!(*ss->stuff)(ss, chunk, j))
-                return false;
-            j = 0;
-        }
-    }
-    return true;
-}
-
 // Fill into the buffer using the data in src
-template <typename Char>
 static bool
-fill2(SprintfState* ss, const Char* src, int srclen, int width, int flags)
+fill2(SprintfState* ss, const char* src, int srclen, int width, int flags)
 {
     char space = ' ';
 
     width -= srclen;
     if (width > 0 && (flags & FLAG_LEFT) == 0) {    // Right adjusting
         if (flags & FLAG_ZEROS)
             space = '0';
         while (--width >= 0) {
@@ -313,29 +291,26 @@ static bool cvt_f(SprintfState* ss, doub
     }
 #endif
     SprintfLiteral(fout, fin, d);
 
     return (*ss->stuff)(ss, fout, strlen(fout));
 }
 
 static inline const char* generic_null_str(const char*) { return "(null)"; }
-static inline const char16_t* generic_null_str(const char16_t*) { return u"(null)"; }
 
 static inline size_t generic_strlen(const char* s) { return strlen(s); }
-static inline size_t generic_strlen(const char16_t* s) { return js_strlen(s); }
 
 /*
  * Convert a string into its printable form.  "width" is the output
  * width. "prec" is the maximum number of characters of "s" to output,
  * where -1 means until NUL.
  */
-template <typename Char>
 static bool
-cvt_s(SprintfState* ss, const Char* s, int width, int prec, int flags)
+cvt_s(SprintfState* ss, const char* s, int width, int prec, int flags)
 {
     if (prec == 0)
         return true;
     if (!s)
         s = generic_null_str(s);
 
     // Limit string length by precision value
     int slen = int(generic_strlen(s));
@@ -447,33 +422,34 @@ BuildArgArray(const char* fmt, va_list a
             while ((c >= '0') && (c <= '9')) {
                 c = *p++;
             }
         }
 
         // size
         nas[cn].type = TYPE_INTN;
         if (c == 'h') {
-            nas[cn].type = TYPE_INT16;
+            nas[cn].type = TYPE_SHORT;
             c = *p++;
         } else if (c == 'L') {
-            // XXX not quite sure here
-            nas[cn].type = TYPE_INT64;
+            nas[cn].type = TYPE_LONGLONG;
             c = *p++;
         } else if (c == 'l') {
-            nas[cn].type = TYPE_INT32;
+            nas[cn].type = TYPE_LONG;
             c = *p++;
             if (c == 'l') {
-                nas[cn].type = TYPE_INT64;
+                nas[cn].type = TYPE_LONGLONG;
                 c = *p++;
             }
         } else if (c == 'z' || c == 'I') {
-            static_assert(sizeof(size_t) == sizeof(int32_t) || sizeof(size_t) == sizeof(int64_t),
+            static_assert(sizeof(size_t) == sizeof(int) || sizeof(size_t) == sizeof(long) ||
+                          sizeof(size_t) == sizeof(long long),
                           "size_t is not one of the expected sizes");
-            nas[cn].type = sizeof(size_t) == sizeof(int64_t) ? TYPE_INT64 : TYPE_INT32;
+            nas[cn].type = sizeof(size_t) == sizeof(int) ? TYPE_INTN :
+                sizeof(size_t) == sizeof(long) ? TYPE_LONG : TYPE_LONGLONG;
             c = *p++;
         }
 
         // format
         switch (c) {
         case 'd':
         case 'c':
         case 'i':
@@ -485,39 +461,30 @@ BuildArgArray(const char* fmt, va_list a
 
         case 'e':
         case 'f':
         case 'g':
             nas[cn].type = TYPE_DOUBLE;
             break;
 
         case 'p':
-            // XXX should use cpp
-            if (sizeof(void*) == sizeof(int32_t)) {
-                nas[cn].type = TYPE_UINT32;
-            } else if (sizeof(void*) == sizeof(int64_t)) {
-                nas[cn].type = TYPE_UINT64;
-            } else if (sizeof(void*) == sizeof(int)) {
-                nas[cn].type = TYPE_UINTN;
-            } else {
-                nas[cn].type = TYPE_UNKNOWN;
-            }
+            nas[cn].type = TYPE_POINTER;
             break;
 
         case 'C':
         case 'S':
         case 'E':
         case 'G':
             // XXX not supported I suppose
             MOZ_ASSERT(0);
             nas[cn].type = TYPE_UNKNOWN;
             break;
 
         case 's':
-            nas[cn].type = (nas[cn].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING;
+            nas[cn].type = TYPE_STRING;
             break;
 
         case 'n':
             nas[cn].type = TYPE_INTSTR;
             break;
 
         default:
             MOZ_ASSERT(0);
@@ -539,28 +506,28 @@ BuildArgArray(const char* fmt, va_list a
         if (nas[cn].type == TYPE_UNKNOWN) {
             cn++;
             continue;
         }
 
         VARARGS_ASSIGN(nas[cn].ap, ap);
 
         switch (nas[cn].type) {
-        case TYPE_INT16:
-        case TYPE_UINT16:
+        case TYPE_SHORT:
+        case TYPE_USHORT:
         case TYPE_INTN:
         case TYPE_UINTN:        (void) va_arg(ap, int);         break;
-        case TYPE_INT32:        (void) va_arg(ap, int32_t);     break;
-        case TYPE_UINT32:       (void) va_arg(ap, uint32_t);    break;
-        case TYPE_INT64:        (void) va_arg(ap, int64_t);     break;
-        case TYPE_UINT64:       (void) va_arg(ap, uint64_t);    break;
+        case TYPE_LONG:         (void) va_arg(ap, long);        break;
+        case TYPE_ULONG:        (void) va_arg(ap, unsigned long); break;
+        case TYPE_LONGLONG:     (void) va_arg(ap, long long);   break;
+        case TYPE_ULONGLONG:    (void) va_arg(ap, unsigned long long); break;
         case TYPE_STRING:       (void) va_arg(ap, char*);       break;
-        case TYPE_WSTRING:      (void) va_arg(ap, char16_t*);   break;
         case TYPE_INTSTR:       (void) va_arg(ap, int*);        break;
         case TYPE_DOUBLE:       (void) va_arg(ap, double);      break;
+        case TYPE_POINTER:      (void) va_arg(ap, void*);       break;
 
         default: MOZ_CRASH();
         }
 
         cn++;
     }
 
     return true;
@@ -571,24 +538,23 @@ BuildArgArray(const char* fmt, va_list a
  */
 static bool
 dosprintf(SprintfState* ss, const char* fmt, va_list ap)
 {
     char c;
     int flags, width, prec, radix, type;
     union {
         char ch;
-        char16_t wch;
         int i;
         long l;
-        int64_t ll;
+        long long ll;
         double d;
         const char* s;
-        const char16_t* ws;
         int* ip;
+        void* p;
     } u;
     const char* fmt0;
     static const char hex[] = "0123456789abcdef";
     static const char HEX[] = "0123456789ABCDEF";
     const char* hexp;
     int i;
     char pattern[20];
     const char* dolPt = nullptr;  // in "%4$.2f", dolPt will point to '.'
@@ -680,33 +646,34 @@ dosprintf(SprintfState* ss, const char* 
                     c = *fmt++;
                 }
             }
         }
 
         // size
         type = TYPE_INTN;
         if (c == 'h') {
-            type = TYPE_INT16;
+            type = TYPE_SHORT;
             c = *fmt++;
         } else if (c == 'L') {
-            // XXX not quite sure here
-            type = TYPE_INT64;
+            type = TYPE_LONGLONG;
             c = *fmt++;
         } else if (c == 'l') {
-            type = TYPE_INT32;
+            type = TYPE_LONG;
             c = *fmt++;
             if (c == 'l') {
-                type = TYPE_INT64;
+                type = TYPE_LONGLONG;
                 c = *fmt++;
             }
         } else if (c == 'z' || c == 'I') {
-            static_assert(sizeof(size_t) == sizeof(int32_t) || sizeof(size_t) == sizeof(int64_t),
+            static_assert(sizeof(size_t) == sizeof(int) || sizeof(size_t) == sizeof(long) ||
+                          sizeof(size_t) == sizeof(long long),
                           "size_t is not one of the expected sizes");
-            type = sizeof(size_t) == sizeof(int64_t) ? TYPE_INT64 : TYPE_INT32;
+            type = sizeof(size_t) == sizeof(int) ? TYPE_INTN :
+                sizeof(size_t) == sizeof(long) ? TYPE_LONG : TYPE_LONGLONG;
             c = *fmt++;
         }
 
         // format
         hexp = hex;
         switch (c) {
           case 'd': case 'i':                   // decimal/integer
             radix = 10;
@@ -730,61 +697,64 @@ dosprintf(SprintfState* ss, const char* 
           case 'X':                             // unsigned HEX
             radix = 16;
             hexp = HEX;
             type |= 1;
             goto fetch_and_convert;
 
           fetch_and_convert:
             switch (type) {
-              case TYPE_INT16:
+              case TYPE_SHORT:
                 u.l = va_arg(ap, int);
                 if (u.l < 0) {
                     u.l = -u.l;
                     flags |= FLAG_NEG;
                 }
                 goto do_long;
-              case TYPE_UINT16:
-                u.l = va_arg(ap, int) & 0xffff;
+              case TYPE_USHORT:
+                u.l = (unsigned short) va_arg(ap, unsigned int);
                 goto do_long;
               case TYPE_INTN:
                 u.l = va_arg(ap, int);
                 if (u.l < 0) {
                     u.l = -u.l;
                     flags |= FLAG_NEG;
                 }
                 goto do_long;
               case TYPE_UINTN:
                 u.l = (long)va_arg(ap, unsigned int);
                 goto do_long;
 
-              case TYPE_INT32:
-                u.l = va_arg(ap, int32_t);
+              case TYPE_LONG:
+                u.l = va_arg(ap, long);
                 if (u.l < 0) {
                     u.l = -u.l;
                     flags |= FLAG_NEG;
                 }
                 goto do_long;
-              case TYPE_UINT32:
-                u.l = (long)va_arg(ap, uint32_t);
+              case TYPE_ULONG:
+                u.l = (long)va_arg(ap, unsigned long);
               do_long:
                 if (!cvt_l(ss, u.l, width, prec, radix, type, flags, hexp))
                     return false;
 
                 break;
 
-              case TYPE_INT64:
-                u.ll = va_arg(ap, int64_t);
+              case TYPE_LONGLONG:
+                u.ll = va_arg(ap, long long);
                 if (u.ll < 0) {
                     u.ll = -u.ll;
                     flags |= FLAG_NEG;
                 }
                 goto do_longlong;
-              case TYPE_UINT64:
-                u.ll = va_arg(ap, uint64_t);
+              case TYPE_POINTER:
+                u.ll = (uintptr_t)va_arg(ap, void*);
+                goto do_longlong;
+              case TYPE_ULONGLONG:
+                u.ll = va_arg(ap, unsigned long long);
               do_longlong:
                 if (!cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp))
                     return false;
 
                 break;
             }
             break;
 
@@ -811,65 +781,50 @@ dosprintf(SprintfState* ss, const char* 
           case 'c':
             if ((flags & FLAG_LEFT) == 0) {
                 while (width-- > 1) {
                     if (!(*ss->stuff)(ss, " ", 1))
                         return false;
                 }
             }
             switch (type) {
-              case TYPE_INT16:
+              case TYPE_SHORT:
               case TYPE_INTN:
                 u.ch = va_arg(ap, int);
                 if (!(*ss->stuff)(ss, &u.ch, 1))
                     return false;
                 break;
             }
             if (flags & FLAG_LEFT) {
                 while (width-- > 1) {
                     if (!(*ss->stuff)(ss, " ", 1))
                         return false;
                 }
             }
             break;
 
           case 'p':
-            if (sizeof(void*) == sizeof(int32_t)) {
-                type = TYPE_UINT32;
-            } else if (sizeof(void*) == sizeof(int64_t)) {
-                type = TYPE_UINT64;
-            } else if (sizeof(void*) == sizeof(int)) {
-                type = TYPE_UINTN;
-            } else {
-                MOZ_ASSERT(0);
-                break;
-            }
+            type = TYPE_POINTER;
             radix = 16;
             goto fetch_and_convert;
 
 #if 0
           case 'C':
           case 'S':
           case 'E':
           case 'G':
             // XXX not supported I suppose
             MOZ_ASSERT(0);
             break;
 #endif
 
           case 's':
-            if(type == TYPE_INT16) {
-                u.ws = va_arg(ap, const char16_t*);
-                if (!cvt_s(ss, u.ws, width, prec, flags))
-                    return false;
-            } else {
-                u.s = va_arg(ap, const char*);
-                if (!cvt_s(ss, u.s, width, prec, flags))
-                    return false;
-            }
+            u.s = va_arg(ap, const char*);
+            if (!cvt_s(ss, u.s, width, prec, flags))
+                return false;
             break;
 
           case 'n':
             u.ip = va_arg(ap, int*);
             if (u.ip) {
                 *u.ip = ss->cur - ss->base;
             }
             break;
@@ -999,27 +954,27 @@ JS_vsprintf_append(char* last, const cha
     }
     if (!dosprintf(&ss, fmt, ap)) {
         js_free(ss.base);
         return 0;
     }
     return ss.base;
 }
 
-#undef TYPE_INT16
-#undef TYPE_UINT16
+#undef TYPE_SHORT
+#undef TYPE_USHORT
 #undef TYPE_INTN
 #undef TYPE_UINTN
-#undef TYPE_INT32
-#undef TYPE_UINT32
-#undef TYPE_INT64
-#undef TYPE_UINT64
+#undef TYPE_LONG
+#undef TYPE_ULONG
+#undef TYPE_LONGLONG
+#undef TYPE_ULONGLONG
 #undef TYPE_STRING
 #undef TYPE_DOUBLE
 #undef TYPE_INTSTR
-#undef TYPE_WSTRING
+#undef TYPE_POINTER
 #undef TYPE_UNKNOWN
 
 #undef FLAG_LEFT
 #undef FLAG_SIGNED
 #undef FLAG_SPACED
 #undef FLAG_ZEROS
 #undef FLAG_NEG
--- a/js/src/jsprf.h
+++ b/js/src/jsprf.h
@@ -9,21 +9,23 @@
 
 /*
 ** API for PR printf like routines. Supports the following formats
 **      %d - decimal
 **      %u - unsigned decimal
 **      %x - unsigned hex
 **      %X - unsigned uppercase hex
 **      %o - unsigned octal
-**      %hd, %hu, %hx, %hX, %ho - 16-bit versions of above
-**      %ld, %lu, %lx, %lX, %lo - 32-bit versions of above
-**      %lld, %llu, %llx, %llX, %llo - 64 bit versions of above
-**      %s - ascii string
-**      %hs - ucs2 string
+**      %hd, %hu, %hx, %hX, %ho - "short" versions of above
+**      %ld, %lu, %lx, %lX, %lo - "long" versions of above
+**      %lld, %llu, %llx, %llX, %llo - "long long" versions of above
+**      %zd, %zo, %zu, %zx, %zX - size_t versions of above
+**      %Id, %Io, %Iu, %Ix, %IX - size_t versions of above (for Windows compat)
+**           You should use PRI*SIZE macros instead
+**      %s - string
 **      %c - character
 **      %p - pointer (deals with machine dependent pointer size)
 **      %f - float
 **      %g - float
 */
 
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/SizePrintfMacros.h"
@@ -32,31 +34,33 @@
 
 #include "jstypes.h"
 
 /*
 ** sprintf into a malloc'd buffer. Return a pointer to the malloc'd
 ** buffer on success, nullptr on failure. Call "JS_smprintf_free" to release
 ** the memory returned.
 */
-extern JS_PUBLIC_API(char*) JS_smprintf(const char* fmt, ...);
+extern JS_PUBLIC_API(char*) JS_smprintf(const char* fmt, ...)
+    MOZ_FORMAT_PRINTF(1, 2);
 
 /*
 ** Free the memory allocated, for the caller, by JS_smprintf
 */
 extern JS_PUBLIC_API(void) JS_smprintf_free(char* mem);
 
 /*
 ** "append" sprintf into a malloc'd buffer. "last" is the last value of
 ** the malloc'd buffer. sprintf will append data to the end of last,
 ** growing it as necessary using realloc. If last is nullptr, JS_sprintf_append
 ** will allocate the initial string. The return value is the new value of
 ** last for subsequent calls, or nullptr if there is a malloc failure.
 */
-extern JS_PUBLIC_API(char*) JS_sprintf_append(char* last, const char* fmt, ...);
+extern JS_PUBLIC_API(char*) JS_sprintf_append(char* last, const char* fmt, ...)
+    MOZ_FORMAT_PRINTF(2, 3);
 
 /*
 ** va_list forms of the above.
 */
 extern JS_PUBLIC_API(char*) JS_vsmprintf(const char* fmt, va_list ap);
 extern JS_PUBLIC_API(char*) JS_vsprintf_append(char* last, const char* fmt, va_list ap);
 
 #endif /* jsprf_h */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -779,13 +779,13 @@ if CONFIG['SPIDERMONKEY_PROMISE']:
 if CONFIG['JS_HAS_CTYPES']:
     if CONFIG['MOZ_SYSTEM_FFI']:
         CXXFLAGS += CONFIG['MOZ_FFI_CFLAGS']
     else:
         # Windows needs this to be linked with a static library.
         DEFINES['FFI_BUILDING'] = True
 
 if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wno-shadow']
+    CXXFLAGS += ['-Wno-shadow', '-Werror=format']
 
 # Suppress warnings in third-party code.
 if CONFIG['CLANG_CXX']:
     SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough']
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5242,17 +5242,17 @@ ReflectTrackedOptimizations(JSContext* c
             // Use endOffset, as startOffset may be associated with a
             // previous, adjacent region ending exactly at startOffset. That
             // is, suppose we have two regions [0, startOffset], [startOffset,
             // endOffset]. Since we are not querying a return address, we want
             // the second region and not the first.
             uint8_t* addr = ion->method()->raw() + endOffset;
             entry.youngestFrameLocationAtAddr(rt, addr, &script, &pc);
 
-            if (!sp.jsprintf("{\"location\":\"%s:%u\",\"offset\":%u,\"index\":%u}%s",
+            if (!sp.jsprintf("{\"location\":\"%s:%" PRIuSIZE "\",\"offset\":%" PRIuSIZE ",\"index\":%u}%s",
                              script->filename(), script->lineno(), script->pcToOffset(pc), index,
                              iter.more() ? "," : ""))
             {
                 return false;
             }
         }
     }
 
--- a/js/src/shell/jsoptparse.cpp
+++ b/js/src/shell/jsoptparse.cpp
@@ -259,17 +259,17 @@ OptionParser::printVersion()
 OptionParser::Result
 OptionParser::extractValue(size_t argc, char** argv, size_t* i, char** value)
 {
     MOZ_ASSERT(*i < argc);
     char* eq = strchr(argv[*i], '=');
     if (eq) {
         *value = eq + 1;
         if (*value[0] == '\0')
-            return error("A value is required for option %.*s", eq - argv[*i], argv[*i]);
+            return error("A value is required for option %.*s", (int) (eq - argv[*i]), argv[*i]);
         return Okay;
     }
 
     if (argc == *i + 1)
         return error("Expected a value for option %s", argv[*i]);
 
     *i += 1;
     *value = argv[*i];
--- a/js/src/shell/jsoptparse.h
+++ b/js/src/shell/jsoptparse.h
@@ -223,17 +223,17 @@ class OptionParser
     Option* findOption(char shortflag);
     const Option* findOption(char shortflag) const;
     Option* findOption(const char* longflag);
     const Option* findOption(const char* longflag) const;
     int findArgumentIndex(const char* name) const;
     Option* findArgument(const char* name);
     const Option* findArgument(const char* name) const;
 
-    Result error(const char* fmt, ...);
+    Result error(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
     Result extractValue(size_t argc, char** argv, size_t* i, char** value);
     Result handleArg(size_t argc, char** argv, size_t* i, bool* optsAllowed);
     Result handleOption(Option* opt, size_t argc, char** argv, size_t* i, bool* optsAllowed);
 
   public:
     explicit OptionParser(const char* usage)
       : helpOption('h', "help", "Display help information"),
         versionOption('v', "version", "Display version information and exit"),
--- a/js/src/shell/moz.build
+++ b/js/src/shell/moz.build
@@ -46,17 +46,17 @@ GENERATED_FILES += ['shellmoduleloader.o
 shellmoduleloader = GENERATED_FILES['shellmoduleloader.out.h']
 shellmoduleloader.script = '../builtin/embedjs.py:generate_shellmoduleloader'
 shellmoduleloader.inputs = [
     '../js.msg',
     'ModuleLoader.js',
 ]
 
 if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wno-shadow']
+    CXXFLAGS += ['-Wno-shadow', '-Werror=format']
 
 # This is intended as a temporary workaround to enable VS2015.
 if CONFIG['_MSC_VER']:
     CXXFLAGS += ['-wd4312']
 
 # Place a GDB Python auto-load file next to the shell executable, both in
 # the build directory and in the dist/bin directory.
 DEFINES['topsrcdir'] = '%s/js/src' % TOPSRCDIR
--- a/js/src/vm/CodeCoverage.cpp
+++ b/js/src/vm/CodeCoverage.cpp
@@ -86,26 +86,26 @@ LCovSource::exportInto(GenericPrinter& o
     // Only write if everything got recorded.
     if (!hasFilename_ || !hasTopLevelScript_)
         return;
 
     outSF_.exportInto(out);
 
     outFN_.exportInto(out);
     outFNDA_.exportInto(out);
-    out.printf("FNF:%d\n", numFunctionsFound_);
-    out.printf("FNH:%d\n", numFunctionsHit_);
+    out.printf("FNF:%" PRIuSIZE "\n", numFunctionsFound_);
+    out.printf("FNH:%" PRIuSIZE "\n", numFunctionsHit_);
 
     outBRDA_.exportInto(out);
-    out.printf("BRF:%d\n", numBranchesFound_);
-    out.printf("BRH:%d\n", numBranchesHit_);
+    out.printf("BRF:%" PRIuSIZE "\n", numBranchesFound_);
+    out.printf("BRH:%" PRIuSIZE "\n", numBranchesHit_);
 
     outDA_.exportInto(out);
-    out.printf("LF:%d\n", numLinesInstrumented_);
-    out.printf("LH:%d\n", numLinesHit_);
+    out.printf("LF:%" PRIuSIZE "\n", numLinesInstrumented_);
+    out.printf("LH:%" PRIuSIZE "\n", numLinesHit_);
 
     out.put("end_of_record\n");
 }
 
 bool
 LCovSource::writeSourceFilename(ScriptSourceObject* sso)
 {
     outSF_.printf("SF:%s\n", sso->source()->filename());
@@ -125,17 +125,17 @@ LCovSource::writeScriptName(LSprinter& o
     out.printf("top-level");
     return true;
 }
 
 bool
 LCovSource::writeScript(JSScript* script)
 {
     numFunctionsFound_++;
-    outFN_.printf("FN:%d,", script->lineno());
+    outFN_.printf("FN:%" PRIuSIZE ",", script->lineno());
     if (!writeScriptName(outFN_, script))
         return false;
     outFN_.put("\n", 1);
 
     uint64_t hits = 0;
     ScriptCounts* sc = nullptr;
     if (script->hasScriptCounts()) {
         sc = &script->getScriptCounts();
@@ -186,17 +186,17 @@ LCovSource::writeScript(JSScript* script
                 else if (type == SRC_TABLESWITCH)
                     tableswitchExitOffset = GetSrcNoteOffset(sn, 0);
 
                 sn = SN_NEXT(sn);
                 snpc += SN_DELTA(sn);
             }
 
             if (oldLine != lineno && fallsthrough) {
-                outDA_.printf("DA:%d,%" PRIu64 "\n", lineno, hits);
+                outDA_.printf("DA:%" PRIuSIZE ",%" PRIu64 "\n", lineno, hits);
 
                 // Count the number of lines instrumented & hit.
                 numLinesInstrumented_++;
                 if (hits)
                     numLinesHit_++;
             }
         }
 
@@ -215,23 +215,23 @@ LCovSource::writeScript(JSScript* script
             uint64_t fallthroughHits = 0;
             if (sc) {
                 const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(fallthroughTarget));
                 if (counts)
                     fallthroughHits = counts->numExec();
             }
 
             uint64_t taken = hits - fallthroughHits;
-            outBRDA_.printf("BRDA:%d,%d,0,", lineno, branchId);
+            outBRDA_.printf("BRDA:%" PRIuSIZE ",%" PRIuSIZE ",0,", lineno, branchId);
             if (taken)
                 outBRDA_.printf("%" PRIu64 "\n", taken);
             else
                 outBRDA_.put("-\n", 2);
 
-            outBRDA_.printf("BRDA:%d,%d,1,", lineno, branchId);
+            outBRDA_.printf("BRDA:%" PRIuSIZE ",%" PRIuSIZE ",1,", lineno, branchId);
             if (fallthroughHits)
                 outBRDA_.printf("%" PRIu64 "\n", fallthroughHits);
             else
                 outBRDA_.put("-\n", 2);
 
             // Count the number of branches, and the number of branches hit.
             numBranchesFound_ += 2;
             if (hits)
@@ -303,17 +303,18 @@ LCovSource::writeScript(JSScript* script
 
                             if (BytecodeFallsThrough(JSOp(*endpc)))
                                 fallsThroughHits = script->getHitCount(endpc);
                         }
 
                         caseHits -= fallsThroughHits;
                     }
 
-                    outBRDA_.printf("BRDA:%d,%d,%d,", lineno, branchId, caseId);
+                    outBRDA_.printf("BRDA:%" PRIuSIZE ",%" PRIuSIZE ",%" PRIuSIZE ",",
+                                    lineno, branchId, caseId);
                     if (caseHits)
                         outBRDA_.printf("%" PRIu64 "\n", caseHits);
                     else
                         outBRDA_.put("-\n", 2);
 
                     numBranchesFound_++;
                     numBranchesHit_ += !!caseHits;
                     defaultHits -= caseHits;
@@ -354,17 +355,18 @@ LCovSource::writeScript(JSScript* script
                     const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(defaultpc));
                     if (counts)
                         defaultHits = counts->numExec();
                 }
                 defaultHits -= fallsThroughHits;
             }
 
             if (defaultHasOwnClause) {
-                outBRDA_.printf("BRDA:%d,%d,%d,", lineno, branchId, caseId);
+                outBRDA_.printf("BRDA:%" PRIuSIZE ",%" PRIuSIZE ",%" PRIuSIZE ",",
+                                lineno, branchId, caseId);
                 if (defaultHits)
                     outBRDA_.printf("%" PRIu64 "\n", defaultHits);
                 else
                     outBRDA_.put("-\n", 2);
                 numBranchesFound_++;
                 numBranchesHit_ += !!defaultHits;
             }
 
--- a/js/src/vm/Printer.h
+++ b/js/src/vm/Printer.h
@@ -38,17 +38,17 @@ class GenericPrinter
     // the beginning of this new data.
     virtual int put(const char* s, size_t len) = 0;
 
     inline int put(const char* s) {
         return put(s, strlen(s));
     }
 
     // Prints a formatted string into the buffer.
-    virtual int printf(const char* fmt, ...);
+    virtual int printf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
     virtual int vprintf(const char* fmt, va_list ap);
 
     // Report that a string operation failed to get the memory it requested. The
     // first call to this function calls JS_ReportOutOfMemory, and sets this
     // Sprinter's outOfMemory flag; subsequent calls do nothing.
     virtual void reportOutOfMemory();
 
     // Return true if this Sprinter ran out of memory.
@@ -110,17 +110,17 @@ class Sprinter final : public GenericPri
     // Puts |len| characters from |s| at the current position and return an offset to
     // the beginning of this new data.
     virtual int put(const char* s, size_t len) override;
     using GenericPrinter::put; // pick up |inline int put(const char* s);|
 
     // Format the given format/arguments as if by JS_vsmprintf, then put it.
     // Return true on success, else return false and report an error (typically
     // OOM).
-    MOZ_MUST_USE bool jsprintf(const char* fmt, ...);
+    MOZ_MUST_USE bool jsprintf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
 
     // Prints a formatted string into the buffer.
     virtual int vprintf(const char* fmt, va_list ap) override;
 
     int putString(JSString* str);
 
     ptrdiff_t getOffset() const;
 
@@ -152,17 +152,17 @@ class Fprinter final : public GenericPri
     void finish();
 
     // Puts |len| characters from |s| at the current position and return an
     // offset to the beginning of this new data.
     virtual int put(const char* s, size_t len) override;
     using GenericPrinter::put; // pick up |inline int put(const char* s);|
 
     // Prints a formatted string into the buffer.
-    virtual int printf(const char* fmt, ...) override;
+    virtual int printf(const char* fmt, ...) override MOZ_FORMAT_PRINTF(2, 3);
     virtual int vprintf(const char* fmt, va_list ap) override;
 };
 
 // LSprinter, is similar to Sprinter except that instead of using an
 // ExclusiveContext to allocate strings, it use a LifoAlloc as a backend for the
 // allocation of the chunk of the string.
 class LSprinter final : public GenericPrinter
 {
@@ -198,17 +198,17 @@ class LSprinter final : public GenericPr
     void clear();
 
     // Puts |len| characters from |s| at the current position and return an
     // offset to the beginning of this new data.
     virtual int put(const char* s, size_t len) override;
     using GenericPrinter::put; // pick up |inline int put(const char* s);|
 
     // Prints a formatted string into the buffer.
-    virtual int printf(const char* fmt, ...) override;
+    virtual int printf(const char* fmt, ...) override MOZ_FORMAT_PRINTF(2, 3);
     virtual int vprintf(const char* fmt, va_list ap) override;
 
     // Report that a string operation failed to get the memory it requested. The
     // first call to this function calls JS_ReportOutOfMemory, and sets this
     // Sprinter's outOfMemory flag; subsequent calls do nothing.
     virtual void reportOutOfMemory() override;
 
     // Return true if this Sprinter ran out of memory.
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/String-inl.h"
 
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/RangedPtr.h"
+#include "mozilla/SizePrintfMacros.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
 #include "gc/Marking.h"
 #include "js/UbiNode.h"
 #include "vm/SPSProfiler.h"
 
 #include "jscntxtinlines.h"
@@ -182,17 +183,17 @@ JSString::dumpRepresentation(FILE* fp, i
 }
 
 void
 JSString::dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const
 {
     uint32_t flags = d.u1.flags;
     // Print the string's address as an actual C++ expression, to facilitate
     // copy-and-paste into a debugger.
-    fprintf(fp, "((%s*) %p) length: %zu  flags: 0x%x", subclass, this, length(), flags);
+    fprintf(fp, "((%s*) %p) length: %" PRIuSIZE "  flags: 0x%x", subclass, this, length(), flags);
     if (flags & FLAT_BIT)               fputs(" FLAT", fp);
     if (flags & HAS_BASE_BIT)           fputs(" HAS_BASE", fp);
     if (flags & INLINE_CHARS_BIT)       fputs(" INLINE_CHARS", fp);
     if (flags & ATOM_BIT)               fputs(" ATOM", fp);
     if (isPermanentAtom())              fputs(" PERMANENT", fp);
     if (flags & LATIN1_CHARS_BIT)       fputs(" LATIN1", fp);
     fputc('\n', fp);
 }
@@ -698,17 +699,17 @@ JSDependentString::undepend(ExclusiveCon
 
 #ifdef DEBUG
 void
 JSDependentString::dumpRepresentation(FILE* fp, int indent) const
 {
     dumpRepresentationHeader(fp, indent, "JSDependentString");
     indent += 2;
 
-    fprintf(fp, "%*soffset: %zu\n", indent, "", baseOffset());
+    fprintf(fp, "%*soffset: %" PRIuSIZE "\n", indent, "", baseOffset());
     fprintf(fp, "%*sbase: ", indent, "");
     base()->dumpRepresentation(fp, indent);
 }
 #endif
 
 template <typename CharT>
 /* static */ bool
 JSFlatString::isIndexSlow(const CharT* s, size_t length, uint32_t* indexp)
@@ -1358,17 +1359,17 @@ NewStringCopyUTF8N<CanGC>(JSContext* cx,
 
 #ifdef DEBUG
 void
 JSExtensibleString::dumpRepresentation(FILE* fp, int indent) const
 {
     dumpRepresentationHeader(fp, indent, "JSExtensibleString");
     indent += 2;
 
-    fprintf(fp, "%*scapacity: %zu\n", indent, "", capacity());
+    fprintf(fp, "%*scapacity: %" PRIuSIZE "\n", indent, "", capacity());
     dumpRepresentationChars(fp, indent);
 }
 
 void
 JSInlineString::dumpRepresentation(FILE* fp, int indent) const
 {
     dumpRepresentationHeader(fp, indent,
                              isFatInline() ? "JSFatInlineString" : "JSThinInlineString");
--- a/js/src/vm/TraceLoggingGraph.cpp
+++ b/js/src/vm/TraceLoggingGraph.cpp
@@ -45,16 +45,17 @@ TraceLoggerGraphState* traceLoggerGraphS
 #endif
 
 #define MAX_LOGGERS 999
 
 // Return a filename relative to the output directory. %u and %d substitutions
 // are allowed, with %u standing for a full 32-bit number and %d standing for
 // an up to 3-digit number.
 static js::UniqueChars
+MOZ_FORMAT_PRINTF(1, 2)
 AllocTraceLogFilename(const char* pattern, ...) {
     js::UniqueChars filename;
 
     va_list ap;
 
     static const char* outdir = getenv("TLDIR") ? getenv("TLDIR") : DEFAULT_TRACE_LOG_DIR;
     size_t len = strlen(outdir) + 1; // "+ 1" is for the '/'
 
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -232,16 +232,17 @@ js::InferSpewImpl(const char* fmt, ...)
     fprintf(stderr, "[infer] ");
     vfprintf(stderr, fmt, ap);
     fprintf(stderr, "\n");
     va_end(ap);
 }
 #endif
 
 MOZ_NORETURN MOZ_COLD static void
+MOZ_FORMAT_PRINTF(2, 3)
 TypeFailure(JSContext* cx, const char* fmt, ...)
 {
     char msgbuf[1024]; /* Larger error messages will be truncated */
     char errbuf[1024];
 
     va_list ap;
     va_start(ap, fmt);
     VsprintfLiteral(errbuf, fmt, ap);
@@ -3283,17 +3284,17 @@ js::TypeMonitorResult(JSContext* cx, JSS
     assertSameCompartment(cx, script, type);
 
     AutoEnterAnalysis enter(cx);
 
     StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
     if (types->hasType(type))
         return;
 
-    InferSpew(ISpewOps, "bytecodeType: %p %05u: %s",
+    InferSpew(ISpewOps, "bytecodeType: %p %05" PRIuSIZE ": %s",
               script, script->pcToOffset(pc), TypeSet::TypeString(type));
     types->addType(cx, type);
 }
 
 void
 js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, const js::Value& rval)
 {
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -1291,17 +1291,17 @@ enum SpewChannel {
 #ifdef DEBUG
 
 bool InferSpewActive(SpewChannel channel);
 const char * InferSpewColorReset();
 const char * InferSpewColor(TypeConstraint* constraint);
 const char * InferSpewColor(TypeSet* types);
 
 #define InferSpew(channel, ...) if (InferSpewActive(channel)) { InferSpewImpl(__VA_ARGS__); } else {}
-void InferSpewImpl(const char* fmt, ...);
+void InferSpewImpl(const char* fmt, ...) MOZ_FORMAT_PRINTF(1, 2);
 
 /* Check that the type property for id in group contains value. */
 bool ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Value& value);
 
 #else
 
 inline const char * InferSpewColorReset() { return nullptr; }
 inline const char * InferSpewColor(TypeConstraint* constraint) { return nullptr; }
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -149,16 +149,17 @@ private:
     char*      mBuf;
 
     // prevent copying and assignment
     JSCLContextHelper(const JSCLContextHelper&) = delete;
     const JSCLContextHelper& operator=(const JSCLContextHelper&) = delete;
 };
 
 static nsresult
+MOZ_FORMAT_PRINTF(2, 3)
 ReportOnCallerUTF8(JSContext* callerContext,
                    const char* format, ...) {
     if (!callerContext) {
         return NS_ERROR_FAILURE;
     }
 
     va_list ap;
     va_start(ap, format);
@@ -172,16 +173,17 @@ ReportOnCallerUTF8(JSContext* callerCont
     JS_ReportErrorUTF8(callerContext, "%s", buf);
     JS_smprintf_free(buf);
 
     va_end(ap);
     return NS_OK;
 }
 
 static nsresult
+MOZ_FORMAT_PRINTF(2, 3)
 ReportOnCallerUTF8(JSCLContextHelper& helper,
                    const char* format, ...)
 {
     va_list ap;
     va_start(ap, format);
 
     char* buf = JS_vsmprintf(format, ap);
     if (!buf) {
--- a/js/xpconnect/src/XPCThrower.cpp
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -111,19 +111,19 @@ XPCThrower::ThrowBadResult(nsresult rv, 
         return;
 
     // else...
 
     if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format) || !format)
         format = "";
 
     if (nsXPCException::NameAndFormatForNSResult(result, &name, nullptr) && name)
-        sz = JS_smprintf("%s 0x%x (%s)", format, result, name);
+        sz = JS_smprintf("%s 0x%x (%s)", format, (unsigned) result, name);
     else
-        sz = JS_smprintf("%s 0x%x", format, result);
+        sz = JS_smprintf("%s 0x%x", format, (unsigned) result);
     NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, true);
 
     dom::Throw(ccx, result, nsDependentCString(sz));
 
     if (sz)
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -966,17 +966,17 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
         return NS_ERROR_FAILURE;
 
     // [implicit_jscontext] and [optional_argc] have a different calling
     // convention, which we don't support for JS-implemented components.
     if (info->WantsOptArgc() || info->WantsContext()) {
         const char* str = "IDL methods marked with [implicit_jscontext] "
                           "or [optional_argc] may not be implemented in JS";
         // Throw and warn for good measure.
-        JS_ReportErrorASCII(cx, str);
+        JS_ReportErrorASCII(cx, "%s", str);
         NS_WARNING(str);
         return CheckForException(ccx, aes, name, GetInterfaceName());
     }
 
     RootedValue fval(cx);
     RootedObject obj(cx, wrapper->GetJSObject());
     RootedObject thisObj(cx, obj);
 
--- a/js/xpconnect/src/moz.build
+++ b/js/xpconnect/src/moz.build
@@ -67,9 +67,9 @@ LOCAL_INCLUDES += [
 ]
 
 if CONFIG['MOZ_B2G_BT']:
     LOCAL_INCLUDES += [
         '/dom/bluetooth/common',
     ]
 
 if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wno-shadow']
+    CXXFLAGS += ['-Wno-shadow', '-Werror=format']
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -269,17 +269,17 @@ AccessCheck::checkPassToPrivilegedCode(J
 }
 
 enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 };
 
 static void
 EnterAndThrowASCII(JSContext* cx, JSObject* wrapper, const char* msg)
 {
     JSAutoCompartment ac(cx, wrapper);
-    JS_ReportErrorASCII(cx, msg);
+    JS_ReportErrorASCII(cx, "%s", msg);
 }
 
 bool
 ExposedPropertiesOnly::check(JSContext* cx, HandleObject wrapper, HandleId id, Wrapper::Action act)
 {
     RootedObject wrappedObject(cx, Wrapper::wrappedObject(wrapper));
 
     if (act == Wrapper::CALL)
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -12800,21 +12800,17 @@ Iterator::AppendItemsToList(const Iterat
       !aTargetList.mUndisplayedItems.IsEmpty()) {
     do {
       AppendItemToList(aTargetList);
     } while (*this != aEnd);
     return;
   }
 
   // Move our entire list of items into the empty target list.
-  // XXX: If LinkedList supports move assignment, we could use
-  // aTargetList.mItems = Move(mList.mItems);
-  aTargetList.mItems.~LinkedList<FrameConstructionItem>();
-  new (&aTargetList.mItems) LinkedList<FrameConstructionItem>(
-    Move(mList.mItems));
+  aTargetList.mItems = Move(mList.mItems);
 
   // Copy over the various counters
   aTargetList.mInlineCount = mList.mInlineCount;
   aTargetList.mBlockCount = mList.mBlockCount;
   aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
   aTargetList.mItemCount = mList.mItemCount;
   memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
          sizeof(aTargetList.mDesiredParentCounts));
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1405,22 +1405,21 @@ nsBlockFrame::Reflow(nsPresContext*     
       }
       if (cbHeightChanged) {
         flags |= AbsPosReflowFlags::eCBHeightChanged;
       }
       // Setup the line cursor here to optimize line searching for
       // calculating hypothetical position of absolutely-positioned
       // frames. The line cursor is immediately cleared afterward to
       // avoid affecting the display list generation.
-      SetupLineCursor();
+      AutoLineCursorSetup autoLineCursor(this);
       absoluteContainer->Reflow(this, aPresContext, *reflowInput,
                                 state.mReflowStatus,
                                 containingBlock, flags,
                                 &aMetrics.mOverflowAreas);
-      ClearLineCursor();
     }
   }
 
   FinishAndStoreOverflow(&aMetrics);
 
   // Clear the float manager pointer in the block reflow state so we
   // don't waste time translating the coordinate system back on a dead
   // float manager.
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -148,18 +148,22 @@ public:
   virtual nsFrameState GetDebugStateBits() const override;
   const char* LineReflowStatusToString(LineReflowStatus aLineReflowStatus) const;
 #endif
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
-  // line cursor methods to speed up searching for the line(s)
-  // containing a point. The basic idea is that we set the cursor
+  // Line cursor methods to speed up line searching in which one query
+  // result is expected to be close to the next in general. This is
+  // mainly for searching line(s) containing a point. It is also used
+  // as a cache for local computation. Use AutoLineCursorSetup for the
+  // latter case so that it wouldn't interact unexpectedly with the
+  // former. The basic idea for the former is that we set the cursor
   // property if the lines' overflowArea.VisualOverflow().ys and
   // overflowArea.VisualOverflow().yMosts are non-decreasing
   // (considering only non-empty overflowArea.VisualOverflow()s; empty
   // overflowArea.VisualOverflow()s never participate in event handling
   // or painting), and the block has sufficient number of lines. The
   // cursor property points to a "recently used" line. If we get a
   // series of requests that work on lines
   // "near" the cursor, then we can find those nearby lines quickly by
@@ -174,16 +178,46 @@ public:
   // to be before any line which does contain 'y'.
   nsLineBox* GetFirstLineContaining(nscoord y);
   // Set the line cursor to our first line. Only call this if you
   // guarantee that either the lines' combinedArea.ys and combinedArea.
   // yMosts are non-decreasing, or the line cursor is cleared before
   // building the display list of this frame.
   void SetupLineCursor();
 
+  /**
+   * Helper RAII class for automatically set and clear line cursor for
+   * temporary use. If the frame already has line cursor, this would be
+   * a no-op.
+   */
+  class MOZ_STACK_CLASS AutoLineCursorSetup
+  {
+  public:
+    explicit AutoLineCursorSetup(nsBlockFrame* aFrame)
+      : mFrame(aFrame)
+      , mOrigCursor(aFrame->GetLineCursor())
+    {
+      if (!mOrigCursor) {
+        mFrame->SetupLineCursor();
+      }
+    }
+    ~AutoLineCursorSetup()
+    {
+      if (mOrigCursor) {
+        mFrame->Properties().Set(LineCursorProperty(), mOrigCursor);
+      } else {
+        mFrame->ClearLineCursor();
+      }
+    }
+
+  private:
+    nsBlockFrame* mFrame;
+    nsLineBox* mOrigCursor;
+  };
+
   virtual void ChildIsDirty(nsIFrame* aChild) override;
   virtual bool IsVisibleInSelection(nsISelection* aSelection) override;
 
   virtual bool IsEmpty() override;
   virtual bool CachedIsEmpty() override;
   virtual bool IsSelfEmpty() override;
 
   // Given that we have a bullet, does it actually draw something, i.e.,
@@ -363,19 +397,19 @@ protected:
   }
   virtual ~nsBlockFrame();
 
 #ifdef DEBUG
   already_AddRefed<nsStyleContext> GetFirstLetterStyle(nsPresContext* aPresContext);
 #endif
 
   NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(LineCursorProperty, nsLineBox)
+  bool HasLineCursor() { return GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR; }
   nsLineBox* GetLineCursor() {
-    return (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR) ?
-      Properties().Get(LineCursorProperty()) : nullptr;
+    return HasLineCursor() ? Properties().Get(LineCursorProperty()) : nullptr;
   }
 
   nsLineBox* NewLineBox(nsIFrame* aFrame, bool aIsBlock) {
     return NS_NewLineBox(PresContext()->PresShell(), aFrame, aIsBlock);
   }
   nsLineBox* NewLineBox(nsLineBox* aFromLine, nsIFrame* aFrame, int32_t aCount) {
     return NS_NewLineBox(PresContext()->PresShell(), aFromLine, aFrame, aCount);
   }
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -9629,27 +9629,20 @@ static void TransformChars(nsTextFrame* 
     } else {
       // Should not happen (see assertion above), but as a fallback...
       aOut.Append(fragString);
     }
   }
 }
 
 static bool
-LineEndsInHardLineBreak(nsTextFrame* aFrame)
-{
-  nsIFrame* lineContainer = FindLineContainer(aFrame);
-  nsBlockFrame* block = do_QueryFrame(lineContainer);
-  if (!block) {
-    // Weird situation where we have a line layout without a block.
-    // No soft breaks occur in this situation.
-    return true;
-  }
+LineEndsInHardLineBreak(nsTextFrame* aFrame, nsBlockFrame* aLineContainer)
+{
   bool foundValidLine;
-  nsBlockInFlowLineIterator iter(block, aFrame, &foundValidLine);
+  nsBlockInFlowLineIterator iter(aLineContainer, aFrame, &foundValidLine);
   if (!foundValidLine) {
     NS_ERROR("Invalid line!");
     return true;
   }
   return !iter.GetLine()->IsLineWrapped();
 }
 
 nsIFrame::RenderedText
@@ -9657,40 +9650,62 @@ nsTextFrame::GetRenderedText(uint32_t aS
                              uint32_t aEndOffset,
                              TextOffsetType aOffsetType,
                              TrailingWhitespace aTrimTrailingWhitespace)
 {
   NS_ASSERTION(!GetPrevContinuation(), "Must be called on first-in-flow");
 
   // The handling of offsets could be more efficient...
   RenderedText result;
+  nsBlockFrame* lineContainer = nullptr;
   nsTextFrame* textFrame;
   const nsTextFragment* textFrag = mContent->GetText();
   uint32_t offsetInRenderedString = 0;
   bool haveOffsets = false;
 
+  Maybe<nsBlockFrame::AutoLineCursorSetup> autoLineCursor;
   for (textFrame = this; textFrame;
        textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation())) {
     if (textFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
       // We don't trust dirty frames, especially when computing rendered text.
       break;
     }
 
     // Ensure the text run and grab the gfxSkipCharsIterator for it
     gfxSkipCharsIterator iter =
       textFrame->EnsureTextRun(nsTextFrame::eInflated);
     if (!textFrame->mTextRun) {
       break;
     }
     gfxSkipCharsIterator tmpIter = iter;
 
+    // Whether we need to trim whitespaces after the text frame.
+    bool trimAfter;
+    if (!textFrame->IsAtEndOfLine() ||
+        aTrimTrailingWhitespace !=
+          TrailingWhitespace::TRIM_TRAILING_WHITESPACE) {
+      trimAfter = false;
+    } else if (nsBlockFrame* thisLc =
+               do_QueryFrame(FindLineContainer(textFrame))) {
+      if (thisLc != lineContainer) {
+        // Setup line cursor when needed.
+        lineContainer = thisLc;
+        autoLineCursor.reset();
+        autoLineCursor.emplace(lineContainer);
+      }
+      trimAfter = LineEndsInHardLineBreak(textFrame, lineContainer);
+    } else {
+      // Weird situation where we have a line layout without a block.
+      // No soft breaks occur in this situation.
+      trimAfter = true;
+    }
+
     // Skip to the start of the text run, past ignored chars at start of line
-    TrimmedOffsets trimmedOffsets = textFrame->GetTrimmedOffsets(textFrag,
-       textFrame->IsAtEndOfLine() && LineEndsInHardLineBreak(textFrame) &&
-       aTrimTrailingWhitespace == TrailingWhitespace::TRIM_TRAILING_WHITESPACE);
+    TrimmedOffsets trimmedOffsets =
+        textFrame->GetTrimmedOffsets(textFrag, trimAfter);
     bool trimmedSignificantNewline =
         trimmedOffsets.GetEnd() < GetContentEnd() &&
         HasSignificantTerminalNewline();
     uint32_t skippedToRenderedStringOffset = offsetInRenderedString -
         tmpIter.ConvertOriginalToSkipped(trimmedOffsets.mStart);
     uint32_t nextOffsetInRenderedString =
         tmpIter.ConvertOriginalToSkipped(trimmedOffsets.GetEnd()) +
         (trimmedSignificantNewline ? 1 : 0) + skippedToRenderedStringOffset;
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -564,9 +564,41 @@
 #  include "mozilla/Compiler.h"
 #  if MOZ_GCC_VERSION_AT_LEAST(4, 8, 1)
 #    define MOZ_HAVE_REF_QUALIFIERS
 #  endif
 #endif
 
 #endif /* __cplusplus */
 
+/**
+ * Printf style formats.  MOZ_FORMAT_PRINTF can be used to annotate a
+ * function or method that is "printf-like"; this will let (some)
+ * compilers check that the arguments match the template string.
+ *
+ * This macro takes two arguments.  The first argument is the argument
+ * number of the template string.  The second argument is the argument
+ * number of the '...' argument holding the arguments.
+ *
+ * Argument numbers start at 1.  Note that the implicit "this"
+ * argument of a non-static member function counts as an argument.
+ *
+ * So, for a simple case like:
+ *   void print_something (int whatever, const char *fmt, ...);
+ * The corresponding annotation would be
+ *   MOZ_FORMAT_PRINTF(2, 3)
+ * However, if "print_something" were a non-static member function,
+ * then the annotation would be:
+ *   MOZ_FORMAT_PRINTF(3, 4)
+ *
+ * Note that the checking is limited to standards-conforming
+ * printf-likes, and in particular this should not be used for
+ * PR_snprintf and friends, which are "printf-like" but which assign
+ * different meanings to the various formats.
+ */
+#ifdef __GNUC__
+#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck)  \
+    __attribute__ ((format (printf, stringIndex, firstToCheck)))
+#else
+#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck)
+#endif
+
 #endif /* mozilla_Attributes_h */
--- a/mfbt/LinkedList.h
+++ b/mfbt/LinkedList.h
@@ -120,44 +120,33 @@ private:
 
 public:
   LinkedListElement()
     : mNext(this),
       mPrev(this),
       mIsSentinel(false)
   { }
 
-  LinkedListElement(LinkedListElement<T>&& other)
-    : mIsSentinel(other.mIsSentinel)
+  /*
+   * Moves |aOther| into |*this|. If |aOther| is already in a list, then
+   * |aOther| is removed from the list and replaced by |*this|.
+   */
+  LinkedListElement(LinkedListElement<T>&& aOther)
+    : mIsSentinel(aOther.mIsSentinel)
   {
-    if (!other.isInList()) {
-      mNext = this;
-      mPrev = this;
-      return;
-    }
-
-    MOZ_ASSERT(other.mNext->mPrev == &other);
-    MOZ_ASSERT(other.mPrev->mNext == &other);
+    adjustLinkForMove(Move(aOther));
+  }
 
-    /*
-     * Initialize |this| with |other|'s mPrev/mNext pointers, and adjust those
-     * element to point to this one.
-     */
-    mNext = other.mNext;
-    mPrev = other.mPrev;
-
-    mNext->mPrev = this;
-    mPrev->mNext = this;
-
-    /*
-     * Adjust |other| so it doesn't think it's in a list.  This makes it
-     * safely destructable.
-     */
-    other.mNext = &other;
-    other.mPrev = &other;
+  LinkedListElement& operator=(LinkedListElement<T>&& aOther)
+  {
+    MOZ_ASSERT(mIsSentinel == aOther.mIsSentinel, "Mismatch NodeKind!");
+    MOZ_ASSERT(!isInList(),
+               "Assigning to an element in a list messes up that list!");
+    adjustLinkForMove(Move(aOther));
+    return *this;
   }
 
   ~LinkedListElement()
   {
     if (!mIsSentinel && isInList()) {
       remove();
     }
   }
@@ -228,25 +217,25 @@ public:
   {
     MOZ_ASSERT((mNext == this) == (mPrev == this));
     return mNext != this;
   }
 
 private:
   friend class LinkedList<T>;
 
-  enum NodeKind {
-    NODE_KIND_NORMAL,
-    NODE_KIND_SENTINEL
+  enum class NodeKind {
+    Normal,
+    Sentinel
   };
 
   explicit LinkedListElement(NodeKind nodeKind)
     : mNext(this),
       mPrev(this),
-      mIsSentinel(nodeKind == NODE_KIND_SENTINEL)
+      mIsSentinel(nodeKind == NodeKind::Sentinel)
   { }
 
   /*
    * Return |this| cast to T* if we're a normal node, or return nullptr if
    * we're a sentinel node.
    */
   T* asT()
   {
@@ -282,17 +271,49 @@ private:
     MOZ_ASSERT(!listElem->isInList());
 
     listElem->mNext = this;
     listElem->mPrev = this->mPrev;
     this->mPrev->mNext = listElem;
     this->mPrev = listElem;
   }
 
-private:
+  /*
+   * Adjust mNext and mPrev for implementing move constructor and move
+   * assignment.
+   */
+  void adjustLinkForMove(LinkedListElement<T>&& aOther)
+  {
+    if (!aOther.isInList()) {
+      mNext = this;
+      mPrev = this;
+      return;
+    }
+
+    MOZ_ASSERT(aOther.mNext->mPrev == &aOther);
+    MOZ_ASSERT(aOther.mPrev->mNext == &aOther);
+
+    /*
+     * Initialize |this| with |aOther|'s mPrev/mNext pointers, and adjust those
+     * element to point to this one.
+     */
+    mNext = aOther.mNext;
+    mPrev = aOther.mPrev;
+
+    mNext->mPrev = this;
+    mPrev->mNext = this;
+
+    /*
+     * Adjust |aOther| so it doesn't think it's in a list.  This makes it
+     * safely destructable.
+     */
+    aOther.mNext = &aOther;
+    aOther.mPrev = &aOther;
+  }
+
   LinkedListElement& operator=(const LinkedListElement<T>& aOther) = delete;
   LinkedListElement(const LinkedListElement<T>& aOther) = delete;
 };
 
 template<typename T>
 class LinkedList
 {
 private:
@@ -314,22 +335,29 @@ public:
       return *this;
     }
 
     bool operator!=(Iterator& aOther) const {
       return mCurrent != aOther.mCurrent;
     }
   };
 
-  LinkedList() : sentinel(LinkedListElement<T>::NODE_KIND_SENTINEL) { }
+  LinkedList() : sentinel(LinkedListElement<T>::NodeKind::Sentinel) { }
 
   LinkedList(LinkedList<T>&& aOther)
     : sentinel(mozilla::Move(aOther.sentinel))
   { }
 
+  LinkedList& operator=(LinkedList<T>&& aOther)
+  {
+    MOZ_ASSERT(isEmpty(), "Assigning to a non-empty list leaks elements in that list!");
+    sentinel = mozilla::Move(aOther.sentinel);
+    return *this;
+  }
+
   ~LinkedList() {
     MOZ_ASSERT(isEmpty(),
                "failing this assertion means this LinkedList's creator is "
                "buggy: it should have removed all this list's elements before "
                "the list's destruction");
   }
 
   /*
--- a/mfbt/Sprintf.h
+++ b/mfbt/Sprintf.h
@@ -8,32 +8,31 @@
 
 #ifndef mozilla_Sprintf_h_
 #define mozilla_Sprintf_h_
 
 #include <stdio.h>
 #include <stdarg.h>
 
 #include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 
 #ifdef __cplusplus
 
 template <size_t N>
 int VsprintfLiteral(char (&buffer)[N], const char* format, va_list args)
 {
     MOZ_ASSERT(format != buffer);
     int result = vsnprintf(buffer, N, format, args);
     buffer[N - 1] = '\0';
     return result;
 }
 
 template <size_t N>
-#if defined(__GNUC__)
-  __attribute__((format(printf, 2, 3)))
-#endif
+MOZ_FORMAT_PRINTF(2, 3)
 int SprintfLiteral(char (&buffer)[N], const char* format, ...)
 {
   va_list args;
   va_start(args, format);
   int result = VsprintfLiteral(buffer, format, args);
   va_end(args);
   return result;
 }
--- a/mfbt/tests/TestLinkedList.cpp
+++ b/mfbt/tests/TestLinkedList.cpp
@@ -7,17 +7,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/LinkedList.h"
 
 using mozilla::LinkedList;
 using mozilla::LinkedListElement;
 
 struct SomeClass : public LinkedListElement<SomeClass> {
   unsigned int mValue;
-  explicit SomeClass(int aValue) : mValue(aValue) {}
+  explicit SomeClass(int aValue = 0) : mValue(aValue) {}
   void incr() { ++mValue; }
 };
 
 template <size_t N>
 static void
 CheckListValues(LinkedList<SomeClass>& list, unsigned int (&values)[N])
 {
   size_t count = 0;
@@ -29,17 +29,17 @@ CheckListValues(LinkedList<SomeClass>& l
 }
 
 static void
 TestList()
 {
   LinkedList<SomeClass> list;
 
   SomeClass one(1), two(2), three(3);
-  
+
   MOZ_RELEASE_ASSERT(list.isEmpty());
   MOZ_RELEASE_ASSERT(!list.getFirst());
   MOZ_RELEASE_ASSERT(!list.getLast());
   MOZ_RELEASE_ASSERT(!list.popFirst());
   MOZ_RELEASE_ASSERT(!list.popLast());
 
   for (SomeClass* x : list) {
     MOZ_RELEASE_ASSERT(x);
@@ -47,17 +47,17 @@ TestList()
   }
 
   list.insertFront(&one);
   { unsigned int check[] { 1 }; CheckListValues(list, check); }
 
   MOZ_RELEASE_ASSERT(one.isInList());
   MOZ_RELEASE_ASSERT(!two.isInList());
   MOZ_RELEASE_ASSERT(!three.isInList());
-  
+
   MOZ_RELEASE_ASSERT(!list.isEmpty());
   MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 1);
   MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1);
 
   list.insertFront(&two);
   { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
 
   MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2);
@@ -112,16 +112,55 @@ TestList()
   }
 
   MOZ_RELEASE_ASSERT(list.getFirst() == &two);
   MOZ_RELEASE_ASSERT(list.getLast() == &three);
   MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 3);
   MOZ_RELEASE_ASSERT(list.getLast()->mValue == 4);
 }
 
+static void
+TestMove()
+{
+  auto MakeSomeClass =
+    [] (unsigned int aValue) -> SomeClass { return SomeClass(aValue); };
+
+  LinkedList<SomeClass> list1;
+
+  // Test move constructor for LinkedListElement.
+  SomeClass c1(MakeSomeClass(1));
+  list1.insertBack(&c1);
+
+  // Test move assignment for LinkedListElement from an element not in a
+  // list.
+  SomeClass c2;
+  c2 = MakeSomeClass(2);
+  list1.insertBack(&c2);
+
+  // Test move assignment of LinkedListElement from an element already in a
+  // list.
+  SomeClass c3;
+  c3 = Move(c2);
+  MOZ_RELEASE_ASSERT(!c2.isInList());
+  MOZ_RELEASE_ASSERT(c3.isInList());
+
+  // Test move constructor for LinkedList.
+  LinkedList<SomeClass> list2(Move(list1));
+  { unsigned int check[] { 1, 2 }; CheckListValues(list2, check); }
+  MOZ_RELEASE_ASSERT(list1.isEmpty());
+
+  // Test move assignment for LinkedList.
+  LinkedList<SomeClass> list3;
+  list3 = Move(list2);
+  { unsigned int check[] { 1, 2 }; CheckListValues(list3, check); }
+  MOZ_RELEASE_ASSERT(list2.isEmpty());
+
+  list3.clear();
+}
+
 struct PrivateClass : private LinkedListElement<PrivateClass> {
   friend class mozilla::LinkedList<PrivateClass>;
   friend class mozilla::LinkedListElement<PrivateClass>;
 };
 
 static void
 TestPrivate()
 {
@@ -138,10 +177,11 @@ TestPrivate()
   MOZ_RELEASE_ASSERT(count == 2);
 }
 
 int
 main()
 {
   TestList();
   TestPrivate();
+  TestMove();
   return 0;
 }
--- a/moz.configure
+++ b/moz.configure
@@ -311,17 +311,19 @@ def nsis_version(nsis):
     out = check_cmd_output(nsis, '-version',
                            onerror=lambda: die('Failed to get nsis version.'))
     m = re.search(r'(?<=v)[0-9]+\.[0-9]+((a|b|rc)[0-9]+)?', out)
 
     if not m:
         raise FatalCheckError('Unknown version of makensis')
     ver = Version(m.group(0))
 
-    if ver < nsis_min_version:
+    # Versions comparisons don't quite work well with beta versions, so ensure
+    # it works for the non-beta version.
+    if ver < nsis_min_version and (ver >= '3.0a' or ver < '3'):
         raise FatalCheckError('To build the installer you must have NSIS'
                               ' version %s or greater in your path'
                               % nsis_min_version)
 
     return ver
 
 # And that makensis is 32-bit.
 @depends_if(nsis)
--- a/python/mozbuild/mozbuild/test/configure/test_moz_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_moz_configure.py
@@ -52,11 +52,42 @@ class TestMozConfigure(BaseConfigureTest
         self.assertEquals('--enable-application=browser --with-foo',
                           get_value_for(['--enable-application=browser',
                                          '--with-foo']))
 
         self.assertEquals("--enable-application=browser '--with-foo=foo bar'",
                           get_value_for(['--enable-application=browser',
                                          '--with-foo=foo bar']))
 
+    def test_nsis_version(self):
+        this = self
+
+        class FakeNSIS(object):
+            def __init__(self, version):
+                self.version = version
+
+            def __call__(self, stdin, args):
+                this.assertEquals(args, ('-version',))
+                return 0, self.version, ''
+
+        def check_nsis_version(version):
+            sandbox = self.get_sandbox(
+                {'/usr/bin/makensis': FakeNSIS(version)}, {}, [],
+                {'PATH': '/usr/bin', 'MAKENSISU': '/usr/bin/makensis'})
+            return sandbox._value_for(sandbox['nsis_version'])
+
+        with self.assertRaises(SystemExit) as e:
+            check_nsis_version('v2.5')
+
+        with self.assertRaises(SystemExit) as e:
+            check_nsis_version('v3.0a2')
+
+        self.assertEquals(check_nsis_version('v3.0b1'), '3.0b1')
+        self.assertEquals(check_nsis_version('v3.0b2'), '3.0b2')
+        self.assertEquals(check_nsis_version('v3.0rc1'), '3.0rc1')
+        self.assertEquals(check_nsis_version('v3.0'), '3.0')
+        self.assertEquals(check_nsis_version('v3.0-2'), '3.0')
+        self.assertEquals(check_nsis_version('v3.0.1'), '3.0')
+        self.assertEquals(check_nsis_version('v3.1'), '3.1')
+
 
 if __name__ == '__main__':
     main()
--- a/security/manager/locales/en-US/chrome/pippki/pippki.properties
+++ b/security/manager/locales/en-US/chrome/pippki/pippki.properties
@@ -106,17 +106,19 @@ pageInfo_EncryptionWithBitsAndProtocol=C
 pageInfo_BrokenEncryption=Broken Encryption (%1$S, %2$S bit keys, %3$S)
 pageInfo_Privacy_Encrypted1=The page you are viewing was encrypted before being transmitted over the Internet.
 pageInfo_Privacy_Encrypted2=Encryption makes it difficult for unauthorized people to view information traveling between computers. It is therefore unlikely that anyone read this page as it traveled across the network.
 pageInfo_MixedContent=Connection Partially Encrypted
 pageInfo_MixedContent2=Parts of the page you are viewing were not encrypted before being transmitted over the Internet.
 pageInfo_WeakCipher=Your connection to this website uses weak encryption and is not private. Other people can view your information or modify the website’s behavior.
 
 # Cert Viewer
-certDetails=Certificate Viewer:
+# LOCALIZATION NOTE(certViewerTitle): Title used for the Certificate Viewer.
+# %1$S is a string representative of the certificate being viewed.
+certViewerTitle=Certificate Viewer: “%1$S”
 notPresent=<Not Part Of Certificate>
 
 # Token Manager
 password_not_set=(not set)
 failed_pw_change=Unable to change Master Password.
 incorrect_pw=You did not enter the correct current Master Password. Please try again.
 pw_change_ok=Master Password successfully changed.
 pw_erased_ok=Warning! You have deleted your Master Password. 
--- a/security/manager/pki/nsNSSDialogHelper.h
+++ b/security/manager/pki/nsNSSDialogHelper.h
@@ -6,22 +6,33 @@
 
 #ifndef nsNSSDialogHelper_h
 #define nsNSSDialogHelper_h
 
 class mozIDOMWindowProxy;
 class nsISupports;
 
 /**
- * Common class that uses the window watcher service to open a
- * standard dialog, with or without a parent context. The params
- * parameter can be an nsISupportsArray so any number of additional
- * arguments can be used.
+ * Helper class that uses the window watcher service to open a standard dialog,
+ * with or without a parent context.
  */
 class nsNSSDialogHelper
 {
 public:
-  // params is a nsIDialogParamBlock or a nsIKeygenThread
+  /**
+   * Opens a XUL dialog.
+   *
+   * @param window
+   *        Parent window of the dialog, or nullptr to signal no parent.
+   * @param url
+   *        URL to the XUL dialog.
+   * @param params
+   *        Parameters to pass to the dialog. Same semantics as the
+   *        nsIWindowWatcher.openWindow() |aArguments| parameter.
+   * @param modal
+   *        true if the dialog should be modal, false otherwise.
+   * @return The result of opening the dialog.
+   */
   static nsresult openDialog(mozIDOMWindowProxy* window, const char* url,
                              nsISupports* params, bool modal = true);
 };
 
-#endif
+#endif // nsNSSDialogHelper_h
--- a/security/manager/pki/nsNSSDialogs.cpp
+++ b/security/manager/pki/nsNSSDialogs.cpp
@@ -2,45 +2,38 @@
  *
  * 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/. */
 
 /*
  * Dialog services for PIP.
  */
+
+#include "nsNSSDialogs.h"
+
 #include "mozIDOMWindow.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "nsArray.h"
-#include "nsDateTimeFormatCID.h"
 #include "nsEmbedCID.h"
-#include "nsIComponentManager.h"
-#include "nsIDateTimeFormat.h"
 #include "nsIDialogParamBlock.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIKeygenThread.h"
 #include "nsIPromptService.h"
 #include "nsIProtectedAuthThread.h"
-#include "nsIServiceManager.h"
 #include "nsIWindowWatcher.h"
 #include "nsIX509CertDB.h"
 #include "nsIX509Cert.h"
-#include "nsIX509CertValidity.h"
 #include "nsNSSDialogHelper.h"
-#include "nsNSSDialogs.h"
-#include "nsPromiseFlatString.h"
-#include "nsReadableUtils.h"
 #include "nsString.h"
 
 #define PIPSTRING_BUNDLE_URL "chrome://pippki/locale/pippki.properties"
 
-/* ==== */
-
 nsNSSDialogs::nsNSSDialogs()
 {
 }
 
 nsNSSDialogs::~nsNSSDialogs()
 {
 }
 
@@ -324,43 +317,28 @@ nsNSSDialogs::GetPKCS12FilePassword(nsII
   if (*_retval) {
     _password.Assign(pwTemp);
     free(pwTemp);
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP 
+NS_IMETHODIMP
 nsNSSDialogs::ViewCert(nsIInterfaceRequestor* ctx, nsIX509Cert* cert)
 {
-  nsCOMPtr<nsIMutableArray> dlgArray = nsArrayBase::Create();
-  if (!dlgArray) {
-    return NS_ERROR_FAILURE;
-  }
-  nsresult rv = dlgArray->AppendElement(cert, false);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  nsCOMPtr<nsIDialogParamBlock> dlgParamBlock(
-    do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID));
-  if (!dlgParamBlock) {
-    return NS_ERROR_FAILURE;
-  }
-  rv = dlgParamBlock->SetObjects(dlgArray);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+  // |ctx| is allowed to be null.
+  NS_ENSURE_ARG(cert);
 
   // Get the parent window for the dialog
   nsCOMPtr<mozIDOMWindowProxy> parent = do_GetInterface(ctx);
   return nsNSSDialogHelper::openDialog(parent,
                                        "chrome://pippki/content/certViewer.xul",
-                                       dlgParamBlock,
-                                       false);
+                                       cert,
+                                       false /*modal*/);
 }
 
 NS_IMETHODIMP
 nsNSSDialogs::DisplayGeneratingKeypairInfo(nsIInterfaceRequestor *aCtx, nsIKeygenThread *runnable) 
 {
   nsresult rv;
 
   // Get the parent window for the dialog
rename from security/manager/pki/resources/content/viewCertDetails.js
rename to security/manager/pki/resources/content/certViewer.js
--- a/security/manager/pki/resources/content/viewCertDetails.js
+++ b/security/manager/pki/resources/content/certViewer.js
@@ -1,27 +1,34 @@
 /* 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/. */
 "use strict";
 
+/**
+ * @file Implements functionality for certViewer.xul and its tabs certDump.xul
+ *       and viewCertDetails.xul: a dialog that allows various attributes of a
+ *       certificate to be viewed.
+ * @argument {nsISupports} window.arguments[0]
+ *           The cert to view, queryable to nsIX509Cert.
+ */
+
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 const nsIX509Cert = Ci.nsIX509Cert;
 const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
 const nsIX509CertDB = Ci.nsIX509CertDB;
 const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
 const nsIPK11TokenDB = Ci.nsIPK11TokenDB;
 const nsIASN1Object = Ci.nsIASN1Object;
 const nsIASN1Sequence = Ci.nsIASN1Sequence;
 const nsIASN1PrintableItem = Ci.nsIASN1PrintableItem;
 const nsIASN1Tree = Ci.nsIASN1Tree;
 const nsASN1Tree = "@mozilla.org/security/nsASN1Tree;1";
-const nsIDialogParamBlock = Ci.nsIDialogParamBlock;
 
 var bundle;
 
 function doPrompt(msg)
 {
   let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
     getService(Components.interfaces.nsIPromptService);
   prompts.alert(window, null, msg);
@@ -66,31 +73,21 @@ function AddUsage(usage)
   text.setAttribute("style", "margin: 2px 5px");
   text.setAttribute("readonly", "true");
   text.setAttribute("class", "scrollfield");
   verifyInfoBox.appendChild(text);
 }
 
 function setWindowName()
 {
-  //  Get the cert from the cert database
-  var certdb = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
-  var myName = self.name;
   bundle = document.getElementById("pippki_bundle");
-  var cert;
 
-  var certDetails = bundle.getString('certDetails');
-  if (myName != "") {
-    document.title = certDetails + '"' + myName + '"'; // XXX l10n?
-    cert = certdb.findCertByNickname(myName);
-  } else {
-    var params = window.arguments[0].QueryInterface(nsIDialogParamBlock);
-    cert = params.objects.queryElementAt(0, nsIX509Cert);
-    document.title = certDetails + '"' + cert.windowTitle + '"'; // XXX l10n?
-  }
+  let cert = window.arguments[0].QueryInterface(Ci.nsIX509Cert);
+  document.title = bundle.getFormattedString("certViewerTitle",
+                                             [cert.windowTitle]);
 
   //
   //  Set the cert attributes for viewing
   //
 
   //  The chain of trust
   AddCertChain("treesetDump", cert.getChain());
   DisplayGeneralDataFromCert(cert);
--- a/security/manager/pki/resources/content/certViewer.xul
+++ b/security/manager/pki/resources/content/certViewer.xul
@@ -15,17 +15,18 @@
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   buttons="accept"
   buttonlabelaccept="&certmgr.close.label;"
   buttonaccesskeyaccept="&certmgr.close.accesskey;"
   onload="setWindowName();">
 
 <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 
-<script type="application/javascript" src="chrome://pippki/content/viewCertDetails.js"/>
+<script type="application/javascript"
+        src="chrome://pippki/content/certViewer.js"/>
 <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
 
   <tabbox flex="1">
     <tabs>
       <tab id="general_tab" label="&certmgr.detail.general_tab.title;"
            accesskey="&certmgr.detail.general_tab.accesskey;"/>
       <tab id="prettyprint_tab" label="&certmgr.detail.prettyprint_tab.title;"
            accesskey="&certmgr.detail.prettyprint_tab.accesskey;"/>
--- a/security/manager/pki/resources/jar.mn
+++ b/security/manager/pki/resources/jar.mn
@@ -19,22 +19,22 @@ pippki.jar:
     content/pippki/OrphanOverlay.xul         (content/OrphanOverlay.xul)
     content/pippki/viewCertDetails.xul       (content/viewCertDetails.xul)
     content/pippki/editcacert.xul            (content/editcacert.xul)
     content/pippki/editcacert.js             (content/editcacert.js)
 *   content/pippki/exceptionDialog.xul       (content/exceptionDialog.xul)
     content/pippki/exceptionDialog.js        (content/exceptionDialog.js)
     content/pippki/deletecert.xul            (content/deletecert.xul)
     content/pippki/deletecert.js             (content/deletecert.js)
-    content/pippki/viewCertDetails.js        (content/viewCertDetails.js)
     content/pippki/setp12password.xul        (content/setp12password.xul)
     content/pippki/pippki.js                 (content/pippki.js)
     content/pippki/clientauthask.xul	     (content/clientauthask.xul)
     content/pippki/clientauthask.js          (content/clientauthask.js)
     content/pippki/certViewer.xul            (content/certViewer.xul)
+    content/pippki/certViewer.js             (content/certViewer.js)
     content/pippki/certDump.xul              (content/certDump.xul)
     content/pippki/device_manager.xul        (content/device_manager.xul)
     content/pippki/device_manager.js         (content/device_manager.js)
     content/pippki/load_device.xul           (content/load_device.xul)
     content/pippki/choosetoken.xul           (content/choosetoken.xul)
     content/pippki/choosetoken.js            (content/choosetoken.js)
     content/pippki/createCertInfo.xul        (content/createCertInfo.xul)
     content/pippki/createCertInfo.js         (content/createCertInfo.js)
--- a/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
@@ -5,113 +5,118 @@
 
 // Repeatedly opens the certificate viewer dialog with various certificates and
 // determines that the viewer correctly identifies either what usages those
 // certificates are valid for or what errors prevented the certificates from
 // being verified.
 
 var { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
 
-add_task(function* () {
+add_task(function* testCAandTitle() {
   let cert = yield readCertificate("ca.pem", "CTu,CTu,CTu");
   let win = yield displayCertificate(cert);
   checkUsages(win, ["SSL Certificate Authority"]);
+
+  // There's no real need to test the title for every cert, so we just test it
+  // once here.
+  Assert.equal(win.document.title, "Certificate Viewer: \u201Cca\u201D",
+               "Actual and expected title should match");
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testSSLEndEntity() {
   let cert = yield readCertificate("ssl-ee.pem", ",,");
   let win = yield displayCertificate(cert);
   checkUsages(win, ["SSL Server Certificate", "SSL Client Certificate"]);
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testEmailEndEntity() {
   let cert = yield readCertificate("email-ee.pem", ",,");
   let win = yield displayCertificate(cert);
   checkUsages(win, ["Email Recipient Certificate", "Email Signer Certificate"]);
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testCodeSignEndEntity() {
   let cert = yield readCertificate("code-ee.pem", ",,");
   let win = yield displayCertificate(cert);
   checkUsages(win, ["Object Signer"]);
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testExpired() {
   let cert = yield readCertificate("expired-ca.pem", ",,");
   let win = yield displayCertificate(cert);
   checkError(win, "Could not verify this certificate because it has expired.");
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testIssuerExpired() {
   let cert = yield readCertificate("ee-from-expired-ca.pem", ",,");
   let win = yield displayCertificate(cert);
   checkError(win,
              "Could not verify this certificate because the CA certificate " +
              "is invalid.");
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testUnknownIssuer() {
   let cert = yield readCertificate("unknown-issuer.pem", ",,");
   let win = yield displayCertificate(cert);
   checkError(win,
              "Could not verify this certificate because the issuer is " +
              "unknown.");
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testInsecureAlgo() {
   let cert = yield readCertificate("md5-ee.pem", ",,");
   let win = yield displayCertificate(cert);
   checkError(win,
              "Could not verify this certificate because it was signed using " +
              "a signature algorithm that was disabled because that algorithm " +
              "is not secure.");
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testUntrusted() {
   let cert = yield readCertificate("untrusted-ca.pem", "p,p,p");
   let win = yield displayCertificate(cert);
   checkError(win,
              "Could not verify this certificate because it is not trusted.");
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testUntrustedIssuer() {
   let cert = yield readCertificate("ee-from-untrusted-ca.pem", ",,");
   let win = yield displayCertificate(cert);
   checkError(win,
              "Could not verify this certificate because the issuer is not " +
              "trusted.");
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testRevoked() {
   // Note that there's currently no way to un-do this. This should only be a
   // problem if another test re-uses a certificate with this same key (perhaps
   // likely) and subject (less likely).
   let certBlocklist = Cc["@mozilla.org/security/certblocklist;1"]
                         .getService(Ci.nsICertBlocklist);
   certBlocklist.revokeCertBySubjectAndPubKey(
     "MBIxEDAOBgNVBAMMB3Jldm9rZWQ=", // CN=revoked
     "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8="); // hash of the shared key
   let cert = yield readCertificate("revoked.pem", ",,");
   let win = yield displayCertificate(cert);
   checkError(win,
              "Could not verify this certificate because it has been revoked.");
   yield BrowserTestUtils.closeWindow(win);
 });
 
-add_task(function* () {
+add_task(function* testInvalid() {
   // This certificate has a keyUsage extension asserting cRLSign and
   // keyCertSign, but it doesn't have a basicConstraints extension. This
   // shouldn't be valid for any usage. Sadly, we give a pretty lame error
   // message in this case.
   let cert = yield readCertificate("invalid.pem", ",,");
   let win = yield displayCertificate(cert);
   checkError(win, "Could not verify this certificate for unknown reasons.");
   yield BrowserTestUtils.closeWindow(win);
@@ -124,23 +129,18 @@ add_task(function* () {
  *
  * @param {nsIX509Cert} certificate
  *        The certificate to view and determine usages for.
  * @return {Promise}
  *         A promise that will resolve with a handle on the opened certificate
  *         viewer window when the usages have been determined.
  */
 function displayCertificate(certificate) {
-  let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
-  array.appendElement(certificate, false);
-  let params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
-                 .createInstance(Ci.nsIDialogParamBlock);
-  params.objects = array;
   let win = window.openDialog("chrome://pippki/content/certViewer.xul", "",
-                              "", params);
+                              "", certificate);
   return TestUtils.topicObserved("ViewCertDetails:CertUsagesDone",
                                  (subject, data) => subject == win)
   .then(([subject, data]) => subject, error => { throw error; });
 }
 
 /**
  * Given a certificate viewer window, finds the usages the certificate is valid
  * for.
--- a/taskcluster/ci/desktop-test/test-platforms.yml
+++ b/taskcluster/ci/desktop-test/test-platforms.yml
@@ -20,14 +20,47 @@ linux64/opt:
 
 # TODO: use 'pgo' and 'asan' labels here, instead of -pgo/opt
 linux64-pgo/opt:
     build-platform: linux64-pgo/opt
     test-set: all-tests
 linux64-asan/opt:
     build-platform: linux64-asan/opt
     test-set: asan-tests
+
 linux64-ccov/opt:
     build-platform: linux64-ccov/opt
     test-set: ccov-code-coverage-tests
 linux64-jsdcov/opt:
     build-platform: linux64-jsdcov/opt
-    test-set: jsdcov-code-coverage-tests
\ No newline at end of file
+    test-set: jsdcov-code-coverage-tests
+
+# win32 vm
+windows7-32-vm/debug:
+    build-platform: win32/debug
+    test-set: windows-vm-tests
+windows7-32-vm/opt:
+    build-platform: win32/opt
+    test-set: windows-vm-tests
+
+# win32 gpu
+#windows7-32/debug:
+#    build-platform: win32/debug
+#    test-set: windows-gpu-tests
+#windows7-32/opt:
+#    build-platform: win32/opt
+#    test-set: windows-gpu-tests
+
+# win64 vm
+windows10-64-vm/debug:
+    build-platform: win64/debug
+    test-set: windows-vm-tests
+windows10-64-vm/opt:
+    build-platform: win64/opt
+    test-set: windows-vm-tests
+
+# win64 gpu
+#windows10-64/debug:
+#    build-platform: win64/debug
+#    test-set: windows-gpu-tests
+#windows10-64/opt:
+#    build-platform: win64/opt
+#    test-set: windows-gpu-tests
--- a/taskcluster/ci/desktop-test/test-sets.yml
+++ b/taskcluster/ci/desktop-test/test-sets.yml
@@ -53,13 +53,42 @@ asan-tests:
     - mochitest-gpu
     - mochitest-jetpack
     - mochitest-media
     - mochitest-webgl
     - reftest
     - reftest-no-accel
     - xpcshell
 
+windows-vm-tests:
+    - cppunit
+    #- crashtest
+    #- external-media-tests
+    #- gtest
+    #- jittest
+    #- jsreftest
+    #- marionette
+    #- mochitest
+    #- mochitest-browser-chrome
+    #- mochitest-devtools-chrome
+    #- mochitest-jetpack
+    #- mochitest-media
+    #- web-platform-tests
+    #- web-platform-tests-reftests
+    #- xpcshell
+
+# windows-gpu-tests:
+#    - reftest
+#    - reftest-no-accel
+#    - mochitest-webgl
+
+# these tests currently run on hardware, but may migrate above when validated
+# note: on win, mochitest-a11y and mochitest-chrome come under mochitest-other
+# windows-hw-tests:
+#    - mochitest-clipboard
+#    - mochitest-gpu
+#    - mochitest-other
+
 ccov-code-coverage-tests:
     - mochitest-browser-chrome
 
 jsdcov-code-coverage-tests:
     - mochitest-browser-chrome
\ No newline at end of file
--- a/taskcluster/ci/desktop-test/tests.yml
+++ b/taskcluster/ci/desktop-test/tests.yml
@@ -10,38 +10,42 @@ cppunit:
     suite: cppunittest
     treeherder-symbol: tc(Cpp)
     e10s: false
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
-                # Coming soon:
-                # win.*:
-                #    - unittests/win_unittest.py
-                #    - remove_executables.py
-                # ...
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --cppunittest-suite=cppunittest
 
 crashtest:
     description: "Crashtest run"
     suite: reftest/crashtest
     treeherder-symbol: tc-R(C)
     docker-image: {"in-tree": "desktop1604-test"}
+    e10s:
+        by-test-platform:
+            # Bug 1304435
+            win.*: false
+            default: both
     mozharness:
         script: desktop_unittest.py
         chunked: true
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --reftest-suite=crashtest
 
 external-media-tests:
     description: "External Media Test run"
@@ -49,18 +53,22 @@ external-media-tests:
     treeherder-symbol: tc-VP(b-m)
     e10s: false
     tier: 2
     max-run-time: 5400
     mozharness:
         script: firefox_media_tests_buildbot.py
         no-read-buildbot-config: true
         config:
-            - mediatests/buildbot_posix_config.py
-            - remove_executables.py
+            by-test-platform:
+                win.*:
+                    - mediatests/taskcluster_windows_config.py
+                default:
+                    - mediatests/buildbot_posix_config.py
+                    - remove_executables.py
 
 firefox-ui-functional-local:
     description: "Firefox-ui-tests functional run"
     suite: "firefox-ui/functional local"
     treeherder-symbol: tc-Fxfn-l(en-US)
     max-run-time: 5400
     tier: 1
     docker-image: {"in-tree": "desktop1604-test"}
@@ -93,80 +101,101 @@ gtest:
     treeherder-symbol: tc(GTest)
     e10s: false
     instance-size: xlarge
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --gtest-suite=gtest
 
 jittest:
     description: "JIT Test run"
     suite: jittest/jittest-chunked
     treeherder-symbol: tc(Jit)
     e10s: false
-    chunks: 6
+    chunks:
+        by-test-platform:
+            win.*: 1
+            default: 6
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --jittest-suite=jittest-chunked
 
 jsreftest:
     description: "JS Reftest run"
     suite: reftest/jsreftest
     treeherder-symbol: tc-R(J)
-    chunks: 2
+    chunks:
+        by-test-platform:
+            win.*: 1
+            default: 2
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --reftest-suite=jsreftest
 
 marionette:
     description: "Marionette unittest run"
     suite: marionette
     treeherder-symbol: tc(Mn)
     max-run-time: 5400
     mozharness:
         script: marionette.py
         no-read-buildbot-config: true
         config:
-            - marionette/prod_config.py
-            - remove_executables.py
+            by-test-platform:
+                win.*:
+                    - marionette/windows_taskcluster_config.py
+                default:
+                    - marionette/prod_config.py
+                    - remove_executables.py
 
 mochitest:
     description: "Mochitest plain run"
     suite: mochitest/plain-chunked
     treeherder-symbol: tc-M()
     loopback-video: true
-    chunks: 10
+    chunks:
+        by-test-platform:
+            win.*: 5
+            default: 10
     max-run-time: 5400
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=plain-chunked
     # Bug 1281241: migrating to m3.large instances
     instance-size: legacy
     allow-software-gl-layers: false
@@ -178,16 +207,18 @@ mochitest-a11y:
     loopback-video: true
     e10s: false
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=a11y
 
 mochitest-browser-chrome:
     description: "Mochitest browser-chrome run"
@@ -217,16 +248,18 @@ mochitest-browser-chrome:
             linux64-ccov/opt: 7200
             linux64/debug: 5400
             default: 3600
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             by-test-platform:
                 linux64-jsdcov/opt:
                     - --mochitest-suite=browser-chrome-coverage
                 linux64-ccov/opt:
@@ -249,16 +282,18 @@ mochitest-chrome:
     loopback-video: true
     chunks: 3
     e10s: false
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=chrome
 
 mochitest-clipboard:
     description: "Mochitest clipboard run"
@@ -267,39 +302,46 @@ mochitest-clipboard:
     loopback-video: true
     instance-size: xlarge
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=plain-clipboard,chrome-clipboard,browser-chrome-clipboard,jetpack-package-clipboard
 
 mochitest-devtools-chrome:
     description: "Mochitest devtools-chrome run"
     suite: mochitest/mochitest-devtools-chrome-chunked
     treeherder-symbol: tc-M(dt)
     loopback-video: true
     max-run-time: 5400
-    chunks: 10
+    chunks:
+        by-test-platform:
+            win.*: 8
+            default: 10
     e10s:
         by-test-platform:
             # Bug 1242986: linux64/debug mochitest-devtools-chrome e10s is not greened up yet
             linux64/debug: false
             default: both
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=mochitest-devtools-chrome-chunked
     instance-size:
         by-test-platform:
             # Bug 1281241: migrating to m3.large instances
@@ -308,22 +350,28 @@ mochitest-devtools-chrome:
     # Bug 1296086: high number of intermittents observed with software GL and large instances
     allow-software-gl-layers: false
 
 mochitest-gpu:
     description: "Mochitest GPU run"
     suite: mochitest/plain-gpu,chrome-gpu,browser-chrome-gpu
     treeherder-symbol: tc-M(gpu)
     loopback-video: true
+    e10s:
+        by-test-platform:
+            win.*: both
+            default: true
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=plain-gpu,chrome-gpu,browser-chrome-gpu
 
 mochitest-jetpack:
     description: "Mochitest jetpack run"
@@ -333,36 +381,41 @@ mochitest-jetpack:
     e10s: false
     max-run-time: 5400
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=jetpack-package
+            - --mochitest-suite=jetpack-addon
 
 mochitest-media:
     description: "Mochitest media run"
     suite: mochitest/mochitest-media
     treeherder-symbol: tc-M(mda)
     max-run-time: 5400
     loopback-video: true
     instance-size: large
     docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=mochitest-media
 
 mochitest-webgl:
     description: "Mochitest webgl run"
@@ -371,16 +424,18 @@ mochitest-webgl:
     chunks: 3
     loopback-video: true
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=mochitest-gl
     # Bug 1296733: llvmpipe with mesa 9.2.1 lacks thread safety
     allow-software-gl-layers: false
 
@@ -390,16 +445,18 @@ reftest:
     treeherder-symbol: tc-R(R)
     chunks: 8
     docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --reftest-suite=reftest
 
 reftest-no-accel:
     description: "Reftest not accelerated run"
@@ -407,16 +464,18 @@ reftest-no-accel:
     treeherder-symbol: tc-R(Ru)
     chunks: 8
     docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --reftest-suite=reftest-no-accel
 
 web-platform-tests:
     description: "Web platform test run"
@@ -426,70 +485,85 @@ web-platform-tests:
     max-run-time: 7200
     instance-size: xlarge
     docker-image: {"in-tree": "desktop1604-test"}
     checkout: true
     mozharness:
         script: web_platform_tests.py
         no-read-buildbot-config: true
         config:
-            - web_platform_tests/prod_config.py
-            - remove_executables.py
+            by-test-platform:
+                win.*:
+                    - web_platform_tests/prod_config_windows_taskcluster.py
+                default:
+                    - web_platform_tests/prod_config.py
+                    - remove_executables.py
         extra-options:
             - --test-type=testharness
 
 web-platform-tests-reftests:
     description: "Web platform reftest run"
     suite: web-platform-tests-reftests
     treeherder-symbol: tc-W(Wr)
     max-run-time: 5400
     instance-size: xlarge
     docker-image: {"in-tree": "desktop1604-test"}
     checkout: true
     mozharness:
         script: web_platform_tests.py
         no-read-buildbot-config: true
         config:
-            - web_platform_tests/prod_config.py
-            - remove_executables.py
+            by-test-platform:
+                win.*:
+                    - web_platform_tests/prod_config_windows_taskcluster.py
+                default:
+                    - web_platform_tests/prod_config.py
+                    - remove_executables.py
         extra-options:
             - --test-type=reftest
 
 web-platform-tests-wdspec:
     description: "Web platform webdriver-spec run"
     suite: web-platform-tests-wdspec
     treeherder-symbol: tc-W(Wd)
     max-run-time: 5400
     instance-size: xlarge
     docker-image: {"in-tree": "desktop1604-test"}
     checkout: true
     mozharness:
         script: web_platform_tests.py
         no-read-buildbot-config: true
         config:
-            - web_platform_tests/prod_config.py
-            - remove_executables.py
+            by-test-platform:
+                win.*:
+                    - web_platform_tests/prod_config_windows_taskcluster.py
+                default:
+                    - web_platform_tests/prod_config.py
+                    - remove_executables.py
         extra-options:
             - --test-type=wdspec
 
 xpcshell:
     description: "xpcshell test run"
     suite: xpcshell
     treeherder-symbol: tc-X()
     chunks:
         by-test-platform:
+            # win.*: 1
             linux64/debug: 10
             default: 8
     max-run-time: 5400
     e10s: false
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                win.*:
+                    - unittests/win_taskcluster_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --xpcshell-suite=xpcshell
     # Bug 1281241: migrating to m3.large instances
     instance-size: legacy
     allow-software-gl-layers: false
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -200,33 +200,36 @@ task_description_schema = Schema({
 
         # the exit status code that indicates the task should be retried
         Optional('retry-exit-status'): int,
 
     }, {
         Required('implementation'): 'generic-worker',
 
         # command is a list of commands to run, sequentially
-        'command': [basestring],
+        'command': [taskref_or_string],
 
         # artifacts to extract from the task image after completion; note that artifacts
         # for the generic worker cannot have names
         Optional('artifacts'): [{
             # type of artifact -- simple file, or recursive directory
             'type': Any('file', 'directory'),
 
             # task image path from which to read artifact
             'path': basestring,
         }],
 
         # environment variables
         Required('env', default={}): {basestring: taskref_or_string},
 
         # the maximum time to run, in seconds
         'max-run-time': int,
+
+        # os user groups for test task workers
+        Optional('os-groups', default=[]): [basestring],
     }, {
         Required('implementation'): 'buildbot-bridge',
 
         # see
         # https://github.com/mozilla/buildbot-bridge/blob/master/bbb/schemas/payload.yml
         'buildername': basestring,
         'sourcestamp': {
             'branch': basestring,
@@ -395,18 +398,19 @@ def build_generic_worker_payload(config,
             'path': artifact['path'],
             'type': artifact['type'],
             'expires': task_def['expires'],  # always expire with the task
         })
 
     task_def['payload'] = {
         'command': worker['command'],
         'artifacts': artifacts,
-        'env': worker['env'],
+        'env': worker.get('env', {}),
         'maxRunTime': worker['max-run-time'],
+        'osGroups': worker.get('os-groups', []),
     }
 
     if 'retry-exit-status' in worker:
         raise Exception("retry-exit-status not supported in generic-worker")
 
 
 transforms = TransformSequence()
 
--- a/taskcluster/taskgraph/transforms/tests/all_kinds.py
+++ b/taskcluster/taskgraph/transforms/tests/all_kinds.py
@@ -19,18 +19,20 @@ import copy
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def set_worker_implementation(config, tests):
     """Set the worker implementation based on the test platform."""
     for test in tests:
-        # this is simple for now, but soon will not be!
-        test['worker-implementation'] = 'docker-worker'
+        if test['test-platform'].startswith('win'):
+            test['worker-implementation'] = 'generic-worker'
+        else:
+            test['worker-implementation'] = 'docker-worker'
         yield test
 
 
 @transforms.add
 def set_tier(config, tests):
     """Set the tier based on policy for all test descriptions that do not
     specify a tier otherwise."""
     for test in tests:
--- a/taskcluster/taskgraph/transforms/tests/desktop_test.py
+++ b/taskcluster/taskgraph/transforms/tests/desktop_test.py
@@ -30,21 +30,22 @@ def set_defaults(config, tests):
 @transforms.add
 def set_treeherder_machine_platform(config, tests):
     """Set the appropriate task.extra.treeherder.machine.platform"""
     # Linux64 build platforms for asan and pgo are specified differently to
     # treeherder.  This is temporary until we can clean up the handling of
     # platforms
     translation = {
         'linux64-asan/opt': 'linux64/asan',
-        'linux64-pgo/opt': 'linux64/pgo',
+        'linux64-pgo/opt': 'linux64/pgo'
     }
     for test in tests:
         build_platform = test['build-platform']
-        test['treeherder-machine-platform'] = translation.get(build_platform, build_platform)
+        test_platform = test['test-platform']
+        test['treeherder-machine-platform'] = translation.get(build_platform, test_platform)
         yield test
 
 
 @transforms.add
 def set_asan_docker_image(config, tests):
     """Set the appropriate task.extra.treeherder.docker-image"""
     # Linux64-asan has many leaks with running mochitest-media jobs
     # on Ubuntu 16.04, please remove this when bug 1289209 is resolved
@@ -78,19 +79,33 @@ def split_e10s(config, tests):
             test['treeherder-symbol'] = join_symbol(group, symbol)
             test['mozharness'].setdefault('extra-options', []).append('--e10s')
         yield test
 
 
 @transforms.add
 def allow_software_gl_layers(config, tests):
     for test in tests:
-        allow = get_keyed_by(item=test, field='allow-software-gl-layers',
+
+        # since this value defaults to true, but is not applicable on windows,
+        # it's overriden for that platform here.
+        allow = not test['test-platform'].startswith('win') \
+            and get_keyed_by(item=test, field='allow-software-gl-layers',
                              item_name=test['test-name'])
         if allow:
             assert test['instance-size'] != 'legacy',\
                    'Software GL layers on a legacy instance is disallowed (bug 1296086).'
 
             # This should be set always once bug 1296086 is resolved.
             test['mozharness'].setdefault('extra-options', [])\
                               .append("--allow-software-gl-layers")
 
         yield test
+
+
+@transforms.add
+def add_os_groups(config, tests):
+    for test in tests:
+        if test['test-platform'].startswith('win'):
+            groups = get_keyed_by(item=test, field='os-groups', item_name=test['test-name'])
+            if groups:
+                test['os-groups'] = groups
+        yield test
--- a/taskcluster/taskgraph/transforms/tests/make_task_description.py
+++ b/taskcluster/taskgraph/transforms/tests/make_task_description.py
@@ -22,23 +22,28 @@ from __future__ import absolute_import, 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.job.common import (
     docker_worker_support_vcs_checkout,
 )
 
 import logging
 
 ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}'
-
-ARTIFACTS = [
-    # (artifact name prefix, in-image path)
-    ("public/logs/", "/home/worker/workspace/build/upload/logs/"),
-    ("public/test", "/home/worker/artifacts/"),
-    ("public/test_info/", "/home/worker/workspace/build/blobber_upload_dir/"),
-]
+WORKER_TYPE = {
+    # default worker types keyed by instance-size
+    'large': 'aws-provisioner-v1/desktop-test-large',
+    'xlarge': 'aws-provisioner-v1/desktop-test-xlarge',
+    'legacy': 'aws-provisioner-v1/desktop-test',
+    'default': 'aws-provisioner-v1/desktop-test-large',
+    # windows worker types keyed by test-platform
+    'windows7-32-vm': 'aws-provisioner-v1/gecko-t-win7-32',
+    'windows7-32': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
+    'windows10-64-vm': 'aws-provisioner-v1/gecko-t-win10-64',
+    'windows10-64': 'aws-provisioner-v1/gecko-t-win10-64-gpu'
+}
 
 logger = logging.getLogger(__name__)
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def make_task_description(config, tests):
@@ -117,47 +122,49 @@ def worker_setup_function(name):
         worker_setup_functions[name] = func
         return func
     return wrap
 
 
 @worker_setup_function("docker-engine")
 @worker_setup_function("docker-worker")
 def docker_worker_setup(config, test, taskdesc):
+
+    artifacts = [
+        # (artifact name prefix, in-image path)
+        ("public/logs/", "/home/worker/workspace/build/upload/logs/"),
+        ("public/test", "/home/worker/artifacts/"),
+        ("public/test_info/", "/home/worker/workspace/build/blobber_upload_dir/"),
+    ]
     mozharness = test['mozharness']
 
     installer_url = ARTIFACT_URL.format('<build>', mozharness['build-artifact-name'])
     test_packages_url = ARTIFACT_URL.format('<build>',
                                             'public/build/target.test_packages.json')
     mozharness_url = ARTIFACT_URL.format('<build>',
                                          'public/build/mozharness.zip')
 
-    taskdesc['worker-type'] = {
-        'default': 'aws-provisioner-v1/desktop-test-large',
-        'large': 'aws-provisioner-v1/desktop-test-large',
-        'xlarge': 'aws-provisioner-v1/desktop-test-xlarge',
-        'legacy': 'aws-provisioner-v1/desktop-test',
-    }[test['instance-size']]
+    taskdesc['worker-type'] = WORKER_TYPE[test['instance-size']]
 
     worker = taskdesc['worker'] = {}
     worker['implementation'] = test['worker-implementation']
     worker['docker-image'] = test['docker-image']
 
     worker['allow-ptrace'] = True  # required for all tests, for crashreporter
     worker['relengapi-proxy'] = False  # but maybe enabled for tooltool below
     worker['loopback-video'] = test['loopback-video']
     worker['loopback-audio'] = test['loopback-audio']
     worker['max-run-time'] = test['max-run-time']
     worker['retry-exit-status'] = test['retry-exit-status']
 
     worker['artifacts'] = [{
         'name': prefix,
         'path': path,
         'type': 'directory',
-    } for (prefix, path) in ARTIFACTS]
+    } for (prefix, path) in artifacts]
 
     worker['caches'] = [{
         'type': 'persistent',
         'name': 'level-{}-{}-test-workspace'.format(
             config.params['level'], config.params['project']),
         'mount-point': "/home/worker/workspace",
     }]
 
@@ -237,8 +244,116 @@ def docker_worker_setup(config, test, ta
                     command[i] += suffix
 
     if 'download-symbols' in mozharness:
         download_symbols = mozharness['download-symbols']
         download_symbols = {True: 'true', False: 'false'}.get(download_symbols, download_symbols)
         command.append('--download-symbols=' + download_symbols)
 
     worker['command'] = command
+
+
+def normpath(path):
+    return path.replace('/', '\\')
+
+
+def get_firefox_version():
+    with open('browser/config/version.txt', 'r') as f:
+        return f.readline().strip()
+
+
+@worker_setup_function('generic-worker')
+def generic_worker_setup(config, test, taskdesc):
+    artifacts = [
+        {
+            'path': 'public\\logs\\localconfig.json',
+            'type': 'file'
+        },
+        {
+            'path': 'public\\logs\\log_critical.log',
+            'type': 'file'
+        },
+        {
+            'path': 'public\\logs\\log_error.log',
+            'type': 'file'
+        },
+        {
+            'path': 'public\\logs\\log_fatal.log',
+            'type': 'file'
+        },
+        {
+            'path': 'public\\logs\\log_info.log',
+            'type': 'file'
+        },
+        {
+            'path': 'public\\logs\\log_raw.log',
+            'type': 'file'
+        },
+        {
+            'path': 'public\\logs\\log_warning.log',
+            'type': 'file'
+        },
+        {
+            'path': 'public\\test_info',
+            'type': 'directory'
+        }
+    ]
+    mozharness = test['mozharness']
+
+    build_platform = taskdesc['attributes']['build_platform']
+    test_platform = test['test-platform'].split('/')[0]
+
+    target = 'firefox-{}.en-US.{}'.format(get_firefox_version(), build_platform)
+
+    installer_url = ARTIFACT_URL.format(
+        '<build>', 'public/build/{}.zip'.format(target))
+    test_packages_url = ARTIFACT_URL.format(
+        '<build>', 'public/build/{}.test_packages.json'.format(target))
+    mozharness_url = ARTIFACT_URL.format(
+        '<build>', 'public/build/mozharness.zip')
+
+    taskdesc['worker-type'] = WORKER_TYPE[test_platform]
+
+    taskdesc['scopes'].extend(
+        ['generic-worker:os-group:{}'.format(group) for group in test['os-groups']])
+
+    worker = taskdesc['worker'] = {}
+    worker['os-groups'] = test['os-groups']
+    worker['implementation'] = test['worker-implementation']
+    worker['max-run-time'] = test['max-run-time']
+    worker['artifacts'] = artifacts
+
+    env = worker['env'] = {
+        # Bug 1306989
+        'APPDATA': '%cd%\\AppData\\Roaming',
+        'LOCALAPPDATA': '%cd%\\AppData\\Local',
+        'TEMP': '%cd%\\AppData\\Local\\Temp',
+        'TMP': '%cd%\\AppData\\Local\\Temp',
+        'USERPROFILE': '%cd%',
+    }
+
+    # assemble the command line
+    mh_command = [
+        'c:\\mozilla-build\\python\\python.exe',
+        '-u',
+        'mozharness\\scripts\\' + normpath(mozharness['script'])
+    ]
+    for mh_config in mozharness['config']:
+        mh_command.extend(['--cfg', 'mozharness\\configs\\' + normpath(mh_config)])
+    mh_command.extend(mozharness.get('extra-options', []))
+    if mozharness.get('no-read-buildbot-config'):
+        mh_command.append('--no-read-buildbot-config')
+    mh_command.extend(['--installer-url', installer_url])
+    mh_command.extend(['--test-packages-url', test_packages_url])
+    if mozharness.get('download-symbols'):
+        if isinstance(mozharness['download-symbols'], basestring):
+            mh_command.extend(['--download-symbols', mozharness['download-symbols']])
+        else:
+            mh_command.extend(['--download-symbols', 'true'])
+
+    worker['command'] = [
+        'mkdir {} {}'.format(env['APPDATA'], env['TMP']),
+        {'task-reference': 'c:\\mozilla-build\\wget\\wget.exe {}'.format(mozharness_url)},
+        'c:\\mozilla-build\\info-zip\\unzip.exe mozharness.zip',
+        {'task-reference': ' '.join(mh_command)},
+        'xcopy build\\blobber_upload_dir public\\test_info /e /i',
+        'copy /y logs\\*.* public\\logs\\'
+    ]
--- a/taskcluster/taskgraph/transforms/tests/test_description.py
+++ b/taskcluster/taskgraph/transforms/tests/test_description.py
@@ -101,18 +101,18 @@ test_description_schema = Schema({
     # using the GL compositor. May not be used with "legacy" sized instances
     # due to poor LLVMPipe performance (bug 1296086).
     Optional('allow-software-gl-layers', default=True): bool,
 
     # The worker implementation for this test, as dictated by policy and by the
     # test platform.
     Optional('worker-implementation'): Any(
         'docker-worker',
+        'generic-worker',
         # coming soon:
-        'generic-worker',
         'docker-engine',
         'buildbot-bridge',
     ),
 
     # For tasks that will run in docker-worker or docker-engine, this is the
     # name of the docker image or in-tree docker image to run the task in.  If
     # in-tree, then a dependency will be created automatically.  This is
     # generally `desktop-test`, or an image that acts an awful lot like it.
@@ -194,16 +194,24 @@ test_description_schema = Schema({
         # chunking-args = test-suite-suffix; "<CHUNK>" in this string will
         # be replaced with the chunk number.
         Optional('chunk-suffix'): basestring,
     }),
 
     # The current chunk; this is filled in by `all_kinds.py`
     Optional('this-chunk'): int,
 
+    # os user groups for test task workers; required scopes, will be
+    # added automatically
+    Optional('os-groups', default=[]): Any(
+        [basestring],
+        # todo: create a dedicated elevated worker group and name here
+        {'by-test-platform': {basestring: [basestring]}},
+    ),
+
     # -- values supplied by the task-generation infrastructure
 
     # the platform of the build this task is testing
     'build-platform': basestring,
 
     # the label of the build task generating the materials to test
     'build-label': basestring,
 
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/marionette/windows_taskcluster_config.py
@@ -0,0 +1,122 @@
+# This is a template config file for marionette production on Windows.
+import os
+import sys
+
+config = {
+    # marionette options
+    "marionette_address": "localhost:2828",
+    "test_manifest": "unit-tests.ini",
+
+    "virtualenv_python_dll": os.path.join(os.path.dirname(sys.executable), 'python27.dll'),
+    "virtualenv_path": 'venv',
+    "exes": {
+        'python': sys.executable,
+        'virtualenv': [
+            sys.executable,
+            os.path.join(os.path.dirname(sys.executable), 'Lib', 'site-packages', 'virtualenv.py')
+        ],
+        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
+        'tooltool.py': [sys.executable, os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')],
+        'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
+    },
+
+    "find_links": [
+        "http://pypi.pvt.build.mozilla.org/pub",
+        "http://pypi.pub.build.mozilla.org/pub",
+    ],
+    "pip_index": False,
+
+    "default_actions": [
+        'clobber',
+        'download-and-extract',
+        'create-virtualenv',
+        'install',
+        'run-tests',
+    ],
+    "default_blob_upload_servers": [
+         "https://blobupload.elasticbeanstalk.com",
+    ],
+    "blob_uploader_auth_file" : 'C:/builds/oauth.txt',
+    "download_minidump_stackwalk": True,
+    "download_symbols": "ondemand",
+    "suite_definitions": {
+        "gaiatest_desktop": {
+            "options": [
+                "--restart",
+                "--timeout=%(timeout)s",
+                "--testvars=%(testvars)s",
+                "--profile=%(profile)s",
+                "--symbols-path=%(symbols_path)s",
+                "--gecko-log=%(gecko_log)s",
+                "--log-xunit=%(xml_output)s",
+                "--html-output=%(html_output)s",
+                "--log-raw=%(raw_log_file)s",
+                "--log-errorsummary=%(error_summary_file)s",
+                "--binary=%(binary)s",
+                "--address=%(address)s",
+                "--total-chunks=%(total_chunks)s",
+                "--this-chunk=%(this_chunk)s"
+            ],
+            "run_filename": "",
+            "testsdir": ""
+        },
+        "gaiatest_emulator": {
+            "options": [
+                "--restart",
+                "--timeout=%(timeout)s",
+                "--testvars=%(testvars)s",
+                "--profile=%(profile)s",
+                "--symbols-path=%(symbols_path)s",
+                "--log-xunit=%(xml_output)s",
+                "--html-output=%(html_output)s",
+                "--log-raw=%(raw_log_file)s",
+                "--log-errorsummary=%(error_summary_file)s",
+                "--logcat-dir=%(logcat_dir)s",
+                "--emulator=%(emulator)s",
+                "--homedir=%(homedir)s"
+            ],
+            "run_filename": "",
+            "testsdir": ""
+        },
+        "marionette_desktop": {
+            "options": [
+                "--log-raw=%(raw_log_file)s",
+                "--log-errorsummary=%(error_summary_file)s",
+                "--binary=%(binary)s",
+                "--address=%(address)s",
+                "--symbols-path=%(symbols_path)s"
+            ],
+            "run_filename": "",
+            "testsdir": ""
+        },
+        "marionette_emulator": {
+            "options": [
+                "--log-raw=%(raw_log_file)s",
+                "--log-errorsummary=%(error_summary_file)s",
+                "--logcat-dir=%(logcat_dir)s",
+                "--emulator=%(emulator)s",
+                "--homedir=%(homedir)s",
+                "--symbols-path=%(symbols_path)s"
+            ],
+            "run_filename": "",
+            "testsdir": ""
+        },
+        "webapi_desktop": {
+            "options": [],
+            "run_filename": "",
+            "testsdir": ""
+        },
+        "webapi_emulator": {
+            "options": [
+                "--log-raw=%(raw_log_file)s",
+                "--log-errorsummary=%(error_summary_file)s",
+                "--symbols-path=%(symbols_path)s",
+                "--logcat-dir=%(logcat_dir)s",
+                "--emulator=%(emulator)s",
+                "--homedir=%(homedir)s"
+            ],
+            "run_filename": "",
+            "testsdir": ""
+        }
+    },
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/mediatests/taskcluster_windows_config.py
@@ -0,0 +1,54 @@
+import os
+import sys
+import mozharness
+
+external_tools_path = os.path.join(
+    os.path.abspath(os.path.dirname(os.path.dirname(mozharness.__file__))),
+    'external_tools',
+)
+
+config = {
+    "virtualenv_python_dll": os.path.join(os.path.dirname(sys.executable), 'python27.dll'),
+    "virtualenv_path": 'venv',
+    "exes": {
+        'python': sys.executable,
+        'virtualenv': [
+            sys.executable,
+            os.path.join(os.path.dirname(sys.executable), 'Lib', 'site-packages', 'virtualenv.py')
+        ],
+        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
+        'tooltool.py': [sys.executable, os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')],
+        'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
+    },
+    "find_links": [
+        "http://pypi.pvt.build.mozilla.org/pub",
+        "http://pypi.pub.build.mozilla.org/pub",
+    ],
+    "pip_index": False,
+
+    "default_actions": [
+        'clobber',
+        'download-and-extract',
+        'create-virtualenv',
+        'install',
+        'run-media-tests',
+    ],
+    "default_blob_upload_servers": [
+         "https://blobupload.elasticbeanstalk.com",
+    ],
+    "blob_uploader_auth_file" : 'C:/builds/oauth.txt',
+    "in_tree_config": "config/mozharness/marionette.py",
+    "download_minidump_stackwalk": True,
+    "download_symbols": "ondemand",
+
+    "suite_definitions": {
+        "media-tests": {
+            "options": [],
+        },
+        "media-youtube-tests": {
+            "options": [
+                "%(test_manifest)s"
+            ],
+        },
+    },
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/unittests/win_taskcluster_unittest.py
@@ -0,0 +1,282 @@
+import os
+import sys
+
+# OS Specifics
+ABS_WORK_DIR = os.path.join(os.getcwd(), "build")
+BINARY_PATH = os.path.join(ABS_WORK_DIR, "firefox", "firefox.exe")
+INSTALLER_PATH = os.path.join(ABS_WORK_DIR, "installer.zip")
+XPCSHELL_NAME = 'xpcshell.exe'
+EXE_SUFFIX = '.exe'
+DISABLE_SCREEN_SAVER = False
+ADJUST_MOUSE_AND_SCREEN = True
+#####
+config = {
+    "exes": {
+        'python': sys.executable,
+        'virtualenv': [
+            sys.executable,
+            os.path.join(os.path.dirname(sys.executable), 'Lib', 'site-packages', 'virtualenv.py')
+        ],
+        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
+        'tooltool.py': [sys.executable, os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')],
+        'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
+    },
+    ###
+    "installer_path": INSTALLER_PATH,
+    "binary_path": BINARY_PATH,
+    "xpcshell_name": XPCSHELL_NAME,
+    "virtualenv_path": 'venv',
+    "virtualenv_python_dll": os.path.join(os.path.dirname(sys.executable), "python27.dll"),
+
+    "find_links": [
+        "http://pypi.pvt.build.mozilla.org/pub",
+        "http://pypi.pub.build.mozilla.org/pub",
+    ],
+    "pip_index": False,
+    "exe_suffix": EXE_SUFFIX,
+    "run_file_names": {
+        "mochitest": "runtests.py",
+        "reftest": "runreftest.py",
+        "xpcshell": "runxpcshelltests.py",
+        "cppunittest": "runcppunittests.py",
+        "gtest": "rungtests.py",
+        "jittest": "jit_test.py",
+        "mozbase": "test.py",
+        "mozmill": "runtestlist.py",
+    },
+    "minimum_tests_zip_dirs": [
+        "bin/*",
+        "certs/*",
+        "config/*",
+        "mach",
+        "marionette/*",
+        "modules/*",
+        "mozbase/*",
+        "tools/*",
+    ],
+    "specific_tests_zip_dirs": {
+        "mochitest": ["mochitest/*"],
+        "reftest": ["reftest/*", "jsreftest/*"],
+        "xpcshell": ["xpcshell/*"],
+        "cppunittest": ["cppunittest/*"],
+        "gtest": ["gtest/*"],
+        "jittest": ["jit-test/*"],
+        "mozbase": ["mozbase/*"],
+        "mozmill": ["mozmill/*"],
+    },
+    "suite_definitions": {
+        "cppunittest": {
+            "options": [
+                "--symbols-path=%(symbols_path)s",
+                "--xre-path=%(abs_app_dir)s"
+            ],
+            "run_filename": "runcppunittests.py",
+            "testsdir": "cppunittest"
+        },
+        "jittest": {
+            "options": [
+                "tests/bin/js",
+                "--no-slow",
+                "--no-progress",
+                "--format=automation",
+                "--jitflags=all",
+                "--timeout=970" # Keep in sync with run_timeout below.
+            ],
+            "run_filename": "jit_test.py",
+            "testsdir": "jit-test/jit-test",
+            "run_timeout": 1000 # Keep in sync with --timeout above.
+        },
+        "mochitest": {
+            "options": [
+                "--appname=%(binary_path)s",
+                "--utility-path=tests/bin",
+                "--extra-profile-file=tests/bin/plugins",
+                "--symbols-path=%(symbols_path)s",
+                "--certificate-path=tests/certs",
+                "--quiet",
+                "--log-raw=%(raw_log_file)s",
+                "--log-errorsummary=%(error_summary_file)s",
+                "--screenshot-on-fail",
+                "--cleanup-crashes",
+            ],
+            "run_filename": "runtests.py",
+            "testsdir": "mochitest"
+        },
+        "mozbase": {
+            "options": [
+                "-b",
+                "%(binary_path)s"
+            ],
+            "run_filename": "test.py",
+            "testsdir": "mozbase"
+        },
+        "mozmill": {
+            "options": [
+                "--binary=%(binary_path)s",
+                "--testing-modules-dir=test/modules",
+                "--plugins-path=%(test_plugin_path)s",
+                "--symbols-path=%(symbols_path)s"
+            ],
+            "run_filename": "runtestlist.py",
+            "testsdir": "mozmill"
+        },
+        "reftest": {
+            "options": [
+                "--appname=%(binary_path)s",
+                "--utility-path=tests/bin",
+                "--extra-profile-file=tests/bin/plugins",
+                "--symbols-path=%(symbols_path)s",
+                "--log-raw=%(raw_log_file)s",
+                "--log-errorsummary=%(error_summary_file)s",
+                "--cleanup-crashes",
+            ],
+            "run_filename": "runreftest.py",
+            "testsdir": "reftest"
+        },
+        "xpcshell": {
+            "options": [
+                "--symbols-path=%(symbols_path)s",
+                "--test-plugin-path=%(test_plugin_path)s",
+                "--log-raw=%(raw_log_file)s",
+                "--log-errorsummary=%(error_summary_file)s",
+                "--utility-path=tests/bin",
+            ],
+            "run_filename": "runxpcshelltests.py",
+            "testsdir": "xpcshell"
+        },
+        "gtest": {
+            "options": [
+                "--xre-path=%(abs_res_dir)s",
+                "--cwd=%(gtest_dir)s",
+                "--symbols-path=%(symbols_path)s",
+                "--utility-path=tests/bin",
+                "%(binary_path)s",
+            ],
+            "run_filename": "rungtests.py",
+        },
+    },
+    # local mochi suites
+    "all_mochitest_suites":
+    {
+        "plain": [],
+        "plain-gpu": ["--subsuite=gpu"],
+        "plain-clipboard": ["--subsuite=clipboard"],
+        "plain-chunked": ["--chunk-by-dir=4"],
+        "mochitest-media": ["--subsuite=media"],
+        "chrome": ["--flavor=chrome"],
+        "chrome-gpu": ["--flavor=chrome", "--subsuite=gpu"],
+        "chrome-clipboard": ["--flavor=chrome", "--subsuite=clipboard"],
+        "chrome-chunked": ["--flavor=chrome", "--chunk-by-dir=4"],
+        "browser-chrome": ["--flavor=browser"],
+        "browser-chrome-gpu": ["--flavor=browser", "--subsuite=gpu"],
+        "browser-chrome-clipboard": ["--flavor=browser", "--subsuite=clipboard"],
+        "browser-chrome-chunked": ["--flavor=browser", "--chunk-by-runtime"],
+        "browser-chrome-addons": ["--flavor=browser", "--chunk-by-runtime", "--tag=addons"],
+        "browser-chrome-screenshots": ["--flavor=browser", "--subsuite=screenshots"],
+        "mochitest-gl": ["--subsuite=webgl"],
+        "mochitest-devtools-chrome": ["--flavor=browser", "--subsuite=devtools"],
+        "mochitest-devtools-chrome-chunked": ["--flavor=browser", "--subsuite=devtools", "--chunk-by-runtime"],
+        "mochitest-metro-chrome": ["--flavor=browser", "--metro-immersive"],
+        "jetpack-package": ["--flavor=jetpack-package"],
+        "jetpack-package-clipboard": ["--flavor=jetpack-package", "--subsuite=clipboard"],
+        "jetpack-addon": ["--flavor=jetpack-addon"],
+        "a11y": ["--flavor=a11y"],
+    },
+    # local reftest suites
+    "all_reftest_suites": {
+        "reftest": {
+            'options': ["--suite=reftest"],
+            'tests': ["tests/reftest/tests/layout/reftests/reftest.list"]
+        },
+        "crashtest": {
+            'options': ["--suite=crashtest"],
+            'tests': ["tests/reftest/tests/testing/crashtest/crashtests.list"]
+        },
+        "jsreftest": {
+            'options':["--extra-profile-file=tests/jsreftest/tests/user.js"],
+            'tests': ["tests/jsreftest/tests/jstests.list"]
+        },
+        "reftest-ipc": {
+            'options': ['--suite=reftest',
+                        '--setpref=browser.tabs.remote=true',
+                        '--setpref=browser.tabs.remote.autostart=true',
+                        '--setpref=extensions.e10sBlocksEnabling=false',
+                        '--setpref=layers.async-pan-zoom.enabled=true'],
+            'tests': ['tests/reftest/tests/layout/reftests/reftest-sanity/reftest.list']
+        },
+        "reftest-no-accel": {
+            "options": ["--suite=reftest",
+                        "--setpref=gfx.direct2d.disabled=true",
+                        "--setpref=layers.acceleration.disabled=true"],
+            "tests": ["tests/reftest/tests/layout/reftests/reftest.list"]
+        },
+        "crashtest-ipc": {
+            "options": ["--suite=crashtest",
+                        '--setpref=browser.tabs.remote=true',
+                        '--setpref=browser.tabs.remote.autostart=true',
+                        '--setpref=extensions.e10sBlocksEnabling=false',
+                        '--setpref=layers.async-pan-zoom.enabled=true'],
+            "tests": ['tests/reftest/tests/testing/crashtest/crashtests.list'],
+        },
+    },
+    "all_xpcshell_suites": {
+        "xpcshell": {
+            'options': ["--xpcshell=%(abs_app_dir)s/" + XPCSHELL_NAME,
+                        "--manifest=tests/xpcshell/tests/xpcshell.ini"],
+            'tests': []
+        },
+        "xpcshell-addons": {
+            'options': ["--xpcshell=%(abs_app_dir)s/" + XPCSHELL_NAME,
+                        "--tag=addons",
+                        "--manifest=tests/xpcshell/tests/xpcshell.ini"],
+            'tests': []
+        },
+    },
+    "all_cppunittest_suites": {
+        "cppunittest": ['tests/cppunittest']
+    },
+    "all_gtest_suites": {
+        "gtest": []
+    },
+    "all_jittest_suites": {
+        "jittest": []
+    },
+    "all_mozbase_suites": {
+        "mozbase": []
+    },
+    "run_cmd_checks_enabled": True,
+    "preflight_run_cmd_suites": [
+        {
+            'name': 'disable_screen_saver',
+            'cmd': ['xset', 's', 'off', 's', 'reset'],
+            'architectures': ['32bit', '64bit'],
+            'halt_on_failure': False,
+            'enabled': DISABLE_SCREEN_SAVER
+        },
+        {
+            'name': 'run mouse & screen adjustment script',
+            'cmd': [
+                sys.executable,
+                os.path.join(os.getcwd(),
+                    'mozharness', 'external_tools', 'mouse_and_screen_resolution.py'),
+                '--configuration-file',
+                os.path.join(os.getcwd(),
+                    'mozharness', 'external_tools', 'machine-configuration.json')
+            ],
+            'architectures': ['32bit'],
+            'halt_on_failure': True,
+            'enabled': ADJUST_MOUSE_AND_SCREEN
+        }
+    ],
+    "vcs_output_timeout": 1000,
+    "minidump_save_path": "%(abs_work_dir)s/../minidumps",
+    "buildbot_max_log_size": 52428800,
+    "default_blob_upload_servers": [
+        "https://blobupload.elasticbeanstalk.com",
+    ],
+    "structured_suites": ["reftest"],
+    'blob_uploader_auth_file': 'C:/builds/oauth.txt',
+    "download_minidump_stackwalk": True,
+    "minidump_stackwalk_path": "win32-minidump_stackwalk.exe",
+    "minidump_tooltool_manifest_path": "config/tooltool-manifests/win32/releng.manifest"
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/web_platform_tests/prod_config_windows_taskcluster.py
@@ -0,0 +1,48 @@
+# ***** BEGIN LICENSE BLOCK *****
+# 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/.
+# ***** END LICENSE BLOCK *****
+
+# This is a template config file for web-platform-tests test.
+
+import os
+import sys
+
+config = {
+    "options": [
+        "--prefs-root=%(test_path)s/prefs",
+        "--processes=1",
+        "--config=%(test_path)s/wptrunner.ini",
+        "--ca-cert-path=%(test_path)s/certs/cacert.pem",
+        "--host-key-path=%(test_path)s/certs/web-platform.test.key",
+        "--host-cert-path=%(test_path)s/certs/web-platform.test.pem",
+        "--certutil-binary=%(test_install_path)s/bin/certutil",
+    ],
+
+    "exes": {
+        'python': sys.executable,
+        'virtualenv': [
+            sys.executable,
+            os.path.join(os.path.dirname(sys.executable), 'Lib', 'site-packages', 'virtualenv.py')
+        ],
+        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
+        'tooltool.py': [sys.executable, os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')],
+        'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
+    },
+
+    "find_links": [
+        "http://pypi.pvt.build.mozilla.org/pub",
+        "http://pypi.pub.build.mozilla.org/pub",
+    ],
+
+    "pip_index": False,
+
+    "default_blob_upload_servers": [
+         "https://blobupload.elasticbeanstalk.com",
+    ],
+
+    "blob_uploader_auth_file" : 'C:/builds/oauth.txt',
+
+    "download_minidump_stackwalk": True,
+}
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -90,16 +90,17 @@ var apiManager = new class extends Schem
 // Represents a content script.
 function Script(extension, options, deferred = PromiseUtils.defer()) {
   this.extension = extension;
   this.options = options;
   this.run_at = this.options.run_at;
   this.js = this.options.js || [];
   this.css = this.options.css || [];
   this.remove_css = this.options.remove_css;
+  this.match_about_blank = this.options.match_about_blank;
 
   this.deferred = deferred;
 
   this.matches_ = new MatchPattern(this.options.matches);
   this.exclude_matches_ = new MatchPattern(this.options.exclude_matches || null);
   // TODO: MatchPattern should pre-mangle host-only patterns so that we
   // don't need to call a separate match function.
   this.matches_host_ = new MatchPattern(this.options.matchesHost || null);
@@ -129,16 +130,22 @@ Script.prototype = {
     let uri = window.document.documentURIObject;
 
     // If mozAddonManager is present on this page, don't allow
     // content scripts.
     if (window.navigator.mozAddonManager !== undefined) {
       return false;
     }
 
+    if (this.match_about_blank && ["about:blank", "about:srcdoc"].includes(uri.spec)) {
+      // When matching about:blank/srcdoc documents, the checks below
+      // need to be performed against the "owner" document's URI.
+      uri = window.document.nodePrincipal.URI;
+    }
+
     if (!(this.matches_.matches(uri) || this.matches_host_.matchesIgnoringPath(uri))) {
       return false;
     }
 
     if (this.exclude_matches_.matches(uri)) {
       return false;
     }
 
@@ -155,18 +162,16 @@ Script.prototype = {
     if (this.options.frame_id != null) {
       if (WebNavigationFrames.getFrameId(window) != this.options.frame_id) {
         return false;
       }
     } else if (!this.options.all_frames && window.top != window) {
       return false;
     }
 
-    // TODO: match_about_blank.
-
     return true;
   },
 
   cleanup(window) {
     if (!this.remove_css) {
       let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
 
@@ -411,44 +416,60 @@ DocumentManager = {
 
   // Map[windowId -> Map[extensionId -> ExtensionContext]]
   contentScriptWindows: new Map(),
 
   // Map[windowId -> ExtensionContext]
   extensionPageWindows: new Map(),
 
   init() {
+    Services.obs.addObserver(this, "content-document-global-created", false);
     Services.obs.addObserver(this, "document-element-inserted", false);
     Services.obs.addObserver(this, "inner-window-destroyed", false);
   },
 
   uninit() {
+    Services.obs.removeObserver(this, "content-document-global-created");
     Services.obs.removeObserver(this, "document-element-inserted");
     Services.obs.removeObserver(this, "inner-window-destroyed");
   },
 
   getWindowState(contentWindow) {
     let readyState = contentWindow.document.readyState;
     if (readyState == "complete") {
       return "document_idle";
     }
     if (readyState == "interactive") {
       return "document_end";
     }
     return "document_start";
   },
 
   observe: function(subject, topic, data) {
-    if (topic == "document-element-inserted") {
+    // For some types of documents (about:blank), we only see the first
+    // notification, for others (data: URIs) we only observe the second.
+    if (topic == "content-document-global-created" || topic == "document-element-inserted") {
       let document = subject;
       let window = document && document.defaultView;
+
+      if (topic == "content-document-global-created") {
+        window = subject;
+        document = window && window.document;
+      }
+
       if (!document || !document.location || !window) {
         return;
       }
 
+      // Make sure we always load exactly once (notice != used as logical XOR),
+      // usually on document-element-inserted, except for about:blank documents.
+      if ((topic == "content-document-global-created") != (window.location.href == "about:blank")) {
+        return;
+      }
+
       // Make sure we only load into frames that ExtensionContent.init
       // was called on (i.e., not frames for social or sidebars).
       let mm = getWindowMessageManager(window);
       if (!mm || !ExtensionContent.globals.has(mm)) {
         return;
       }
 
       // Enable the content script APIs should be available in subframes' window
@@ -458,17 +479,18 @@ DocumentManager = {
 
       if (ExtensionManagement.getAPILevelForWindow(window, extensionId) == CONTENTSCRIPT_PRIVILEGES) {
         let extension = ExtensionManager.get(extensionId);
         if (extension) {
           DocumentManager.getExtensionPageContext(extension, window);
         }
       }
 
-      this.trigger("document_start", window);
+      this.trigger(window);
+
       /* eslint-disable mozilla/balanced-listeners */
       window.addEventListener("DOMContentLoaded", this, true);
       window.addEventListener("load", this, true);
       /* eslint-enable mozilla/balanced-listeners */
     } else if (topic == "inner-window-destroyed") {
       let windowId = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
 
       MessageChannel.abortResponses({innerWindowID: windowId});
@@ -500,19 +522,19 @@ DocumentManager = {
       // listening on.
       return;
     }
     window.removeEventListener(event.type, this, true);
 
     // Need to check if we're still on the right page? Greasemonkey does this.
 
     if (event.type == "DOMContentLoaded") {
-      this.trigger("document_end", window);
+      this.trigger(window);
     } else if (event.type == "load") {
-      this.trigger("document_idle", window);
+      this.trigger(window);
     }
   },
 
   // Used to executeScript, insertCSS and removeCSS.
   executeScript(global, extensionId, options) {
     let extension = ExtensionManager.get(extensionId);
 
     let executeInWin = (window) => {
@@ -638,17 +660,17 @@ DocumentManager = {
     MessageChannel.abortResponses({extensionId});
 
     this.extensionCount--;
     if (this.extensionCount == 0) {
       this.uninit();
     }
   },
 
-  trigger(when, window) {
+  trigger(window) {
     let state = this.getWindowState(window);
 
     if (state == "document_start") {
       for (let extension of ExtensionManager.extensions.values()) {
         for (let script of extension.scripts) {
           if (script.matches(window)) {
             let context = this.getContentScriptContext(extension, window);
             context.addScript(script);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/file_with_about_blank.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+</head>
+<body>
+  <iframe id="a_b" src="about:blank"></iframe>
+  <iframe srcdoc="galactica actual" src="adama"></iframe>
+</body>
+</html>
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -13,16 +13,17 @@ support-files =
   file_webNavigation_frameClientRedirect.html
   file_webNavigation_frameRedirect.html
   file_webNavigation_manualSubframe.html
   file_webNavigation_manualSubframe_page1.html
   file_webNavigation_manualSubframe_page2.html
   file_WebNavigation_page1.html
   file_WebNavigation_page2.html
   file_WebNavigation_page3.html
+  file_with_about_blank.html
   file_image_good.png
   file_image_bad.png
   file_image_redirect.png
   file_style_good.css
   file_style_bad.css
   file_style_redirect.css
   file_script_good.js
   file_script_bad.js
@@ -45,16 +46,17 @@ skip-if = os == 'android' # Android does
 [test_ext_content_security_policy.html]
 [test_ext_contentscript.html]
 [test_ext_contentscript_api_injection.html]
 [test_ext_contentscript_context.html]
 [test_ext_contentscript_create_iframe.html]
 [test_ext_contentscript_devtools_metadata.html]
 [test_ext_contentscript_exporthelpers.html]
 [test_ext_contentscript_css.html]
+[test_ext_contentscript_about_blank.html]
 [test_ext_contentscript_teardown.html]
 skip-if = (os == 'android') # Android does not support tabs API. Bug 1260250
 [test_ext_exclude_include_globs.html]
 [test_ext_i18n_css.html]
 [test_ext_generate.html]
 [test_ext_notifications.html]
 [test_ext_permission_xhr.html]
 [test_ext_runtime_connect.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_about_blank.html
@@ -0,0 +1,117 @@
+<!doctype html>
+<html>
+<head>
+  <title>Test content script match_about_blank option</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+add_task(function* test_contentscript_about_blank() {
+  const manifest = {
+    content_scripts: [
+      {
+        match_about_blank: true,
+        matches: ["http://mochi.test/*/file_with_about_blank.html", "http://example.com/*"],
+        all_frames: true,
+        css: ["all.css"],
+        js: ["all.js"],
+      }, {
+        matches: ["http://mochi.test/*/file_with_about_blank.html"],
+        css: ["mochi_without.css"],
+        js: ["mochi_without.js"],
+        all_frames: true,
+      }, {
+        match_about_blank: true,
+        matches: ["http://mochi.test/*/file_with_about_blank.html"],
+        css: ["mochi_with.css"],
+        js: ["mochi_with.js"],
+        all_frames: true,
+      },
+    ],
+  };
+
+  const files = {
+    "all.js": function() {
+      browser.runtime.sendMessage("all");
+    },
+    "all.css": `
+      body { color: red; }
+    `,
+    "mochi_without.js": function() {
+      browser.runtime.sendMessage("mochi_without");
+    },
+    "mochi_without.css": `
+      body { background: yellow; }
+    `,
+    "mochi_with.js": function() {
+      browser.runtime.sendMessage("mochi_with");
+    },
+    "mochi_with.css": `
+      body { text-align: right; }
+    `,
+  };
+
+  function background() {
+    browser.runtime.onMessage.addListener((script, {url}) => {
+      const kind = url.startsWith("about:") ? url : "top";
+      browser.test.sendMessage("script", [script, kind, url]);
+      browser.test.sendMessage(`${script}:${kind}`);
+    });
+  }
+
+  const PATH = "tests/toolkit/components/extensions/test/mochitest/file_with_about_blank.html";
+  const extension = ExtensionTestUtils.loadExtension({manifest, files, background});
+  yield extension.startup();
+
+  let count = 0;
+  extension.onMessage("script", script => {
+    info(`script ran: ${script}`);
+    count++;
+  });
+
+  let win = window.open("http://example.com/" + PATH);
+  yield Promise.all([
+    extension.awaitMessage("all:top"),
+    extension.awaitMessage("all:about:blank"),
+    extension.awaitMessage("all:about:srcdoc"),
+  ]);
+  is(count, 3, "exactly 3 scripts ran");
+  win.close();
+
+  win = window.open("http://mochi.test:8888/" + PATH);
+  yield Promise.all([
+    extension.awaitMessage("all:top"),
+    extension.awaitMessage("all:about:blank"),
+    extension.awaitMessage("all:about:srcdoc"),
+    extension.awaitMessage("mochi_without:top"),
+    extension.awaitMessage("mochi_with:top"),
+    extension.awaitMessage("mochi_with:about:blank"),
+    extension.awaitMessage("mochi_with:about:srcdoc"),
+  ]);
+
+  let style = win.getComputedStyle(win.document.body);
+  is(style.color, "rgb(255, 0, 0)", "top window text color is red");
+  is(style.backgroundColor, "rgb(255, 255, 0)", "top window background is yellow");
+  is(style.textAlign, "right", "top window text is right-aligned");
+
+  let a_b = win.document.getElementById("a_b");
+  style = a_b.contentWindow.getComputedStyle(a_b.contentDocument.body);
+  is(style.color, "rgb(255, 0, 0)", "about:blank iframe text color is red");
+  is(style.backgroundColor, "transparent", "about:blank iframe background is transparent");
+  is(style.textAlign, "right", "about:blank text is right-aligned");
+
+  is(count, 10, "exactly 7 more scripts ran");
+  win.close();
+
+  yield extension.unload();
+});
+</script>
+
+</body>
+</html>
--- a/toolkit/components/url-classifier/ProtocolParser.cpp
+++ b/toolkit/components/url-classifier/ProtocolParser.cpp
@@ -64,16 +64,17 @@ ParseChunkRange(nsACString::const_iterat
   return false;
 }
 
 ///////////////////////////////////////////////////////////////
 // ProtocolParser implementation
 
 ProtocolParser::ProtocolParser()
   : mUpdateStatus(NS_OK)
+  , mUpdateWaitSec(0)
 {
 }
 
 ProtocolParser::~ProtocolParser()
 {
   CleanupUpdates();
 }
 
@@ -111,17 +112,16 @@ ProtocolParser::GetTableUpdate(const nsA
   return update;
 }
 
 ///////////////////////////////////////////////////////////////////////
 // ProtocolParserV2
 
 ProtocolParserV2::ProtocolParserV2()
   : mState(PROTOCOL_STATE_CONTROL)
-  , mUpdateWait(0)
   , mResetRequested(false)
   , mTableUpdate(nullptr)
 {
 }
 
 ProtocolParserV2::~ProtocolParserV2()
 {
 }
@@ -175,18 +175,18 @@ ProtocolParserV2::ProcessControl(bool* a
   *aDone = true;
   while (NextLine(line)) {
     PARSER_LOG(("Processing %s\n", line.get()));
 
     if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) {
       // Set the table name from the table header line.
       SetCurrentTable(Substring(line, 2));
     } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) {
-      if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) {
-        PARSER_LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait));
+      if (PR_sscanf(line.get(), "n:%d", &mUpdateWaitSec) != 1) {
+        PARSER_LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWaitSec));
         return NS_ERROR_FAILURE;
       }
     } else if (line.EqualsLiteral("r:pleasereset")) {
       mResetRequested = true;
     } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) {
       rv = ProcessForward(line);
       NS_ENSURE_SUCCESS(rv, rv);
     } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) ||
@@ -766,16 +766,20 @@ ProtocolParserProtobuf::End()
   mUpdateStatus = NS_ERROR_FAILURE;
 
   FetchThreatListUpdatesResponse response;
   if (!response.ParseFromArray(mPending.get(), mPending.Length())) {
     NS_WARNING("ProtocolParserProtobuf failed parsing data.");
     return;
   }
 
+  auto minWaitDuration = response.minimum_wait_duration();
+  mUpdateWaitSec = minWaitDuration.seconds() +
+                   minWaitDuration.nanos() / 1000000000;
+
   for (int i = 0; i < response.list_update_responses_size(); i++) {
     auto r = response.list_update_responses(i);
     nsresult rv = ProcessOneResponse(r);
     if (NS_SUCCEEDED(rv)) {
       mUpdateStatus = rv;
     } else {
       NS_WARNING("Failed to process one response.");
     }
--- a/toolkit/components/url-classifier/ProtocolParser.h
+++ b/toolkit/components/url-classifier/ProtocolParser.h
@@ -35,32 +35,33 @@ public:
   void SetRequestedTables(const nsTArray<nsCString>& aRequestTables)
   {
     mRequestedTables = aRequestTables;
   }
 
   nsresult Begin();
   virtual nsresult AppendStream(const nsACString& aData) = 0;
 
+  uint32_t UpdateWaitSec() { return mUpdateWaitSec; }
+
   // Notify that the inbound data is ready for parsing if progressive
   // parsing is not supported, for example in V4.
   virtual void End() = 0;
 
   // Forget the table updates that were created by this pass.  It
   // becomes the caller's responsibility to free them.  This is shitty.
   TableUpdate *GetTableUpdate(const nsACString& aTable);
   void ForgetTableUpdates() { mTableUpdates.Clear(); }
   nsTArray<TableUpdate*> &GetTableUpdates() { return mTableUpdates; }
 
   // These are only meaningful to V2. Since they were originally public,
   // moving them to ProtocolParserV2 requires a dymamic cast in the call
-  // sites. As a result, we will leave them until we remove support 
+  // sites. As a result, we will leave them until we remove support
   // for V2 entirely..
   virtual const nsTArray<ForwardedUpdate> &Forwards() const { return mForwards; }
-  virtual int32_t UpdateWait() { return 0; }
   virtual bool ResetRequested() { return false; }
 
 protected:
   virtual TableUpdate* CreateTableUpdate(const nsACString& aTableName) const = 0;
 
   nsCString mPending;
   nsresult mUpdateStatus;
 
@@ -68,16 +69,19 @@ protected:
   nsTArray<TableUpdate*> mTableUpdates;
 
   nsTArray<ForwardedUpdate> mForwards;
   nsCOMPtr<nsICryptoHash> mCryptoHash;
 
   // The table names that were requested from the client.
   nsTArray<nsCString> mRequestedTables;
 
+  // How long we should wait until the next update.
+  uint32_t mUpdateWaitSec;
+
 private:
   void CleanupUpdates();
 };
 
 /**
  * Helpers to parse the "shavar", "digest256" and "simple" list formats.
  */
 class ProtocolParserV2 final : public ProtocolParser {
@@ -86,17 +90,16 @@ public:
   virtual ~ProtocolParserV2();
 
   virtual void SetCurrentTable(const nsACString& aTable) override;
   virtual nsresult AppendStream(const nsACString& aData) override;
   virtual void End() override;
 
   // Update information.
   virtual const nsTArray<ForwardedUpdate> &Forwards() const override { return mForwards; }
-  virtual int32_t UpdateWait() override { return mUpdateWait; }
   virtual bool ResetRequested() override { return mResetRequested; }
 
 private:
   virtual TableUpdate* CreateTableUpdate(const nsACString& aTableName) const override;
 
   nsresult ProcessControl(bool* aDone);
   nsresult ProcessExpirations(const nsCString& aLine);
   nsresult ProcessChunkControl(const nsCString& aLine);
@@ -142,17 +145,16 @@ private:
     ChunkType type;
     uint32_t num;
     uint32_t hashSize;
     uint32_t length;
     void Clear() { num = 0; hashSize = 0; length = 0; }
   };
   ChunkState mChunkState;
 
-  uint32_t mUpdateWait;
   bool mResetRequested;
 
   // Updates to apply to the current table being parsed.
   TableUpdateV2 *mTableUpdate;
 };
 
 // Helpers to parse the "proto" list format.
 class ProtocolParserProtobuf final : public ProtocolParser {
--- a/toolkit/components/url-classifier/content/listmanager.js
+++ b/toolkit/components/url-classifier/content/listmanager.js
@@ -199,17 +199,17 @@ PROT_ListManager.prototype.kickoffUpdate
   // Add a fuzz of 0-1 minutes for both v2 and v4 according to Bug 1305478.
   initialUpdateDelay += Math.floor(Math.random() * (1 * 60 * 1000));
 
   // If the user has never downloaded tables, do the check now.
   log("needsUpdate: " + JSON.stringify(this.needsUpdate_, undefined, 2));
   for (var updateUrl in this.needsUpdate_) {
     // If we haven't already kicked off updates for this updateUrl, set a
     // non-repeating timer for it. The timer delay will be reset either on
-    // updateSuccess to this.updateinterval, or backed off on downloadError.
+    // updateSuccess to this.updateInterval, or backed off on downloadError.
     // Don't set the updateChecker unless at least one table has updates
     // enabled.
     if (this.updatesNeeded_(updateUrl) && !this.updateCheckers_[updateUrl]) {
       let provider = null;
       Object.keys(this.tablesData).forEach(function(table) {
         if (this.tablesData[table].updateUrl === updateUrl) {
           let newProvider = this.tablesData[table].provider;
           if (provider) {
@@ -487,22 +487,25 @@ PROT_ListManager.prototype.makeUpdateReq
 }
 
 /**
  * Callback function if the update request succeeded.
  * @param waitForUpdate String The number of seconds that the client should
  *        wait before requesting again.
  */
 PROT_ListManager.prototype.updateSuccess_ = function(tableList, updateUrl,
-                                                     waitForUpdate) {
+                                                     waitForUpdateSec) {
   log("update success for " + tableList + " from " + updateUrl + ": " +
-      waitForUpdate + "\n");
+      waitForUpdateSec + "\n");
+
+  // The time unit below are all milliseconds if not specified.
+
   var delay = 0;
-  if (waitForUpdate) {
-    delay = parseInt(waitForUpdate, 10) * 1000;
+  if (waitForUpdateSec) {
+    delay = parseInt(waitForUpdateSec, 10) * 1000;
   }
   // As long as the delay is something sane (5 min to 1 day), update
   // our delay time for requesting updates. We always use a non-repeating
   // timer since the delay is set differently at every callback.
   if (delay > maxDelayMs) {
     log("Ignoring delay from server (too long), waiting " +
         maxDelayMs + "ms");
     delay = maxDelayMs;
@@ -551,17 +554,17 @@ PROT_ListManager.prototype.updateSuccess
 
 /**
  * Callback function if the update request succeeded.
  * @param result String The error code of the failure
  */
 PROT_ListManager.prototype.updateError_ = function(table, updateUrl, result) {
   log("update error for " + table + " from " + updateUrl + ": " + result + "\n");
   // There was some trouble applying the updates. Don't try again for at least
-  // updateInterval seconds.
+  // updateInterval milliseconds.
   this.updateCheckers_[updateUrl] =
     new G_Alarm(BindToObject(this.checkForUpdates, this, updateUrl),
                 this.updateInterval, false);
 }
 
 /**
  * Callback function when the download failed
  * @param status String http status or an empty string if connection refused.
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -387,17 +387,17 @@ nsUrlClassifierDBServiceWorker::ResetStr
   mInStream = false;
   mProtocolParser = nullptr;
 }
 
 void
 nsUrlClassifierDBServiceWorker::ResetUpdate()
 {
   LOG(("ResetUpdate"));
-  mUpdateWait = 0;
+  mUpdateWaitSec = 0;
   mUpdateStatus = NS_OK;
   mUpdateObserver = nullptr;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
                                                  nsIUrlClassifierHashCompleter *completer)
 {
@@ -538,18 +538,18 @@ nsUrlClassifierDBServiceWorker::FinishSt
   NS_ENSURE_STATE(mInStream);
   NS_ENSURE_STATE(mUpdateObserver);
 
   mInStream = false;
 
   mProtocolParser->End();
 
   if (NS_SUCCEEDED(mProtocolParser->Status())) {
-    if (mProtocolParser->UpdateWait()) {
-      mUpdateWait = mProtocolParser->UpdateWait();
+    if (mProtocolParser->UpdateWaitSec()) {
+      mUpdateWaitSec = mProtocolParser->UpdateWaitSec();
     }
     // XXX: Only allow forwards from the initial update?
     const nsTArray<ProtocolParser::ForwardedUpdate> &forwards =
       mProtocolParser->Forwards();
     for (uint32_t i = 0; i < forwards.Length(); i++) {
       const ProtocolParser::ForwardedUpdate &forward = forwards[i];
       mUpdateObserver->UpdateUrlRequested(forward.url, forward.table);
     }
@@ -587,18 +587,18 @@ nsUrlClassifierDBServiceWorker::FinishUp
   } else {
     LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate() Not running "
          "ApplyUpdate() since the update has already failed."));
   }
 
   mMissCache.Clear();
 
   if (NS_SUCCEEDED(mUpdateStatus)) {
-    LOG(("Notifying success: %d", mUpdateWait));
-    mUpdateObserver->UpdateSuccess(mUpdateWait);
+    LOG(("Notifying success: %d", mUpdateWaitSec));
+    mUpdateObserver->UpdateSuccess(mUpdateWaitSec);
   } else if (NS_ERROR_NOT_IMPLEMENTED == mUpdateStatus) {
     LOG(("Treating NS_ERROR_NOT_IMPLEMENTED a successful update "
          "but still mark it spoiled."));
     mUpdateObserver->UpdateSuccess(0);
     mClassifier->MarkSpoiled(mUpdateTables);
   } else {
     if (LOG_ENABLED()) {
       nsAutoCString errorName;
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -212,17 +212,17 @@ private:
 
   // Directory where to store the SB databases.
   nsCOMPtr<nsIFile> mCacheDir;
 
   // XXX: maybe an array of autoptrs.  Or maybe a class specifically
   // storing a series of updates.
   nsTArray<mozilla::safebrowsing::TableUpdate*> mTableUpdates;
 
-  int32_t mUpdateWait;
+  uint32_t mUpdateWaitSec;
 
   // Entries that cannot be completed. We expect them to die at
   // the next update
   PrefixArray mMissCache;
 
   // Stores the last results that triggered a table update.
   CacheResultArray mLastResults;
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestProtocolParser.cpp
@@ -0,0 +1,87 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#include "gtest/gtest.h"
+#include "ProtocolParser.h"
+#include "mozilla/EndianUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::safebrowsing;
+
+typedef FetchThreatListUpdatesResponse_ListUpdateResponse ListUpdateResponse;
+
+static void
+InitUpdateResponse(ListUpdateResponse* aUpdateResponse,
+                   ThreatType aThreatType,
+                   const nsACString& aState,
+                   const nsACString& aChecksum,
+                   bool isFullUpdate,
+                   const nsTArray<uint32_t>& aFixedLengthPrefixes);
+
+static void
+DumpBinary(const nsACString& aBinary);
+
+TEST(ProtocolParser, UpdateWait)
+{
+  // Top level response which contains a list of update response
+  // for different lists.
+  FetchThreatListUpdatesResponse response;
+
+  auto r = response.mutable_list_update_responses()->Add();
+  InitUpdateResponse(r, SOCIAL_ENGINEERING_PUBLIC,
+                        nsCString("sta\x00te", 6),
+                        nsCString("check\x0sum", 9),
+                        true,
+                        {0, 1, 2, 3});
+
+  // Set min wait duration.
+  auto minWaitDuration = response.mutable_minimum_wait_duration();
+  minWaitDuration->set_seconds(8);
+  minWaitDuration->set_nanos(1 * 1000000000);
+
+  std::string s;
+  response.SerializeToString(&s);
+
+  DumpBinary(nsCString(s.c_str(), s.length()));
+
+  ProtocolParser* p = new ProtocolParserProtobuf();
+  p->AppendStream(nsCString(s.c_str(), s.length()));
+  p->End();
+  ASSERT_EQ(p->UpdateWaitSec(), 9u);
+  delete p;
+}
+
+static void
+InitUpdateResponse(ListUpdateResponse* aUpdateResponse,
+                   ThreatType aThreatType,
+                   const nsACString& aState,
+                   const nsACString& aChecksum,
+                   bool isFullUpdate,
+                   const nsTArray<uint32_t>& aFixedLengthPrefixes)
+{
+  aUpdateResponse->set_threat_type(aThreatType);
+  aUpdateResponse->set_new_client_state(aState.BeginReading(), aState.Length());
+  aUpdateResponse->mutable_checksum()->set_sha256(aChecksum.BeginReading(), aChecksum.Length());
+  aUpdateResponse->set_response_type(isFullUpdate ? ListUpdateResponse::FULL_UPDATE
+                                                  : ListUpdateResponse::PARTIAL_UPDATE);
+
+  auto additions = aUpdateResponse->mutable_additions()->Add();
+  additions->set_compression_type(RAW);
+  auto rawHashes = additions->mutable_raw_hashes();
+  rawHashes->set_prefix_size(4);
+  auto prefixes = rawHashes->mutable_raw_hashes();
+  for (auto p : aFixedLengthPrefixes) {
+    char buffer[4];
+    NativeEndian::copyAndSwapToBigEndian(buffer, &p, 1);
+    prefixes->append(buffer, 4);
+  }
+}
+
+static void DumpBinary(const nsACString& aBinary)
+{
+  nsCString s;
+  for (size_t i = 0; i < aBinary.Length(); i++) {
+    s.AppendPrintf("\\x%.2X", (uint8_t)aBinary[i]);
+  }
+  printf("%s\n", s.get());
+}
\ No newline at end of file
--- a/toolkit/components/url-classifier/tests/gtest/moz.build
+++ b/toolkit/components/url-classifier/tests/gtest/moz.build
@@ -6,16 +6,17 @@
 
 LOCAL_INCLUDES += [
     '../..',
 ]
 
 UNIFIED_SOURCES += [
     'TestChunkSet.cpp',
     'TestPerProviderDirectory.cpp',
+    'TestProtocolParser.cpp',
     'TestRiceDeltaDecoder.cpp',
     'TestSafebrowsingHash.cpp',
     'TestSafeBrowsingProtobuf.cpp',
     'TestTable.cpp',
     'TestUrlClassifierTableUpdateV4.cpp',
     'TestUrlClassifierUtils.cpp',
     'TestVariableLengthPrefixSet.cpp',
 ]
--- a/xpcom/base/nscore.h
+++ b/xpcom/base/nscore.h
@@ -155,26 +155,16 @@
 # define MOZ_DEPRECATED __attribute__((deprecated))
 #elif defined(_MSC_VER)
 # define MOZ_DEPRECATED __declspec(deprecated)
 #else
 # define MOZ_DEPRECATED
 #endif
 
 /**
- * Printf style formats
- */
-#ifdef __GNUC__
-#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck)  \
-    __attribute__ ((format (printf, stringIndex, firstToCheck)))
-#else
-#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck)
-#endif
-
-/**
  * Generic API modifiers which return the standard XPCOM nsresult type
  *
  * - NS_IMETHOD: use for in-class declarations and definitions.
  * - NS_IMETHODIMP: use for out-of-class definitions.
  * - NS_METHOD: usually used in conjunction with NS_CALLBACK.
  * - NS_CALLBACK: used in some legacy situations. Best avoided.
  */
 #define NS_IMETHOD          NS_IMETHOD_(nsresult)