merge mozilla-inbound to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 19 Feb 2014 13:58:42 +0100
changeset 186663 a6f7ad1e60908c90483992509c4098e931adbfa8
parent 186631 bf0e76f2a7d431337f51602bbc5a2f5d3a9d3f5f (current diff)
parent 186662 6139f344bbe52f7f0c8edf4a69437b828bde4623 (diff)
child 186671 2439b2e44928b1407ff5b8fc857787cdac3295a3
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central
accessible/src/defs.mk
accessible/src/generic/Makefile.in
accessible/src/html/Makefile.in
accessible/src/mac/Makefile.in
accessible/src/other/Makefile.in
accessible/src/shared.mozbuild
accessible/src/windows/Makefile.in
accessible/src/windows/msaa/Makefile.in
accessible/src/xul/Makefile.in
dom/apps/src/Makefile.in
toolkit/mozapps/extensions/Makefile.in
--- a/accessible/src/atk/Makefile.in
+++ b/accessible/src/atk/Makefile.in
@@ -7,12 +7,8 @@ include $(topsrcdir)/config/rules.mk
 ifdef MOZ_ENABLE_GTK
 CFLAGS      += $(TK_CFLAGS)
 CXXFLAGS    += $(TK_CFLAGS)
 endif
 
 ifdef MOZ_ENABLE_DBUS
 CXXFLAGS += $(MOZ_DBUS_CFLAGS)
 endif
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -2,12 +2,8 @@
 # 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 $(topsrcdir)/config/rules.mk
 
 ifdef MOZ_ENABLE_GTK
 CXXFLAGS        += $(MOZ_CAIRO_CFLAGS)
 endif
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
--- a/accessible/src/base/moz.build
+++ b/accessible/src/base/moz.build
@@ -1,16 +1,14 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-include('../shared.mozbuild')
-
 EXPORTS += [
     'AccEvent.h',
     'nsAccessibilityService.h'
 ]
 
 EXPORTS.mozilla.a11y += [
     'AccTypes.h',
     'DocManager.h',
@@ -48,17 +46,17 @@ UNIFIED_SOURCES += [
     'nsTextEquivUtils.cpp',
     'SelectionManager.cpp',
     'StyleInfo.cpp',
     'TextAttrs.cpp',
     'TextUpdater.cpp',
     'TreeWalker.cpp',
 ]
 
-if a11y_log:
+if CONFIG['A11Y_LOG']:
     UNIFIED_SOURCES += [
         'Logging.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '../generic',
     '../html',
     '../xpcom',
deleted file mode 100644
--- a/accessible/src/defs.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-# 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/.
-
-A11Y_LOG = 0
-ifdef MOZ_DEBUG
-  A11Y_LOG = 1
-endif
-ifeq (,$(filter aurora beta release esr,$(MOZ_UPDATE_CHANNEL)))
-  A11Y_LOG = 1
-endif
deleted file mode 100644
--- a/accessible/src/generic/Makefile.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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 $(topsrcdir)/config/rules.mk
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
deleted file mode 100644
--- a/accessible/src/html/Makefile.in
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# 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 $(topsrcdir)/config/rules.mk
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
deleted file mode 100644
--- a/accessible/src/mac/Makefile.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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 $(topsrcdir)/config/rules.mk
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
deleted file mode 100644
--- a/accessible/src/other/Makefile.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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 $(topsrcdir)/config/rules.mk
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
deleted file mode 100644
--- a/accessible/src/shared.mozbuild
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-a11y_log = 0
-if CONFIG['MOZ_DEBUG']:
-    a11y_log = 1
-
-if CONFIG['MOZ_UPDATE_CHANNEL'] not in ('aurora', 'beta', 'release', 'esr'):
-    a11y_log = 1
deleted file mode 100644
--- a/accessible/src/windows/Makefile.in
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# 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 $(topsrcdir)/config/rules.mk
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
--- a/accessible/src/windows/ia2/Makefile.in
+++ b/accessible/src/windows/ia2/Makefile.in
@@ -2,12 +2,8 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # The midl generated code include Windows headers which defines min and max
 # macros which conflicts with std::min/max.  Suppress the macros:
 OS_CXXFLAGS += -DNOMINMAX
 
 include $(topsrcdir)/config/rules.mk
-
-ifdef A11Y_LOG
-  DEFINES += -DA11Y_LOG
-endif
deleted file mode 100644
--- a/accessible/src/windows/msaa/Makefile.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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 $(topsrcdir)/config/rules.mk
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
--- a/accessible/src/windows/uia/Makefile.in
+++ b/accessible/src/windows/uia/Makefile.in
@@ -2,12 +2,8 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # The midl generated code include Windows headers which defines min and max
 # macros which conflicts with std::min/max.  Suppress the macros:
 OS_CXXFLAGS += -DNOMINMAX
 
 include $(topsrcdir)/config/rules.mk
-
-ifdef A11Y_LOG
-  DEFINES += -DA11Y_LOG
-endif
--- a/accessible/src/xpcom/Makefile.in
+++ b/accessible/src/xpcom/Makefile.in
@@ -6,20 +6,16 @@ EXTRA_MDDEPEND_FILES = xpcAccEvents.pp
 
 INSTALL_TARGETS += xpcaccevents
 xpcaccevents_FILES := xpcAccEvents.h
 xpcaccevents_DEST = $(DIST)/include
 xpcaccevents_TARGET := export
 
 include $(topsrcdir)/config/rules.mk
 
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
-
 xpcAccEvents.cpp: $(srcdir)/AccEvents.conf \
                      $(srcdir)/AccEventGen.py \
                      $(LIBXUL_DIST)/sdk/bin/header.py \
                      $(LIBXUL_DIST)/sdk/bin/xpidl.py
 	$(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  -I$(LIBXUL_DIST)/sdk/bin \
 	  $(srcdir)/AccEventGen.py \
 	  -I $(DEPTH)/dist/idl \
deleted file mode 100644
--- a/accessible/src/xul/Makefile.in
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# 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 $(topsrcdir)/config/rules.mk
-
-ifneq ($(A11Y_LOG),0)
-  DEFINES += -DA11Y_LOG
-endif
new file mode 100644
--- /dev/null
+++ b/build/docs/androideclipse.rst
@@ -0,0 +1,89 @@
+.. _build_androideclipse:
+
+========================
+Android Eclipse Projects
+========================
+
+The build system contains alpha support for generating Android Eclipse
+project files to aid with development.
+
+To generate Android Eclipse project files, you'll need to have a fully
+built and packaged tree::
+
+   mach build && mach package
+
+(This is because Eclipse itself packages an APK containing
+``omni.ja``, and ``omni.ja`` is only assembled during packaging.)
+
+Then, simply generate the Android Eclipse build backend::
+
+   mach build-backend -b AndroidEclipse
+
+If all goes well, the path to the generated projects should be
+printed (currently, ``$OBJDIR/android_eclipse``).
+
+To use the generated Android Eclipse project files, you'll need to
+have a recent version of Eclipse (see `Tested Versions`_) with the
+`Eclipse ADT plugin
+<http://developer.android.com/tools/sdk/eclipse-adt.html>`_
+installed. You can then import all the projects into Eclipse using
+*File > Import ... > General > Existing Projects into Workspace*.
+
+Updating Project Files
+======================
+
+As you pull and update the source tree, your Android Eclipse files may
+fall out of sync with the build configuration. The tree should still
+build fine from within Eclipse, but source files may be missing and in
+rare circumstances Eclipse's index may not have the proper build
+configuration.
+
+To account for this, you'll want to periodically regenerate the
+Android Eclipse project files. You can do this by running ``mach build
+&& mach package && mach build-backend -b AndroidEclipse`` from the
+command line. It's a good idea to refresh and clean build all projects
+in Eclipse after doing this.
+
+In future, we'd like to include an Android Eclipse run configuration
+or build target that integrates updating the project files.
+
+Currently, regeneration rewrites the original project files. **If
+you've made any customizations to the projects, they will likely get
+overwritten.** We would like to improve this user experience in the
+future.
+
+Troubleshooting
+===============
+
+If Eclipse's builder gets confused, you should always refresh and
+clean build all projects. If Eclipse's builder is continually
+confused, you can see a log of what is happening at
+``$OBJDIR/android_eclipse/build.log``.
+
+If you run into memory problems executing ``dex``, you should
+`Increase Eclipse's memory limits <http://stackoverflow.com/a/11093228>`_.
+
+The produced Android Eclipse project files are unfortunately not
+portable. Please don't move them around.
+
+Structure of Android Eclipse projects
+=====================================
+
+The Android Eclipse backend generates several projects spanning Fennec
+itself and its tests. You'll mostly interact with the *Fennec* project
+itself.
+
+In future, we'd like to expand this documentation to include some of
+the technical details of how the Eclipse integration works, and how to
+add additional Android Eclipse projects using the ``moz.build``
+system.
+
+Tested Versions
+===============
+
+============    ====================================    =================
+OS              Version                                 Working as of
+============    ====================================    =================
+Mac OS X        Luna (Build id: 20130919-0819)          February 2014
+Mac OS X        Kepler (Build id: 20131219-0014)        February 2014
+============    ====================================    =================
--- a/build/docs/index.rst
+++ b/build/docs/index.rst
@@ -18,16 +18,17 @@ Important Concepts
    environment-variables
    build-targets
    python
    test_manifests
    mozinfo
    preprocessor
    jar-manifests
    visualstudio
+   androideclipse
 
 mozbuild
 ========
 
 mozbuild is a Python package containing a lot of the code for the
 Mozilla build system.
 
 .. toctree::
--- a/config/config.mk
+++ b/config/config.mk
@@ -329,19 +329,19 @@ endif
 #
 # Build using PIC by default
 #
 _ENABLE_PIC=1
 
 # Determine if module being compiled is destined
 # to be merged into libxul
 
-ifeq ($(FINAL_LIBRARY),xul)
+ifneq (,$(filter xul,$(FINAL_LIBRARY) $(LIBRARY_NAME)))
   ifdef LIBXUL_LIBRARY
-    $(error FINAL_LIBRARY is "xul", LIBXUL_LIBRARY is implied)
+    $(error LIBRARY_NAME or FINAL_LIBRARY is "xul", LIBXUL_LIBRARY is implied)
   endif
   LIBXUL_LIBRARY := 1
 endif
 
 ifdef LIBXUL_LIBRARY
 ifdef IS_COMPONENT
 $(error IS_COMPONENT is set, but is not compatible with LIBXUL_LIBRARY)
 endif
--- a/configure.in
+++ b/configure.in
@@ -8613,16 +8613,21 @@ AC_SUBST(LIBJPEG_TURBO_X64_ASM)
 AC_SUBST(LIBJPEG_TURBO_ARM_ASM)
 
 AC_SUBST(MOZ_PACKAGE_JSSHELL)
 AC_SUBST(MOZ_FOLD_LIBS)
 
 AC_SUBST(MOZ_ENABLE_SZIP)
 AC_SUBST(MOZ_SZIP_FLAGS)
 
+if test "$MOZ_DEBUG"; then
+    MOZ_EM_DEBUG=1
+fi
+AC_SUBST(MOZ_EM_DEBUG)
+
 if test -n "$COMPILE_ENVIRONMENT"; then
 AC_MSG_CHECKING([for posix_fallocate])
 AC_TRY_LINK([#define _XOPEN_SOURCE 600
   #include <fcntl.h>],
                  [posix_fallocate(0, 0, 0);],
                  [ac_cv___posix_fallocate=true],
                  [ac_cv___posix_fallocate=false])
 
@@ -8688,16 +8693,31 @@ if test "$ACCESSIBILITY" -a "$MOZ_ENABLE
     ATK_MAJOR_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $1 }'`
     ATK_MINOR_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $2 }'`
     ATK_REV_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $3 }'`
     AC_DEFINE_UNQUOTED(ATK_MAJOR_VERSION, $ATK_MAJOR_VERSION)
     AC_DEFINE_UNQUOTED(ATK_MINOR_VERSION, $ATK_MINOR_VERSION)
     AC_DEFINE_UNQUOTED(ATK_REV_VERSION, $ATK_REV_VERSION)
 fi
 
+if test "$MOZ_DEBUG"; then
+    A11Y_LOG=1
+fi
+case "$MOZ_UPDATE_CHANNEL" in
+aurora|beta|release|esr)
+    ;;
+*)
+    A11Y_LOG=1
+    ;;
+esac
+AC_SUBST(A11Y_LOG)
+if test -n "$A11Y_LOG"; then
+    AC_DEFINE(A11Y_LOG)
+fi
+
 AC_SUBST(MOZILLA_VERSION)
 
 AC_SUBST(ac_configure_args)
 
 dnl Spit out some output
 dnl ========================================================
 
 dnl The following defines are used by xpcom
--- a/content/html/content/test/forms/test_input_number_data.js
+++ b/content/html/content/test/forms/test_input_number_data.js
@@ -11,13 +11,22 @@ var tests = [
   { desc: "French",
     langTag: "fr-FR", inputWithGrouping: "123 456,78",
     inputWithoutGrouping: "123456,78", value: 123456.78
   },
   { desc: "German",
     langTag: "de", inputWithGrouping: "123.456,78",
     inputWithoutGrouping: "123456,78", value: 123456.78
   },
+  // Extra german test to check that a locale that uses '.' as its grouping
+  // separator doesn't result in it being invalid (due to step mismatch) due
+  // to the de-localization code mishandling numbers that look like other
+  // numbers formatted for English speakers (i.e. treating this as 123.456
+  // instead of 123456):
+  { desc: "German (test 2)",
+    langTag: "de", inputWithGrouping: "123.456",
+    inputWithoutGrouping: "123456", value: 123456
+  },
   { desc: "Hebrew",
     langTag: "he", inputWithGrouping: "123,456.78",
     inputWithoutGrouping: "123456.78", value: 123456.78
   },
 ];
