Merge m-c to inbound on a CLOSED TREE. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 21 Oct 2016 17:15:01 -0400
changeset 361873 32f298a71ce9f6d52387a929dd82b66d6cf13af8
parent 361872 4a98b5d3a6c04a3c81d2edc1aa91de41f4509697 (current diff)
parent 361812 5639a9f476d08f300c079117e61697f5026b6367 (diff)
child 361874 bb93e4805f913accf5d022b7e8e2cd27a46dd57d
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound on a CLOSED TREE. a=merge
devtools/client/animationinspector/test/browser_animation_timeline_pause_button.js
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)