--- a/content/html/content/test/forms/test_input_typing_sanitization.html
+++ b/content/html/content/test/forms/test_input_typing_sanitization.html
@@ -121,17 +121,17 @@ function runTest()
   var data = [
     {
       type: 'number',
       canHaveBadInputValidityState: true,
       validData: [
         "42",
         "-42", // should work for negative values
         "42.1234",
-        "123.12345678912345",  // double precision
+        "123.123456789123",  // double precision
         "1e2", // e should be usable
         "2e1",
         "1e-1", // value after e can be negative
         "1E2", // E can be used instead of e
       ],
       invalidData: [
         "e",
         "e2",
--- a/content/media/webaudio/test/test_delayNodeTailWithDisconnect.html
+++ b/content/media/webaudio/test/test_delayNodeTailWithDisconnect.html
@@ -40,24 +40,22 @@ function onDelayOutput(e) {
 
 function onSourceOutput(e) {
   // Record the first buffer
   e.inputBuffer.copyFromChannel(sourceOutput, 0);
   e.target.onaudioprocess = null;
 }
 
 function disconnectSources() {
-  dump("disconnecting\n")
   for (var i = 0; i < sourceCount; ++i) {
     sources[i].disconnect();
   }
 
   SpecialPowers.forceGC();
   SpecialPowers.forceCC();
-  dump("forced GC\n");
 }
 
 function startTest() {
   var ctx = new AudioContext();
 
   var sourceProcessor = ctx.createScriptProcessor(bufferSize, 1, 0);
   sourceProcessor.onaudioprocess = onSourceOutput;
   // Keep audioprocess events going after source disconnect.
--- a/content/media/webaudio/test/test_oscillatorNodeStart.html
+++ b/content/media/webaudio/test/test_oscillatorNodeStart.html
@@ -1,14 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test the OscillatorNode interface</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
@@ -23,32 +22,33 @@ addLoadEvent(function() {
     var input = e.inputBuffer.getChannelData(0);
     var isSilent = true;
     for (var i = 0; i < input.length; i++) {
       if (input[i] != 0.0) {
         isSilent = false;
       }
     }
     sp.onaudioprocess = null;
+    dump("audioprocess received\n");
     ok(isSilent, "OscillatorNode should be silent before calling start.");
     SimpleTest.finish();
   }
 
   // Debug Logging for bug 966322
 
-  ok(true, "Load event ran");
+  dump("Load event ran\n");
 
   var bs = context.createBufferSource();
   bs.buffer = context.createBuffer(1, 1, context.sampleRate);
   bs.start();
   bs.onended = function() {
-    ok(true, "Graph is running");
+    dump("Graph is running\n");
   }
 
 });
 
 // Debug Logging for bug 966322
-ok(true, "script ran");
+dump("script ran\n");
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_pannerNodeTail.html
+++ b/content/media/webaudio/test/test_pannerNodeTail.html
@@ -55,17 +55,16 @@ var referenceProcessCount = 0;
 var referenceOutput = [new Float32Array(bufferSize),
                        new Float32Array(bufferSize)];
 var testProcessor;
 var testProcessCount = 0;
 
 function isChannelSilent(channel) {
   for (var i = 0; i < channel.length; ++i) {
     if (channel[i] != 0.0) {
-      dump("input at " + i + "\n");
       return false;
     }
   }
   return true;
 }
 
 function onReferenceOutput(e) {
   switch(referenceProcessCount) {
deleted file mode 100644
--- a/dom/apps/src/Makefile.in
+++ /dev/null
@@ -1,7 +0,0 @@
-# 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 MOZ_DEBUG
-  DEFINES += -DMOZ_DEBUG=1
-endif
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -50,17 +50,17 @@ Cu.import("resource://gre/modules/Promis
 #ifdef MOZ_WIDGET_GONK
 XPCOMUtils.defineLazyGetter(this, "libcutils", function() {
   Cu.import("resource://gre/modules/systemlibs.js");
   return libcutils;
 });
 #endif
 
 function debug(aMsg) {
-#ifdef MOZ_DEBUG
+#ifdef DEBUG
   dump("-*- Webapps.jsm : " + aMsg + "\n");
 #endif
 }
 
 function getNSPRErrorCode(err) {
   return -1 * ((err) & 0xffff);
 }
 
--- a/gfx/cairo/libpixman/src/Makefile.in
+++ b/gfx/cairo/libpixman/src/Makefile.in
@@ -4,42 +4,34 @@
 
 ifeq ($(OS_TARGET),Android)
 MODULE_OPTIMIZE_FLAGS = -O2
 endif
 
 # Build MMX code either with VC or with gcc-on-x86
 ifdef _MSC_VER
 ifeq (86,$(findstring 86,$(OS_TEST)))
-ifneq (64,$(findstring 64,$(OS_TEST)))
-USE_MMX=1
-endif
 USE_SSE2=1
 MMX_CFLAGS=
 ifneq (,$(filter 1400 1500, $(_MSC_VER)))
 # MSVC 2005 and 2008 generate code that breaks alignment
 # restrictions in debug mode so always optimize.
 # See bug 640250 for more info.
 SSE2_CFLAGS=-O2
 else
 SSE2_CFLAGS=
 endif
 endif
-ifeq (arm,$(findstring arm,$(OS_TEST)))
-USE_ARM_SIMD_MSVC=1
-endif
 endif
 
 ifdef GNU_CC
 ifeq (ppc,$(findstring ppc,$(OS_TEST)))
-USE_VMX=1
 VMX_CFLAGS=-maltivec
 endif
 ifeq (86,$(findstring 86,$(OS_TEST)))
-USE_MMX=1
 MMX_CFLAGS=-mmmx -Winline
 ifeq (64,$(findstring 64,$(OS_TEST)))
 USE_SSE2=1
 endif
 ifdef HAVE_GCC_ALIGN_ARG_POINTER
 USE_SSE2=1
 endif
 ifdef USE_SSE2
@@ -48,54 +40,24 @@ endif
 MMX_CFLAGS+=--param inline-unit-growth=10000 --param large-function-growth=10000
 endif
 ifeq (arm,$(findstring arm,$(OS_TEST)))
 # Apple's arm assembler doesn't support the same syntax as
 # the standard GNU assembler, so use the C fallback paths for now.
 # This may be fixable if clang's ARM/iOS assembler improves into a
 # viable solution in the future.
 ifneq (Darwin,$(OS_ARCH))
-ifdef HAVE_ARM_SIMD
-USE_ARM_SIMD_GCC=1
-endif
 ifdef HAVE_ARM_NEON
-USE_ARM_NEON_GCC=1
+ARM_NEON_CFLAGS = -mfpu=neon
 endif
 endif
 endif
 
 endif
 
-
-ifdef USE_MMX
-CSRCS += pixman-mmx.c
-DEFINES += -DUSE_MMX
-endif
-
-ifdef USE_SSE2
-CSRCS += pixman-sse2.c
-DEFINES += -DUSE_SSE -DUSE_SSE2
-endif
-
-ifdef USE_VMX
-CSRCS += pixman-vmx.c
-DEFINES += -DUSE_VMX
-endif
-
-ifdef USE_ARM_SIMD_GCC
-CSRCS += pixman-arm-simd.c
-DEFINES += -DUSE_ARM_SIMD
-endif
-
-ifdef USE_ARM_NEON_GCC
-CSRCS += pixman-arm-neon.c
-DEFINES += -DUSE_ARM_NEON
-ARM_NEON_CFLAGS = -mfpu=neon
-endif
-
 include $(topsrcdir)/config/rules.mk
 
 # Disable spammy "missing initializer" GCC warning
 ifdef GNU_CC
 CFLAGS += -Wno-missing-field-initializers
 endif # GNU_CC
 
 # special rule for pixman-mmx to get the right cflags
--- a/gfx/cairo/libpixman/src/moz.build
+++ b/gfx/cairo/libpixman/src/moz.build
@@ -70,8 +70,61 @@ if CONFIG['MOZ_USE_PTHREADS']:
     DEFINES['HAVE_PTHREAD_SETSPECIFIC'] = True
 
 if CONFIG['_MSC_VER']:
     DEFINES['PIXMAN_USE_XP_DLL_TLS_WORKAROUND'] = True
 
 DEFINES['PACKAGE'] = 'mozpixman'
 
 DEFINES['_USE_MATH_DEFINES'] = True
+
+use_mmx = False
+use_sse2 = False
+use_vmx = False
+use_arm_simd_gcc = False
+use_arm_neon_gcc = False
+if '86' in CONFIG['OS_TEST']:
+    if '64' in CONFIG['OS_TEST']:
+        if CONFIG['GNU_CC']:
+            use_sse2 = True
+    else:
+        if CONFIG['_MSC_VER']:
+            use_mmx = True
+        if CONFIG['HAVE_GCC_ALIGN_ARG_POINTER']:
+            use_sse2 = True
+    if CONFIG['GNU_CC']:
+        use_mmx = True
+    if CONFIG['_MSC_VER']:
+        use_sse2 = True
+elif 'ppc' in CONFIG['OS_TEST']:
+    if CONFIG['GNU_CC']:
+        use_vmx = True
+# Apple's arm assembler doesn't support the same syntax as
+# the standard GNU assembler, so use the C fallback paths for now.
+# This may be fixable if clang's ARM/iOS assembler improves into a
+# viable solution in the future.
+elif 'arm' in CONFIG['OS_TEST']:
+    if CONFIG['OS_ARCH'] != 'Darwin':
+        if CONFIG['HAVE_ARM_SIMD']:
+            use_arm_simd_gcc = True
+        if CONFIG['HAVE_ARM_NEON']:
+            use_arm_neon_gcc = True
+
+if use_mmx:
+    DEFINES['USE_MMX'] = True
+    SOURCES += ['pixman-mmx.c']
+
+if use_sse2:
+    DEFINES['USE_SSE'] = True
+    DEFINES['USE_SSE2'] = True
+    SOURCES += ['pixman-sse2.c']
+
+if use_vmx:
+    DEFINES['USE_VMX'] = True
+    SOURCES += ['pixman-vmx.c']
+
+if use_arm_simd_gcc:
+    DEFINES['USE_ARM_SIMD'] = True
+    SOURCES += ['pixman-arm-simd.c']
+
+if use_arm_neon_gcc:
+    DEFINES['USE_ARM_NEON'] = True
+    SOURCES += ['pixman-arm-neon.c']
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -198,21 +198,16 @@ DIST_GARBAGE = config.cache config.log c
    backend.mk config/backend.mk devtools/backend.mk editline/backend.mk \
    gdb/backend.mk jsapi-tests/backend.mk shell/backend.mk tests/backend.mk \
    backend.RecursiveMakeBackend backend.RecursiveMakeBackend.pp \
    devtools/rootAnalysis/Makefile
 
 distclean::
 	$(RM) $(DIST_GARBAGE)
 
-ifneq (,$(filter WINNT,$(OS_ARCH)))
-# _CRT_RAND_S must be #defined before #including stdlib.h to get rand_s()
-DEFINES         += -D_CRT_RAND_S
-endif
-
 ifneq ($(findstring -L,$(NSPR_LIBS)),)
 NSPR_STATIC_PATH = $(subst -L,,$(findstring -L,$(NSPR_LIBS)))
 else
 NSPR_STATIC_PATH = $(DIST)/lib
 endif
 
 # HP-UX does not require the extra linking of "-lm"
 ifeq (,$(filter HP-UX WINNT,$(OS_ARCH)))
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -338,16 +338,18 @@ if CONFIG['ENABLE_ION']:
                 'jit/arm/Simulator-arm.cpp'
             ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     SOURCES += [
         'assembler/jit/ExecutableAllocatorWin.cpp',
         'yarr/OSAllocatorWin.cpp',
     ]
+    # _CRT_RAND_S must be #defined before #including stdlib.h to get rand_s()
+    DEFINES['_CRT_RAND_S'] = True
 else:
     SOURCES += [
         'assembler/jit/ExecutableAllocatorPosix.cpp',
         'yarr/OSAllocatorPosix.cpp',
     ]
 
 if CONFIG['ENABLE_ION'] or CONFIG['ENABLE_YARR_JIT']:
     if CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
--- a/layout/forms/nsImageControlFrame.cpp
+++ b/layout/forms/nsImageControlFrame.cpp
@@ -17,58 +17,59 @@ void
 IntPointDtorFunc(void *aObject, nsIAtom *aPropertyName,
                  void *aPropertyValue, void *aData)
 {
   nsIntPoint *propertyValue = static_cast<nsIntPoint*>(aPropertyValue);
   delete propertyValue;
 }
 
 
-#define nsImageControlFrameSuper nsImageFrame
+typedef nsImageFrame nsImageControlFrameSuper;
 class nsImageControlFrame : public nsImageControlFrameSuper,
                             public nsIFormControlFrame
 {
 public:
   nsImageControlFrame(nsStyleContext* aContext);
   ~nsImageControlFrame();
 
-  virtual void DestroyFrom(nsIFrame* aDestructRoot);
+  virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
   virtual void Init(nsIContent*      aContent,
                     nsIFrame*        aParent,
                     nsIFrame*        aPrevInFlow) MOZ_OVERRIDE;
 
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   virtual nsresult Reflow(nsPresContext*           aPresContext,
                           nsHTMLReflowMetrics&     aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
-                          nsReflowStatus&          aStatus);
+                          nsReflowStatus&          aStatus) MOZ_OVERRIDE;
 
   virtual nsresult HandleEvent(nsPresContext* aPresContext,
                                WidgetGUIEvent* aEvent,
-                               nsEventStatus* aEventStatus);
+                               nsEventStatus* aEventStatus) MOZ_OVERRIDE;
 
-  virtual nsIAtom* GetType() const;
+  virtual nsIAtom* GetType() const MOZ_OVERRIDE;
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE;
 #endif
 
 #ifdef DEBUG_FRAME_DUMP
-  virtual nsresult GetFrameName(nsAString& aResult) const {
+  virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE {
     return MakeFrameName(NS_LITERAL_STRING("ImageControl"), aResult);
   }
 #endif
 
   virtual nsresult GetCursor(const nsPoint&    aPoint,
-                             nsIFrame::Cursor& aCursor);
+                             nsIFrame::Cursor& aCursor) MOZ_OVERRIDE;
   // nsIFormContromFrame
-  virtual void SetFocus(bool aOn, bool aRepaint);
-  virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue);
+  virtual void SetFocus(bool aOn, bool aRepaint) MOZ_OVERRIDE;
+  virtual nsresult SetFormProperty(nsIAtom* aName, 
+                                   const nsAString& aValue) MOZ_OVERRIDE;
 };
 
 
 nsImageControlFrame::nsImageControlFrame(nsStyleContext* aContext):
   nsImageControlFrameSuper(aContext)
 {
 }
 
--- a/layout/forms/nsNumberControlFrame.cpp
+++ b/layout/forms/nsNumberControlFrame.cpp
@@ -622,34 +622,63 @@ nsNumberControlFrame::GetValueOfAnonText
   if (!mTextField) {
     aValue.Truncate();
     return;
   }
 
   HTMLInputElement::FromContent(mTextField)->GetValue(aValue);
 
 #ifdef ENABLE_INTL_API
-  // Here we check if the text field's value is a localized serialization of a
-  // number. If it is we set aValue to the de-localize value, but only if the
-  // localized value isn't also a valid floating-point number according to the
-  // HTML 5 spec:
+  // Here we need to de-localize any number typed in by the user. That is, we
+  // need to convert it from the number format of the user's language, region,
+  // etc. to the format that the HTML 5 spec defines to be a "valid
+  // floating-point number":
   //
   //   http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#floating-point-numbers
   //
-  // This is because content (and tests) expect us to avoid "normalizing" the
-  // number that the user types in if it's not necessary. (E.g. if the user
-  // types "2e2" then inputElement.value should be "2e2" and not "100".
+  // so that it can be parsed by functions like HTMLInputElement::
+  // StringToDecimal (the HTML-5-conforming parsing function) which don't know
+  // how to handle numbers that are formatted differently (for example, with
+  // non-ASCII digits, with grouping separator characters or with a decimal
+  // separator character other than '.').
+  //
+  // We need to be careful to avoid normalizing numbers that are already
+  // formatted for a locale that matches the format of HTML 5's "valid
+  // floating-point number" and have no grouping separator characters. (In
+  // other words we want to return the number as specified by the user, not the
+  // de-localized serialization, since the latter will normalize the value.)
+  // For example, if the user's locale is English and the user types in "2e2"
+  // then inputElement.value should be "2e2" and not "100". This is because
+  // content (and tests) expect us to avoid "normalizing" the number that the
+  // user types in if it's not necessary in order to make sure it conforms to
+  // HTML 5's "valid floating-point number" format.
+  //
+  // Note that we also need to be careful when trying to avoid normalization.
+  // For example, just because "1.234" _looks_ like a valid floating-point
+  // number according to the spec does not mean that it should be returned
+  // as-is. If the user's locale is German, then this represents the value
+  // 1234, not 1.234, so it still needs to be de-localized. Alternatively, if
+  // the user's locale is English and they type in "1,234" we _do_ need to
+  // normalize the number to "1234" because HTML 5's valid floating-point
+  // number format does not allow the ',' grouping separator. We can detect all
+  // the cases where we need to convert by seeing if the locale-specific
+  // parsing function understands the user input to mean the same thing as the
+  // HTML-5-conforming parsing function. If so, then we should return the value
+  // as-is to avoid normalization. Otherwise, we return the de-localized
+  // serialization.
   ICUUtils::LanguageTagIterForContent langTagIter(mContent);
   double value = ICUUtils::ParseNumber(aValue, langTagIter);
   if (NS_finite(value) &&
-      !HTMLInputElement::StringToDecimal(aValue).isFinite()) {
+      value != HTMLInputElement::StringToDecimal(aValue).toDouble()) {
     aValue.Truncate();
     aValue.AppendFloat(value);
   }
 #endif
+  // else, we return whatever FromContent put into aValue (the number as typed
+  // in by the user)
 }
 
 bool
 nsNumberControlFrame::AnonTextControlIsEmpty()
 {
   if (!mTextField) {
     return true;
   }
--- a/layout/forms/nsRangeFrame.cpp
+++ b/layout/forms/nsRangeFrame.cpp
@@ -5,17 +5,17 @@
 
 #include "nsRangeFrame.h"
 
 #include "mozilla/TouchEvents.h"
 
 #include "nsContentCreatorFunctions.h"
 #include "nsContentList.h"
 #include "nsContentUtils.h"
-#include "nsCSSRenderingBorders.h"
+#include "nsCSSRendering.h"
 #include "nsFormControlFrame.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsINameSpaceManager.h"
 #include "nsINodeInfo.h"
 #include "nsIPresShell.h"
 #include "nsGkAtoms.h"
 #include "mozilla/dom/HTMLInputElement.h"
@@ -77,16 +77,24 @@ nsRangeFrame::Init(nsIContent* aContent,
     nsIDocument* document = presShell->GetDocument();
     if (document) {
       nsPIDOMWindow* innerWin = document->GetInnerWindow();
       if (innerWin) {
         innerWin->SetHasTouchEventListeners();
       }
     }
   }
+
+  nsStyleSet *styleSet = PresContext()->StyleSet();
+
+  mOuterFocusStyle =
+    styleSet->ProbePseudoElementStyle(aContent->AsElement(),
+                                      nsCSSPseudoElements::ePseudo_mozFocusOuter,
+                                      StyleContext());
+
   return nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
 }
 
 void
 nsRangeFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
                "nsRangeFrame should not have continuations; if it does we "
@@ -161,55 +169,49 @@ public:
     : nsDisplayItem(aBuilder, aFrame) {
     MOZ_COUNT_CTOR(nsDisplayRangeFocusRing);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayRangeFocusRing() {
     MOZ_COUNT_DTOR(nsDisplayRangeFocusRing);
   }
 #endif
-  
+
+  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE;
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("RangeFocusRing", TYPE_OUTLINE)
 };
 
+nsRect
+nsDisplayRangeFocusRing::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
+{
+  *aSnap = false;
+  nsRect rect(ToReferenceFrame(), Frame()->GetSize());
+
+  // We want to paint as if specifying a border for ::-moz-focus-outer
+  // specifies an outline for our frame, so inflate by the border widths:
+  nsStyleContext* styleContext =
+    static_cast<nsRangeFrame*>(mFrame)->mOuterFocusStyle;
+  MOZ_ASSERT(styleContext, "We only exist if mOuterFocusStyle is non-null");
+  rect.Inflate(styleContext->StyleBorder()->GetComputedBorder());
+
+  return rect;
+}
+
 void
 nsDisplayRangeFocusRing::Paint(nsDisplayListBuilder* aBuilder,
                                nsRenderingContext* aCtx)
 {
-  nsPresContext *presContext = mFrame->PresContext();
-  nscoord appUnitsPerDevPixel = presContext->DevPixelsToAppUnits(1);
-  gfxContext* ctx = aCtx->ThebesContext();
-  nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
-  gfxRect pxRect(nsLayoutUtils::RectToGfxRect(r, appUnitsPerDevPixel));
-  uint8_t borderStyles[4] = { NS_STYLE_BORDER_STYLE_DOTTED,
-                              NS_STYLE_BORDER_STYLE_DOTTED,
-                              NS_STYLE_BORDER_STYLE_DOTTED,
-                              NS_STYLE_BORDER_STYLE_DOTTED };
-  gfxFloat borderWidths[4] = { 1, 1, 1, 1 };
-  gfxCornerSizes borderRadii(0);
-  nscolor borderColors[4] = { NS_RGB(0, 0, 0), NS_RGB(0, 0, 0),
-                              NS_RGB(0, 0, 0), NS_RGB(0, 0, 0) };
-  nsStyleContext* bgContext = mFrame->StyleContext();
-  nscolor bgColor =
-    bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
-
-  ctx->Save();
-  nsCSSBorderRenderer br(appUnitsPerDevPixel,
-                         ctx,
-                         pxRect,
-                         borderStyles,
-                         borderWidths,
-                         borderRadii,
-                         borderColors,
-                         nullptr,
-                         0,
-                         bgColor);
-  br.DrawBorders();
-  ctx->Restore();
+  bool unused;
+  nsStyleContext* styleContext =
+    static_cast<nsRangeFrame*>(mFrame)->mOuterFocusStyle;
+  MOZ_ASSERT(styleContext, "We only exist if mOuterFocusStyle is non-null");
+  nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame,
+                              mVisibleRect, GetBounds(aBuilder, &unused),
+                              styleContext);
 }
 
 void
 nsRangeFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                const nsRect&           aDirtyRect,
                                const nsDisplayListSet& aLists)
 {
   if (IsThemed()) {
@@ -226,29 +228,45 @@ nsRangeFrame::BuildDisplayList(nsDisplay
       nsDisplayListSet set(aLists, aLists.Content());
       BuildDisplayListForChild(aBuilder, thumb, aDirtyRect, set, DISPLAY_CHILD_INLINE);
     }
   } else {
     BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
   }
 
   // Draw a focus outline if appropriate:
-  nsEventStates eventStates = mContent->AsElement()->State();
-  if (!eventStates.HasState(NS_EVENT_STATE_FOCUSRING) ||
-      eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
+
+  if (!aBuilder->IsForPainting() ||
+      !IsVisibleForPainting(aBuilder)) {
+    // we don't want the focus ring item for hit-testing or if the item isn't
+    // in the area being [re]painted
     return;
   }
-  nsPresContext *presContext = PresContext();
+
+  nsEventStates eventStates = mContent->AsElement()->State();
+  if (eventStates.HasState(NS_EVENT_STATE_DISABLED) ||
+      !eventStates.HasState(NS_EVENT_STATE_FOCUSRING)) {
+    return; // can't have focus or doesn't match :-moz-focusring
+  }
+
+  if (!mOuterFocusStyle ||
+      !mOuterFocusStyle->StyleBorder()->HasBorder()) {
+    // no ::-moz-focus-outer specified border (how style specifies a focus ring
+    // for range)
+    return;
+  }
+
   const nsStyleDisplay *disp = StyleDisplay();
-  if ((!IsThemed(disp) ||
-       !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) &&
-      IsVisibleForPainting(aBuilder)) {
-    aLists.Content()->AppendNewToTop(
-      new (aBuilder) nsDisplayRangeFocusRing(aBuilder, this));
+  if (IsThemed(disp) &&
+      PresContext()->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
+    return; // the native theme displays its own visual indication of focus
   }
+
+  aLists.Content()->AppendNewToTop(
+    new (aBuilder) nsDisplayRangeFocusRing(aBuilder, this));
 }
 
 nsresult
 nsRangeFrame::Reflow(nsPresContext*           aPresContext,
                      nsHTMLReflowMetrics&     aDesiredSize,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
@@ -840,8 +858,28 @@ nsRangeFrame::GetPseudoElement(nsCSSPseu
   }
 
   if (aType == nsCSSPseudoElements::ePseudo_mozRangeProgress) {
     return mProgressDiv;
   }
 
   return nsContainerFrame::GetPseudoElement(aType);
 }
+
+nsStyleContext*
+nsRangeFrame::GetAdditionalStyleContext(int32_t aIndex) const
+{
+  // We only implement this so that SetAdditionalStyleContext will be
+  // called if style changes that would change the -moz-focus-outer
+  // pseudo-element have occurred.
+  return aIndex == 0 ? mOuterFocusStyle : nullptr;
+}
+
+void
+nsRangeFrame::SetAdditionalStyleContext(int32_t aIndex,
+                                        nsStyleContext* aStyleContext)
+{
+  MOZ_ASSERT(aIndex == 0,
+             "GetAdditionalStyleContext is handling other indexes?");
+
+  // The -moz-focus-outer pseudo-element's style has changed.
+  mOuterFocusStyle = aStyleContext;
+}
--- a/layout/forms/nsRangeFrame.h
+++ b/layout/forms/nsRangeFrame.h
@@ -9,23 +9,26 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/Decimal.h"
 #include "mozilla/EventForwards.h"
 #include "nsContainerFrame.h"
 #include "nsIAnonymousContentCreator.h"
 #include "nsCOMPtr.h"
 
 class nsBaseContentList;
+class nsDisplayRangeFocusRing;
 
 class nsRangeFrame : public nsContainerFrame,
                      public nsIAnonymousContentCreator
 {
   friend nsIFrame*
   NS_NewRangeFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
+  friend class nsDisplayRangeFocusRing;
+
   nsRangeFrame(nsStyleContext* aContext);
   virtual ~nsRangeFrame();
 
   typedef mozilla::dom::Element Element;
 
 public:
   NS_DECL_QUERYFRAME_TARGET(nsRangeFrame)
   NS_DECL_QUERYFRAME
@@ -79,16 +82,20 @@ public:
   virtual nsIAtom* GetType() const MOZ_OVERRIDE;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE
   {
     return nsContainerFrame::IsFrameOfType(aFlags &
       ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
   }
 
+  nsStyleContext* GetAdditionalStyleContext(int32_t aIndex) const MOZ_OVERRIDE;
+  void SetAdditionalStyleContext(int32_t aIndex,
+                                 nsStyleContext* aStyleContext) MOZ_OVERRIDE;
+
   /**
    * Returns true if the slider's thumb moves horizontally, or else false if it
    * moves vertically.
    *
    * aOverrideFrameSize If specified, this will be used instead of the size of
    *   the frame's rect (i.e. the frame's border-box size) if the frame's
    *   rect would have otherwise been examined. This should only be specified
    *   during reflow when the frame's [new] border-box size has not yet been
@@ -157,11 +164,16 @@ private:
    */
   nsCOMPtr<Element> mProgressDiv;
 
   /**
    * The div used to show the ::-moz-range-thumb pseudo-element.
    * @see nsRangeFrame::CreateAnonymousContent
    */
   nsCOMPtr<Element> mThumbDiv;
+
+  /**
+   * Cached style context for -moz-focus-outer CSS pseudo-element style.
+   */
+  nsRefPtr<nsStyleContext> mOuterFocusStyle;
 };
 
 #endif
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -65,17 +65,17 @@ class nsFirstLineFrame;
  * appear with the prev-in-flow before the next-in-flow.
  * -- While reflowing a block, its overflow line list
  * will usually be empty but in some cases will have lines
  * (while we reflow the block at its shrink-wrap width).
  * In this case any new overflowing content must be
  * prepended to the overflow lines.
  */
 
-#define nsBlockFrameSuper nsContainerFrame
+typedef nsContainerFrame nsBlockFrameSuper;
 
 /*
  * Base class for block and inline frames.
  * The block frame has an additional child list, kAbsoluteList, which
  * contains the absolutely positioned frames.
  */ 
 class nsBlockFrame : public nsBlockFrameSuper
 {
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -49,73 +49,74 @@ public:
   NS_DECL_IMGINOTIFICATIONOBSERVER
 
   void SetFrame(nsImageFrame *frame) { mFrame = frame; }
 
 private:
   nsImageFrame *mFrame;
 };
 
-#define ImageFrameSuper nsSplittableFrame
+typedef nsSplittableFrame ImageFrameSuper;
 
 class nsImageFrame : public ImageFrameSuper,
                      public nsIReflowCallback {
 public:
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::layers::ImageLayer ImageLayer;
   typedef mozilla::layers::LayerManager LayerManager;
 
   NS_DECL_FRAMEARENA_HELPERS
 
   nsImageFrame(nsStyleContext* aContext);
 
   NS_DECL_QUERYFRAME_TARGET(nsImageFrame)
   NS_DECL_QUERYFRAME
 
-  virtual void DestroyFrom(nsIFrame* aDestructRoot);
+  virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
   virtual void Init(nsIContent*      aContent,
                     nsIFrame*        aParent,
                     nsIFrame*        aPrevInFlow) MOZ_OVERRIDE;
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
-  virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext);
-  virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext);
-  virtual mozilla::IntrinsicSize GetIntrinsicSize();
-  virtual nsSize GetIntrinsicRatio();
+  virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
+  virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
+  virtual mozilla::IntrinsicSize GetIntrinsicSize() MOZ_OVERRIDE;
+  virtual nsSize GetIntrinsicRatio() MOZ_OVERRIDE;
   virtual nsresult Reflow(nsPresContext*          aPresContext,
                           nsHTMLReflowMetrics&     aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
-                          nsReflowStatus&          aStatus);
+                          nsReflowStatus&          aStatus) MOZ_OVERRIDE;
   
   virtual nsresult  GetContentForEvent(mozilla::WidgetEvent* aEvent,
-                                       nsIContent** aContent);
+                                       nsIContent** aContent) MOZ_OVERRIDE;
   virtual nsresult HandleEvent(nsPresContext* aPresContext,
                                mozilla::WidgetGUIEvent* aEvent,
-                               nsEventStatus* aEventStatus);
+                               nsEventStatus* aEventStatus) MOZ_OVERRIDE;
   virtual nsresult GetCursor(const nsPoint& aPoint,
-                             nsIFrame::Cursor& aCursor);
+                             nsIFrame::Cursor& aCursor) MOZ_OVERRIDE;
   virtual nsresult AttributeChanged(int32_t aNameSpaceID,
                                     nsIAtom* aAttribute,
-                                    int32_t aModType);
+                                    int32_t aModType) MOZ_OVERRIDE;
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE;
 #endif
 
-  virtual nsIAtom* GetType() const;
+  virtual nsIAtom* GetType() const MOZ_OVERRIDE;
 
-  virtual bool IsFrameOfType(uint32_t aFlags) const
+  virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE
   {
     return ImageFrameSuper::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced));
   }
 
 #ifdef DEBUG_FRAME_DUMP
-  virtual nsresult GetFrameName(nsAString& aResult) const;
-  void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const;
+  virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
+  void List(FILE* out = stderr, const char* aPrefix = "", 
+            uint32_t aFlags = 0) const MOZ_OVERRIDE;
 #endif
 
   virtual int GetSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const MOZ_OVERRIDE;
 
   nsresult GetIntrinsicImageSize(nsSize& aSize);
 
   static void ReleaseGlobals() {
     if (gIconLoad) {
@@ -158,17 +159,17 @@ public:
    * Return true if the image has associated image map.
    */
   bool HasImageMap() const { return mImageMap || GetMapElement(); }
 
   nsImageMap* GetImageMap();
   nsImageMap* GetExistingImageMap() const { return mImageMap; }
 
   virtual void AddInlineMinWidth(nsRenderingContext *aRenderingContext,
-                                 InlineMinWidthData *aData);
+                                 InlineMinWidthData *aData) MOZ_OVERRIDE;
 
   void DisconnectMap();
 
   // nsIReflowCallback
   virtual bool ReflowFinished() MOZ_OVERRIDE;
   virtual void ReflowCallbackCanceled() MOZ_OVERRIDE;
 
 protected:
--- a/layout/generic/nsObjectFrame.h
+++ b/layout/generic/nsObjectFrame.h
@@ -29,17 +29,17 @@ class nsPluginInstanceOwner;
 namespace mozilla {
 namespace layers {
 class ImageContainer;
 class Layer;
 class LayerManager;
 }
 }
 
-#define nsObjectFrameSuper nsFrame
+typedef nsFrame nsObjectFrameSuper;
 
 class nsObjectFrame : public nsObjectFrameSuper,
                       public nsIObjectFrame,
                       public nsIReflowCallback {
 public:
   typedef mozilla::LayerState LayerState;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
@@ -51,55 +51,56 @@ public:
   friend nsIFrame* NS_NewObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   NS_DECL_QUERYFRAME
   NS_DECL_QUERYFRAME_TARGET(nsObjectFrame)
 
   virtual void Init(nsIContent* aContent,
                     nsIFrame* aParent,
                     nsIFrame* aPrevInFlow) MOZ_OVERRIDE;
-  virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext);
-  virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext);
+  virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
+  virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
   virtual nsresult Reflow(nsPresContext* aPresContext,
                           nsHTMLReflowMetrics& aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
-                          nsReflowStatus& aStatus);
+                          nsReflowStatus& aStatus) MOZ_OVERRIDE;
   virtual nsresult DidReflow(nsPresContext* aPresContext,
                              const nsHTMLReflowState* aReflowState,
-                             nsDidReflowStatus aStatus);
+                             nsDidReflowStatus aStatus) MOZ_OVERRIDE;
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
 
   virtual nsresult  HandleEvent(nsPresContext* aPresContext,
                                 mozilla::WidgetGUIEvent* aEvent,
-                                nsEventStatus* aEventStatus);
+                                nsEventStatus* aEventStatus) MOZ_OVERRIDE;
 
-  virtual nsIAtom* GetType() const;
+  virtual nsIAtom* GetType() const MOZ_OVERRIDE;
 
-  virtual bool IsFrameOfType(uint32_t aFlags) const
+  virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE
   {
     return nsObjectFrameSuper::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced));
   }
 
-  virtual bool NeedsView() { return true; }
+  virtual bool NeedsView() MOZ_OVERRIDE { return true; }
 
 #ifdef DEBUG_FRAME_DUMP
-  virtual nsresult GetFrameName(nsAString& aResult) const;
+  virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
-  virtual void DestroyFrom(nsIFrame* aDestructRoot);
+  virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
 
-  virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext);
+  virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE;
 
   NS_METHOD GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance) MOZ_OVERRIDE;
 
   virtual void SetIsDocumentActive(bool aIsActive) MOZ_OVERRIDE;
 
-  virtual nsresult GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor);
+  virtual nsresult GetCursor(const nsPoint& aPoint, 
+                             nsIFrame::Cursor& aCursor) MOZ_OVERRIDE;
 
   // APIs used by nsRootPresContext to set up the widget position/size/clip
   // region.
   /**
    * Set the next widget configuration for the plugin to the desired
    * position of the plugin's widget, on the assumption that it is not visible
    * (clipped out or covered by opaque content).
    * This will only be called for plugins which have been registered
@@ -208,17 +209,18 @@ protected:
   virtual ~nsObjectFrame();
 
   // NOTE:  This frame class does not inherit from |nsLeafFrame|, so
   // this is not a virtual method implementation.
   void GetDesiredSize(nsPresContext* aPresContext,
                       const nsHTMLReflowState& aReflowState,
                       nsHTMLReflowMetrics& aDesiredSize);
 
-  bool IsFocusable(int32_t *aTabIndex = nullptr, bool aWithMouse = false);
+  bool IsFocusable(int32_t *aTabIndex = nullptr, 
+                   bool aWithMouse = false) MOZ_OVERRIDE;
 
   // check attributes and optionally CSS to see if we should display anything
   bool IsHidden(bool aCheckVisibilityStyle = true) const;
 
   bool IsOpaque() const;
   bool IsTransparentMode() const;
   bool IsPaintedByGecko() const;
 
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -801,16 +801,26 @@ input[type=range] {
 }
 
 input[type=range][orient=vertical] {
   width: 1.3em;
   height: 12em;
 }
 
 /**
+ * Ideally we'd also require :-moz-focusring here, but that doesn't currently
+ * work. Instead we only use the -moz-focus-outer border style if
+ * NS_EVENT_STATE_FOCUSRING is set (the check is in
+ * nsRangeFrame::BuildDisplayList).
+ */
+input[type=range]::-moz-focus-outer {
+  border: 1px dotted black;
+}
+
+/**
  * Layout handles positioning of this pseudo-element specially (so that content
  * authors can concentrate on styling the thumb without worrying about the
  * logic to position it). Specifically the 'margin', 'top' and 'left'
  * properties are ignored.
  *
  * If content authors want to have a vertical range, they will also need to
  * set the width/height of this pseudo-element.
  */
--- a/layout/tables/nsCellMap.cpp
+++ b/layout/tables/nsCellMap.cpp
@@ -103,34 +103,32 @@ nsTableCellMap::GetRightMostBorder(int32
 {
   if (!mBCInfo) ABORT1(nullptr);
 
   int32_t numRows = mBCInfo->mRightBorders.Length();
   if (aRowIndex < numRows) {
     return &mBCInfo->mRightBorders.ElementAt(aRowIndex);
   }
 
-  if (!mBCInfo->mRightBorders.SetLength(aRowIndex+1))
-    ABORT1(nullptr);
+  mBCInfo->mRightBorders.SetLength(aRowIndex+1);
   return &mBCInfo->mRightBorders.ElementAt(aRowIndex);
 }
 
 // Get the bcData holding the border segments of the bottom edge of the table
 BCData*
 nsTableCellMap::GetBottomMostBorder(int32_t aColIndex)
 {
   if (!mBCInfo) ABORT1(nullptr);
 
   int32_t numCols = mBCInfo->mBottomBorders.Length();
   if (aColIndex < numCols) {
     return &mBCInfo->mBottomBorders.ElementAt(aColIndex);
   }
 
-  if (!mBCInfo->mBottomBorders.SetLength(aColIndex+1))
-    ABORT1(nullptr);
+  mBCInfo->mBottomBorders.SetLength(aColIndex+1);
   return &mBCInfo->mBottomBorders.ElementAt(aColIndex);
 }
 
 // delete the borders corresponding to the right and bottom edges of the table
 void
 nsTableCellMap::DeleteRightBottomBorders()
 {
   if (mBCInfo) {
@@ -483,25 +481,23 @@ nsTableCellMap::InsertRows(nsTableRowGro
                           rgStartRowIndex, aDamageArea);
 #ifdef DEBUG_TABLE_CELLMAP
       Dump("after InsertRows");
 #endif
       if (mBCInfo) {
         int32_t count = mBCInfo->mRightBorders.Length();
         if (aFirstRowIndex < count) {
           for (int32_t rowX = aFirstRowIndex; rowX < aFirstRowIndex + numNewRows; rowX++) {
-            if (!mBCInfo->mRightBorders.InsertElementAt(rowX))
-              ABORT0();
+            mBCInfo->mRightBorders.InsertElementAt(rowX);
           }
         }
         else {
           GetRightMostBorder(aFirstRowIndex); // this will create missing entries
           for (int32_t rowX = aFirstRowIndex + 1; rowX < aFirstRowIndex + numNewRows; rowX++) {
-            if (!mBCInfo->mRightBorders.AppendElement())
-              ABORT0();
+            mBCInfo->mRightBorders.AppendElement();
           }
         }
       }
       return;
     }
     int32_t rowCount = cellMap->GetRowCount();
     rgStartRowIndex += rowCount;
     rowIndex -= rowCount;
--- a/media/libopus/moz.build
+++ b/media/libopus/moz.build
@@ -14,16 +14,19 @@ EXPORTS.opus += [
 MSVC_ENABLE_PGO = True
 
 FINAL_LIBRARY = 'gkmedias'
 
 DEFINES['OPUS_BUILD'] = True
 DEFINES['OPUS_VERSION'] = '"v1.1-mozilla"'
 DEFINES['USE_ALLOCA'] = True
 
+if CONFIG['MOZ_DEBUG']:
+    DEFINES['ENABLE_ASSERTIONS'] = True
+
 if CONFIG['OS_ARCH'] in ('Linux', 'Darwin', 'DragonFly', 'FreeBSD',
                          'NetBSD', 'OpenBSD'):
     DEFINES['HAVE_LRINTF'] = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['inline'] = '__inline'
     if CONFIG['GNU_CC']:
         DEFINES['HAVE_LRINTF'] = True
--- a/media/libvpx/Makefile.in
+++ b/media/libvpx/Makefile.in
@@ -46,36 +46,37 @@ VPX_OIE_FORMAT := gas
 endif
 
 GARBAGE += vpx_scale_asm_offsets.$(OBJ_SUFFIX) vpx_scale_asm_offsets.asm
 GARBAGE += vp8_asm_enc_offsets.$(OBJ_SUFFIX) vp8_asm_enc_offsets.asm
 
 else
 
 # We can extract the asm offsets directly from generated assembly using inline
-# asm. This is the preferred method.
+# asm. This is the preferred method. However we need to strip out CFLAGS that
+# cause LTO because then the resulting .S file is useless.
 
-vpx_scale_asm_offsets.s: CFLAGS += -DINLINE_ASM
+vpx_scale_asm_offsets.s: CFLAGS := -DINLINE_ASM
 
 OFFSET_PATTERN := '^[a-zA-Z0-9_]* EQU'
 
 # This rule, as well as the rule for vp8_asm_enc_offsets.s further below are here
 # because the generic rule in rules.mk was made to not be implicit, and we
 # can't put the C files in CSRCS.
 vpx_scale_asm_offsets.s: $(srcdir)/vpx_scale/vpx_scale_asm_offsets.c
 	$(REPORT_BUILD)
 	$(CC) -S $(COMPILE_CFLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
 vpx_scale_asm_offsets.asm: vpx_scale_asm_offsets.s
 	grep $(OFFSET_PATTERN) $< | sed -e 's/[$$\#]//g' \
 	    $(if $(VPX_AS_CONVERSION),| $(VPX_AS_CONVERSION)) > $@
 
 GARBAGE += vpx_scale_asm_offsets.s vpx_scale_asm_offsets.asm
 
-vp8_asm_enc_offsets.s: CFLAGS += -DINLINE_ASM
+vp8_asm_enc_offsets.s: CFLAGS := -DINLINE_ASM
 
 vp8_asm_enc_offsets.s: $(srcdir)/vp8/encoder/vp8_asm_enc_offsets.c
 	$(REPORT_BUILD)
 	$(CC) -S $(COMPILE_CFLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
 vp8_asm_enc_offsets.asm: vp8_asm_enc_offsets.s
 	grep $(OFFSET_PATTERN) $< | sed -e 's/[$$\#]//g' \
 	    $(if $(VPX_AS_CONVERSION),| $(VPX_AS_CONVERSION)) > $@
--- a/python/mozbuild/mozbuild/backend/templates/android_eclipse/build.xml
+++ b/python/mozbuild/mozbuild/backend/templates/android_eclipse/build.xml
@@ -1,18 +1,18 @@
 #filter substitution
 <?xml version="1.0" encoding="UTF-8"?>
 <project>
     <property name="topsrcdir" value="@IDE_TOPSRCDIR@"/>
     <property name="topobjdir" value="@IDE_TOPOBJDIR@"/>
     <property name="objdir" value="@IDE_OBJDIR@"/>
     <property name="project_name" value="@IDE_PROJECT_NAME@"/>
 
-    // This file can get large (!), but for a short time we want to
-    // log as much information for debugging build loops as possible.
+    <!-- This file can get large (!), but for a short time we want to
+         log as much information for debugging build loops as possible. -->
     <record name="${topobjdir}/android_eclipse/build.log" append="yes" />
 
     <target name="build_needed" >
 
         <script language="javascript" >
 <![CDATA[
   importClass(java.io.File);
 
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -600,17 +600,17 @@ VerifySignature(AppTrustedRoot trustedRo
 
   // Verify certificate.
   AppTrustDomain trustDomain(nullptr); // TODO: null pinArg
   if (trustDomain.SetTrustedRoot(trustedRoot) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
   if (BuildCertChain(trustDomain, signerCert, PR_Now(), MustBeEndEntity,
                      KU_DIGITAL_SIGNATURE, SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
-                     builtChain) != SECSuccess) {
+                     nullptr, builtChain) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
 
   // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
   SECOidData* contentTypeOidData =
     SECOID_FindOID(&signedData->contentInfo.contentType);
   if (!contentTypeOidData) {
     return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -173,9 +173,21 @@ AppTrustDomain::GetCertTrust(EndEntityOr
 
 SECStatus
 AppTrustDomain::VerifySignedData(const CERTSignedData* signedData,
                                   const CERTCertificate* cert)
 {
   return ::insanity::pkix::VerifySignedData(signedData, cert, mPinArg);
 }
 
+SECStatus
+AppTrustDomain::CheckRevocation(EndEntityOrCA,
+                                const CERTCertificate*,
+                                /*const*/ CERTCertificate*,
+                                PRTime time,
+                                /*optional*/ const SECItem*)
+{
+  // We don't currently do revocation checking. If we need to distrust an Apps
+  // certificate, we will use the active distrust mechanism.
+  return SECSuccess;
+}
+
 } }
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -24,17 +24,21 @@ public:
                          const CERTCertificate* candidateCert,
                  /*out*/ TrustLevel* trustLevel) MOZ_OVERRIDE;
   SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
                                  PRTime time,
                          /*out*/ insanity::pkix::ScopedCERTCertList& results)
                                  MOZ_OVERRIDE;
   SECStatus VerifySignedData(const CERTSignedData* signedData,
                              const CERTCertificate* cert) MOZ_OVERRIDE;
-
+  SECStatus CheckRevocation(insanity::pkix::EndEntityOrCA endEntityOrCA,
+                            const CERTCertificate* cert,
+                            /*const*/ CERTCertificate* issuerCertToDup,
+                            PRTime time,
+                            /*optional*/ const SECItem* stapledOCSPresponse);
 private:
   void* mPinArg; // non-owning!
   insanity::pkix::ScopedCERTCertificate mTrustedRoot;
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm_AppsTrustDomain_h
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -18,17 +18,17 @@
 #include "sslerr.h"
 
 // ScopedXXX in this file are insanity::pkix::ScopedXXX, not
 // mozilla::ScopedXXX.
 using namespace insanity::pkix;
 using namespace mozilla::psm;
 
 #ifdef PR_LOGGING
-static PRLogModuleInfo* gCertVerifierLog = nullptr;
+PRLogModuleInfo* gCertVerifierLog = nullptr;
 #endif
 
 namespace mozilla { namespace psm {
 
 const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
 const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
 
 CertVerifier::CertVerifier(implementation_config ic,
@@ -189,47 +189,48 @@ destroyCertListThatShouldNotExist(CERTCe
   }
 }
 #endif
 
 static SECStatus
 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
                              PRTime time, KeyUsages ku1, KeyUsages ku2,
                              KeyUsages ku3, SECOidTag eku,
+                             const SECItem* stapledOCSPResponse,
                              ScopedCERTCertList& builtChain)
 {
   PR_ASSERT(ku1);
   PR_ASSERT(ku2);
 
   SECStatus rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                                ku1, eku, builtChain);
+                                ku1, eku, stapledOCSPResponse, builtChain);
   if (rv != SECSuccess && ku2 &&
       PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
     rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                        ku2, eku, builtChain);
+                        ku2, eku, stapledOCSPResponse, builtChain);
     if (rv != SECSuccess && ku3 &&
         PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                          ku3, eku, builtChain);
+                          ku3, eku, stapledOCSPResponse, builtChain);
       if (rv != SECSuccess) {
         PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
       }
     }
   }
   return rv;
 }
 
 SECStatus
 CertVerifier::InsanityVerifyCert(
                    CERTCertificate* cert,
-      /*optional*/ const SECItem* /*TODO: stapledOCSPResponse*/,
                    const SECCertificateUsage usage,
                    const PRTime time,
                    void* pinArg,
                    const Flags flags,
+      /*optional*/ const SECItem* stapledOCSPResponse,
   /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain)
 {
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of InsanityVerifyCert\n"));
   SECStatus rv;
 
   // TODO(bug 970750): anyExtendedKeyUsage
   // TODO: encipherOnly/decipherOnly
   // S/MIME Key Usage: http://tools.ietf.org/html/rfc3850#section-4.4.2
@@ -243,78 +244,78 @@ CertVerifier::InsanityVerifyCert(
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
       NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
                                        mOCSPStrict, pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
-                          builtChain);
+                          stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLServer: {
       // TODO: When verifying a certificate in an SSL handshake, we should
       // restrict the acceptable key usage based on the key exchange method
       // chosen by the server.
       NSSCertDBTrustDomain trustDomain(trustSSL, mOCSPDownloadEnabled,
                                        mOCSPStrict, pinArg);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_DIGITAL_SIGNATURE, // ECDHE/DHE
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
-                                        builtChain);
+                                        stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, mOCSPDownloadEnabled,
                                        mOCSPStrict, pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeCA,
                           KU_KEY_CERT_SIGN,
                           SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
-                          builtChain);
+                          stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
                                        mOCSPStrict, pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
-                          builtChain);
+                          stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailRecipient: {
       // TODO: The higher level S/MIME processing should pass in which key
       // usage it is trying to verify for, and base its algorithm choices
       // based on the result of the verification(s).
       NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
                                        mOCSPStrict, pinArg);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         0,
                                         SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
-                                        builtChain);
+                                        stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning,
                                        mOCSPDownloadEnabled, mOCSPStrict,
                                        pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
-                          builtChain);
+                          stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageVerifyCA:
     case certificateUsageStatusResponder: {
       // XXX This is a pretty useless way to verify a certificate. It is used
       // by the implementation of window.crypto.importCertificates and in the
       // certificate viewer UI. Because we don't know what trust bit is
@@ -330,28 +331,28 @@ CertVerifier::InsanityVerifyCert(
         endEntityOrCA = MustBeEndEntity;
         keyUsage = KU_DIGITAL_SIGNATURE;
         eku = SEC_OID_OCSP_RESPONDER;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL,
                                     mOCSPDownloadEnabled, mOCSPStrict, pinArg);
       rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
-                          keyUsage, eku, builtChain);
+                          keyUsage, eku, stapledOCSPResponse, builtChain);
       if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, mOCSPDownloadEnabled,
                                         mOCSPStrict, pinArg);
         rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
-                            eku, builtChain);
+                            eku, stapledOCSPResponse, builtChain);
         if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   mOCSPDownloadEnabled,
                                                   mOCSPStrict, pinArg);
           rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
-                              keyUsage, eku, builtChain);
+                              keyUsage, eku, stapledOCSPResponse, builtChain);
         }
       }
 
       break;
     }
 
     default:
       PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
@@ -595,18 +596,18 @@ CertVerifier::VerifyCert(CERTCertificate
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
 #else
     PR_SetError(PR_INVALID_STATE_ERROR, 0);
 #endif
     return SECFailure;
   }
 
   if (mImplementation == insanity) {
-    return InsanityVerifyCert(cert, stapledOCSPResponse, usage, time,
-                              pinArg, flags, validationChain);
+    return InsanityVerifyCert(cert, usage, time, pinArg, flags,
+                              stapledOCSPResponse, validationChain);
   }
 
   if (mImplementation == classic) {
     // XXX: we do not care about the localOnly flag (currently) as the
     // caller that wants localOnly should disable and reenable the fetching.
     return ClassicVerifyCert(cert, usage, time, pinArg, validationChain,
                              verifyLog);
   }
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -73,20 +73,20 @@ public:
   const bool mCRLDownloadEnabled;
 #endif
   const bool mOCSPDownloadEnabled;
   const bool mOCSPStrict;
   const bool mOCSPGETEnabled;
 
 private:
   SECStatus InsanityVerifyCert(CERTCertificate* cert,
-      /*optional*/ const SECItem* stapledOCSPResponse,
       const SECCertificateUsage usage,
       const PRTime time,
       void* pinArg,
       const Flags flags,
+      /*optional*/ const SECItem* stapledOCSPResponse,
       /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain);
 };
 
 void InitCertVerifierLog();
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__CertVerifier_h
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -33,22 +33,22 @@ namespace {
 
 inline void PORT_Free_string(char* str) { PORT_Free(str); }
 
 typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
 
 } // unnamed namespace
 
 NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
-                                           bool /*ocspDownloadEnabled*/,
-                                           bool /*ocspStrict*/,
+                                           bool ocspDownloadEnabled,
+                                           bool ocspStrict,
                                            void* pinArg)
   : mCertDBTrustType(certDBTrustType)
-//  , mOCSPDownloadEnabled(ocspDownloadEnabled)
-//  , mOCSPStrict(ocspStrict)
+  , mOCSPDownloadEnabled(ocspDownloadEnabled)
+  , mOCSPStrict(ocspStrict)
   , mPinArg(pinArg)
 {
 }
 
 SECStatus
 NSSCertDBTrustDomain::FindPotentialIssuers(
   const SECItem* encodedIssuerName, PRTime time,
   /*out*/ insanity::pkix::ScopedCERTCertList& results)
@@ -119,16 +119,128 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn
 
 SECStatus
 NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData,
                                        const CERTCertificate* cert)
 {
   return ::insanity::pkix::VerifySignedData(signedData, cert, mPinArg);
 }
 
+SECStatus
+NSSCertDBTrustDomain::CheckRevocation(
+  insanity::pkix::EndEntityOrCA endEntityOrCA,
+  const CERTCertificate* cert,
+  /*const*/ CERTCertificate* issuerCert,
+  PRTime time,
+  /*optional*/ const SECItem* stapledOCSPResponse)
+{
+  // Actively distrusted certificates will have already been blocked by
+  // GetCertTrust.
+
+  // TODO: need to verify that IsRevoked isn't called for trust anchors AND
+  // that that fact is documented in insanity.
+
+  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+         ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
+
+  PORT_Assert(cert);
+  PORT_Assert(issuerCert);
+  if (!cert || !issuerCert) {
+    PORT_SetError(SEC_ERROR_INVALID_ARGS);
+    return SECFailure;
+  }
+
+  // If we have a stapled OCSP response then the verification of that response
+  // determines the result unless the OCSP response is expired. We make an
+  // exception for expired responses because some servers, nginx in particular,
+  // are known to serve expired responses due to bugs.
+  if (stapledOCSPResponse) {
+    PR_ASSERT(endEntityOrCA == MustBeEndEntity);
+    SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time,
+                                             stapledOCSPResponse);
+    if (rv == SECSuccess) {
+      return rv;
+    }
+    if (PR_GetError() != SEC_ERROR_OCSP_OLD_RESPONSE) {
+      return rv;
+    }
+  }
+
+  // TODO(bug 921885): We need to change this when we add EV support.
+
+  // TODO: when !mOCSPDownloadEnabled, we still need to handle the fallback for
+  // expired responses. But, if/when we disable OCSP fetching by default, it
+  // would be ambiguous whether !mOCSPDownloadEnabled means "I want the default"
+  // or "I really never want you to ever fetch OCSP."
+  if (mOCSPDownloadEnabled) {
+    // We don't do OCSP fetching for intermediates.
+    if (endEntityOrCA == MustBeCA) {
+      PR_ASSERT(!stapledOCSPResponse);
+      return SECSuccess;
+    }
+
+    ScopedPtr<char, PORT_Free_string>
+      url(CERT_GetOCSPAuthorityInfoAccessLocation(cert));
+
+    // Nothing to do if we don't have an OCSP responder URI for the cert; just
+    // assume it is good. Note that this is the confusing, but intended,
+    // interpretation of "strict" revocation checking in the face of a
+    // certificate that lacks an OCSP responder URI.
+    if (!url) {
+      if (stapledOCSPResponse) {
+        PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
+        return SECFailure;
+      }
+      return SECSuccess;
+    }
+
+    ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+    if (!arena) {
+      return SECFailure;
+    }
+
+    const SECItem* request
+      = CreateEncodedOCSPRequest(arena.get(), cert, issuerCert);
+    if (!request) {
+      return SECFailure;
+    }
+
+    const SECItem* response(CERT_PostOCSPRequest(arena.get(), url.get(),
+                                                 request));
+    if (!response) {
+      if (mOCSPStrict) {
+        return SECFailure;
+      }
+      // Soft fail -> success :(
+    } else {
+      SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time,
+                                               response);
+      if (rv == SECSuccess) {
+        return SECSuccess;
+      }
+      PRErrorCode error = PR_GetError();
+      switch (error) {
+        case SEC_ERROR_OCSP_UNKNOWN_CERT:
+        case SEC_ERROR_REVOKED_CERTIFICATE:
+          return SECFailure;
+        default:
+          if (mOCSPStrict) {
+            return SECFailure;
+          }
+          break; // Soft fail -> success :(
+      }
+    }
+  }
+
+  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+         ("NSSCertDBTrustDomain: end of CheckRevocation"));
+
+  return SECSuccess;
+}
+
 namespace {
 
 static char*
 nss_addEscape(const char* string, char quote)
 {
   char* newString = 0;
   int escapes = 0, size = 0;
   const char* src;
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -58,18 +58,24 @@ public:
 
   virtual SECStatus GetCertTrust(insanity::pkix::EndEntityOrCA endEntityOrCA,
                                  const CERTCertificate* candidateCert,
                          /*out*/ TrustLevel* trustLevel);
 
   virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
                                      const CERTCertificate* cert);
 
+  virtual SECStatus CheckRevocation(insanity::pkix::EndEntityOrCA endEntityOrCA,
+                                    const CERTCertificate* cert,
+                          /*const*/ CERTCertificate* issuerCert,
+                                    PRTime time,
+                       /*optional*/ const SECItem* stapledOCSPResponse);
+
 private:
   const SECTrustType mCertDBTrustType;
-//  const bool mOCSPDownloadEnabled;
-//  const bool mOCSPStrict;
+  const bool mOCSPDownloadEnabled;
+  const bool mOCSPStrict;
   void* mPinArg; // non-owning!
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__NSSCertDBTrustDomain_h
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 UNIFIED_SOURCES += [
     '../insanity/lib/pkixbuild.cpp',
     '../insanity/lib/pkixcheck.cpp',
     '../insanity/lib/pkixder.cpp',
     '../insanity/lib/pkixkey.cpp',
+    '../insanity/lib/pkixocsp.cpp',
     'CertVerifier.cpp',
     'NSSCertDBTrustDomain.cpp',
 ]
 
 if not CONFIG['NSS_NO_LIBPKIX']:
     UNIFIED_SOURCES += [
         'ExtendedValidation.cpp',
     ]
--- a/security/insanity/include/insanity/pkix.h
+++ b/security/insanity/include/insanity/pkix.h
@@ -18,25 +18,79 @@
 #ifndef insanity_pkix__pkix_h
 #define insanity_pkix__pkix_h
 
 #include "pkixtypes.h"
 #include "prtime.h"
 
 namespace insanity { namespace pkix {
 
+// ----------------------------------------------------------------------------
+// ERROR RANKING
+//
+// BuildCertChain prioritizes certain checks ahead of others so that when a
+// certificate chain has multiple errors, the "most serious" error is
+// returned. In practice, this ranking of seriousness is tied directly to how
+// Firefox's certificate error override mechanism.
+//
+// The ranking is:
+//
+//    1. Active distrust (SEC_ERROR_UNTRUSTED_CERT).
+//    2. Problems with issuer-independent properties other than
+//       notBefore/notAfter.
+//    3. For CA certificates: Expiration.
+//    4. Unknown issuer (SEC_ERROR_UNKNOWN_ISSUER).
+//    5. For end-entity certificates: Expiration.
+//    6. Revocation.
+//
+// In particular, if BuildCertChain returns SEC_ERROR_UNKNOWN_ISSUER then the
+// caller can call CERT_CheckCertValidTimes to determine if the certificate is
+// ALSO expired.
+//
+// It would be better if revocation were prioritized above expiration and
+// unknown issuer. However, it is impossible to do revocation checking without
+// knowing the issuer, since the issuer information is needed to validate the
+// revocation information. Also, generally revocation checking only works
+// during the validity period of the certificate.
+//
+// In general, when path building fails, BuildCertChain will return
+// SEC_ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in the
+// same error (which is trivially true when there is only one potential path),
+// more specific errors will be returned.
+//
+// ----------------------------------------------------------------------------
+// Meaning of specific error codes
+//
+// SEC_ERROR_UNTRUSTED_CERT means that the end-entity certificate was actively
+//                          distrusted.
+// SEC_ERROR_UNTRUSTED_ISSUER means that path building failed because of active
+//                            distrust.
+// TODO(bug 968451): Document more of these.
+
 SECStatus BuildCertChain(TrustDomain& trustDomain,
                          CERTCertificate* cert,
                          PRTime time,
                          EndEntityOrCA endEntityOrCA,
             /*optional*/ KeyUsages requiredKeyUsagesIfPresent,
             /*optional*/ SECOidTag requiredEKUIfPresent,
+            /*optional*/ const SECItem* stapledOCSPResponse,
                  /*out*/ ScopedCERTCertList& results);
 
 // Verify the given signed data using the public key of the given certificate.
 // (EC)DSA parameter inheritance is not supported.
 SECStatus VerifySignedData(const CERTSignedData* sd,
                            const CERTCertificate* cert,
                            void* pkcs11PinArg);
 
+// The return value, if non-null, is owned by the arena and MUST NOT be freed.
+SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena,
+                                  const CERTCertificate* cert,
+                                  const CERTCertificate* issuerCert);
+
+SECStatus VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
+                                    const CERTCertificate* cert,
+                                    CERTCertificate* issuerCert,
+                                    PRTime time,
+                                    const SECItem* encodedResponse);
+
 } } // namespace insanity::pkix
 
 #endif // insanity_pkix__pkix_h
--- a/security/insanity/include/insanity/pkixtypes.h
+++ b/security/insanity/include/insanity/pkixtypes.h
@@ -74,16 +74,24 @@ public:
   // has all the public key information needed--i.e. it should ensure that the
   // certificate is not trying to use EC(DSA) parameter inheritance.
   //
   // Most implementations of this function should probably forward the call
   // directly to insanity::pkix::VerifySignedData.
   virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
                                      const CERTCertificate* cert) = 0;
 
+  // issuerCertToDup is only non-const so CERT_DupCertificate can be called on
+  // it.
+  virtual SECStatus CheckRevocation(EndEntityOrCA endEntityOrCA,
+                                    const CERTCertificate* cert,
+                          /*const*/ CERTCertificate* issuerCertToDup,
+                                    PRTime time,
+                       /*optional*/ const SECItem* stapledOCSPresponse) = 0;
+
 protected:
   TrustDomain() { }
 
 private:
   TrustDomain(const TrustDomain&) /* = delete */;
   void operator=(const TrustDomain&) /* = delete */;
 };
 
--- a/security/insanity/lib/pkixbuild.cpp
+++ b/security/insanity/lib/pkixbuild.cpp
@@ -91,27 +91,29 @@ BackCert::Init()
 }
 
 static Result BuildForward(TrustDomain& trustDomain,
                            BackCert& subject,
                            PRTime time,
                            EndEntityOrCA endEntityOrCA,
                            KeyUsages requiredKeyUsagesIfPresent,
                            SECOidTag requiredEKUIfPresent,
+                           /*optional*/ const SECItem* stapledOCSPResponse,
                            unsigned int subCACount,
                            /*out*/ ScopedCERTCertList& results);
 
 // The code that executes in the inner loop of BuildForward
 static Result
 BuildForwardInner(TrustDomain& trustDomain,
                   BackCert& subject,
                   PRTime time,
                   EndEntityOrCA endEntityOrCA,
                   SECOidTag requiredEKUIfPresent,
                   CERTCertificate* potentialIssuerCertToDup,
+                  /*optional*/ const SECItem* stapledOCSPResponse,
                   unsigned int subCACount,
                   ScopedCERTCertList& results)
 {
   PORT_Assert(potentialIssuerCertToDup);
 
   BackCert potentialIssuer(potentialIssuerCertToDup, &subject,
                            BackCert::ExcludeCN);
   Result rv = potentialIssuer.Init();
@@ -131,88 +133,86 @@ BuildForwardInner(TrustDomain& trustDoma
     if (SECITEM_ItemsAreEqual(&potentialIssuer.GetNSSCert()->derPublicKey,
                               &prev->GetNSSCert()->derPublicKey) &&
         SECITEM_ItemsAreEqual(&potentialIssuer.GetNSSCert()->derSubject,
                               &prev->GetNSSCert()->derSubject)) {
       return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER); // XXX: error code
     }
   }
 
-  rv = CheckTimes(potentialIssuer.GetNSSCert(), time);
-  if (rv != Success) {
-    return rv;
-  }
-
   rv = CheckNameConstraints(potentialIssuer);
   if (rv != Success) {
     return rv;
   }
 
   unsigned int newSubCACount = subCACount;
   if (endEntityOrCA == MustBeCA) {
     newSubCACount = subCACount + 1;
   } else {
     PR_ASSERT(newSubCACount == 0);
   }
-
   rv = BuildForward(trustDomain, potentialIssuer, time, MustBeCA,
                     KU_KEY_CERT_SIGN, requiredEKUIfPresent,
-                    newSubCACount, results);
+                    nullptr, newSubCACount, results);
   if (rv != Success) {
     return rv;
   }
 
   if (trustDomain.VerifySignedData(&subject.GetNSSCert()->signatureWrap,
                                    potentialIssuer.GetNSSCert()) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
 
   return Success;
 }
 
-// Caller must check for expiration before calling this function
+// Recursively build the path from the given subject certificate to the root.
+//
+// Be very careful about changing the order of checks. The order is significant
+// because it affects which error we return when a certificate or certificate
+// chain has multiple problems. See the error ranking documentation in
+// insanity/pkix.h.
 static Result
 BuildForward(TrustDomain& trustDomain,
              BackCert& subject,
              PRTime time,
              EndEntityOrCA endEntityOrCA,
              KeyUsages requiredKeyUsagesIfPresent,
              SECOidTag requiredEKUIfPresent,
+             /*optional*/ const SECItem* stapledOCSPResponse,
              unsigned int subCACount,
              /*out*/ ScopedCERTCertList& results)
 {
   // Avoid stack overflows and poor performance by limiting cert length.
   // XXX: 6 is not enough for chains.sh anypolicywithlevel.cfg tests
   static const size_t MAX_DEPTH = 8;
   if (subCACount >= MAX_DEPTH - 1) {
     return RecoverableError;
   }
 
+  Result rv;
+
   TrustDomain::TrustLevel trustLevel;
-  Result rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA,
-                                                    subject.GetNSSCert(),
-                                                    &trustLevel));
+  bool expiredEndEntity = false;
+  rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
+                                        endEntityOrCA,
+                                        requiredKeyUsagesIfPresent,
+                                        requiredEKUIfPresent, subCACount,
+                                        &trustLevel);
   if (rv != Success) {
-    return rv;
-  }
-  if (trustLevel == TrustDomain::ActivelyDistrusted) {
-    return Fail(RecoverableError, SEC_ERROR_UNTRUSTED_CERT);
-  }
-  if (trustLevel != TrustDomain::TrustAnchor &&
-      trustLevel != TrustDomain::InheritsTrust) {
-    // The TrustDomain returned a trust level that we weren't expecting.
-    return Fail(FatalError, PR_INVALID_STATE_ERROR);
-  }
-
-  rv = CheckExtensions(subject, endEntityOrCA,
-                       trustLevel == TrustDomain::TrustAnchor,
-                       requiredKeyUsagesIfPresent, requiredEKUIfPresent,
-                       subCACount);
-  if (rv != Success) {
-    return rv;
+    // CheckIssuerIndependentProperties checks for expiration last, so if
+    // it returned SEC_ERROR_EXPIRED_CERTIFICATE we know that is the only
+    // problem with the cert found so far. Keep going to see if we can build
+    // a path; if not, it's better to return the path building failure.
+    expiredEndEntity = endEntityOrCA == MustBeEndEntity &&
+                       trustLevel != TrustDomain::TrustAnchor &&
+                       PR_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE;
+    if (!expiredEndEntity) {
+      return rv;
+    }
   }
 
   if (trustLevel == TrustDomain::TrustAnchor) {
     // End of the recursion. Create the result list and add the trust anchor to
     // it.
     results = CERT_NewCertList();
     if (!results) {
       return FatalError;
@@ -228,40 +228,82 @@ BuildForward(TrustDomain& trustDomain,
                                        candidates) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
   PORT_Assert(candidates.get());
   if (!candidates) {
     return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER);
   }
 
+  PRErrorCode errorToReturn = 0;
+
   for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
        !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
     rv = BuildForwardInner(trustDomain, subject, time, endEntityOrCA,
                            requiredEKUIfPresent,
-                           n->cert, subCACount, results);
+                           n->cert, stapledOCSPResponse, subCACount,
+                           results);
     if (rv == Success) {
+      if (expiredEndEntity) {
+        // We deferred returning this error to see if we should return
+        // "unknown issuer" instead. Since we found a valid issuer, it's
+        // time to return "expired."
+        PR_SetError(SEC_ERROR_EXPIRED_CERTIFICATE, 0);
+        return RecoverableError;
+      }
+
+      SECStatus srv = trustDomain.CheckRevocation(endEntityOrCA,
+                                                  subject.GetNSSCert(),
+                                                  n->cert, time,
+                                                  stapledOCSPResponse);
+      if (srv != SECSuccess) {
+        return MapSECStatus(SECFailure);
+      }
+
       // We found a trusted issuer. At this point, we know the cert is valid
       return subject.PrependNSSCertToList(results.get());
     }
     if (rv != RecoverableError) {
       return rv;
     }
+
+    PRErrorCode currentError = PR_GetError();
+    switch (currentError) {
+      case 0:
+        PR_NOT_REACHED("Error code not set!");
+        PR_SetError(PR_INVALID_STATE_ERROR, 0);
+        return FatalError;
+      case SEC_ERROR_UNTRUSTED_CERT:
+        currentError = SEC_ERROR_UNTRUSTED_ISSUER;
+        break;
+      default:
+        break;
+    }
+    if (errorToReturn == 0) {
+      errorToReturn = currentError;
+    } else if (errorToReturn != currentError) {
+      errorToReturn = SEC_ERROR_UNKNOWN_ISSUER;
+    }
   }
 
-  return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER);
+  if (errorToReturn == 0) {
+    errorToReturn = SEC_ERROR_UNKNOWN_ISSUER;
+  }
+
+  return Fail(RecoverableError, errorToReturn);
 }
 
 SECStatus
 BuildCertChain(TrustDomain& trustDomain,
                CERTCertificate* certToDup,
                PRTime time,
                EndEntityOrCA endEntityOrCA,
                /*optional*/ KeyUsages requiredKeyUsagesIfPresent,
                /*optional*/ SECOidTag requiredEKUIfPresent,
+               /*optional*/ const SECItem* stapledOCSPResponse,
                /*out*/ ScopedCERTCertList& results)
 {
   PORT_Assert(certToDup);
 
   if (!certToDup) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return SECFailure;
   }
@@ -280,29 +322,22 @@ BuildCertChain(TrustDomain& trustDomain,
   BackCert cert(certToDup, nullptr, cnOptions);
   Result rv = cert.Init();
   if (rv != Success) {
     return SECFailure;
   }
 
   rv = BuildForward(trustDomain, cert, time, endEntityOrCA,
                     requiredKeyUsagesIfPresent, requiredEKUIfPresent,
-                    0, results);
+                    stapledOCSPResponse, 0, results);
   if (rv != Success) {
     results = nullptr;
     return SECFailure;
   }
 
-  // Build the cert chain even if the cert is expired, because we would
-  // rather report the untrusted issuer error than the expired error.
-  if (CheckTimes(cert.GetNSSCert(), time) != Success) {
-    PR_SetError(SEC_ERROR_EXPIRED_CERTIFICATE, 0);
-    return SECFailure;
-  }
-
   return SECSuccess;
 }
 
 PLArenaPool*
 BackCert::GetArena()
 {
   if (!arena) {
     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
--- a/security/insanity/lib/pkixcheck.cpp
+++ b/security/insanity/lib/pkixcheck.cpp
@@ -226,19 +226,23 @@ CheckNameConstraints(BackCert& cert)
     const CERTGeneralName* names = nullptr;
     Result rv = prev->GetConstrainedNames(&names);
     if (rv != Success) {
       return rv;
     }
     PORT_Assert(names);
     CERTGeneralName* currentName = const_cast<CERTGeneralName*>(names);
     do {
-      rv = MapSECStatus(CERT_CheckNameSpace(arena, constraints, currentName));
-      if (rv != Success) {
-        return rv;
+      if (CERT_CheckNameSpace(arena, constraints, currentName) != SECSuccess) {
+        // XXX: It seems like CERT_CheckNameSpace doesn't always call
+        // PR_SetError when it fails. We set the error code here, though this
+        // may be papering over some fatal errors. NSS's
+        // cert_VerifyCertChainOld does something similar.
+        PR_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE, 0);
+        return RecoverableError;
       }
       currentName = CERT_GetNextGeneralName(currentName);
     } while (currentName != names);
   }
 
   return Success;
 }
 
@@ -313,36 +317,60 @@ CheckExtendedKeyUsage(EndEntityOrCA endE
     // we can't require the EKU be explicitly included for CA certificates.
     PR_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE, 0);
     return RecoverableError;
   }
 
   return Success;
 }
 
-// Checks extensions that apply to both EE and intermediate certs,
-// except for AIA, CRL, and AKI/SKI, which are handled elsewhere.
 Result
-CheckExtensions(BackCert& cert,
-                EndEntityOrCA endEntityOrCA,
-                bool isTrustAnchor,
-                KeyUsages requiredKeyUsagesIfPresent,
-                SECOidTag requiredEKUIfPresent,
-                unsigned int subCACount)
+CheckIssuerIndependentProperties(TrustDomain& trustDomain,
+                                 BackCert& cert,
+                                 PRTime time,
+                                 EndEntityOrCA endEntityOrCA,
+                                 KeyUsages requiredKeyUsagesIfPresent,
+                                 SECOidTag requiredEKUIfPresent,
+                                 unsigned int subCACount,
+                /*optional out*/ TrustDomain::TrustLevel* trustLevelOut)
 {
+  Result rv;
+
+  TrustDomain::TrustLevel trustLevel;
+  rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA,
+                                             cert.GetNSSCert(),
+                                             &trustLevel));
+  if (rv != Success) {
+    return rv;
+  }
+  if (trustLevel == TrustDomain::ActivelyDistrusted) {
+    PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
+    return RecoverableError;
+  }
+  if (trustLevel != TrustDomain::TrustAnchor &&
+      trustLevel != TrustDomain::InheritsTrust) {
+    // The TrustDomain returned a trust level that we weren't expecting.
+    PORT_SetError(PR_INVALID_STATE_ERROR);
+    return FatalError;
+  }
+  if (trustLevelOut) {
+    *trustLevelOut = trustLevel;
+  }
+
+  bool isTrustAnchor = endEntityOrCA == MustBeCA &&
+                       trustLevel == TrustDomain::TrustAnchor;
+
   // 4.2.1.1. Authority Key Identifier dealt with as part of path building
   // 4.2.1.2. Subject Key Identifier dealt with as part of path building
 
   PLArenaPool* arena = cert.GetArena();
   if (!arena) {
     return FatalError;
   }
 
-  Result rv;
-
   // 4.2.1.3. Key Usage
 
   rv = CheckKeyUsage(endEntityOrCA, isTrustAnchor, cert.encodedKeyUsage,
                      requiredKeyUsagesIfPresent, arena);
   if (rv != Success) {
     return rv;
   }
 
@@ -368,12 +396,19 @@ CheckExtensions(BackCert& cert,
                              requiredEKUIfPresent);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.13. CRL Distribution Points will be dealt with elsewhere
   // 4.2.1.14. Inhibit anyPolicy
 
+  // IMPORTANT: This check must come after the other checks in order for error
+  // ranking to work correctly.
+  rv = CheckTimes(cert.GetNSSCert(), time);
+  if (rv != Success) {
+    return rv;
+  }
+
   return Success;
 }
 
 } } // namespace insanity::pkix
--- a/security/insanity/lib/pkixcheck.h
+++ b/security/insanity/lib/pkixcheck.h
@@ -18,22 +18,23 @@
 #ifndef insanity__pkixcheck_h
 #define insanity__pkixcheck_h
 
 #include "pkixutil.h"
 #include "certt.h"
 
 namespace insanity { namespace pkix {
 
-Result CheckTimes(const CERTCertificate* cert, PRTime time);
-
-Result CheckExtensions(BackCert& certExt,
-                       EndEntityOrCA endEntityOrCA,
-                       bool isTrustAnchor,
-                       KeyUsages requiredKeyUsagesIfPresent,
-                       SECOidTag requiredEKUIfPresent,
-                       unsigned int depth);
+Result CheckIssuerIndependentProperties(
+          TrustDomain& trustDomain,
+          BackCert& cert,
+          PRTime time,
+          EndEntityOrCA endEntityOrCA,
+          KeyUsages requiredKeyUsagesIfPresent,
+          SECOidTag requiredEKUIfPresent,
+          unsigned int subCACount,
+          /*optional out*/ TrustDomain::TrustLevel* trustLevel = nullptr);
 
 Result CheckNameConstraints(BackCert& cert);
 
 } } // namespace insanity::pkix
 
 #endif // insanity__pkixcheck_h
--- a/security/insanity/lib/pkixder.h
+++ b/security/insanity/lib/pkixder.h
@@ -213,16 +213,23 @@ ExpectTagAndLength(Input& input, uint8_t
 
   return Success;
 }
 
 Result
 ExpectTagAndGetLength(Input& input, uint8_t expectedTag, uint16_t& length);
 
 inline Result
+ExpectTagAndIgnoreLength(Input& input, uint8_t expectedTag)
+{
+  uint16_t ignored;
+  return ExpectTagAndGetLength(input, expectedTag, ignored);
+}
+
+inline Result
 End(Input& input)
 {
   if (!input.AtEnd()) {
     return Fail(SEC_ERROR_BAD_DER);
   }
 
   return Success;
 }
new file mode 100644
--- /dev/null
+++ b/security/insanity/lib/pkixocsp.cpp
@@ -0,0 +1,949 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Copyright 2013 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits>
+
+#include "insanity/bind.h"
+#include "insanity/pkix.h"
+#include "pkixcheck.h"
+#include "pkixder.h"
+
+#include "hasht.h"
+#include "pk11pub.h"
+#include "secder.h"
+
+#ifdef _MSC_VER
+// C4480: nonstandard extension used: specifying underlying type for enum
+#define ENUM_CLASS  __pragma(warning(disable: 4480)) enum
+#else
+#define ENUM_CLASS enum class
+#endif
+
+// TODO: use typed/qualified typedefs everywhere?
+// TODO: When should we return SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE?
+
+namespace insanity { namespace pkix {
+
+static const PRTime ONE_DAY
+  = INT64_C(24) * INT64_C(60) * INT64_C(60) * PR_USEC_PER_SEC;
+static const PRTime SLOP = ONE_DAY;
+
+// These values correspond to the tag values in the ASN.1 CertStatus
+ENUM_CLASS CertStatus : uint8_t {
+  Good = der::CONTEXT_SPECIFIC | 0,
+  Revoked = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
+  Unknown = der::CONTEXT_SPECIFIC | 2
+};
+
+class Context
+{
+public:
+  Context(TrustDomain& trustDomain,
+          const CERTCertificate& cert,
+          CERTCertificate& issuerCert,
+          PRTime time)
+    : trustDomain(trustDomain)
+    , cert(cert)
+    , issuerCert(issuerCert)
+    , time(time)
+    , certStatus(CertStatus::Unknown)
+  {
+  }
+
+  TrustDomain& trustDomain;
+  const CERTCertificate& cert;
+  CERTCertificate& issuerCert;
+  const PRTime time;
+  CertStatus certStatus;
+
+private:
+  Context(const Context&); // delete
+  void operator=(const Context&); // delete
+};
+
+// Verify that potentialSigner is a valid delegated OCSP response signing cert
+// according to RFC 6960 section 4.2.2.2.
+static Result
+CheckOCSPResponseSignerCert(TrustDomain& trustDomain,
+                            CERTCertificate& potentialSigner,
+                            const CERTCertificate& issuerCert, PRTime time)
+{
+  Result rv;
+
+  BackCert cert(&potentialSigner, nullptr, BackCert::ExcludeCN);
+  rv = cert.Init();
+  if (rv != Success) {
+    return rv;
+  }
+
+  // We don't need to do a complete verification of the signer (i.e. we don't
+  // have to call BuildCertChain to verify the entire chain) because we
+  // already know that the issuerCert is valid, since revocation checking is
+  // done from the root to the parent after we've built a complete chain that
+  // we know is otherwise valid. Rather, we just need to do a one-step
+  // validation from potentialSigner to issuerCert.
+  //
+  // It seems reasonable to require the KU_DIGITAL_SIGNATURE key usage on the
+  // OCSP responder certificate if the OCSP responder certificate has a
+  // key usage extension. However, according to bug 240456, some OCSP responder
+  // certificates may have only the nonRepudiation bit set. Also, the OCSP
+  // specification (RFC 6960) does not mandate any particular key usage to be
+  // asserted for OCSP responde signers. Oddly, the CABForum Baseline
+  // Requirements v.1.1.5 do say "If the Root CA Private Key is used for
+  // signing OCSP responses, then the digitalSignature bit MUST be set."
+  //
+  // Note that CheckIssuerIndependentProperties processes
+  // SEC_OID_OCSP_RESPONDER in the way that the OCSP specification requires us
+  // to--in particular, it doesn't allow SEC_OID_OCSP_RESPONDER to be implied
+  // by a missing EKU extension, unlike other EKUs.
+  //
+  // TODO(bug 926261): If we're validating for a policy then the policy OID we
+  // are validating for should be passed to CheckIssuerIndependentProperties.
+  rv = CheckIssuerIndependentProperties(trustDomain, cert, time,
+                                        MustBeEndEntity, 0,
+                                        SEC_OID_OCSP_RESPONDER, 0);
+  if (rv != Success) {
+    return rv;
+  }
+
+  // It is possible that there exists a certificate with the same key as the
+  // issuer but with a different name, so we need to compare names
+  // TODO: needs test
+  if (!SECITEM_ItemsAreEqual(&cert.GetNSSCert()->derIssuer,
+                             &issuerCert.derSubject) &&
+      CERT_CompareName(&cert.GetNSSCert()->issuer,
+                       &issuerCert.subject) != SECEqual) {
+    return Fail(RecoverableError, SEC_ERROR_OCSP_RESPONDER_CERT_INVALID);
+  }
+
+  // TODO(bug 926260): check name constraints
+
+  if (trustDomain.VerifySignedData(&potentialSigner.signatureWrap,
+                                   &issuerCert) != SECSuccess) {
+    return MapSECStatus(SECFailure);
+  }
+
+  // TODO: check for revocation of the OCSP responder certificate unless no-check
+  // or the caller forcing no-check. To properly support the no-check policy, we'd
+  // need to enforce policy constraints from the issuerChain.
+
+  return Success;
+}
+
+//typedef enum {
+//    ocspResponderID_byName = 1,
+//    ocspResponderID_byKey = 2
+//} ResponderIDType;
+
+ENUM_CLASS ResponderIDType : uint8_t
+{
+  byName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
+  byKey = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 2
+};
+
+static inline der::Result OCSPResponse(der::Input&, Context&);
+static inline der::Result ResponseBytes(der::Input&, Context&);
+static inline der::Result BasicResponse(der::Input&, Context&);
+static inline der::Result ResponseData(
+                              der::Input& tbsResponseData, Context& context,
+                              const CERTSignedData& signedResponseData,
+                              /*const*/ SECItem* certs, size_t numCerts);
+static inline der::Result SingleResponse(der::Input& input,
+                                          Context& context);
+static inline der::Result CheckExtensionsForCriticality(der::Input&);
+static inline der::Result CertID(der::Input& input,
+                                  const Context& context,
+                                  /*out*/ bool& match);
+static der::Result MatchIssuerKey(const SECItem& issuerKeyHash,
+                                   const CERTCertificate& issuer,
+                                   /*out*/ bool& match);
+
+// RFC 6960 section 4.2.2.2: The OCSP responder must either be the issuer of
+// the cert or it must be a delegated OCSP response signing cert directly
+// issued by the issuer. If the OCSP responder is a delegated OCSP response
+// signer, then its certificate is (probably) embedded within the OCSP
+// response and we'll need to verify that it is a valid certificate that chains
+// *directly* to issuerCert.
+static CERTCertificate*
+GetOCSPSignerCertificate(TrustDomain& trustDomain,
+                         ResponderIDType responderIDType,
+                         const SECItem& responderIDItem,
+                         const SECItem* certs, size_t numCerts,
+                         CERTCertificate& issuerCert, PRTime time)
+{
+  bool isIssuer = true;
+  size_t i = 0;
+  for (;;) {
+    ScopedCERTCertificate potentialSigner;
+    if (isIssuer) {
+      potentialSigner = CERT_DupCertificate(&issuerCert);
+    } else if (i < numCerts) {
+      potentialSigner = CERT_NewTempCertificate(
+                          CERT_GetDefaultCertDB(),
+                          /*TODO*/const_cast<SECItem*>(&certs[i]), nullptr,
+                          false, false);
+      if (!potentialSigner) {
+        return nullptr;
+      }
+      ++i;
+    } else {
+      PR_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, 0);
+      return nullptr;
+    }
+
+    bool match;
+    switch (responderIDType) {
+      case ResponderIDType::byName:
+        // The CA is very likely to have encoded the name in the OCSP response
+        // exactly the same as the name is encoded in the signing certificate.
+        // Consequently, most of the time we will avoid parsing the name
+        // completely. We're assuming here that the signer's subject name is
+        // correctly formatted.
+        // TODO: need test for exact name
+        // TODO: need test for non-exact name match
+        match = SECITEM_ItemsAreEqual(&responderIDItem,
+                                      &potentialSigner->derSubject);
+        if (!match) {
+          ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+          if (!arena) {
+            return nullptr;
+          }
+          CERTName name;
+          if (SEC_QuickDERDecodeItem(arena.get(), &name,
+                                     SEC_ASN1_GET(CERT_NameTemplate),
+                                     &responderIDItem) != SECSuccess) {
+            return nullptr;
+          }
+          match = CERT_CompareName(&name, &potentialSigner->subject) == SECEqual;
+        }
+        break;
+
+      case ResponderIDType::byKey:
+      {
+        der::Input responderID;
+        if (responderID.Init(responderIDItem.data, responderIDItem.len)
+              != der::Success) {
+          return nullptr;
+        }
+        SECItem issuerKeyHash;
+        if (der::Skip(responderID, der::OCTET_STRING, issuerKeyHash) != der::Success) {
+          return nullptr;
+        }
+        if (MatchIssuerKey(issuerKeyHash, *potentialSigner.get(), match)
+              != der::Success) {
+          return nullptr;
+        }
+        break;
+      }
+
+      default:
+        PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
+        return nullptr;
+    }
+
+    if (match && !isIssuer) {
+      Result rv = CheckOCSPResponseSignerCert(trustDomain,
+                                              *potentialSigner.get(),
+                                              issuerCert, time);
+      if (rv == RecoverableError) {
+        match = false;
+      } else if (rv != Success) {
+        return nullptr;
+      }
+    }
+
+    if (match) {
+      return potentialSigner.release();
+    }
+
+    isIssuer = false;
+  }
+}
+
+static SECStatus
+VerifySignature(Context& context, ResponderIDType responderIDType,
+                const SECItem& responderID, const SECItem* certs,
+                size_t numCerts, const CERTSignedData& signedResponseData)
+{
+  ScopedCERTCertificate signer(
+    GetOCSPSignerCertificate(context.trustDomain, responderIDType, responderID,
+                             certs, numCerts, context.issuerCert,
+                             context.time));
+  if (!signer) {
+    return SECFailure;
+  }
+
+  if (context.trustDomain.VerifySignedData(&signedResponseData, signer.get())
+        != SECSuccess) {
+    if (PR_GetError() == SEC_ERROR_BAD_SIGNATURE) {
+      PR_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE, 0);
+    }
+    return SECFailure;
+  }
+
+  return SECSuccess;
+}
+
+SECStatus
+VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
+                          const CERTCertificate* cert,
+                          CERTCertificate* issuerCert, PRTime time,
+                          const SECItem* encodedResponse)
+{
+  PR_ASSERT(cert);
+  PR_ASSERT(issuerCert);
+  // TODO: PR_Assert(pinArg)
+  PR_ASSERT(encodedResponse);
+  if (!cert || !issuerCert || !encodedResponse || !encodedResponse->data) {
+    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+    return SECFailure;
+  }
+
+  der::Input input;
+  if (input.Init(encodedResponse->data, encodedResponse->len) != der::Success) {
+    return SECFailure;
+  }
+
+  Context context(trustDomain, *cert, *issuerCert, time);
+
+  if (der::Nested(input, der::SEQUENCE,
+                  bind(OCSPResponse, _1, ref(context))) != der::Success) {
+    return SECFailure;
+  }
+
+  if (der::End(input) != der::Success) {
+    return SECFailure;
+  }
+
+  switch (context.certStatus) {
+    case CertStatus::Good:
+      return SECSuccess;
+    case CertStatus::Revoked:
+      PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
+      return SECFailure;
+    case CertStatus::Unknown:
+      PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
+      return SECFailure;
+  }
+
+  PR_NOT_REACHED("unknown CertStatus");
+  PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
+  return SECFailure;
+}
+
+// OCSPResponse ::= SEQUENCE {
+//       responseStatus         OCSPResponseStatus,
+//       responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+//
+static inline der::Result
+OCSPResponse(der::Input& input, Context& context)
+{
+  // OCSPResponseStatus ::= ENUMERATED {
+  //     successful            (0),  -- Response has valid confirmations
+  //     malformedRequest      (1),  -- Illegal confirmation request
+  //     internalError         (2),  -- Internal error in issuer
+  //     tryLater              (3),  -- Try again later
+  //                                 -- (4) is not used
+  //     sigRequired           (5),  -- Must sign the request
+  //     unauthorized          (6)   -- Request unauthorized
+  // }
+  uint8_t responseStatus;
+
+  if (der::Enumerated(input, responseStatus) != der::Success) {
+    return der::Failure;
+  }
+  switch (responseStatus) {
+    case 0: break; // successful
+    case 1: return der::Fail(SEC_ERROR_OCSP_MALFORMED_REQUEST);
+    case 2: return der::Fail(SEC_ERROR_OCSP_SERVER_ERROR);
+    case 3: return der::Fail(SEC_ERROR_OCSP_TRY_SERVER_LATER);
+    case 5: return der::Fail(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG);
+    case 6: return der::Fail(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST);
+    default: return der::Fail(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS);
+  }
+
+  return der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
+                     der::SEQUENCE, bind(ResponseBytes, _1, ref(context)));
+}
+
+// ResponseBytes ::=       SEQUENCE {
+//     responseType   OBJECT IDENTIFIER,
+//     response       OCTET STRING }
+static inline der::Result
+ResponseBytes(der::Input& input, Context& context)
+{
+  static const uint8_t id_pkix_ocsp_basic[] = {
+    0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
+  };
+
+  if (der::OID(input, id_pkix_ocsp_basic) != der::Success) {
+    return der::Failure;
+  }
+
+  return der::Nested(input, der::OCTET_STRING, der::SEQUENCE,
+                     bind(BasicResponse, _1, ref(context)));
+}
+
+// BasicOCSPResponse       ::= SEQUENCE {
+//    tbsResponseData      ResponseData,
+//    signatureAlgorithm   AlgorithmIdentifier,
+//    signature            BIT STRING,
+//    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+der::Result
+BasicResponse(der::Input& input, Context& context)
+{
+  der::Input::Mark mark(input.GetMark());
+
+  uint16_t length;
+  if (der::ExpectTagAndGetLength(input, der::SEQUENCE, length)
+        != der::Success) {
+    return der::Failure;
+  }
+
+  // The signature covers the entire DER encoding of tbsResponseData, including
+  // the beginning tag and length. However, when we're parsing tbsResponseData,
+  // we want to strip off the tag and length because we don't need it after
+  // we've confirmed it's there and figured out what length it is.
+
+  der::Input tbsResponseData;
+
+  if (input.Skip(length, tbsResponseData) != der::Success) {
+    return der::Failure;
+  }
+
+  CERTSignedData signedData;
+
+  input.GetSECItem(siBuffer, mark, signedData.data);
+
+  if (der::Nested(input, der::SEQUENCE,
+                  bind(der::AlgorithmIdentifier, _1,
+                       ref(signedData.signatureAlgorithm))) != der::Success) {
+    return der::Failure;
+  }
+
+  if (der::Skip(input, der::BIT_STRING, signedData.signature) != der::Success) {
+    return der::Failure;
+  }
+  if (signedData.signature.len == 0) {
+    return der::Fail(SEC_ERROR_OCSP_BAD_SIGNATURE);
+  }
+  unsigned int unusedBitsAtEnd = signedData.signature.data[0];
+  // XXX: Really the constraint should be that unusedBitsAtEnd must be less
+  // than 7. But, we suspect there are no valid OCSP response signatures with
+  // non-zero unused bits. It seems like NSS assumes this in various places, so
+  // we enforce it. If we find compatibility issues, we'll know we're wrong.
+  if (unusedBitsAtEnd != 0) {
+    return der::Fail(SEC_ERROR_OCSP_BAD_SIGNATURE);
+  }
+  ++signedData.signature.data;
+  --signedData.signature.len;
+  signedData.signature.len = (signedData.signature.len << 3); // Bytes to bits
+
+  // Parse certificates, if any
+
+  SECItem certs[8];
+  size_t numCerts = 0;
+
+  if (!input.AtEnd()) {
+    // We ignore the lengths of the wrappers because we'll detect bad lengths
+    // during parsing--too short and we'll run out of input for parsing a cert,
+    // and too long and we'll have leftover data that won't parse as a cert.
+
+    // [0] wrapper
+    if (der::ExpectTagAndIgnoreLength(
+          input, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0)
+        != der::Success) {
+      return der::Failure;
+    }
+
+    // SEQUENCE wrapper
+    if (der::ExpectTagAndIgnoreLength(input, der::SEQUENCE) != der::Success) {
+      return der::Failure;
+    }
+
+    // sequence of certificates
+    while (!input.AtEnd()) {
+      if (numCerts == PR_ARRAY_SIZE(certs)) {
+        return der::Fail(SEC_ERROR_BAD_DER);
+      }
+
+      // Unwrap the SEQUENCE that contains the certificate, which is itself a
+      // SEQUENCE.
+      der::Input::Mark mark(input.GetMark());
+      if (der::Skip(input, der::SEQUENCE) != der::Success) {
+        return der::Failure;
+      }
+
+      input.GetSECItem(siBuffer, mark, certs[numCerts]);
+      ++numCerts;
+    }
+  }
+
+  return ResponseData(tbsResponseData, context, signedData, certs, numCerts);
+}
+
+// ResponseData ::= SEQUENCE {
+//    version             [0] EXPLICIT Version DEFAULT v1,
+//    responderID             ResponderID,
+//    producedAt              GeneralizedTime,
+//    responses               SEQUENCE OF SingleResponse,
+//    responseExtensions  [1] EXPLICIT Extensions OPTIONAL }
+static inline der::Result
+ResponseData(der::Input& input, Context& context,
+             const CERTSignedData& signedResponseData,
+             /*const*/ SECItem* certs, size_t numCerts)
+{
+  uint8_t version;
+  if (der::OptionalVersion(input, version) != der::Success) {
+    return der::Failure;
+  }
+  if (version != der::v1) {
+    // TODO: more specific error code for bad version?
+    return der::Fail(SEC_ERROR_BAD_DER);
+  }
+
+  // ResponderID ::= CHOICE {
+  //    byName              [1] Name,
+  //    byKey               [2] KeyHash }
+  SECItem responderID;
+  uint16_t responderIDLength;
+  ResponderIDType responderIDType
+    = input.Peek(static_cast<uint8_t>(ResponderIDType::byName))
+    ? ResponderIDType::byName
+    : ResponderIDType::byKey;
+  if (ExpectTagAndGetLength(input, static_cast<uint8_t>(responderIDType),
+                            responderIDLength) != der::Success) {
+    return der::Failure;
+  }
+  // TODO: responderID probably needs to have another level of ASN1 tag/length
+  // checked and stripped.
+  if (input.Skip(responderIDLength, responderID) != der::Success) {
+    return der::Failure;
+  }
+
+  // This is the soonest we can verify the signature. We verify the signature
+  // right away to follow the principal of minimizing the processing of data
+  // before verifying its signature.
+  if (VerifySignature(context, responderIDType, responderID, certs, numCerts,
+                      signedResponseData) != SECSuccess) {
+    return der::Failure;
+  }
+
+  // TODO: Do we even need to parse this? Should we just skip it?
+  PRTime producedAt;
+  if (der::GeneralizedTime(input, producedAt) != der::Success) {
+    return der::Failure;
+  }
+
+  // We don't accept an empty sequence of responses. In practice, a legit OCSP
+  // responder will never return an empty response, and handling the case of an
+  // empty response makes things unnecessarily complicated.
+  if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE,
+                    der::MustNotBeEmpty,
+                    bind(SingleResponse, _1, ref(context))) != der::Success) {
+    return der::Failure;
+  }
+
+  if (!input.AtEnd()) {
+    if (CheckExtensionsForCriticality(input) != der::Success) {
+      return der::Failure;
+    }
+  }
+
+  return der::Success;
+}
+
+// SingleResponse ::= SEQUENCE {
+//    certID                       CertID,
+//    certStatus                   CertStatus,
+//    thisUpdate                   GeneralizedTime,
+//    nextUpdate           [0]     EXPLICIT GeneralizedTime OPTIONAL,
+//    singleExtensions     [1]     EXPLICIT Extensions{{re-ocsp-crl |
+//                                              re-ocsp-archive-cutoff |
+//                                              CrlEntryExtensions, ...}
+//                                              } OPTIONAL }
+static inline der::Result
+SingleResponse(der::Input& input, Context& context)
+{
+  bool match = false;
+  if (der::Nested(input, der::SEQUENCE,
+                  bind(CertID, _1, cref(context), ref(match)))
+        != der::Success) {
+    return der::Failure;
+  }
+
+  if (!match) {
+    return der::Success;
+  }
+
+  // CertStatus ::= CHOICE {
+  //     good        [0]     IMPLICIT NULL,
+  //     revoked     [1]     IMPLICIT RevokedInfo,
+  //     unknown     [2]     IMPLICIT UnknownInfo }
+  //
+  // In the event of multiple SingleResponses for a cert that have conflicting
+  // statuses, we use the following precedence rules:
+  //
+  // * revoked overrides good and unknown
+  // * good overrides unknown
+  if (input.Peek(static_cast<uint8_t>(CertStatus::Good))) {
+    if (ExpectTagAndLength(input, static_cast<uint8_t>(CertStatus::Good), 0)
+          != der::Success) {
+      return der::Failure;
+    }
+    if (context.certStatus != CertStatus::Revoked) {
+      context.certStatus = CertStatus::Good;
+    }
+  } else if (input.Peek(static_cast<uint8_t>(CertStatus::Revoked))) {
+    // We don't need any info from the RevokedInfo structure, so we don't even
+    // parse it. TODO: We should mention issues like this in the explanation of
+    // why we treat invalid OCSP responses equivalently to revoked for OCSP
+    // stapling.
+    if (der::Skip(input, static_cast<uint8_t>(CertStatus::Revoked))
+          != der::Success) {
+      return der::Failure;
+    }
+    context.certStatus = CertStatus::Revoked;
+  } else if (ExpectTagAndLength(input,
+                                static_cast<uint8_t>(CertStatus::Unknown),
+                                0) != der::Success) {
+    return der::Failure;
+  }
+
+  // http://tools.ietf.org/html/rfc6960#section-3.2
+  // 5. The time at which the status being indicated is known to be
+  //    correct (thisUpdate) is sufficiently recent;
+  // 6. When available, the time at or before which newer information will
+  //    be available about the status of the certificate (nextUpdate) is
+  //    greater than the current time.
+
+  // We won't accept any OCSP responses that are more than 10 days old, even if
+  // the nextUpdate time is further in the future.
+  static const PRTime OLDEST_ACCEPTABLE = INT64_C(10) * ONE_DAY;
+
+  PRTime thisUpdate;
+  if (der::GeneralizedTime(input, thisUpdate) != der::Success) {
+    return der::Failure;
+  }
+
+  if (thisUpdate > context.time + SLOP) {
+    return der::Fail(SEC_ERROR_OCSP_FUTURE_RESPONSE);
+  }
+
+  PRTime notAfter;
+  static const uint8_t NEXT_UPDATE_TAG =
+    der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0;
+  if (input.Peek(NEXT_UPDATE_TAG)) {
+    PRTime nextUpdate;
+    if (der::Nested(input, NEXT_UPDATE_TAG,
+                    bind(der::GeneralizedTime, _1, ref(nextUpdate)))
+          != der::Success) {
+      return der::Failure;
+    }
+
+    if (nextUpdate < thisUpdate) {
+      return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+    }
+    if (nextUpdate - thisUpdate <= OLDEST_ACCEPTABLE) {
+      notAfter = nextUpdate;
+    } else {
+      notAfter = thisUpdate + OLDEST_ACCEPTABLE;
+    }
+  } else {
+    // NSS requires all OCSP responses without a nextUpdate to be recent.
+    // Match that stricter behavior.
+    notAfter = thisUpdate + ONE_DAY;
+  }
+
+  if (context.time < SLOP) { // prevent underflow
+    return der::Fail(SEC_ERROR_INVALID_ARGS);
+  }
+  if (context.time - SLOP > notAfter) {
+    return der::Fail(SEC_ERROR_OCSP_OLD_RESPONSE);
+  }
+
+
+  if (!input.AtEnd()) {
+    if (CheckExtensionsForCriticality(input) != der::Success) {
+      return der::Failure;
+    }
+  }
+
+  return der::Success;
+}
+
+// CertID          ::=     SEQUENCE {
+//        hashAlgorithm       AlgorithmIdentifier,
+//        issuerNameHash      OCTET STRING, -- Hash of issuer's DN
+//        issuerKeyHash       OCTET STRING, -- Hash of issuer's public key
+//        serialNumber        CertificateSerialNumber }
+static inline der::Result
+CertID(der::Input& input, const Context& context, /*out*/ bool& match)
+{
+  match = false;
+
+  SECAlgorithmID hashAlgorithm;
+  if (der::Nested(input, der::SEQUENCE,
+                  bind(der::AlgorithmIdentifier, _1, ref(hashAlgorithm)))
+         != der::Success) {
+    return der::Failure;
+  }
+
+  SECItem issuerNameHash;
+  if (der::Skip(input, der::OCTET_STRING, issuerNameHash) != der::Success) {
+    return der::Failure;
+  }
+
+  SECItem issuerKeyHash;
+  if (der::Skip(input, der::OCTET_STRING, issuerKeyHash) != der::Success) {
+    return der::Failure;
+  }
+
+  SECItem serialNumber;
+  if (der::CertificateSerialNumber(input, serialNumber) != der::Success) {
+    return der::Failure;
+  }
+
+  const CERTCertificate& cert = context.cert;
+  const CERTCertificate& issuerCert = context.issuerCert;
+
+  if (!SECITEM_ItemsAreEqual(&serialNumber, &cert.serialNumber)) {
+    return der::Success;
+  }
+
+  // TODO: support SHA-2 hashes.
+
+  SECOidTag hashAlg = SECOID_GetAlgorithmTag(&hashAlgorithm);
+  if (hashAlg != SEC_OID_SHA1) {
+    return der::Success;
+  }
+
+  if (issuerNameHash.len != SHA1_LENGTH) {
+    return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+  }
+
+  // From http://tools.ietf.org/html/rfc6960#section-4.1.1:
+  // "The hash shall be calculated over the DER encoding of the
+  // issuer's name field in the certificate being checked."
+  uint8_t hashBuf[SHA1_LENGTH];
+  if (PK11_HashBuf(SEC_OID_SHA1, hashBuf, cert.derIssuer.data,
+                   cert.derIssuer.len) != SECSuccess) {
+    return der::Failure;
+  }
+  if (memcmp(hashBuf, issuerNameHash.data, issuerNameHash.len)) {
+    return der::Success;
+  }
+
+  return MatchIssuerKey(issuerKeyHash, issuerCert, match);
+}
+
+// From http://tools.ietf.org/html/rfc6960#section-4.1.1:
+// "The hash shall be calculated over the value (excluding tag and length) of
+// the subject public key field in the issuer's certificate."
+static der::Result
+MatchIssuerKey(const SECItem& issuerKeyHash, const CERTCertificate& issuer,
+               /*out*/ bool& match)
+{
+  if (issuerKeyHash.len != SHA1_LENGTH)  {
+    return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+  }
+
+  // TODO(bug 966856): support SHA-2 hashes
+
+  // Copy just the length and data pointer (nothing needs to be freed) of the
+  // subject public key so we can convert the length from bits to bytes, which
+  // is what the digest function expects.
+  SECItem spk = issuer.subjectPublicKeyInfo.subjectPublicKey;
+  DER_ConvertBitString(&spk);
+
+  static uint8_t hashBuf[SHA1_LENGTH];
+  if (PK11_HashBuf(SEC_OID_SHA1, hashBuf, spk.data, spk.len) != SECSuccess) {
+    return der::Failure;
+  }
+
+  match = !memcmp(hashBuf, issuerKeyHash.data, issuerKeyHash.len);
+  return der::Success;
+}
+
+// Extension  ::=  SEQUENCE  {
+//      extnID      OBJECT IDENTIFIER,
+//      critical    BOOLEAN DEFAULT FALSE,
+//      extnValue   OCTET STRING
+//      }
+static der::Result
+CheckExtensionForCriticality(der::Input& input)
+{
+  uint16_t toSkip;
+  if (ExpectTagAndGetLength(input, der::OIDTag, toSkip) != der::Success) {
+    return der::Failure;
+  }
+
+  // TODO: maybe we should check the syntax of the OID value
+  if (input.Skip(toSkip) != der::Success) {
+    return der::Failure;
+  }
+
+  // The only valid explicit encoding of the value is TRUE, so don't even
+  // bother parsing it, since we're going to fail either way.
+  if (input.Peek(der::BOOLEAN)) {
+    return der::Fail(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
+  }
+
+  if (ExpectTagAndGetLength(input, der::OCTET_STRING, toSkip)
+        != der::Success) {
+    return der::Failure;
+  }
+  return input.Skip(toSkip);
+}
+
+static der::Result
+CheckExtensionsForCriticality(der::Input& input)
+{
+  return der::NestedOf(input, der::SEQUENCE | 1, der::SEQUENCE,
+                       der::MustNotBeEmpty, CheckExtensionForCriticality);
+}
+
+//   1. The certificate identified in a received response corresponds to
+//      the certificate that was identified in the corresponding request;
+//   2. The signature on the response is valid;
+//   3. The identity of the signer matches the intended recipient of the
+//      request;
+//   4. The signer is currently authorized to provide a response for the
+//      certificate in question;
+//   5. The time at which the status being indicated is known to be
+//      correct (thisUpdate) is sufficiently recent;
+//   6. When available, the time at or before which newer information will
+//      be available about the status of the certificate (nextUpdate) is
+//      greater than the current time.
+//
+//   Responses whose nextUpdate value is earlier than
+//   the local system time value SHOULD be considered unreliable.
+//   Responses whose thisUpdate time is later than the local system time
+//   SHOULD be considered unreliable.
+//
+//   If nextUpdate is not set, the responder is indicating that newer
+//   revocation information is available all the time.
+//
+// http://tools.ietf.org/html/rfc5019#section-4
+
+SECItem*
+CreateEncodedOCSPRequest(PLArenaPool* arena,
+                         const CERTCertificate* cert,
+                         const CERTCertificate* issuerCert)
+{
+  if (!arena || !cert || !issuerCert) {
+    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+    return nullptr;
+  }
+
+  // We do not add any extensions to the request.
+
+  // RFC 6960 says "An OCSP client MAY wish to specify the kinds of response
+  // types it understands. To do so, it SHOULD use an extension with the OID
+  // id-pkix-ocsp-response." This use of MAY and SHOULD is unclear. MSIE11
+  // on Windows 8.1 does not include any extensions, whereas NSS has always
+  // included the id-pkix-ocsp-response extension. Avoiding the sending the
+  // extension is better for OCSP GET because it makes the request smaller,
+  // and thus more likely to fit within the 255 byte limit for OCSP GET that
+  // is specified in RFC 5019 Section 5.
+
+  // Bug 966856: Add the id-pkix-ocsp-pref-sig-algs extension.
+
+  // Since we don't know whether the OCSP responder supports anything other
+  // than SHA-1, we have no choice but to use SHA-1 for issuerNameHash and
+  // issuerKeyHash.
+  static const uint8_t hashAlgorithm[11] = {
+    0x30, 0x09,                               // SEQUENCE
+    0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, //   OBJECT IDENTIFIER id-sha1
+    0x05, 0x00,                               //   NULL
+  };
+  static const uint8_t hashLen = SHA1_LENGTH;
+
+  static const unsigned int totalLenWithoutSerialNumberData
+    = 2                             // OCSPRequest
+    + 2                             //   tbsRequest
+    + 2                             //     requestList
+    + 2                             //       Request
+    + 2                             //         reqCert (CertID)
+    + PR_ARRAY_SIZE(hashAlgorithm)  //           hashAlgorithm
+    + 2 + hashLen                   //           issuerNameHash
+    + 2 + hashLen                   //           issuerKeyHash
+    + 2;                            //           serialNumber (header)
+
+  // The only way we could have a request this large is if the serialNumber was
+  // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST
+  // NOT use serialNumber values longer than 20 octets." With this restriction,
+  // we allow for some amount of non-conformance with that requirement while
+  // still ensuring we can encode the length values in the ASN.1 TLV structures
+  // in a single byte.
+  if (issuerCert->serialNumber.len > 127u - totalLenWithoutSerialNumberData) {
+    PR_SetError(SEC_ERROR_BAD_DATA, 0);
+    return nullptr;
+  }
+
+  uint8_t totalLen = static_cast<uint8_t>(totalLenWithoutSerialNumberData +
+    cert->serialNumber.len);
+
+  SECItem* encodedRequest = SECITEM_AllocItem(arena, nullptr, totalLen);
+  if (!encodedRequest) {
+    return nullptr;
+  }
+
+  uint8_t* d = encodedRequest->data;
+  *d++ = 0x30; *d++ = totalLen - 2;  // OCSPRequest (SEQUENCE)
+  *d++ = 0x30; *d++ = totalLen - 4;  //   tbsRequest (SEQUENCE)
+  *d++ = 0x30; *d++ = totalLen - 6;  //     requestList (SEQUENCE OF)
+  *d++ = 0x30; *d++ = totalLen - 8;  //       Request (SEQUENCE)
+  *d++ = 0x30; *d++ = totalLen - 10; //         reqCert (CertID SEQUENCE)
+
+  // reqCert.hashAlgorithm
+  for (size_t i = 0; i < PR_ARRAY_SIZE(hashAlgorithm); ++i) {
+    *d++ = hashAlgorithm[i];
+  }
+
+  // reqCert.issuerNameHash (OCTET STRING)
+  *d++ = 0x04;
+  *d++ = hashLen;
+  if (PK11_HashBuf(SEC_OID_SHA1, d, issuerCert->derSubject.data,
+                   issuerCert->derSubject.len) != SECSuccess) {
+    return nullptr;
+  }
+  d += hashLen;
+
+  // reqCert.issuerKeyHash (OCTET STRING)
+  *d++ = 0x04;
+  *d++ = hashLen;
+  SECItem key = issuerCert->subjectPublicKeyInfo.subjectPublicKey;
+  DER_ConvertBitString(&key);
+  if (PK11_HashBuf(SEC_OID_SHA1, d, key.data, key.len) != SECSuccess) {
+    return nullptr;
+  }
+  d += hashLen;
+
+  // reqCert.serialNumber (INTEGER)
+  *d++ = 0x02; // INTEGER
+  *d++ = static_cast<uint8_t>(cert->serialNumber.len);
+  for (size_t i = 0; i < cert->serialNumber.len; ++i) {
+    *d++ = cert->serialNumber.data[i];
+  }
+
+  PR_ASSERT(d == encodedRequest->data + totalLen);
+
+  return encodedRequest;
+}
+
+} } // namespace insanity::pkix
--- a/security/insanity/lib/pkixutil.h
+++ b/security/insanity/lib/pkixutil.h
@@ -104,17 +104,22 @@ public:
 
   const SECItem* encodedBasicConstraints;
   const SECItem* encodedExtendedKeyUsage;
   const SECItem* encodedKeyUsage;
   const SECItem* encodedNameConstraints;
 
   BackCert* const childCert;
 
-  const CERTCertificate* GetNSSCert() const { return nssCert; }
+  // Only non-const so that we can pass this to TrustDomain::IsRevoked,
+  // which only takes a non-const pointer because VerifyEncodedOCSPResponse
+  // requires it, and that is only because the implementation of
+  // VerifyEncodedOCSPResponse does a CERT_DupCertificate. TODO: get rid
+  // of that CERT_DupCertificate so that we can make all these things const.
+  /*const*/ CERTCertificate* GetNSSCert() const { return nssCert; }
 
   // Returns the names that should be considered when evaluating name
   // constraints. The list is constructed lazily and cached. The result is a
   // weak reference; do not try to free it, and do not hold long-lived
   // references to it.
   Result GetConstrainedNames(/*out*/ const CERTGeneralName** result);
 
   // This is the only place where we should be dealing with non-const
--- a/security/manager/ssl/tests/unit/test_ev_certs.js
+++ b/security/manager/ssl/tests/unit/test_ev_certs.js
@@ -109,74 +109,129 @@ function run_test() {
     addCertFromFile(certdb, "test_ev_certs/" + cert_filename, ',,');
   }
   load_ca("evroot");
   load_ca("non-evroot-ca");
 
   // setup and start ocsp responder
   Services.prefs.setCharPref("network.dns.localDomains",
                              'www.example.com, crl.example.com');
-
+  add_tests_in_mode(true);
+  add_tests_in_mode(false);
   run_next_test();
 }
 
-add_test(function() {
-  clearOCSPCache();
-  let ocspResponder = start_ocsp_responder(
-                        isDebugBuild ? ["int-ev-valid", "ev-valid"]
-                                     : ["ev-valid"]);
-  check_ee_for_ev("ev-valid", isDebugBuild);
-  ocspResponder.stop(run_next_test);
-});
+function add_tests_in_mode(useInsanity)
+{
+  add_test(function () {
+    Services.prefs.setBoolPref("security.use_insanity_verification",
+                               useInsanity);
+    run_next_test();
+  });
+
+  add_test(function () {
+    clearOCSPCache();
+    let ocspResponder = start_ocsp_responder(
+                          isDebugBuild ? ["int-ev-valid", "ev-valid"]
+                                       : ["ev-valid"]);
+    check_ee_for_ev("ev-valid", isDebugBuild);
+    ocspResponder.stop(run_next_test);
+  });
+
+  add_test(function() {
+    clearOCSPCache();
+    let ocspResponder = start_ocsp_responder(["non-ev-root"]);
+    check_ee_for_ev("non-ev-root", false);
+    ocspResponder.stop(run_next_test);
+  });
 
-add_test(function() {
-  clearOCSPCache();
-  let ocspResponder = start_ocsp_responder(["non-ev-root"]);
-  check_ee_for_ev("non-ev-root", false);
-  ocspResponder.stop(run_next_test);
-});
+  add_test(function() {
+    clearOCSPCache();
+    // libpkix will attempt to validate the intermediate, which does have an
+    // OCSP URL.
+    let ocspResponder = isDebugBuild ? start_ocsp_responder(["int-ev-valid"])
+                                     : failingOCSPResponder();
+    check_ee_for_ev("no-ocsp-url-cert", false);
+    ocspResponder.stop(run_next_test);
+  });
 
-add_test(function() {
-  clearOCSPCache();
-  // libpkix will attempt to validate the intermediate, which does have an
-  // OCSP URL.
-  let ocspResponder = isDebugBuild ? start_ocsp_responder(["int-ev-valid"])
-                                   : failingOCSPResponder();
-  check_ee_for_ev("no-ocsp-url-cert", false);
-  ocspResponder.stop(run_next_test);
-});
+  // bug 917380: Chcek that an untrusted EV root is untrusted.
+  const nsIX509Cert = Ci.nsIX509Cert;
+  add_test(function() {
+    let evRootCA = certdb.findCertByNickname(null, evrootnick);
+    certdb.setCertTrust(evRootCA, nsIX509Cert.CA_CERT, 0);
+
+    clearOCSPCache();
+    let ocspResponder = failingOCSPResponder();
+    check_cert_err("ev-valid",
+                   useInsanity ? SEC_ERROR_UNKNOWN_ISSUER
+                               : SEC_ERROR_UNTRUSTED_ISSUER);
+    ocspResponder.stop(run_next_test);
+  });
 
-// bug 917380: Chcek that an untrusted EV root is untrusted.
-const nsIX509Cert = Ci.nsIX509Cert;
-add_test(function() {
-  let evRootCA = certdb.findCertByNickname(null, evrootnick);
-  certdb.setCertTrust(evRootCA, nsIX509Cert.CA_CERT, 0);
+  // bug 917380: Chcek that a trusted EV root is trusted after disabling and
+  // re-enabling trust.
+  add_test(function() {
+    let evRootCA = certdb.findCertByNickname(null, evrootnick);
+    certdb.setCertTrust(evRootCA, nsIX509Cert.CA_CERT,
+                        Ci.nsIX509CertDB.TRUSTED_SSL |
+                        Ci.nsIX509CertDB.TRUSTED_EMAIL |
+                        Ci.nsIX509CertDB.TRUSTED_OBJSIGN);
 
-  clearOCSPCache();
-  let ocspResponder = failingOCSPResponder();
-  check_cert_err("ev-valid", SEC_ERROR_UNTRUSTED_ISSUER);
-  ocspResponder.stop(run_next_test);
-});
+    clearOCSPCache();
+    let ocspResponder = start_ocsp_responder(
+                          isDebugBuild ? ["int-ev-valid", "ev-valid"]
+                                       : ["ev-valid"]);
+    check_ee_for_ev("ev-valid", isDebugBuild);
+    ocspResponder.stop(run_next_test);
+  });
+
+
+  add_test(function () {
+    check_no_ocsp_requests("ev-valid",
+                           isDebugBuild ? SEC_ERROR_REVOKED_CERTIFICATE
+                                        : SEC_ERROR_EXTENSION_NOT_FOUND);
+  });
+
+  add_test(function () {
+    check_no_ocsp_requests("non-ev-root",
+                           isDebugBuild ? SEC_ERROR_UNTRUSTED_ISSUER
+                                        : SEC_ERROR_EXTENSION_NOT_FOUND);
+  });
 
-// bug 917380: Chcek that a trusted EV root is trusted after disabling and
-// re-enabling trust.
-add_test(function() {
-  let evRootCA = certdb.findCertByNickname(null, evrootnick);
-  certdb.setCertTrust(evRootCA, nsIX509Cert.CA_CERT,
-                      Ci.nsIX509CertDB.TRUSTED_SSL |
-                      Ci.nsIX509CertDB.TRUSTED_EMAIL |
-                      Ci.nsIX509CertDB.TRUSTED_OBJSIGN);
+  add_test(function () {
+    check_no_ocsp_requests("no-ocsp-url-cert",
+                           isDebugBuild ? SEC_ERROR_REVOKED_CERTIFICATE
+                                        : SEC_ERROR_EXTENSION_NOT_FOUND);
+  });
 
-  clearOCSPCache();
-  let ocspResponder = start_ocsp_responder(
-                        isDebugBuild ? ["int-ev-valid", "ev-valid"]
-                                     : ["ev-valid"]);
-  check_ee_for_ev("ev-valid", isDebugBuild);
-  ocspResponder.stop(run_next_test);
-});
+  // Test the EV continues to work with flags after successful EV verification
+  add_test(function () {
+    clearOCSPCache();
+    let ocspResponder = start_ocsp_responder(
+                          isDebugBuild ? ["int-ev-valid", "ev-valid"]
+                                       : ["ev-valid"]);
+    check_ee_for_ev("ev-valid", isDebugBuild);
+    ocspResponder.stop(function () {
+      // without net it must be able to EV verify
+      let failingOcspResponder = failingOCSPResponder();
+      let cert = certdb.findCertByNickname(null, "ev-valid");
+      let hasEVPolicy = {};
+      let verifiedChain = {};
+      let flags = Ci.nsIX509CertDB.FLAG_LOCAL_ONLY |
+                  Ci.nsIX509CertDB.FLAG_MUST_BE_EV;
+
+      let error = certdb.verifyCertNow(cert, certificateUsageSSLServer,
+                                       flags, verifiedChain, hasEVPolicy);
+      do_check_eq(hasEVPolicy.value, isDebugBuild);
+      do_check_eq(error, isDebugBuild ? 0 : SEC_ERROR_EXTENSION_NOT_FOUND);
+      failingOcspResponder.stop(run_next_test);
+    });
+  });
+}
 
 // bug 950240: add FLAG_MUST_BE_EV to CertVerifier::VerifyCert
 // to prevent spurious OCSP requests that race with OCSP stapling.
 // This has the side-effect of saying an EV certificate is not EV if
 // it hasn't already been verified (e.g. on the verification thread when
 // connecting to a site).
 // This flag is mostly a hack that should be removed once FLAG_LOCAL_ONLY
 // works as intended.
@@ -193,54 +248,8 @@ function check_no_ocsp_requests(cert_nam
   // Since we're not doing OCSP requests, no certificate will be EV.
   do_check_eq(hasEVPolicy.value, false);
   do_check_eq(expected_error, error);
   // Also check that isExtendedValidation doesn't cause OCSP requests.
   let identityInfo = cert.QueryInterface(Ci.nsIIdentityInfo);
   do_check_eq(identityInfo.isExtendedValidation, false);
   ocspResponder.stop(run_next_test);
 }
-
-
-add_test(function() {
-  check_no_ocsp_requests("ev-valid",   isDebugBuild ?
-                                       SEC_ERROR_REVOKED_CERTIFICATE:
-                                       SEC_ERROR_EXTENSION_NOT_FOUND);
-});
-
-add_test(function() {
-  check_no_ocsp_requests("non-ev-root", isDebugBuild ?
-                                        SEC_ERROR_UNTRUSTED_ISSUER:
-                                        SEC_ERROR_EXTENSION_NOT_FOUND);
-});
-
-
-add_test(function() {
-  check_no_ocsp_requests("no-ocsp-url-cert", isDebugBuild?
-                                             SEC_ERROR_REVOKED_CERTIFICATE:
-                                             SEC_ERROR_EXTENSION_NOT_FOUND);
-});
-
-
-// Test the EV continues to work with flags after successful EV verification
-add_test(function() {
-  clearOCSPCache();
-  let ocspResponder = start_ocsp_responder(
-                        isDebugBuild ? ["int-ev-valid", "ev-valid"]
-                                     : ["ev-valid"]);
-  check_ee_for_ev("ev-valid", isDebugBuild);
-  ocspResponder.stop(function () {
-    // without net it must be able to EV verify
-    let failingOcspResponder = failingOCSPResponder();
-    let cert = certdb.findCertByNickname(null, "ev-valid");
-    let hasEVPolicy = {};
-    let verifiedChain = {};
-    let flags = Ci.nsIX509CertDB.FLAG_LOCAL_ONLY |
-                Ci.nsIX509CertDB.FLAG_MUST_BE_EV;
-
-    let error = certdb.verifyCertNow(cert, certificateUsageSSLServer,
-                                     flags, verifiedChain, hasEVPolicy);
-    do_check_eq(hasEVPolicy.value, isDebugBuild);
-    do_check_eq(error, isDebugBuild ? 0 : SEC_ERROR_EXTENSION_NOT_FOUND);
-    failingOcspResponder.stop(run_next_test);
-  });
-});
-
--- a/security/manager/ssl/tests/unit/test_ocsp_caching.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_caching.js
@@ -102,12 +102,12 @@ function run_test() {
   // honored.
   add_connection_test("ocsp-stapling-revoked.example.com",
                       getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE),
                       clearSessionCache);
   add_test(function() { do_check_eq(gFetchCount, 1); run_next_test(); });
 
   //---------------------------------------------------------------------------
 
-  add_test(function() { ocspResponder.stop(run_next_test); run_next_test(); });
+  add_test(function() { ocspResponder.stop(run_next_test); });
 
   run_next_test();
 }
--- a/security/manager/ssl/tests/unit/test_ocsp_required.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_required.js
@@ -4,41 +4,63 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 "use strict";
 
 // In which we connect to a domain (as faked by a server running locally)
 // and start up an OCSP responder (also basically faked) that gives a
 // response with a bad signature. With security.OCSP.require set to true,
 // this should fail (but it also shouldn't cause assertion failures).
 
+let gOCSPRequestCount = 0;
+
 function run_test() {
   do_get_profile();
   Services.prefs.setBoolPref("security.OCSP.require", true);
 
-  let args = [ ["bad-signature", "localhostAndExampleCom", "unused" ] ];
+  // We don't actually make use of stapling in this test. This is just how we
+  // get a TLS connection.
+  add_tls_server_setup("OCSPStaplingServer");
+
+  let args = [["bad-signature", "localhostAndExampleCom", "unused"]];
   let ocspResponses = generateOCSPResponses(args, "tlsserver");
   let ocspResponseBadSignature = ocspResponses[0];
-  let ocspRequestCount = 0;
 
   let ocspResponder = new HttpServer();
-  ocspResponder.registerPrefixHandler("/", function(request, response) {
+  ocspResponder.registerPrefixHandler("/", function (request, response) {
     response.setStatusLine(request.httpVersion, 200, "OK");
     response.setHeader("Content-Type", "application/ocsp-response");
     response.write(ocspResponseBadSignature);
-    ocspRequestCount++;
+    gOCSPRequestCount++;
   });
   ocspResponder.start(8080);
 
-  // We don't actually make use of stapling in this test. This is just how we
-  // get a TLS connection.
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tests_in_mode(true);
+  add_tests_in_mode(false);
+
+  add_test(function () { ocspResponder.stop(run_next_test); });
+
+  run_next_test();
+}
+
+function add_tests_in_mode(useInsanity)
+{
+  add_test(function () {
+    Services.prefs.setBoolPref("security.use_insanity_verification",
+                               useInsanity);
+    run_next_test();
+  });
+
   add_connection_test("ocsp-stapling-none.example.com",
                       getXPCOMStatusFromNSS(SEC_ERROR_OCSP_INVALID_SIGNING_CERT));
   // bug 964493 - using a cached OCSP response with a bad signature would cause
   // the verification library to return a failure error code without calling
   // PORT_SetError with the specific error, violating the expectations
   // of the error handling code.
   add_connection_test("ocsp-stapling-none.example.com",
                       getXPCOMStatusFromNSS(SEC_ERROR_OCSP_INVALID_SIGNING_CERT));
-  add_test(function() { ocspResponder.stop(run_next_test); });
-  add_test(function() { do_check_eq(ocspRequestCount, 1); run_next_test(); });
-  run_next_test();
+  add_test(function () {
+    // XXX(bug 915932): special case for insanity::pkix due to the temporary
+    // lack of an OCSP cache.
+    do_check_eq(gOCSPRequestCount, useInsanity ? 2 : 1);
+    gOCSPRequestCount = 0;
+    run_next_test();
+  });
 }
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
@@ -16,27 +16,22 @@ function add_ocsp_test(aHost, aExpectedR
       gExpectOCSPRequest = !aStaplingEnabled;
       clearOCSPCache();
       clearSessionCache();
       Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling",
                                  aStaplingEnabled);
     });
 }
 
-function run_test() {
-  do_get_profile();
-
-  let fakeOCSPResponder = new HttpServer();
-  fakeOCSPResponder.registerPrefixHandler("/", function(request, response) {
-    response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
-    do_check_true(gExpectOCSPRequest);
+function add_tests_in_mode(useInsanity, certDB, otherTestCA) {
+  add_test(function () {
+    Services.prefs.setBoolPref("security.use_insanity_verification",
+                               useInsanity);
+    run_next_test();
   });
-  fakeOCSPResponder.start(8080);
-
-  add_tls_server_setup("OCSPStaplingServer");
 
   // In the absence of OCSP stapling, these should actually all work.
   add_ocsp_test("ocsp-stapling-good.example.com", Cr.NS_OK, false);
   add_ocsp_test("ocsp-stapling-revoked.example.com", Cr.NS_OK, false);
   add_ocsp_test("ocsp-stapling-good-other-ca.example.com", Cr.NS_OK, false);
   add_ocsp_test("ocsp-stapling-malformed.example.com", Cr.NS_OK, false);
   add_ocsp_test("ocsp-stapling-srverr.example.com", Cr.NS_OK, false);
   add_ocsp_test("ocsp-stapling-trylater.example.com", Cr.NS_OK, false);
@@ -51,35 +46,43 @@ function run_test() {
   // Now test OCSP stapling
   // The following error codes are defined in security/nss/lib/util/SECerrs.h
 
   add_ocsp_test("ocsp-stapling-good.example.com", Cr.NS_OK, true);
 
   add_ocsp_test("ocsp-stapling-revoked.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE), true);
 
+  // SEC_ERROR_OCSP_INVALID_SIGNING_CERT vs SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE
+  // depends on whether the CA that signed the response is a trusted CA.
+
   // This stapled response is from a CA that is untrusted and did not issue
   // the server's certificate.
-  add_ocsp_test("ocsp-stapling-good-other-ca.example.com",
-                getXPCOMStatusFromNSS(SEC_ERROR_BAD_DATABASE), true);
-
-  // SEC_ERROR_BAD_DATABASE vs SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE depends on
-  // whether the CA that signed the response is a trusted CA.
   add_test(function() {
-    let certdb = Cc["@mozilla.org/security/x509certdb;1"]
-                   .getService(Ci.nsIX509CertDB);
-    // Another trusted CA that shouldn't be trusted for OCSP responses, etc.
-    // for the "good" CA.
-    addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u");
+    certDB.setCertTrust(otherTestCA, Ci.nsIX509Cert.CA_CERT,
+                        Ci.nsIX509CertDB.UNTRUSTED);
     run_next_test();
   });
+  add_ocsp_test("ocsp-stapling-good-other-ca.example.com",
+                getXPCOMStatusFromNSS(SEC_ERROR_OCSP_INVALID_SIGNING_CERT), true);
 
+  // The stapled response is from a CA that is trusted but did not issue the
+  // server's certificate.
+  add_test(function() {
+    certDB.setCertTrust(otherTestCA, Ci.nsIX509Cert.CA_CERT,
+                        Ci.nsIX509CertDB.TRUSTED_SSL);
+    run_next_test();
+  });
   add_ocsp_test("ocsp-stapling-good-other-ca.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE),
                 true);
+
+  // TODO: Test the case where the signing cert can't be found at all, which
+  // will result in SEC_ERROR_BAD_DATABASE in the NSS classic case.
+
   add_ocsp_test("ocsp-stapling-malformed.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_MALFORMED_REQUEST), true);
   add_ocsp_test("ocsp-stapling-srverr.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_SERVER_ERROR), true);
   add_ocsp_test("ocsp-stapling-trylater.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_TRY_SERVER_LATER), true);
   add_ocsp_test("ocsp-stapling-needssig.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG), true);
@@ -101,27 +104,50 @@ function run_test() {
       Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
     }
   );
   add_ocsp_test("ocsp-stapling-empty.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_MALFORMED_RESPONSE), true);
   // ocsp-stapling-expired.example.com and
   // ocsp-stapling-expired-fresh-ca.example.com are handled in
   // test_ocsp_stapling_expired.js
-
-  add_test(function() { fakeOCSPResponder.stop(run_next_test); });
-
-  add_test(check_ocsp_stapling_telemetry);
-  run_next_test();
 }
 
 function check_ocsp_stapling_telemetry() {
   let histogram = Cc["@mozilla.org/base/telemetry;1"]
                     .getService(Ci.nsITelemetry)
                     .getHistogramById("SSL_OCSP_STAPLING")
                     .snapshot();
-  do_check_eq(histogram.counts[0], 0); // histogram bucket 0 is unused
-  do_check_eq(histogram.counts[1], 1); // 1 connection with a good response
-  do_check_eq(histogram.counts[2], 14); // 14 connections with no stapled resp.
-  do_check_eq(histogram.counts[3], 0); // 0 connections with an expired response
-  do_check_eq(histogram.counts[4], 11); // 11 connections with bad responses
+  do_check_eq(histogram.counts[0], 2 * 0); // histogram bucket 0 is unused
+  do_check_eq(histogram.counts[1], 2 * 1); // 1 connection with a good response
+  do_check_eq(histogram.counts[2], 2 * 14); // 14 connections with no stapled resp.
+  do_check_eq(histogram.counts[3], 2 * 0); // 0 connections with an expired response
+  do_check_eq(histogram.counts[4], 2 * 11); // 11 connections with bad responses
   run_next_test();
 }
+
+function run_test() {
+  do_get_profile();
+
+  let certDB = Cc["@mozilla.org/security/x509certdb;1"]
+                  .getService(Ci.nsIX509CertDB);
+  let otherTestCAFile = do_get_file("tlsserver/other-test-ca.der", false);
+  let otherTestCADER = readFile(otherTestCAFile);
+  let otherTestCA = certDB.constructX509(otherTestCADER, otherTestCADER.length);
+
+  let fakeOCSPResponder = new HttpServer();
+  fakeOCSPResponder.registerPrefixHandler("/", function (request, response) {
+    response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+    do_check_true(gExpectOCSPRequest);
+  });
+  fakeOCSPResponder.start(8080);
+
+  add_tls_server_setup("OCSPStaplingServer");
+
+  add_tests_in_mode(true, certDB, otherTestCA);
+  add_tests_in_mode(false, certDB, otherTestCA);
+
+  add_test(function () {
+    fakeOCSPResponder.stop(check_ocsp_stapling_telemetry);
+  });
+
+  run_next_test();
+}
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -20,43 +20,60 @@ function add_ocsp_test(aHost, aExpectedR
       gCurrentOCSPResponse = aOCSPResponseToServe;
       gOCSPRequestCount = 0;
     },
     function() {
       do_check_eq(gOCSPRequestCount, 1);
     });
 }
 
+do_get_profile();
+Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
+let args = [["good", "localhostAndExampleCom", "unused"],
+             ["expiredresponse", "localhostAndExampleCom", "unused"],
+             ["oldvalidperiod", "localhostAndExampleCom", "unused"],
+             ["revoked", "localhostAndExampleCom", "unused"],
+             ["unknown", "localhostAndExampleCom", "unused"],
+            ];
+let ocspResponses = generateOCSPResponses(args, "tlsserver");
+// Fresh response, certificate is good.
+let ocspResponseGood = ocspResponses[0];
+// Expired response, certificate is good.
+let expiredOCSPResponseGood = ocspResponses[1];
+// Fresh signature, old validity period, certificate is good.
+let oldValidityPeriodOCSPResponseGood = ocspResponses[2];
+// Fresh signature, certificate is revoked.
+let ocspResponseRevoked = ocspResponses[3];
+// Fresh signature, certificate is unknown.
+let ocspResponseUnknown = ocspResponses[4];
+
 function run_test() {
-  do_get_profile();
-  Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
-  let args = [ ["good", "localhostAndExampleCom", "unused" ],
-               ["expiredresponse", "localhostAndExampleCom", "unused"],
-               ["oldvalidperiod", "localhostAndExampleCom", "unused"],
-               ["revoked", "localhostAndExampleCom", "unused"] ];
-  let ocspResponses = generateOCSPResponses(args, "tlsserver");
-  // Fresh response, certificate is good.
-  let ocspResponseGood = ocspResponses[0];
-  // Expired response, certificate is good.
-  let expiredOCSPResponseGood = ocspResponses[1];
-  // Fresh signature, old validity period, certificate is good.
-  let oldValidityPeriodOCSPResponseGood = ocspResponses[2];
-  // Fresh signature, certificate is revoked.
-  let ocspResponseRevoked = ocspResponses[3];
-
   let ocspResponder = new HttpServer();
   ocspResponder.registerPrefixHandler("/", function(request, response) {
     response.setStatusLine(request.httpVersion, 200, "OK");
     response.setHeader("Content-Type", "application/ocsp-response");
     response.write(gCurrentOCSPResponse);
     gOCSPRequestCount++;
   });
   ocspResponder.start(8080);
+  add_tls_server_setup("OCSPStaplingServer");
+  add_tests_in_mode(true);
+  add_tests_in_mode(false);
+  add_test(function () { ocspResponder.stop(run_next_test); });
+  add_test(check_ocsp_stapling_telemetry);
+  run_next_test();
+}
 
-  add_tls_server_setup("OCSPStaplingServer");
+function add_tests_in_mode(useInsanity)
+{
+  add_test(function () {
+    Services.prefs.setBoolPref("security.use_insanity_verification",
+                               useInsanity);
+    run_next_test();
+  });
 
   // In these tests, the OCSP stapling server gives us a stapled
   // response based on the host name ("ocsp-stapling-expired" or
   // "ocsp-stapling-expired-fresh-ca"). We then ensure that we're
   // properly falling back to fetching revocation information.
   // For ocsp-stapling-expired.example.com, the OCSP stapling server
   // staples an expired OCSP response. The certificate has not expired.
   // For ocsp-stapling-expired-fresh-ca.example.com, the OCSP stapling
@@ -75,25 +92,25 @@ function run_test() {
   add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK,
                 oldValidityPeriodOCSPResponseGood);
   add_ocsp_test("ocsp-stapling-expired.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE),
                 ocspResponseRevoked);
   add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE),
                 ocspResponseRevoked);
-  add_test(function() { ocspResponder.stop(run_next_test); });
-  add_test(check_ocsp_stapling_telemetry);
-  run_next_test();
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNKNOWN_CERT),
+                ocspResponseUnknown);
 }
 
 function check_ocsp_stapling_telemetry() {
   let histogram = Cc["@mozilla.org/base/telemetry;1"]
                     .getService(Ci.nsITelemetry)
                     .getHistogramById("SSL_OCSP_STAPLING")
                     .snapshot();
-  do_check_eq(histogram.counts[0], 0); // histogram bucket 0 is unused
-  do_check_eq(histogram.counts[1], 0); // 0 connections with a good response
-  do_check_eq(histogram.counts[2], 0); // 0 connections with no stapled resp.
-  do_check_eq(histogram.counts[3], 8); // 8 connections with an expired response
-  do_check_eq(histogram.counts[4], 0); // 0 connections with bad responses
+  do_check_eq(histogram.counts[0], 2 * 0); // histogram bucket 0 is unused
+  do_check_eq(histogram.counts[1], 2 * 0); // 0 connections with a good response
+  do_check_eq(histogram.counts[2], 2 * 0); // 0 connections with no stapled resp.
+  do_check_eq(histogram.counts[3], 2 * 9); // 8 connections with an expired response
+  do_check_eq(histogram.counts[4], 2 * 0); // 0 connections with bad responses
   run_next_test();
 }
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js
@@ -28,16 +28,28 @@ function run_test() {
     response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
     let body = "Refusing to return a response";
     response.bodyOutputStream.write(body, body.length);
   });
   ocspResponder.start(8080);
 
   add_tls_server_setup("OCSPStaplingServer");
 
-  add_ocsp_test("ocsp-stapling-with-intermediate.example.com", Cr.NS_OK);
-  add_test(function() { ocspResponder.stop(run_next_test); });
+  add_tests_in_mode(true);
+  add_tests_in_mode(false);
+
+  add_test(function () { ocspResponder.stop(run_next_test); });
   add_test(function() {
     do_check_eq(gOCSPRequestCount, 0);
     run_next_test();
   });
   run_next_test();
 }
+
+function add_tests_in_mode(useInsanity) {
+  add_test(function () {
+    Services.prefs.setBoolPref("security.use_insanity_verification",
+                               useInsanity);
+    run_next_test();
+  });
+
+  add_ocsp_test("ocsp-stapling-with-intermediate.example.com", Cr.NS_OK);
+}
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -15,18 +15,16 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'nsDllMain.cpp',
     ]
     if not CONFIG['GNU_CC']:
         LOCAL_INCLUDES += [
 	    '/widget/windows',
 	    '/xpcom/base',
 	]
 
-DEFINES['MOZILLA_INTERNAL_API'] = True
-
 # component libraries
 additional_defines = (
     'MOZ_AUTH_EXTENSION',
     'MOZ_GIO_COMPONENT',
     'MOZ_JSDEBUGGER',
     'MOZ_PERMISSIONS',
     'MOZ_PREF_EXTENSIONS',
     'MOZ_SPELLCHECK',
deleted file mode 100644
--- a/toolkit/mozapps/extensions/Makefile.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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/.
-
-# Additional debugging info is exposed by setting the MOZ_EM_DEBUG
-# environment variable when building.
-ifneq (,$(MOZ_EM_DEBUG))
-DEFINES += -DMOZ_EM_DEBUG=1
-endif
--- a/toolkit/mozapps/extensions/moz.build
+++ b/toolkit/mozapps/extensions/moz.build
@@ -48,12 +48,12 @@ EXTRA_PP_JS_MODULES += [
 if CONFIG['MOZ_UPDATE_CHANNEL'] not in ('aurora', 'beta', 'release', 'esr'):
     DEFINES['MOZ_COMPATIBILITY_NIGHTLY'] = 1
 
 # This is used in multiple places, so is defined here to avoid it getting
 # out of sync.
 DEFINES['MOZ_EXTENSIONS_DB_SCHEMA'] = 16
 
 # Additional debugging info is exposed in debug builds
-if CONFIG['MOZ_DEBUG']:
+if CONFIG['MOZ_EM_DEBUG']:
     DEFINES['MOZ_EM_DEBUG'] = 1
 
-JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file
+JAR_MANIFESTS += ['jar.mn']
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -259,38 +259,16 @@ WinUtils::GetMessage(LPMSG aMsg, HWND aW
     NS_ENSURE_TRUE(SUCCEEDED(hr), false);
     return ret;
   }
 #endif // #ifdef NS_ENABLE_TSF
   return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage);
 }
 
 /* static */
-void
-WinUtils::WaitForMessage()
-{
-  DWORD result = ::MsgWaitForMultipleObjectsEx(0, nullptr, INFINITE, QS_ALLINPUT,
-                                               MWMO_INPUTAVAILABLE);
-  NS_WARN_IF_FALSE(result != WAIT_FAILED, "Wait failed");
-
-  // This idiom is taken from the Chromium ipc code, see
-  // ipc/chromium/src/base/message+puimp_win.cpp:270.
-  // The intent is to avoid a busy wait when MsgWaitForMultipleObjectsEx
-  // returns quickly but PeekMessage would not return a message.
-  if (result == WAIT_OBJECT_0) {
-    MSG msg = {0};
-    DWORD queue_status = ::GetQueueStatus(QS_MOUSE);
-    if (HIWORD(queue_status) & QS_MOUSE &&
-        !PeekMessage(&msg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) {
-      ::WaitMessage();
-    }
-  }
-}
-
-/* static */
 bool
 WinUtils::GetRegistryKey(HKEY aRoot,
                          char16ptr_t aKeyName,
                          char16ptr_t aValueName,
                          wchar_t* aBuffer,
                          DWORD aBufferLength)
 {
   NS_PRECONDITION(aKeyName, "The key name is NULL");
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -90,29 +90,16 @@ public:
    * GetMessageW(), ITfMessageMgr::PeekMessageW() and
    * ITfMessageMgr::GetMessageW().
    * Don't call the native APIs directly.  You MUST use these methods instead.
    */
   static bool PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
                           UINT aLastMessage, UINT aOption);
   static bool GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
                          UINT aLastMessage);
-
-  /**
-   * Wait until a message is ready to be processed.
-   * Prefer using this method to directly calling ::WaitMessage since
-   * ::WaitMessage will wait if there is an unread message in the queue.
-   * That can cause freezes until another message enters the queue if the
-   * message is marked read by a call to PeekMessage which the caller is
-   * not aware of (e.g., from a different thread).
-   * Note that this method may cause sync dispatch of sent (as opposed to
-   * posted) messages.
-   */
-  static void WaitForMessage();
-
   /**
    * Gets the value of a string-typed registry value.
    *
    * @param aRoot The registry root to search in.
    * @param aKeyName The name of the registry key to open.
    * @param aValueName The name of the registry value in the specified key whose
    *   value is to be retrieved.  Can be null, to retrieve the key's unnamed/
    *   default value.
--- a/widget/windows/nsAppShell.cpp
+++ b/widget/windows/nsAppShell.cpp
@@ -277,17 +277,17 @@ nsAppShell::ProcessNextNativeEvent(bool 
         }
 
         ::TranslateMessage(&msg);
         ::DispatchMessageW(&msg);
       }
     } else if (mayWait) {
       // Block and wait for any posted application message
       mozilla::HangMonitor::Suspend();
-      WinUtils::WaitForMessage();
+      ::WaitMessage();
     }
   } while (!gotMessage && mayWait);
 
   // See DoProcessNextNativeEvent, mEventloopNestingLevel will be
   // one when a modal loop unwinds.
   if (mNativeCallbackPending && mEventloopNestingLevel == 1)
     DoProcessMoreGeckoEvents();
 
--- a/xpcom/threads/nsThreadPool.cpp
+++ b/xpcom/threads/nsThreadPool.cpp
@@ -47,18 +47,18 @@ NS_IMPL_CLASSINFO(nsThreadPool, nullptr,
 NS_IMPL_QUERY_INTERFACE3_CI(nsThreadPool, nsIThreadPool, nsIEventTarget,
                             nsIRunnable)
 NS_IMPL_CI_INTERFACE_GETTER2(nsThreadPool, nsIThreadPool, nsIEventTarget)
 
 nsThreadPool::nsThreadPool()
   : mThreadLimit(DEFAULT_THREAD_LIMIT)
   , mIdleThreadLimit(DEFAULT_IDLE_THREAD_LIMIT)
   , mIdleThreadTimeout(DEFAULT_IDLE_THREAD_TIMEOUT)
+  , mIdleCount(0)
   , mStackSize(nsIThreadManager::DEFAULT_STACK_SIZE)
-  , mIdleCount(0)
   , mShutdown(false)
 {
 }
 
 nsThreadPool::~nsThreadPool()
 {
   // Threads keep a reference to the nsThreadPool until they return from Run()
   // after removing themselves from mThreads.
@@ -251,17 +251,17 @@ nsThreadPool::Dispatch(nsIRunnable *even
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThreadPool::IsOnCurrentThread(bool *result)
 {
   ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
   nsIThread* thread = NS_GetCurrentThread();
-  for (uint32_t i = 0; i < mThreads.Count(); ++i) {
+  for (uint32_t i = 0; i < static_cast<uint32_t>(mThreads.Count()); ++i) {
     if (mThreads[i] == thread) {
       *result = true;
       return NS_OK;
     }
   }
   *result = false;
   return NS_OK;
 }