Merge inbound to m-c
authorWes Kocher <wkocher@mozilla.com>
Wed, 12 Mar 2014 19:36:50 -0700
changeset 191448 663cc8a7d6ec7d1cc132a057db701d00fa975d6e
parent 191405 4b3e5148fd02c8948adb3befb21f21b4ccd735a0 (current diff)
parent 191447 4647aa53d2868dda962cc86f82ea9614cdd32a96 (diff)
child 191487 c12c92db5588a0fb6083486f7bcc1ed55f0b86f5
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [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 inbound to m-c
config/makefiles/mochitest.mk
--- a/accessible/src/xul/XULTreeGridAccessible.cpp
+++ b/accessible/src/xul/XULTreeGridAccessible.cpp
@@ -404,16 +404,24 @@ XULTreeGridRowAccessible::RowInvalidated
       Accessible* cellAccessible = GetCellAccessible(column);
       if (cellAccessible) {
         nsRefPtr<XULTreeGridCellAccessible> cellAcc = do_QueryObject(cellAccessible);
 
         cellAcc->CellInvalidated();
       }
     }
   }
+
+  nsAutoString name;
+  Name(name);
+
+  if (name != mCachedName) {
+    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
+    mCachedName = name;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridRowAccessible: Accessible protected implementation
 
 void
 XULTreeGridRowAccessible::CacheChildren()
 {
--- a/accessible/src/xul/XULTreeGridAccessible.h
+++ b/accessible/src/xul/XULTreeGridAccessible.h
@@ -101,16 +101,17 @@ public:
 
 protected:
 
   // Accessible
   virtual void CacheChildren();
 
   // XULTreeItemAccessibleBase
   mutable AccessibleHashtable mAccessibleCache;
+  nsString mCachedName;
 };
 
 
 /**
  * Represents an accessible for XUL tree cell in the case when XUL tree has
  * multiple columns.
  */
 
--- a/accessible/tests/mochitest/events/test_coalescence.html
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -87,16 +87,17 @@
             if (aNode == this.parentNode)
               this.hostNode.appendChild(this.parentNode);
             else
               this.parentNode.appendChild(this.childNode);
             break;
 
           case kShowElm:
             aNode.style.display = "block";
+            break;
 
           default:
             return INVOKER_ACTION_FAILED;
         }
       }
 
       this.getEventType = function coalescenceBase_getEventType(aAction)
       {
--- a/accessible/tests/mochitest/events/test_tree.xul
+++ b/accessible/tests/mochitest/events/test_tree.xul
@@ -92,16 +92,36 @@
       Object.defineProperty(this, "target", { get: targetGetter });
 
       this.getID = function getID()
       {
         return aMsg + "name changed";
       }
     }
 
+    /**
+     * Check name changed a11y event for a row.
+     */
+    function rowNameChangeChecker(aMsg, aRow)
+    {
+      this.type = EVENT_NAME_CHANGE;
+
+      function targetGetter()
+      {
+        var acc = getAccessible(gTree);
+        return acc.getChildAt(aRow + 1);
+      }
+      Object.defineProperty(this, "target", { get: targetGetter });
+
+      this.getID = function getID()
+      {
+        return aMsg + "name changed";
+      }
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
     /**
      * Set tree view.
      */
     function setTreeView()
     {
@@ -216,19 +236,20 @@
           column = column.getNext();
         }
 
         gTreeBox.invalidateRow(1);
       }
 
       this.eventSeq =
       [
-        new nameChangeChecker("invalidateColumn: ", 1, 0),
-        new nameChangeChecker("invalidateColumn: ", 1, 1),
-        new treeInvalidatedChecker("invalidateColumn", 1, 1, null, null)
+        new nameChangeChecker("invalidateRow: ", 1, 0),
+        new nameChangeChecker("invalidateRow: ", 1, 1),
+        new rowNameChangeChecker("invalidateRow: ", 1),
+        new treeInvalidatedChecker("invalidateRow", 1, 1, null, null)
       ];
 
       this.getID = function invalidateRow_getID()
       {
         return "invalidate row";
       }
     }
 
@@ -272,21 +293,26 @@
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=368835"
          title="Fire TreeViewChanged/TreeRowCountChanged events.">
         Mozilla Bug 368835
       </a><br/>
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=308564"
          title="No accessibility events when data in a tree row changes.">
         Mozilla Bug 308564
-      </a>
+      </a><br/>
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=739524"
          title="replace TreeViewChanged DOM event on direct call from XUL tree.">
         Mozilla Bug 739524
+      </a><br/>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=743568"
+         title="Thunderbird message list tree emitting incorrect focus signals after message deleted.">
+        Mozilla Bug 743568
       </a>
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
--- a/accessible/tests/mochitest/jsat/a11y.ini
+++ b/accessible/tests/mochitest/jsat/a11y.ini
@@ -10,9 +10,10 @@ support-files =
 [test_alive.html]
 [test_content_integration.html]
 [test_explicit_names.html]
 [test_landmarks.html]
 [test_live_regions.html]
 [test_output.html]
 [test_tables.html]
 [test_touch_adapter.html]
+skip-if = true # disabled for a number of intermitten failures: Bug 982326
 [test_traversal.html]
--- a/browser/branding/aurora/pref/firefox-branding.js
+++ b/browser/branding/aurora/pref/firefox-branding.js
@@ -23,12 +23,14 @@ pref("app.update.url.details", "https://
 
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 2);
 
 // Search codes belong only in builds with official branding
 pref("browser.search.param.yahoo-fr", "");
-pref("browser.search.param.yahoo-fr-metro", "");
 pref("browser.search.param.yahoo-fr-cjkt", ""); // now unused
 pref("browser.search.param.yahoo-fr-ja", "");
 pref("browser.search.param.yahoo-f-CN", "");
+#ifdef MOZ_METRO
+pref("browser.search.param.yahoo-fr-metro", "");
+#endif
--- a/browser/branding/nightly/pref/firefox-branding.js
+++ b/browser/branding/nightly/pref/firefox-branding.js
@@ -20,12 +20,14 @@ pref("app.update.url.details", "https://
 
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 2);
 
 // Search codes belong only in builds with official branding
 pref("browser.search.param.yahoo-fr", "");
-pref("browser.search.param.yahoo-fr-metro", "");
 pref("browser.search.param.yahoo-fr-cjkt", ""); // now unused
 pref("browser.search.param.yahoo-fr-ja", "");
 pref("browser.search.param.yahoo-f-CN", "");
+#ifdef MOZ_METRO
+pref("browser.search.param.yahoo-fr-metro", "");
+#endif
--- a/browser/branding/official/pref/firefox-branding.js
+++ b/browser/branding/official/pref/firefox-branding.js
@@ -19,13 +19,15 @@ pref("app.update.url.manual", "https://w
 pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes");
 
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 63);
 
 pref("browser.search.param.ms-pc", "MOZI");
-pref("browser.search.param.ms-pc-metro", "MOZW");
 pref("browser.search.param.yahoo-fr", "moz35");
-pref("browser.search.param.yahoo-fr-metro", "mozilla_metro_search");
 pref("browser.search.param.yahoo-fr-cjkt", "moz35"); // now unused
 pref("browser.search.param.yahoo-fr-ja", "mozff");
+#ifdef MOZ_METRO
+pref("browser.search.param.ms-pc-metro", "MOZW");
+pref("browser.search.param.yahoo-fr-metro", "mozilla_metro_search");
+#endif
--- a/browser/branding/unofficial/pref/firefox-branding.js
+++ b/browser/branding/unofficial/pref/firefox-branding.js
@@ -20,12 +20,14 @@ pref("app.update.url.details", "https://
 
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 2);
 
 // Search codes belong only in builds with official branding
 pref("browser.search.param.yahoo-fr", "");
-pref("browser.search.param.yahoo-fr-metro", "");
 pref("browser.search.param.yahoo-fr-cjkt", ""); // now unused
 pref("browser.search.param.yahoo-fr-ja", "");
 pref("browser.search.param.yahoo-f-CN", "");
+#ifdef MOZ_METRO
+pref("browser.search.param.yahoo-fr-metro", "");
+#endif
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -52,26 +52,29 @@ INSTALL_TARGETS += robocop
 robocop_TARGET  := libs
 robocop_DEST    := $(CURDIR)
 robocop_FILES   := \
   $(TESTPATH)/robocop.ini \
   $(TESTPATH)/robocop_autophone.ini \
   $(NULL)
 robocop-deps := $(notdir $(robocop_FILES))
 
-MOCHITEST_ROBOCOP_FILES := \
+ROBOCOP_FILES := \
   $(wildcard $(TESTPATH)/*.html) \
   $(wildcard $(TESTPATH)/*.jpg) \
   $(wildcard $(TESTPATH)/*.sjs) \
   $(wildcard $(TESTPATH)/test*.js) \
   $(wildcard $(TESTPATH)/robocop*.js) \
   $(wildcard $(TESTPATH)/*.xml) \
   $(wildcard $(TESTPATH)/*.swf) \
   $(NULL)
 
+ROBOCOP_DEST = $(DEPTH)/_tests/testing/mochitest/tests/robocop/
+INSTALL_TARGETS += ROBOCOP
+
 GARBAGE += \
   AndroidManifest.xml \
   $(robocop-deps) \
   $(testconstants-dep) \
   $(NULL)
 
 JAVAFILES += \
   $(java-harness) \
--- a/config/config.mk
+++ b/config/config.mk
@@ -68,18 +68,24 @@ endif
   TIERS \
   TOOL_DIRS \
   XPCSHELL_TESTS \
   XPIDL_MODULE \
   $(NULL)
 
 _DEPRECATED_VARIABLES := \
   ANDROID_RESFILES \
+  MOCHITEST_A11Y_FILES \
+  MOCHITEST_BROWSER_FILES \
+  MOCHITEST_BROWSER_FILES_PARTS \
+  MOCHITEST_CHROME_FILES \
+  MOCHITEST_FILES \
   MOCHITEST_FILES_PARTS \
-  MOCHITEST_BROWSER_FILES_PARTS \
+  MOCHITEST_METRO_FILES \
+  MOCHITEST_ROBOCOP_FILES \
   SHORT_LIBNAME \
   $(NULL)
 
 ifndef EXTERNALLY_MANAGED_MAKE_FILE
 # Using $(firstword) may not be perfect. But it should be good enough for most
 # scenarios.
 _current_makefile = $(CURDIR)/$(firstword $(MAKEFILE_LIST))
 
deleted file mode 100644
--- a/config/makefiles/mochitest.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- makefile -*-
-# vim:set ts=8 sw=8 sts=8 noet:
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this file,
-# You can obtain one at http://mozilla.org/MPL/2.0/.
-
-ifndef INCLUDED_TESTS_MOCHITEST_MK #{
-
-#   $1- test directory name
-#   $2- optional: if passed dot used to flatten directory hierarchy copy
-# else- relativesrcdir
-mochitestdir = \
-    $(strip \
-      $(if $(2),$(DEPTH)/_tests/testing/mochitest/$1/. \
-        ,$(DEPTH)/_tests/testing/mochitest/$1/$(relativesrcdir) \
-    ))
-
-
-ifdef MOCHITEST_FILES
-MOCHITEST_DEST := $(call mochitestdir,tests)
-INSTALL_TARGETS += MOCHITEST
-endif
-
-ifdef MOCHITEST_CHROME_FILES
-MOCHITEST_CHROME_DEST := $(call mochitestdir,chrome)
-INSTALL_TARGETS += MOCHITEST_CHROME
-endif
-
-ifdef MOCHITEST_BROWSER_FILES
-MOCHITEST_BROWSER_DEST := $(call mochitestdir,browser)
-INSTALL_TARGETS += MOCHITEST_BROWSER
-endif
-
-ifdef MOCHITEST_A11Y_FILES
-MOCHITEST_A11Y_DEST := $(call mochitestdir,a11y)
-INSTALL_TARGETS += MOCHITEST_A11Y
-endif
-
-ifdef MOCHITEST_METRO_FILES
-MOCHITEST_METRO_DEST := $(call mochitestdir,metro)
-INSTALL_TARGETS += MOCHITEST_METRO
-endif
-
-ifdef MOCHITEST_ROBOCOP_FILES
-MOCHITEST_ROBOCOP_DEST := $(call mochitestdir,tests/robocop,flat_hierarchy)
-INSTALL_TARGETS += MOCHITEST_ROBOCOP
-endif
-
-INCLUDED_TESTS_MOCHITEST_MK := 1
-
-endif #} INCLUDED_TESTS_MOCHITEST_MK
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -94,20 +94,16 @@ ifdef ENABLE_TESTS
 # The current developer workflow expects tests to be updated when processing
 # the default target. If we ever change this implementation, the behavior
 # should be preserved or the change should be widely communicated. A
 # consequence of not processing test dir targets during the default target is
 # that changes to tests may not be updated and code could assume to pass
 # locally against non-current test code.
 DIRS += $(TEST_DIRS)
 
-ifndef INCLUDED_TESTS_MOCHITEST_MK #{
-  include $(topsrcdir)/config/makefiles/mochitest.mk
-endif #}
-
 ifdef CPP_UNIT_TESTS
 ifdef COMPILE_ENVIRONMENT
 
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
 # through TestHarness.h, by modifying the list of includes and the libs against
 # which stuff links.
 CPPSRCS += $(CPP_UNIT_TESTS)
 CPP_UNIT_TEST_BINS := $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX))
@@ -1630,22 +1626,16 @@ FREEZE_VARIABLES = \
   CPPSRCS \
   EXPORTS \
   DIRS \
   LIBRARY \
   MODULE \
   TIERS \
   EXTRA_COMPONENTS \
   EXTRA_PP_COMPONENTS \
-  MOCHITEST_FILES \
-  MOCHITEST_CHROME_FILES \
-  MOCHITEST_BROWSER_FILES \
-  MOCHITEST_A11Y_FILES \
-  MOCHITEST_METRO_FILES \
-  MOCHITEST_ROBOCOP_FILES \
   $(NULL)
 
 $(foreach var,$(FREEZE_VARIABLES),$(eval $(var)_FROZEN := '$($(var))'))
 
 CHECK_FROZEN_VARIABLES = $(foreach var,$(FREEZE_VARIABLES), \
   $(if $(subst $($(var)_FROZEN),,'$($(var))'),$(error Makefile variable '$(var)' changed value after including rules.mk. Was $($(var)_FROZEN), now $($(var)).)))
 
 libs export::
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1959,16 +1959,17 @@ GK_ATOM(animationsOfBeforeProperty, "Ani
 GK_ATOM(animationsOfAfterProperty, "AnimationsOfAfterProperty") // FrameAnimations*
 GK_ATOM(transitionsProperty, "TransitionsProperty")        // FrameTransitions*
 GK_ATOM(transitionsOfBeforeProperty, "TransitionsOfBeforeProperty") // FrameTransitions*
 GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*
 GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
 GK_ATOM(labelMouseDownPtProperty, "LabelMouseDownPtProperty")
 GK_ATOM(baseURIProperty, "baseURIProperty")
 GK_ATOM(lockedStyleStates, "lockedStyleStates")
+GK_ATOM(apzCallbackTransform, "apzCallbackTransform")
 
 // Languages for lang-specific transforms
 GK_ATOM(Japanese, "ja")
 GK_ATOM(Chinese, "zh-CN")
 GK_ATOM(Taiwanese, "zh-TW")
 GK_ATOM(HongKongChinese, "zh-HK")
 GK_ATOM(Unicode, "x-unicode")
 
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -236,16 +236,26 @@ public:
   nsSimplePluginEvent(nsIDocument* aTarget, const nsAString& aEvent)
     : mTarget(aTarget)
     , mDocument(aTarget)
     , mEvent(aEvent)
   {
     MOZ_ASSERT(aTarget);
   }
 
+  nsSimplePluginEvent(nsIContent* aTarget,
+                      nsIDocument* aDocument,
+                      const nsAString& aEvent)
+    : mTarget(aTarget)
+    , mDocument(aDocument)
+    , mEvent(aEvent)
+  {
+    MOZ_ASSERT(aTarget && aDocument);
+  }
+
   ~nsSimplePluginEvent() {}
 
   NS_IMETHOD Run();
 
 private:
   nsCOMPtr<nsISupports> mTarget;
   nsCOMPtr<nsIDocument> mDocument;
   nsString mEvent;
@@ -698,17 +708,17 @@ nsObjectLoadingContent::UnbindFromTree(b
   nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
   MOZ_ASSERT(thisContent);
   nsIDocument* ownerDoc = thisContent->OwnerDoc();
   ownerDoc->RemovePlugin(this);
 
-  if (mType == eType_Plugin && mInstanceOwner) {
+  if (mType == eType_Plugin && (mInstanceOwner || mInstantiating)) {
     // we'll let the plugin continue to run at least until we get back to
     // the event loop. If we get back to the event loop and the node
     // has still not been added back to the document then we tear down the
     // plugin
     QueueCheckPluginStopEvent();
   } else if (mType != eType_Image) {
     // nsImageLoadingContent handles the image case.
     // Reset state and clear pending events
@@ -739,17 +749,17 @@ nsObjectLoadingContent::nsObjectLoadingC
 nsObjectLoadingContent::~nsObjectLoadingContent()
 {
   // Should have been unbound from the tree at this point, and
   // CheckPluginStopEvent keeps us alive
   if (mFrameLoader) {
     NS_NOTREACHED("Should not be tearing down frame loaders at this point");
     mFrameLoader->Destroy();
   }
-  if (mInstanceOwner) {
+  if (mInstanceOwner || mInstantiating) {
     // This is especially bad as delayed stop will try to hold on to this
     // object...
     NS_NOTREACHED("Should not be tearing down a plugin at this point!");
     StopPluginInstance();
   }
   DestroyImageLoadingContent();
 }
 
@@ -767,17 +777,17 @@ nsObjectLoadingContent::InstantiatePlugi
   }
 
   mInstantiating = true;
   AutoSetInstantiatingToFalse autoInstantiating(this);
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent *>(this));
 
-  nsIDocument* doc = thisContent->GetCurrentDoc();
+  nsCOMPtr<nsIDocument> doc = thisContent->GetCurrentDoc();
   if (!doc || !InActiveDocument(thisContent)) {
     NS_ERROR("Shouldn't be calling "
              "InstantiatePluginInstance without an active document");
     return NS_ERROR_FAILURE;
   }
 
   // Instantiating an instance can result in script execution, which
   // can destroy this DOM object. Don't allow that for the scope
@@ -889,32 +899,34 @@ nsObjectLoadingContent::InstantiatePlugi
     if ((mURI && !mChannelLoaded) || (mChannelLoaded && !aIsLoading)) {
       NS_ASSERTION(!mChannel, "should not have an existing channel here");
       // We intentionally ignore errors here, leaving it up to the plugin to
       // deal with not having an initial stream.
       OpenChannel();
     }
   }
 
-  nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent,
-    NS_LITERAL_STRING("PluginInstantiated"));
+  nsCOMPtr<nsIRunnable> ev = \
+    new nsSimplePluginEvent(thisContent,
+                            doc,
+                            NS_LITERAL_STRING("PluginInstantiated"));
   NS_DispatchToCurrentThread(ev);
 
   return NS_OK;
 }
 
 void
 nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged()
 {
   // XXX(johns): We cannot touch plugins or run arbitrary script from this call,
   //             as nsDocument is in a non-reentrant state.
 
   // If we have a plugin we want to queue an event to stop it unless we are
   // moved into an active document before returning to the event loop.
-  if (mInstanceOwner)
+  if (mInstanceOwner || mInstantiating)
     QueueCheckPluginStopEvent();
 }
 
 // nsIRequestObserver
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
                                        nsISupports *aContext)
 {
@@ -1068,18 +1080,20 @@ nsObjectLoadingContent::HasNewFrame(nsIO
   if (mType != eType_Plugin) {
     return NS_OK;
   }
 
   if (!aFrame) {
     // Lost our frame. If we aren't going to be getting a new frame, e.g. we've
     // become display:none, we'll want to stop the plugin. Queue a
     // CheckPluginStopEvent
-    if (mInstanceOwner) {
-      mInstanceOwner->SetFrame(nullptr);
+    if (mInstanceOwner || mInstantiating) {
+      if (mInstanceOwner) {
+        mInstanceOwner->SetFrame(nullptr);
+      }
       QueueCheckPluginStopEvent();
     }
     return NS_OK;
   }
 
   // Have a new frame
 
   if (!mInstanceOwner) {
@@ -2821,16 +2835,20 @@ nsObjectLoadingContent::DoStopPlugin(nsP
 
 NS_IMETHODIMP
 nsObjectLoadingContent::StopPluginInstance()
 {
   // Clear any pending events
   mPendingInstantiateEvent = nullptr;
   mPendingCheckPluginStopEvent = nullptr;
 
+  // If we're currently instantiating, clearing this will cause
+  // InstantiatePluginInstance's re-entrance check to destroy the created plugin
+  mInstantiating = false;
+
   if (!mInstanceOwner) {
     return NS_OK;
   }
 
   if (mChannel) {
     // The plugin has already used data from this channel, we'll need to
     // re-open it to handle instantiating again, even if we don't invalidate
     // our loaded state.
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -1001,16 +1001,17 @@ void
 nsScriptLoader::FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest,
                                              JS::Handle<JSObject *> aScopeChain,
                                              JS::CompileOptions *aOptions)
 {
   // It's very important to use aRequest->mURI, not the final URI of the channel
   // aRequest ended up getting script data from, as the script filename.
   nsContentUtils::GetWrapperSafeScriptFilename(mDocument, aRequest->mURI, aRequest->mURL);
 
+  aOptions->setIntroductionType("scriptElement");
   aOptions->setFileAndLine(aRequest->mURL.get(), aRequest->mLineNo);
   aOptions->setVersion(JSVersion(aRequest->mJSVersion));
   aOptions->setCompileAndGo(JS_IsGlobalObject(aScopeChain));
   if (aRequest->mHasSourceMapURL) {
     aOptions->setSourceMapURL(aRequest->mSourceMapURL.get());
   }
   if (aRequest->mOriginPrincipal) {
     aOptions->setOriginPrincipals(nsJSPrincipals::get(aRequest->mOriginPrincipal));
--- a/content/base/test/mochitest-child-permissions.ini
+++ b/content/base/test/mochitest-child-permissions.ini
@@ -1,2 +1,4 @@
 [test_messagemanager_assertpermission.html]
+skip-if = buildapp == 'b2g' #b2g(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE) b2g-debug(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE) b2g-desktop(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE)
 [test_child_process_shutdown_message.html]
+skip-if = buildapp == 'b2g' #b2g(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE) b2g-debug(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE) b2g-desktop(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE)
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -539,17 +539,17 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_bug902847.html]
 [test_bug907892.html]
 [test_bug922681.html]
 [test_bug927196.html]
 [test_caretPositionFromPoint.html]
 [test_classList.html]
 # This test fails on the Mac for some reason
 [test_copyimage.html]
-skip-if = toolkit != 'gtk2' && toolkit != 'gtk3' && toolkit != 'windows'
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit != 'gtk2' && toolkit != 'gtk3' && toolkit != 'windows' #b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_copypaste.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
 [test_copypaste.xhtml]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(bug 904183) b2g-debug(bug 904183) b2g-desktop(bug 904183)
 [test_createHTMLDocument.html]
 [test_declare_stylesheet_obsolete.html]
 [test_domparser_null_char.html]
 [test_domparsing.html]
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -470,25 +470,25 @@ skip-if = buildapp == 'b2g'
 # about reenabling it on any platform unless you *know* that you have fixed
 # that. Then don't think about reenabling it on Windows until you know that
 # you have fixed the timeouts of bug 832768, bug 814533, bug 840742
 
 [test_play_twice.html]
 skip-if = appname == "seamonkey" # See bug 598252
 
 [test_buffered.html]
-skip-if = toolkit == 'android' || os == "win" # See bug 832768 and 864682
+skip-if = toolkit == 'android' || os == "win" || (toolkit == 'gonk' && !debug) # See bug 832768 and 864682, b2g(assertion failures)
 [test_bug465498.html]
-skip-if = os == "win" # See bug 832768 and 864682
+skip-if = os == "win" || (toolkit == 'gonk' && !debug) # See bug 832768 and 864682
 [test_bug493187.html]
-skip-if = os == "win" || (buildapp=='b2g'&&debug) # See bug 707777, #b2g-emulator-debug - process crash
+skip-if = os == "win" || (buildapp=='b2g'&&debug) || (toolkit == 'gonk' && !debug) # See bug 707777, #b2g-emulator-debug - process crash
 [test_media_selection.html]
-skip-if = os == "win" # See bug 897843
+skip-if = os == "win" || (toolkit == 'gonk' && !debug) # See bug 897843, b2g(timed out)
 [test_seek.html]
-skip-if = toolkit == 'android' || os == "win" # See bug 832678, 795271, and 857424 # android(bug 845162) androidx86(bug 845162)
+skip-if = toolkit == 'android' || os == "win" || (toolkit == 'gonk' && !debug) # See bug 832678, 795271, and 857424 # android(bug 845162) androidx86(bug 845162)
 
 # The tests below contain backend-specific tests. Write backend independent
 # tests rather than adding to this list.
 
 [test_can_play_type_webm.html]
 run-if = webm
 [test_can_play_type_no_webm.html]
 skip-if = webm
--- a/content/media/webaudio/test/mochitest.ini
+++ b/content/media/webaudio/test/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) #b2g-debug(bug 916135) b2g-desktop(bug 916135)
 support-files =
   audio-expected.wav
   audio-mono-expected-2.wav
   audio-mono-expected.wav
   audio-quad.wav
   audio.ogv
   audioBufferSourceNodeNeutered_worker.js
   invalid.txt
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2638,17 +2638,18 @@ nsXULPrototypeScript::Compile(const char
     JSAutoCompartment ac(cx, aProtoDoc->GetCompilationGlobal());
 
     nsAutoCString urlspec;
     nsContentUtils::GetWrapperSafeScriptFilename(aDocument, aURI, urlspec);
 
     // Ok, compile it to create a prototype script object!
     NS_ENSURE_TRUE(JSVersion(mLangVersion) != JSVERSION_UNKNOWN, NS_OK);
     JS::CompileOptions options(cx);
-    options.setFileAndLine(urlspec.get(), aLineNo)
+    options.setIntroductionType("scriptElement")
+           .setFileAndLine(urlspec.get(), aLineNo)
            .setVersion(JSVersion(mLangVersion));
     // If the script was inline, tell the JS parser to save source for
     // Function.prototype.toSource(). If it's out of line, we retrieve the
     // source from the files on demand.
     options.setSourcePolicy(mOutOfLine ? JS::CompileOptions::LAZY_SOURCE
                                        : JS::CompileOptions::SAVE_SOURCE);
     JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
     if (scope) {
--- a/content/xul/content/test/mochitest.ini
+++ b/content/xul/content/test/mochitest.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
+skip-if = buildapp == 'b2g' #tests that use xul
 
 [test_bug486990.xul]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug749367.xul]
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = toolkit=='gonk' #b2g(bug 972927, nearly perma-fail) b2g-debug(bug 972927, nearly perma-fail)
 support-files =
   file_app.sjs
   file_app.template.html
   file_cached_app.template.appcache
   file_cached_app.template.webapp
   file_hosted_app.template.webapp
   file_packaged_app.sjs
   file_packaged_app.template.html
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -1,68 +1,81 @@
 [DEFAULT]
 # Both the "inproc" and "oop" versions of OpenMixedProcess open remote frames,
 # so we don't run that test on platforms which don't support OOP tests.
 # OOP tests don't work on native-fennec (bug 774939).
 # Bug 960345 - Disabled on OSX debug for frequent crashes.
-skip-if = os == "android" || (toolkit == "cocoa" && debug)
+skip-if = os == "android" || (toolkit == "cocoa" && debug) || (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 support-files =
   browserElement_OpenMixedProcess.js
   file_browserElement_OpenMixedProcess.html
 
 [test_browserElement_inproc_ErrorSecurity.html]
 skip-if = toolkit=='gonk'
 [test_browserElement_inproc_OpenMixedProcess.html]
-skip-if = toolkit=='gonk'
+skip-if = toolkit=='gonk' || (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_Alert.html]
 [test_browserElement_oop_AlertInFrame.html]
 [test_browserElement_oop_AppFramePermission.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_AppWindowNamespace.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_Auth.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BackForward.html]
 [test_browserElement_oop_BadScreenshot.html]
 [test_browserElement_oop_BrowserWindowNamespace.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BrowserWindowResize.html]
 [test_browserElement_oop_Close.html]
 [test_browserElement_oop_CookiesNotThirdParty.html]
 [test_browserElement_oop_DOMRequestError.html]
 [test_browserElement_oop_DataURI.html]
 [test_browserElement_oop_DocumentFirstPaint.html]
 [test_browserElement_oop_ErrorSecurity.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_FirstPaint.html]
 [test_browserElement_oop_ForwardName.html]
 [test_browserElement_oop_FrameWrongURI.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_GetScreenshot.html]
 [test_browserElement_oop_GetScreenshotDppx.html]
 [test_browserElement_oop_Iconchange.html]
 [test_browserElement_oop_LoadEvents.html]
 [test_browserElement_oop_Metachange.html]
 [test_browserElement_oop_OpenMixedProcess.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenNamed.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenWindow.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenWindowDifferentOrigin.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenWindowInFrame.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenWindowRejected.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_Opensearch.html]
 [test_browserElement_oop_PromptCheck.html]
 [test_browserElement_oop_PromptConfirm.html]
 [test_browserElement_oop_PurgeHistory.html]
 [test_browserElement_oop_Reload.html]
 [test_browserElement_oop_ReloadPostRequest.html]
 [test_browserElement_oop_RemoveBrowserElement.html]
 [test_browserElement_oop_ScrollEvent.html]
 [test_browserElement_oop_SecurityChange.html]
-skip-if = toolkit == 'android' #TIMED_OUT, bug 766586
+skip-if = toolkit == 'android' || (toolkit == 'gonk' && !debug) #TIMED_OUT, bug 766586
 [test_browserElement_oop_SendEvent.html]
 [test_browserElement_oop_SetInputMethodActive.html]
 [test_browserElement_oop_SetVisible.html]
 [test_browserElement_oop_SetVisibleFrames.html]
 [test_browserElement_oop_SetVisibleFrames2.html]
 [test_browserElement_oop_Stop.html]
 [test_browserElement_oop_TargetBlank.html]
+skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_TargetTop.html]
 [test_browserElement_oop_Titlechange.html]
 [test_browserElement_oop_TopBarrier.html]
 [test_browserElement_oop_VisibilityChange.html]
 [test_browserElement_oop_XFrameOptions.html]
 [test_browserElement_oop_XFrameOptionsAllowFrom.html]
 [test_browserElement_oop_XFrameOptionsDeny.html]
 [test_browserElement_oop_XFrameOptionsSameOrigin.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 support-files =
   ../../../browser/base/content/test/general/audio.ogg
   ../../../content/media/test/short-video.ogv
   browserElementTestHelpers.js
   browserElement_Alert.js
   browserElement_AlertInFrame.js
   browserElement_AppFramePermission.js
   browserElement_AppWindowNamespace.js
--- a/dom/browser-element/mochitest/priority/mochitest.ini
+++ b/dom/browser-element/mochitest/priority/mochitest.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 # Good luck running these tests on anything but desktop Linux.
-skip-if = toolkit != "gtk2"
+skip-if = toolkit != "gtk2" || (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 
 # Note: ../browserElementTestHelpers.js makes all tests in this directory OOP,
 # because testing the process-priority manager without OOP frames does not make
 # much sense.
 
 [test_Simple.html]
 [test_Visibility.html]
 [test_HighPriority.html]
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = toolkit=='gonk' #b2g(bug 974270, frequent failures) b2g-debug(bug 974270, frequent failures)
 support-files =
   file_app_install.html
   file_readonly.html
   file_basic.html
   file_changes.html
   file_changes2.html
   file_app.sjs
   file_app.template.webapp
--- a/dom/imptests/editing/mochitest.ini
+++ b/dom/imptests/editing/mochitest.ini
@@ -5,22 +5,24 @@ support-files =
   css/reset.css
   implementation.js
   selecttest/common.js
   selecttest/test-iframe.html
   tests.js
 
 [conformancetest/test_event.html]
 [conformancetest/test_runtest.html]
-skip-if = toolkit == 'android'
+skip-if = (toolkit == 'android') || (buildapp == 'b2g') #b2g(takes too long) b2g-debug(takes too long) b2g-desktop(takes too long) 
 [selecttest/test_Document-open.html]
 [selecttest/test_addRange.html]
-skip-if = toolkit == 'android' #bug 775227
+skip-if = (toolkit == 'android') || (buildapp == 'b2g') #android(bug 775227) b2g(oom?, bug 775227) b2g-debug(oom?, bug 775227) b2g-desktop(oom?, bug 775227)
 [selecttest/test_collapse.html]
 [selecttest/test_collapseToStartEnd.html]
 [selecttest/test_deleteFromDocument.html]
 [selecttest/test_extend.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure; time out)
 [selecttest/test_getRangeAt.html]
 [selecttest/test_getSelection.html]
 [selecttest/test_interfaces.html]
 [selecttest/test_isCollapsed.html]
 [selecttest/test_removeAllRanges.html]
 [selecttest/test_selectAllChildren.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
--- a/dom/imptests/html/mochitest.ini
+++ b/dom/imptests/html/mochitest.ini
@@ -52,233 +52,436 @@ support-files =
   dom/nodes/encoding.php
   dom/nodes/productions.js
   dom/ranges/Range-test-iframe.html
   dom/traversal/unfinished/TODO
   js/builtins/Math.maxmin.js
   webgl/common.js
 
 [dom/collections/test_HTMLCollection-empty-name.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/errors/test_DOMException-constants.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/errors/test_exceptions.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_Event-constants.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_Event-constructors.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_Event-defaultPrevented.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_Event-initEvent.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_Event-propagation.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_Event-type.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_EventTarget-addEventListener.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_EventTarget-dispatchEvent.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_EventTarget-removeEventListener.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/events/test_ProgressEvent.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/lists/test_DOMTokenList-stringifier.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_CharacterData-appendData.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_CharacterData-deleteData.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_CharacterData-insertData.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_CharacterData-remove.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_CharacterData-replaceData.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Comment-constructor.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_DOMImplementation-createDocument.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_DOMImplementation-createDocumentType.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_DOMImplementation-createHTMLDocument.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_DOMImplementation-hasFeature.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-adoptNode.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createComment.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createElement-namespace.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createElement.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createElementNS.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createEvent.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createProcessingInstruction-literal-1.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createProcessingInstruction-literal-2.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createProcessingInstruction.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createProcessingInstruction.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-createTreeWalker.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-getElementById.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-getElementsByTagName.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-getElementsByTagNameNS.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Document-importNode.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_DocumentType-remove.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Element-children.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Element-getElementsByClassName.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Element-remove.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Element-removeAttributeNS.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Element-tagName.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-appendChild.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-cloneNode.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-compareDocumentPosition.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-constants.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-contains.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-contains.xml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-insertBefore.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-isEqualNode.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-lookupPrefix.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-nodeName.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-nodeName.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-normalize.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-parentElement.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-parentNode.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-properties.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-removeChild.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_Node-replaceChild.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_attributes.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_case.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-01.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-02.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-03.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-04.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-05.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-06.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-07.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-08.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-09.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-10.xml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-11.xml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-12.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-13.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-14.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-15.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-16.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-17.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/nodes/test_getElementsByClassName-18.htm]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-attributes.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-cloneContents.html]
+skip-if = buildapp == 'b2g'
 [dom/ranges/test_Range-cloneRange.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-collapse.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-commonAncestorContainer-2.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-commonAncestorContainer.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-compareBoundaryPoints.html]
+skip-if = buildapp == 'b2g' #b2g(times out, bug 862196) b2g-debug(times out, bug 862196) b2g-desktop(times out, bug 862196)
 [dom/ranges/test_Range-comparePoint-2.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-comparePoint.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-deleteContents.html]
+skip-if = buildapp == 'b2g'
 [dom/ranges/test_Range-detach.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-extractContents.html]
+skip-if = buildapp == 'b2g'
 [dom/ranges/test_Range-insertNode.html]
+skip-if = buildapp == 'b2g' #b2g(oom?, bug 775227) b2g-debug(oom?, bug 775227) b2g-desktop(oom?, bug 775227)
 [dom/ranges/test_Range-intersectsNode-binding.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-intersectsNode.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-isPointInRange.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-mutations.html]
+skip-if = buildapp == 'b2g' #Test timed out.
 [dom/ranges/test_Range-selectNode.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/ranges/test_Range-set.html]
+skip-if = buildapp == 'b2g'
 [dom/ranges/test_Range-surroundContents.html]
+skip-if = buildapp == 'b2g'
 [dom/test_historical.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/test_interface-objects.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/test_interfaces.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [dom/traversal/test_NodeFilter-constants.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [domxpath/test_evaluator-constructor.html]
 [html/browsers/browsing-the-web/read-media/test_pageload-image.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/browsers/browsing-the-web/read-media/test_pageload-video.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/browsers/the-window-object/named-access-on-the-window-object/test_window-null-names.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/browsers/the-window-object/test_window-indexed-properties-strict.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/browsers/the-window-object/test_window-indexed-properties.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/browsers/the-window-object/test_window-named-properties.html]
-skip-if = true # bug 859075
+skip-if = true || (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure) bug 859075
 [html/browsers/the-window-object/test_window-properties.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/browsers/the-window-object/test_window-prototype-chain.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-case.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-case.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-id.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-id.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-namespace.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-namespace.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-newelements.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-newelements.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-null-undef.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-null-undef.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-param.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-param.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/doc.gEBN/test_document.getElementsByName-same.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_Document.getElementsByClassName-null-undef.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_Element.getElementsByClassName-null-undef.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.body-getter.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.body-setter-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.embeds-document.plugins-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.getElementsByClassName-same.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.head-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.head-02.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.images.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.title-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.title-02.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.title-03.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.title-04.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.title-05.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.title-06.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_document.title-07.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_nameditem-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_nameditem-02.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_nameditem-03.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_nameditem-04.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_nameditem-05.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/documents/dta/test_nameditem-06.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_classlist-nonstring.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_dataset-delete.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_dataset-enumeration.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_dataset-get.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_dataset-prototype.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_dataset-set.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_dataset.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_document-dir.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_id-attribute.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/dom/elements/global-attributes/test_id-name.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/obsolete/implreq/oeaaa/test_document-color-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/obsolete/implreq/oeaaa/test_document-color-02.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/obsolete/implreq/oeaaa/test_document-color-03.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/obsolete/implreq/oeaaa/test_document-color-04.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/obsolete/implreq/oeaaa/test_heading-obsolete-attributes-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/obsolete/implreq/oeaaa/test_script-IDL-event-htmlfor.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/document-metadata/the-title-element/test_title.text-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/document-metadata/the-title-element/test_title.text-02.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/document-metadata/the-title-element/test_title.text-03.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/document-metadata/the-title-element/test_title.text-04.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-form-element/test_form-elements-interfaces-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-form-element/test_form-elements-matches.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-form-element/test_form-elements-nameditem-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-form-element/test_form-elements-nameditem-02.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-form-element/test_form-nameditem.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-option-element/test_option-text-backslash.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-option-element/test_option-text-recurse.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-option-element/test_option-text-spaces.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-select-element/test_select-named-getter.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/forms/the-select-element/test_select-remove.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-for-event.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-for-event.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-for-onload.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-language-type.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-languages-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-languages-02.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-noembed-noframes-iframe.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-onload-string.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-text.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/scripting-1/the-script-element/test_script-text.xhtml]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/tabular-data/the-table-element/test_createTBody.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/tabular-data/the-table-element/test_insertRow-method-01.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/tabular-data/the-table-element/test_insertRow-method-02.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/tabular-data/the-table-element/test_tBodies.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/tabular-data/the-table-element/test_table-insertRow.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/tabular-data/the-table-element/test_table-rows.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/semantics/text-level-semantics/the-time-element/test_001.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/webappapis/atob/test_base64.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/webappapis/scripting/events/test_body-onload.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/webappapis/scripting/events/test_event-handler-javascript.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/webappapis/scripting/events/test_event-handler-spec-example.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/webappapis/scripting/processing-model-2/test_window-onerror-parse-error.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/webappapis/scripting/processing-model-2/test_window-onerror-runtime-error-throw.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/webappapis/scripting/processing-model-2/test_window-onerror-runtime-error.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [html/webappapis/timers/test_evil-spec-example.html]
+skip-if = (toolkit == 'gonk' && debug) #b2g-debug(debug-only failure)
 [js/builtins/test_Array.DefineOwnProperty.html]
 [js/builtins/test_Array.prototype.join-order.html]
 [js/builtins/test_Math.max.html]
 [js/builtins/test_Math.min.html]
 [js/builtins/test_Object.prototype.hasOwnProperty-order.html]
 [js/builtins/test_WeakMap.prototype-properties.html]
 [microdata/microdata-dom-api/test_001.html]
 [typedarrays/test_constructors.html]
 [webgl/test_bufferSubData.html]
-skip-if = toolkit == 'android' #WebGL
+skip-if = (toolkit == 'android') || (buildapp == 'b2g') #android(WebGL)
 [webgl/test_compressedTexImage2D.html]
-skip-if = toolkit == 'android' #WebGL
+skip-if = (toolkit == 'android') || (buildapp == 'b2g') #android(WebGL)
 [webgl/test_compressedTexSubImage2D.html]
-skip-if = toolkit == 'android' #WebGL
+skip-if = (toolkit == 'android') || (buildapp == 'b2g') #android(WebGL)
 [webgl/test_texImage2D.html]
-skip-if = toolkit == 'android' #WebGL
+skip-if = (toolkit == 'android') || (buildapp == 'b2g') #android(WebGL)
 [webgl/test_texSubImage2D.html]
-skip-if = toolkit == 'android' #WebGL
+skip-if = (toolkit == 'android') || (buildapp == 'b2g') #android(WebGL)
 [webgl/test_uniformMatrixNfv.html]
-skip-if = toolkit == 'android' #WebGL
+skip-if = (toolkit == 'android') || (buildapp == 'b2g') #android(WebGL)
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -42,17 +42,17 @@ using class mozilla::WidgetMouseEvent fr
 using class mozilla::WidgetWheelEvent from "ipc/nsGUIEventIPC.h";
 using struct nsRect from "nsRect.h";
 using class mozilla::WidgetSelectionEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetTextEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetTouchEvent from "ipc/nsGUIEventIPC.h";
 using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h";
 using mozilla::dom::ScreenOrientation from "mozilla/dom/ScreenOrientation.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
-using mozilla::CSSIntPoint from "Units.h";
+using mozilla::CSSPoint from "Units.h";
 using mozilla::CSSToScreenScale from "Units.h";
 
 namespace mozilla {
 namespace dom {
 
 intr protocol PBrowser
 {
     manager PContent;
@@ -361,40 +361,40 @@ child:
      */
     AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
 
     /**
      * Requests handling of a double tap. |point| is in CSS pixels, relative to
      * the scroll offset. This message is expected to round-trip back to
      * ZoomToRect() with a rect indicating where we should zoom to.
      */
-    HandleDoubleTap(CSSIntPoint point, ScrollableLayerGuid aGuid);
+    HandleDoubleTap(CSSPoint point, ScrollableLayerGuid aGuid);
 
     /**
      * Requests handling of a single tap. |point| is in CSS pixels, relative to
      * the scroll offset. This message is expected to send a "mousedown" and
      * "mouseup" series of events at this point.
      */
-    HandleSingleTap(CSSIntPoint point, ScrollableLayerGuid aGuid);
+    HandleSingleTap(CSSPoint point, ScrollableLayerGuid aGuid);
 
     /**
      * Requests handling of a long tap. |point| is in CSS pixels, relative to
      * the scroll offset. This message is expected to send a "contextmenu"
      * events at this point.
      */
-    HandleLongTap(CSSIntPoint point, ScrollableLayerGuid aGuid);
+    HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid);
 
     /**
      * Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
      * relative to the current scroll offset. In the case the "contextmenu"
      * event generated by the preceding HandleLongTap call was not handled,
      * this message is expected to generate a "mousedown" and "mouseup"
      * series of events
      */
-    HandleLongTapUp(CSSIntPoint point, ScrollableLayerGuid aGuid);
+    HandleLongTapUp(CSSPoint point, ScrollableLayerGuid aGuid);
 
     /**
      * Notifies the child that the parent has begun or finished transforming
      * the visible child content area. Useful for showing/hiding scrollbars.
      */
     NotifyTransformBegin(ViewID aViewId);
     NotifyTransformEnd(ViewID aViewId);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -656,17 +656,17 @@ TabChild::HandlePossibleViewportChange()
   metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort(
     // The page must have been refreshed in some way such as a new document or
     // new CSS viewport, so we know that there's no velocity, acceleration, and
     // we have no idea how long painting will take.
     metrics, ScreenPoint(0.0f, 0.0f), 0.0);
 
   // Force a repaint with these metrics. This, among other things, sets the
   // displayport, so we start with async painting.
-  ProcessUpdateFrame(metrics);
+  mLastRootMetrics = ProcessUpdateFrame(metrics);
 
   if (viewportInfo.IsZoomAllowed() && scrollIdentifiersValid) {
     // If the CSS viewport is narrower than the screen (i.e. width <= device-width)
     // then we disable double-tap-to-zoom behaviour.
     bool allowDoubleTapZoom = (viewport.width > screenW / metrics.mDevPixelsPerCSSPixel.scale);
     if (allowDoubleTapZoom != viewportInfo.IsDoubleTapZoomAllowed()) {
       viewportInfo.SetAllowDoubleTapZoom(allowDoubleTapZoom);
 
@@ -827,17 +827,17 @@ TabChild::SetChromeFlags(uint32_t aChrom
   NS_NOTREACHED("trying to SetChromeFlags from content process?");
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 TabChild::DestroyBrowserWindow()
 {
-  NS_NOTREACHED("TabChild::SetWebBrowser not supported in TabChild");
+  NS_NOTREACHED("TabChild::DestroyBrowserWindow not supported in TabChild");
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 TabChild::SizeBrowserTo(int32_t aCX, int32_t aCY)
 {
   NS_NOTREACHED("TabChild::SizeBrowserTo not supported in TabChild");
@@ -1472,49 +1472,53 @@ TabChild::DispatchMessageManagerMessage(
 bool
 TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
 {
   MOZ_ASSERT(aFrameMetrics.mScrollId != FrameMetrics::NULL_SCROLL_ID);
 
   if (aFrameMetrics.mIsRoot) {
     nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
     if (APZCCallbackHelper::HasValidPresShellId(utils, aFrameMetrics)) {
-      return ProcessUpdateFrame(aFrameMetrics);
+      mLastRootMetrics = ProcessUpdateFrame(aFrameMetrics);
+      APZCCallbackHelper::UpdateCallbackTransform(aFrameMetrics, mLastRootMetrics);
+      return true;
     }
   } else {
     // aFrameMetrics.mIsRoot is false, so we are trying to update a subframe.
     // This requires special handling.
     nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(
                                       aFrameMetrics.mScrollId);
     if (content) {
       FrameMetrics newSubFrameMetrics(aFrameMetrics);
       APZCCallbackHelper::UpdateSubFrame(content, newSubFrameMetrics);
+      APZCCallbackHelper::UpdateCallbackTransform(aFrameMetrics, newSubFrameMetrics);
       return true;
     }
   }
 
   // We've recieved a message that is out of date and we want to ignore.
   // However we can't reply without painting so we reply by painting the
   // exact same thing as we did before.
-  return ProcessUpdateFrame(mLastRootMetrics);
+  mLastRootMetrics = ProcessUpdateFrame(mLastRootMetrics);
+  return true;
 }
 
 bool
 TabChild::RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
                                       const uint32_t& aScrollGeneration)
 {
   APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
   return true;
 }
 
-bool
+FrameMetrics
 TabChild::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
   {
     if (!mGlobal || !mTabChildGlobal) {
-        return true;
+        return aFrameMetrics;
     }
 
     nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
 
     FrameMetrics newMetrics = aFrameMetrics;
     APZCCallbackHelper::UpdateRootFrame(utils, newMetrics);
 
     CSSRect cssCompositedRect = CSSRect(newMetrics.CalculateCompositedRectInCssPixels());
@@ -1544,47 +1548,45 @@ TabChild::ProcessUpdateFrame(const Frame
         data.AppendLiteral("{ \"width\" : ");
         data.AppendFloat(cssCompositedRect.width);
         data.AppendLiteral(", \"height\" : ");
         data.AppendFloat(cssCompositedRect.height);
         data.AppendLiteral(" }");
     data.AppendLiteral(" }");
 
     DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data);
-
-    mLastRootMetrics = newMetrics;
-
-    return true;
+    return newMetrics;
 }
 
 bool
-TabChild::RecvHandleDoubleTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid)
+TabChild::RecvHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
     if (!mGlobal || !mTabChildGlobal) {
         return true;
     }
 
+    CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
     nsCString data;
-    data += nsPrintfCString("{ \"x\" : %d", aPoint.x);
-    data += nsPrintfCString(", \"y\" : %d", aPoint.y);
+    data += nsPrintfCString("{ \"x\" : %f", point.x);
+    data += nsPrintfCString(", \"y\" : %f", point.y);
     data += nsPrintfCString(" }");
 
     DispatchMessageManagerMessage(NS_LITERAL_STRING("Gesture:DoubleTap"), data);
 
     return true;
 }
 
 bool
-TabChild::RecvHandleSingleTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid)
+TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
-  LayoutDevicePoint currentPoint = CSSPoint(aPoint) * mWidget->GetDefaultScale();;
+  LayoutDevicePoint currentPoint = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();;
 
   MessageLoop::current()->PostDelayedTask(
     FROM_HERE,
     NewRunnableMethod(this, &TabChild::FireSingleTapEvent, currentPoint),
     sActiveDurationMs);
   return true;
 }
 
@@ -1593,33 +1595,35 @@ TabChild::FireSingleTapEvent(LayoutDevic
 {
   int time = 0;
   DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, aPoint);
   DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, aPoint);
   DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint);
 }
 
 bool
-TabChild::RecvHandleLongTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid)
+TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
   mContextMenuHandled =
-      DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"), aPoint, 2, 1, 0, false,
+      DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"),
+                         APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid),
+                         2, 1, 0, false,
                          nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
 
   SendContentReceivedTouch(aGuid, mContextMenuHandled);
 
   return true;
 }
 
 bool
-TabChild::RecvHandleLongTapUp(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid)
+TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (mContextMenuHandled) {
     mContextMenuHandled = false;
     return true;
   }
 
   RecvHandleSingleTap(aPoint, aGuid);
   return true;
@@ -1852,16 +1856,20 @@ TabChild::CancelTapTracking()
   mTapHoldTimer = nullptr;
 }
 
 bool
 TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
                              const ScrollableLayerGuid& aGuid)
 {
   WidgetTouchEvent localEvent(aEvent);
+  for (size_t i = 0; i < localEvent.touches.Length(); i++) {
+    aEvent.touches[i]->mRefPoint = APZCCallbackHelper::ApplyCallbackTransform(aEvent.touches[i]->mRefPoint, aGuid, mWidget->GetDefaultScale());
+  }
+
   nsEventStatus status = DispatchWidgetEvent(localEvent);
 
   if (!IsAsyncPanZoomEnabled()) {
     UpdateTapState(localEvent, status);
     return true;
   }
 
   nsCOMPtr<nsPIDOMWindow> outerWindow = do_GetInterface(mWebNav);
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -213,23 +213,23 @@ public:
                                          MOZ_OVERRIDE;
     virtual bool RecvShow(const nsIntSize& size) MOZ_OVERRIDE;
     virtual bool RecvUpdateDimensions(const nsRect& rect,
                                       const nsIntSize& size,
                                       const ScreenOrientation& orientation) MOZ_OVERRIDE;
     virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
     virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
                                              const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
-    virtual bool RecvHandleDoubleTap(const CSSIntPoint& aPoint,
+    virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    virtual bool RecvHandleSingleTap(const CSSIntPoint& aPoint,
+    virtual bool RecvHandleSingleTap(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    virtual bool RecvHandleLongTap(const CSSIntPoint& aPoint,
+    virtual bool RecvHandleLongTap(const CSSPoint& aPoint,
                                    const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    virtual bool RecvHandleLongTapUp(const CSSIntPoint& aPoint,
+    virtual bool RecvHandleLongTapUp(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvNotifyTransformBegin(const ViewID& aViewId) MOZ_OVERRIDE;
     virtual bool RecvNotifyTransformEnd(const ViewID& aViewId) MOZ_OVERRIDE;
     virtual bool RecvActivate() MOZ_OVERRIDE;
     virtual bool RecvDeactivate() MOZ_OVERRIDE;
     virtual bool RecvMouseEvent(const nsString& aType,
                                 const float&    aX,
                                 const float&    aY,
@@ -412,17 +412,17 @@ private:
 
     void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
     enum FrameScriptLoading { DONT_LOAD_SCRIPTS, DEFAULT_LOAD_SCRIPTS };
     bool InitTabChildGlobal(FrameScriptLoading aScriptLoading = DEFAULT_LOAD_SCRIPTS);
     bool InitRenderingState();
     void DestroyWindow();
     void SetProcessNameToAppName();
-    bool ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics);
+    FrameMetrics ProcessUpdateFrame(const FrameMetrics& aFrameMetrics);
 
     // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
     void DoFakeShow();
 
     // Wrapper for nsIDOMWindowUtils.setCSSViewport(). This updates some state
     // variables local to this class before setting it.
     void SetCSSViewport(const CSSSize& aSize);
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -511,45 +511,45 @@ TabParent::UpdateFrame(const FrameMetric
 void
 TabParent::AcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration)
 {
   if (!mIsDestroyed) {
     unused << SendAcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
   }
 }
 
-void TabParent::HandleDoubleTap(const CSSIntPoint& aPoint,
+void TabParent::HandleDoubleTap(const CSSPoint& aPoint,
                                 int32_t aModifiers,
                                 const ScrollableLayerGuid &aGuid)
 {
   if (!mIsDestroyed) {
     unused << SendHandleDoubleTap(aPoint, aGuid);
   }
 }
 
-void TabParent::HandleSingleTap(const CSSIntPoint& aPoint,
+void TabParent::HandleSingleTap(const CSSPoint& aPoint,
                                 int32_t aModifiers,
                                 const ScrollableLayerGuid &aGuid)
 {
   // TODO Send the modifier data to TabChild for use in mouse events.
   if (!mIsDestroyed) {
     unused << SendHandleSingleTap(aPoint, aGuid);
   }
 }
 
-void TabParent::HandleLongTap(const CSSIntPoint& aPoint,
+void TabParent::HandleLongTap(const CSSPoint& aPoint,
                               int32_t aModifiers,
                               const ScrollableLayerGuid &aGuid)
 {
   if (!mIsDestroyed) {
     unused << SendHandleLongTap(aPoint, aGuid);
   }
 }
 
-void TabParent::HandleLongTapUp(const CSSIntPoint& aPoint,
+void TabParent::HandleLongTapUp(const CSSPoint& aPoint,
                                 int32_t aModifiers,
                                 const ScrollableLayerGuid &aGuid)
 {
   if (!mIsDestroyed) {
     unused << SendHandleLongTapUp(aPoint, aGuid);
   }
 }
 
@@ -716,63 +716,63 @@ bool TabParent::SendRealMouseEvent(Widge
   }
   MaybeForwardEventToRenderFrame(event, nullptr);
   if (!MapEventCoordinatesForChildProcess(&event)) {
     return false;
   }
   return PBrowserParent::SendRealMouseEvent(event);
 }
 
-CSSIntPoint TabParent::AdjustTapToChildWidget(const CSSIntPoint& aPoint)
+CSSPoint TabParent::AdjustTapToChildWidget(const CSSPoint& aPoint)
 {
   nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
 
   if (!content || !content->OwnerDoc()) {
     return aPoint;
   }
 
   nsIDocument* doc = content->OwnerDoc();
   if (!doc || !doc->GetShell()) {
     return aPoint;
   }
   nsPresContext* presContext = doc->GetShell()->GetPresContext();
 
-  return CSSIntPoint(
-    aPoint.x + presContext->DevPixelsToIntCSSPixels(mChildProcessOffsetAtTouchStart.x),
-    aPoint.y + presContext->DevPixelsToIntCSSPixels(mChildProcessOffsetAtTouchStart.y));
+  return aPoint + CSSPoint(
+    presContext->DevPixelsToFloatCSSPixels(mChildProcessOffsetAtTouchStart.x),
+    presContext->DevPixelsToFloatCSSPixels(mChildProcessOffsetAtTouchStart.y));
 }
 
-bool TabParent::SendHandleSingleTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid)
+bool TabParent::SendHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
   return PBrowserParent::SendHandleSingleTap(AdjustTapToChildWidget(aPoint), aGuid);
 }
 
-bool TabParent::SendHandleLongTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid)
+bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
   return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aGuid);
 }
 
-bool TabParent::SendHandleLongTapUp(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid)
+bool TabParent::SendHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
   return PBrowserParent::SendHandleLongTapUp(AdjustTapToChildWidget(aPoint), aGuid);
 }
 
-bool TabParent::SendHandleDoubleTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid)
+bool TabParent::SendHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
   return PBrowserParent::SendHandleDoubleTap(AdjustTapToChildWidget(aPoint), aGuid);
 }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -193,26 +193,26 @@ public:
     void LoadURL(nsIURI* aURI);
     // XXX/cjones: it's not clear what we gain by hiding these
     // message-sending functions under a layer of indirection and
     // eating the return values
     void Show(const nsIntSize& size);
     void UpdateDimensions(const nsRect& rect, const nsIntSize& size);
     void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
     void AcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration);
-    void HandleDoubleTap(const CSSIntPoint& aPoint,
+    void HandleDoubleTap(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const ScrollableLayerGuid& aGuid);
-    void HandleSingleTap(const CSSIntPoint& aPoint,
+    void HandleSingleTap(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const ScrollableLayerGuid& aGuid);
-    void HandleLongTap(const CSSIntPoint& aPoint,
+    void HandleLongTap(const CSSPoint& aPoint,
                        int32_t aModifiers,
                        const ScrollableLayerGuid& aGuid);
-    void HandleLongTapUp(const CSSIntPoint& aPoint,
+    void HandleLongTapUp(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const ScrollableLayerGuid& aGuid);
     void NotifyTransformBegin(ViewID aViewId);
     void NotifyTransformEnd(ViewID aViewId);
     void Activate();
     void Deactivate();
 
     bool MapEventCoordinatesForChildProcess(mozilla::WidgetEvent* aEvent);
@@ -224,20 +224,20 @@ public:
                         int32_t aModifiers, bool aIgnoreRootScrollFrame);
     void SendKeyEvent(const nsAString& aType, int32_t aKeyCode,
                       int32_t aCharCode, int32_t aModifiers,
                       bool aPreventDefault);
     bool SendRealMouseEvent(mozilla::WidgetMouseEvent& event);
     bool SendMouseWheelEvent(mozilla::WidgetWheelEvent& event);
     bool SendRealKeyEvent(mozilla::WidgetKeyboardEvent& event);
     bool SendRealTouchEvent(WidgetTouchEvent& event);
-    bool SendHandleSingleTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid);
-    bool SendHandleLongTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid);
-    bool SendHandleLongTapUp(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid);
-    bool SendHandleDoubleTap(const CSSIntPoint& aPoint, const ScrollableLayerGuid& aGuid);
+    bool SendHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
+    bool SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
+    bool SendHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
+    bool SendHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
 
     virtual PDocumentRendererParent*
     AllocPDocumentRendererParent(const nsRect& documentRect,
                                  const gfx::Matrix& transform,
                                  const nsString& bgcolor,
                                  const uint32_t& renderFlags,
                                  const bool& flushLayout,
                                  const nsIntSize& renderSize) MOZ_OVERRIDE;
@@ -355,17 +355,17 @@ protected:
 
 private:
     already_AddRefed<nsFrameLoader> GetFrameLoader() const;
     already_AddRefed<nsIWidget> GetWidget() const;
     layout::RenderFrameParent* GetRenderFrame();
     nsRefPtr<ContentParent> mManager;
     void TryCacheDPIAndScale();
 
-    CSSIntPoint AdjustTapToChildWidget(const CSSIntPoint& aPoint);
+    CSSPoint AdjustTapToChildWidget(const CSSPoint& aPoint);
 
     // When true, we create a pan/zoom controller for our frame and
     // notify it of input events targeting us.
     bool UseAsyncPanZoom();
     // If we have a render frame currently, notify it that we're about
     // to dispatch |aEvent| to our child.  If there's a relevant
     // transform in place, |aEvent| will be transformed in-place so that
     // it is ready to be dispatched to content.
--- a/dom/permission/tests/mochitest.ini
+++ b/dom/permission/tests/mochitest.ini
@@ -16,9 +16,9 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_systemXHR.html]
 [test_tcp-socket.html]
 [test_webapps-manage.html]
 [test_camera.html]
 disabled = disabled until bug 859593 is fixed
 [test_keyboard.html]
 skip-if = buildapp != 'b2g'
 [test_wifi-manage.html]
-skip-if = buildapp != 'b2g'
+skip-if = (buildapp != 'b2g') || (buildapp == 'b2g' && toolkit != 'gonk') #b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1561,23 +1561,17 @@ bool nsPluginHost::IsJavaMIMEType(const 
   nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
   return aType &&
     (javaMIME.EqualsIgnoreCase(aType) ||
      (0 == PL_strncasecmp(aType, "application/x-java-vm",
                           sizeof("application/x-java-vm") - 1)) ||
      (0 == PL_strncasecmp(aType, "application/x-java-applet",
                           sizeof("application/x-java-applet") - 1)) ||
      (0 == PL_strncasecmp(aType, "application/x-java-bean",
-                          sizeof("application/x-java-bean") - 1))
-#ifdef DEBUG
-     // Emulate java handling for the npjavatest plugin
-     || (0 == PL_strncasecmp(aType, "application/x-java-test",
-                             sizeof("application/x-java-test") - 1))
-#endif
-     );
+                          sizeof("application/x-java-bean") - 1)));
 }
 
 // Check whether or not a tag is a live, valid tag, and that it's loaded.
 bool
 nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag)
 {
   nsPluginTag* tag;
   for (tag = mPlugins; tag; tag = tag->mNext) {
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1677,34 +1677,37 @@ nsPluginInstanceOwner::ProcessMouseDown(
     if (nsEventStatus_eConsumeNoDefault == rv) {
       return aMouseEvent->PreventDefault(); // consume event
     }
   }
   
   return NS_OK;
 }
 
-nsresult nsPluginInstanceOwner::DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent)
+nsresult nsPluginInstanceOwner::DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
+                                                      bool aAllowPropagate)
 {
 #if !defined(XP_MACOSX)
   if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
     return aMouseEvent->PreventDefault(); // consume event
   // continue only for cases without child window
 #endif
   // don't send mouse events if we are hidden
   if (!mWidgetVisible)
     return NS_OK;
 
   WidgetMouseEvent* mouseEvent =
     aMouseEvent->GetInternalNSEvent()->AsMouseEvent();
   if (mouseEvent && mouseEvent->eventStructType == NS_MOUSE_EVENT) {
     nsEventStatus rv = ProcessEvent(*mouseEvent);
     if (nsEventStatus_eConsumeNoDefault == rv) {
       aMouseEvent->PreventDefault();
-      aMouseEvent->StopPropagation();
+      if (!aAllowPropagate) {
+        aMouseEvent->StopPropagation();
+      }
     }
     if (mouseEvent->message == NS_MOUSE_BUTTON_UP) {
       mLastMouseDownButtonType = -1;
     }
   }
   return NS_OK;
 }
 
@@ -1736,18 +1739,20 @@ nsPluginInstanceOwner::HandleEvent(nsIDO
     WidgetMouseEvent* mouseEvent = aEvent->GetInternalNSEvent()->AsMouseEvent();
     if (mouseEvent &&
         static_cast<int>(mouseEvent->button) != mLastMouseDownButtonType) {
       aEvent->PreventDefault();
       return NS_OK;
     }
     return DispatchMouseToPlugin(aEvent);
   }
-  if (eventType.EqualsLiteral("mousemove") ||
-      eventType.EqualsLiteral("click") ||
+  if (eventType.EqualsLiteral("mousemove")) {
+    return DispatchMouseToPlugin(aEvent, true);
+  }
+  if (eventType.EqualsLiteral("click") ||
       eventType.EqualsLiteral("dblclick") ||
       eventType.EqualsLiteral("mouseover") ||
       eventType.EqualsLiteral("mouseout")) {
     return DispatchMouseToPlugin(aEvent);
   }
   if (eventType.EqualsLiteral("keydown") ||
       eventType.EqualsLiteral("keyup")) {
     return DispatchKeyToPlugin(aEvent);
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -360,17 +360,18 @@ private:
   // returned true.
   bool mUseAsyncRendering;
 #endif
   
   // pointer to wrapper for nsIDOMContextMenuListener
   nsRefPtr<nsPluginDOMContextMenuListener> mCXMenuListener;
   
   nsresult DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent);
-  nsresult DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent);
+  nsresult DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
+                                 bool aAllowPropagate = false);
   nsresult DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent);
 
   int mLastMouseDownButtonType;
   
   nsresult EnsureCachedAttrParamArrays();
   
 #ifdef MOZ_X11
   class Renderer
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -164,28 +164,31 @@ PluginModuleChild::Init(const std::strin
     bool exists;
     localFile->Exists(&exists);
     NS_ASSERTION(exists, "plugin file ain't there");
 
     nsPluginFile pluginFile(localFile);
 
 #if defined(MOZ_X11) || defined(OS_MACOSX)
     nsPluginInfo info = nsPluginInfo();
-    if (NS_FAILED(pluginFile.GetPluginInfo(info, &mLibrary)))
+    if (NS_FAILED(pluginFile.GetPluginInfo(info, &mLibrary))) {
         return false;
+    }
 
 #if defined(MOZ_X11)
     NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10.");
     if (StringBeginsWith(nsDependentCString(info.fDescription), flash10Head)) {
         AddQuirk(QUIRK_FLASH_EXPOSE_COORD_TRANSLATION);
     }
 #else // defined(OS_MACOSX)
     mozilla::plugins::PluginUtilsOSX::SetProcessName(info.fName);
 #endif
 
+    pluginFile.FreePluginInfo(info);
+
     if (!mLibrary)
 #endif
     {
         nsresult rv = pluginFile.LoadPlugin(&mLibrary);
         if (NS_FAILED(rv))
             return false;
     }
     NS_ASSERTION(mLibrary, "couldn't open shared object");
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #b2g-desktop(tests that use plugins)
 support-files =
   307-xo-redirect.sjs
   crashing_subpage.html
   file_bug738396.html
   file_bug771202.html
   file_bug863792.html
   large-pic.jpg
   loremipsum.txt
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -1,40 +1,40 @@
 /* ***** BEGIN LICENSE BLOCK *****
- * 
+ *
  * Copyright (c) 2008, Mozilla Corporation
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * * Redistributions of source code must retain the above copyright notice, this
  *   list of conditions and the following disclaimer.
  * * Redistributions in binary form must reproduce the above copyright notice,
  *   this list of conditions and the following disclaimer in the documentation
  *   and/or other materials provided with the distribution.
  * * Neither the name of the Mozilla Corporation nor the names of its
  *   contributors may be used to endorse or promote products derived from this
  *   software without specific prior written permission.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * 
+ *
  * Contributor(s):
  *   Dave Townsend <dtownsend@oxymoronical.com>
  *   Josh Aas <josh@mozilla.com>
- * 
+ *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nptest.h"
 #include "nptest_utils.h"
 #include "nptest_platform.h"
 
 #include "mozilla/IntentionalCrash.h"
 
@@ -384,17 +384,17 @@ static bool sClearByAgeSupported;
 static void initializeIdentifiers()
 {
   if (!sIdentifiersInitialized) {
     NPN_GetStringIdentifiers(sPluginMethodIdentifierNames,
         ARRAY_LENGTH(sPluginMethodIdentifierNames), sPluginMethodIdentifiers);
     NPN_GetStringIdentifiers(sPluginPropertyIdentifierNames,
         ARRAY_LENGTH(sPluginPropertyIdentifierNames), sPluginPropertyIdentifiers);
 
-    sIdentifiersInitialized = true;    
+    sIdentifiersInitialized = true;
 
     // Check whether nullptr is handled in NPN_GetStringIdentifiers
     NPIdentifier IDList[2];
     static char const *const kIDNames[2] = { nullptr, "setCookie" };
     NPN_GetStringIdentifiers(const_cast<const NPUTF8**>(kIDNames), 2, IDList);
   }
 }
 
@@ -426,48 +426,48 @@ static void addRange(InstanceData* insta
 
 static void sendBufferToFrame(NPP instance)
 {
   InstanceData* instanceData = (InstanceData*)(instance->pdata);
   string outbuf;
   if (!instanceData->npnNewStream) outbuf = "data:text/html,";
   const char* buf = reinterpret_cast<char *>(instanceData->streamBuf);
   int32_t bufsize = instanceData->streamBufSize;
-  if (instanceData->streamMode == NP_ASFILE || 
+  if (instanceData->streamMode == NP_ASFILE ||
       instanceData->streamMode == NP_ASFILEONLY) {
     buf = reinterpret_cast<char *>(instanceData->fileBuf);
     bufsize = instanceData->fileBufSize;
   }
   if (instanceData->err.str().length() > 0) {
     outbuf.append(instanceData->err.str());
   }
   else if (bufsize > 0) {
     outbuf.append(buf);
   }
   else {
     outbuf.append("Error: no data in buffer");
   }
-  
+
   if (instanceData->npnNewStream &&
       instanceData->err.str().length() == 0) {
     char typeHTML[] = "text/html";
     NPStream* stream;
     printf("calling NPN_NewStream...");
-    NPError err = NPN_NewStream(instance, typeHTML, 
+    NPError err = NPN_NewStream(instance, typeHTML,
         instanceData->frame.c_str(), &stream);
     printf("return value %d\n", err);
     if (err != NPERR_NO_ERROR) {
       instanceData->err << "NPN_NewStream returned " << err;
       return;
     }
-    
+
     int32_t bytesToWrite = outbuf.length();
     int32_t bytesWritten = 0;
     while ((bytesToWrite - bytesWritten) > 0) {
-      int32_t numBytes = (bytesToWrite - bytesWritten) < 
+      int32_t numBytes = (bytesToWrite - bytesWritten) <
           instanceData->streamChunkSize ?
           bytesToWrite - bytesWritten : instanceData->streamChunkSize;
       int32_t written = NPN_Write(instance, stream,
           numBytes, (void*)(outbuf.c_str() + bytesWritten));
       if (written <= 0) {
         instanceData->err << "NPN_Write returned " << written;
         break;
       }
@@ -498,17 +498,17 @@ static void sendBufferToFrame(NPP instan
           char hex[8];
           sprintf(hex, "%%%x", ascii);
           outbuf.replace(i, 1, hex);
           i += 2;
         }
       }
     }
 
-    NPError err = NPN_GetURL(instance, outbuf.c_str(), 
+    NPError err = NPN_GetURL(instance, outbuf.c_str(),
                              instanceData->frame.c_str());
     if (err != NPERR_NO_ERROR) {
       instanceData->err << "NPN_GetURL returned " << err;
     }
   }
 }
 
 static void XPSleep(unsigned int seconds)
@@ -518,17 +518,17 @@ static void XPSleep(unsigned int seconds
 #else
   sleep(seconds);
 #endif
 }
 
 TestFunction
 getFuncFromString(const char* funcname)
 {
-  FunctionTable funcTable[] = 
+  FunctionTable funcTable[] =
     {
       { FUNCTION_NPP_NEWSTREAM, "npp_newstream" },
       { FUNCTION_NPP_WRITEREADY, "npp_writeready" },
       { FUNCTION_NPP_WRITE, "npp_write" },
       { FUNCTION_NPP_DESTROYSTREAM, "npp_destroystream" },
       { FUNCTION_NPP_WRITE_RPC, "npp_write_rpc" },
       { FUNCTION_NONE, nullptr }
     };
@@ -851,17 +851,17 @@ NPP_New(NPMIMEType pluginType, NPP insta
     addRange(instanceData, "100,100");
   }
 
   bool requestWindow = false;
   // handle extra params
   for (int i = 0; i < argc; i++) {
     if (strcmp(argn[i], "drawmode") == 0) {
       if (strcmp(argv[i], "solid") == 0)
-        scriptableObject->drawMode = DM_SOLID_COLOR;    
+        scriptableObject->drawMode = DM_SOLID_COLOR;
     }
     else if (strcmp(argn[i], "color") == 0) {
       scriptableObject->drawColor = parseHexColor(argv[i], strlen(argv[i]));
     }
     else if (strcmp(argn[i], "wmode") == 0) {
       if (strcmp(argv[i], "window") == 0) {
         requestWindow = true;
       }
@@ -1029,17 +1029,17 @@ NPP_New(NPMIMEType pluginType, NPP insta
   NPObject* o = nullptr;
   err = NPN_GetValue(instance, NPNVPluginElementNPObject, &o);
   if (err == NPERR_NO_ERROR) {
     NPN_SetProperty(instance, o,
                     NPN_GetStringIdentifier("pluginFoundElement"), &variantTrue);
     NPN_ReleaseObject(o);
     o = nullptr;
   }
-  
+
   // Set a property on NPNVWindowNPObject
   err = NPN_GetValue(instance, NPNVWindowNPObject, &o);
   if (err == NPERR_NO_ERROR) {
     NPN_SetProperty(instance, o,
                     NPN_GetStringIdentifier("pluginFoundWindow"), &variantTrue);
     NPN_ReleaseObject(o);
     o = nullptr;
   }
@@ -1048,17 +1048,17 @@ NPP_New(NPMIMEType pluginType, NPP insta
 
   if (instanceData->testFunction == FUNCTION_NPP_GETURL) {
     NPError err = NPN_GetURL(instance, instanceData->testUrl.c_str(), nullptr);
     if (err != NPERR_NO_ERROR) {
       instanceData->err << "NPN_GetURL returned " << err;
     }
   }
   else if (instanceData->testFunction == FUNCTION_NPP_GETURLNOTIFY) {
-    NPError err = NPN_GetURLNotify(instance, instanceData->testUrl.c_str(), 
+    NPError err = NPN_GetURLNotify(instance, instanceData->testUrl.c_str(),
                                    nullptr, static_cast<void*>(&kNotifyData));
     if (err != NPERR_NO_ERROR) {
       instanceData->err << "NPN_GetURLNotify returned " << err;
     }
   }
 
   if ((instanceData->bugMode == 813906) && instanceData->frame.length()) {
     bug813906(instance, "f", "browser.xul", instanceData->frame.c_str());
@@ -1173,17 +1173,17 @@ NPP_SetWindow(NPP instance, NPWindow* wi
   return NPERR_NO_ERROR;
 }
 
 NPError
 NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
 {
   printf("NPP_NewStream\n");
   InstanceData* instanceData = (InstanceData*)(instance->pdata);
-  
+
   if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM &&
       instanceData->failureCode) {
     instanceData->err << SUCCESS_STRING;
     if (instanceData->frame.length() > 0) {
       sendBufferToFrame(instance);
     }
     return instanceData->failureCode;
   }
@@ -1252,30 +1252,30 @@ NPP_DestroyStream(NPP instance, NPStream
     }
 
     if (!instanceData->fileBuf) {
       instanceData->err <<
         "Error: no data written with NPP_StreamAsFile";
       return NPERR_GENERIC_ERROR;
     }
 
-    if (strcmp(reinterpret_cast<char *>(instanceData->fileBuf), 
+    if (strcmp(reinterpret_cast<char *>(instanceData->fileBuf),
                reinterpret_cast<char *>(instanceData->streamBuf))) {
       instanceData->err <<
         "Error: data passed to NPP_Write and NPP_StreamAsFile differed";
     }
   }
-  if (instanceData->frame.length() > 0 && 
+  if (instanceData->frame.length() > 0 &&
       instanceData->testFunction != FUNCTION_NPP_GETURLNOTIFY &&
       instanceData->testFunction != FUNCTION_NPP_POSTURL) {
     sendBufferToFrame(instance);
   }
   if (instanceData->testFunction == FUNCTION_NPP_POSTURL) {
-    NPError err = NPN_PostURL(instance, instanceData->testUrl.c_str(), 
-      instanceData->postMode == POSTMODE_FRAME ? instanceData->frame.c_str() : nullptr, 
+    NPError err = NPN_PostURL(instance, instanceData->testUrl.c_str(),
+      instanceData->postMode == POSTMODE_FRAME ? instanceData->frame.c_str() : nullptr,
       instanceData->streamBufSize,
       reinterpret_cast<char *>(instanceData->streamBuf), false);
     if (err != NPERR_NO_ERROR)
       instanceData->err << "Error: NPN_PostURL returned error value " << err;
   }
   return NPERR_NO_ERROR;
 }
 
@@ -1283,17 +1283,17 @@ int32_t
 NPP_WriteReady(NPP instance, NPStream* stream)
 {
   printf("NPP_WriteReady\n");
   InstanceData* instanceData = (InstanceData*)(instance->pdata);
   instanceData->writeReadyCount++;
   if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM) {
     instanceData->err << "NPP_WriteReady called";
   }
-  
+
   // temporarily disabled per bug 519870
   //if (instanceData->writeReadyCount == 1) {
   //  return 0;
   //}
 
   return instanceData->streamChunkSize;
 }
 
@@ -1313,17 +1313,17 @@ NPP_Write(NPP instance, NPStream* stream
     // Make an RPC call and pretend to consume the data
     NPObject* windowObject = nullptr;
     NPN_GetValue(instance, NPNVWindowNPObject, &windowObject);
     if (windowObject)
       NPN_ReleaseObject(windowObject);
 
     return len;
   }
-  
+
   if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM) {
     instanceData->err << "NPP_Write called";
   }
 
   if (instanceData->functionToFail == FUNCTION_NPP_WRITE) {
     return -1;
   }
 
@@ -1349,17 +1349,17 @@ NPP_Write(NPP instance, NPStream* stream
   if (instanceData->closeStream) {
     instanceData->closeStream = false;
     if (instanceData->testrange != nullptr) {
       NPN_RequestRead(stream, instanceData->testrange);
     }
     NPN_DestroyStream(instance, stream, NPRES_USER_BREAK);
   }
   else if (instanceData->streamMode == NP_SEEK &&
-      stream->end != 0 && 
+      stream->end != 0 &&
       stream->end == ((uint32_t)instanceData->streamBufSize + len)) {
     // If the complete stream has been written, and we're doing a seek test,
     // then call NPN_RequestRead.
     // prevent recursion
     instanceData->streamMode = NP_NORMAL;
 
     if (instanceData->testrange != nullptr) {
       NPError err = NPN_RequestRead(stream, instanceData->testrange);
@@ -1368,17 +1368,17 @@ NPP_Write(NPP instance, NPStream* stream
       }
       printf("called NPN_RequestRead, return %d\n", err);
     }
   }
 
   char* streamBuf = reinterpret_cast<char *>(instanceData->streamBuf);
   if (offset + len <= instanceData->streamBufSize) {
     if (memcmp(buffer, streamBuf + offset, len)) {
-      instanceData->err << 
+      instanceData->err <<
           "Error: data written from NPN_RequestRead doesn't match";
     }
     else {
       printf("data matches!\n");
     }
     TestRange* range = instanceData->testrange;
     bool stillwaiting = false;
     while(range != nullptr) {
@@ -1397,18 +1397,18 @@ NPP_Write(NPP instance, NPStream* stream
     }
   }
   else {
     if (instanceData->streamBufSize == 0) {
       instanceData->streamBuf = malloc(len + 1);
       streamBuf = reinterpret_cast<char *>(instanceData->streamBuf);
     }
     else {
-      instanceData->streamBuf = 
-        realloc(reinterpret_cast<char *>(instanceData->streamBuf), 
+      instanceData->streamBuf =
+        realloc(reinterpret_cast<char *>(instanceData->streamBuf),
         instanceData->streamBufSize + len + 1);
       streamBuf = reinterpret_cast<char *>(instanceData->streamBuf);
     }
     memcpy(streamBuf + instanceData->streamBufSize, buffer, len);
     instanceData->streamBufSize = instanceData->streamBufSize + len;
     streamBuf[instanceData->streamBufSize] = '\0';
   }
   return len;
@@ -1808,40 +1808,40 @@ NPN_GetURL(NPP instance, const char* url
 
 NPError
 NPN_RequestRead(NPStream* stream, NPByteRange* rangeList)
 {
   return sBrowserFuncs->requestread(stream, rangeList);
 }
 
 NPError
-NPN_PostURLNotify(NPP instance, const char* url, 
-                  const char* target, uint32_t len, 
+NPN_PostURLNotify(NPP instance, const char* url,
+                  const char* target, uint32_t len,
                   const char* buf, NPBool file, void* notifyData)
 {
   return sBrowserFuncs->posturlnotify(instance, url, target, len, buf, file, notifyData);
 }
 
-NPError 
+NPError
 NPN_PostURL(NPP instance, const char *url,
                     const char *target, uint32_t len,
                     const char *buf, NPBool file)
 {
   return sBrowserFuncs->posturl(instance, url, target, len, buf, file);
 }
 
 NPError
 NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason)
 {
   return sBrowserFuncs->destroystream(instance, stream, reason);
 }
 
 NPError
-NPN_NewStream(NPP instance, 
-              NPMIMEType  type, 
+NPN_NewStream(NPP instance,
+              NPMIMEType  type,
               const char* target,
               NPStream**  stream)
 {
   return sBrowserFuncs->newstream(instance, type, target, stream);
 }
 
 int32_t
 NPN_Write(NPP instance,
@@ -1853,17 +1853,17 @@ NPN_Write(NPP instance,
 }
 
 bool
 NPN_Enumerate(NPP instance,
               NPObject *npobj,
               NPIdentifier **identifiers,
               uint32_t *identifierCount)
 {
-  return sBrowserFuncs->enumerate(instance, npobj, identifiers, 
+  return sBrowserFuncs->enumerate(instance, npobj, identifiers,
       identifierCount);
 }
 
 bool
 NPN_GetProperty(NPP instance,
                 NPObject *npobj,
                 NPIdentifier propertyName,
                 NPVariant *result)
@@ -1994,17 +1994,17 @@ scriptableInvoke(NPObject* npobj, NPIden
     else {
       for (uint32_t i = 0; i < argCount; i++) {
         const NPString* argstr = &NPVARIANT_TO_STRING(args[i]);
         NPN_SetException(npobj, argstr->UTF8Characters);
       }
     }
     return false;
   }
-  
+
   for (int i = 0; i < int(ARRAY_LENGTH(sPluginMethodIdentifiers)); i++) {
     if (name == sPluginMethodIdentifiers[i])
       return sPluginMethodFunctions[i](npobj, args, argCount, result);
   }
   return false;
 }
 
 bool
@@ -2043,34 +2043,41 @@ scriptableInvokeDefault(NPObject* npobj,
         break;
       case NPVariantType_Null:
         value << ";null";
         break;
       default:
         value << ";other";
     }
   }
-  STRINGZ_TO_NPVARIANT(NPN_StrDup(value.str().c_str()), *result);
+
+  char *outval = NPN_StrDup(value.str().c_str());
+  STRINGZ_TO_NPVARIANT(outval, *result);
   return true;
 }
 
 bool
 scriptableHasProperty(NPObject* npobj, NPIdentifier name)
 {
   if (NPN_IdentifierIsString(name)) {
-    if (NPN_GetStringIdentifier(NPN_UTF8FromIdentifier(name)) != name)
+    NPUTF8 *asUTF8 = NPN_UTF8FromIdentifier(name);
+    if (NPN_GetStringIdentifier(asUTF8) != name) {
       Crash();
+    }
+    NPN_MemFree(asUTF8);
   }
   else {
-    if (NPN_GetIntIdentifier(NPN_IntFromIdentifier(name)) != name)
+    if (NPN_GetIntIdentifier(NPN_IntFromIdentifier(name)) != name) {
       Crash();
+    }
   }
   for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
-    if (name == sPluginPropertyIdentifiers[i])
+    if (name == sPluginPropertyIdentifiers[i]) {
       return true;
+    }
   }
   return false;
 }
 
 bool
 scriptableGetProperty(NPObject* npobj, NPIdentifier name, NPVariant* result)
 {
   for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
@@ -2136,17 +2143,17 @@ compareVariants(NPP instance, const NPVa
 {
   bool success = true;
   InstanceData* id = static_cast<InstanceData*>(instance->pdata);
   if (var1->type != var2->type) {
     id->err << "Variant types don't match; got " << var1->type <<
         " expected " << var2->type;
     return false;
   }
-  
+
   switch (var1->type) {
     case NPVariantType_Int32: {
         int32_t result = NPVARIANT_TO_INT32(*var1);
         int32_t expected = NPVARIANT_TO_INT32(*var2);
         if (result != expected) {
           id->err << "Variant values don't match; got " << result <<
               " expected " << expected;
           success = false;
@@ -2179,18 +2186,18 @@ compareVariants(NPP instance, const NPVa
         }
         break;
       }
     case NPVariantType_String: {
         const NPString* result = &NPVARIANT_TO_STRING(*var1);
         const NPString* expected = &NPVARIANT_TO_STRING(*var2);
         if (strcmp(result->UTF8Characters, expected->UTF8Characters) ||
             strlen(result->UTF8Characters) != strlen(expected->UTF8Characters)) {
-          id->err << "Variant values don't match; got " << 
-              result->UTF8Characters << " expected " << 
+          id->err << "Variant values don't match; got " <<
+              result->UTF8Characters << " expected " <<
               expected->UTF8Characters;
           success = false;
         }
         break;
       }
     case NPVariantType_Object: {
         uint32_t i, identifierCount = 0;
         NPIdentifier* identifiers;
@@ -2216,67 +2223,67 @@ compareVariants(NPP instance, const NPVa
             }
             else {
               if (!NPN_GetProperty(instance, result, identifiers[i],
               &resultVariant)) {
                 id->err << "NPN_GetProperty 2 returned false";
                 success = false;
               }
               else {
-                success = compareVariants(instance, &resultVariant, 
+                success = compareVariants(instance, &resultVariant,
                     &expectedVariant);
                 NPN_ReleaseVariantValue(&expectedVariant);
               }
             }
             NPN_ReleaseVariantValue(&resultVariant);
           }
         }
         break;
       }
     default:
       id->err << "Unknown variant type";
       success = false;
   }
-  
+
   return success;
 }
 
 static bool
 throwExceptionNextInvoke(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
   InstanceData* id = static_cast<InstanceData*>(npp->pdata);
   id->throwOnNextInvoke = true;
   BOOLEAN_TO_NPVARIANT(true, *result);
-  return true;  
+  return true;
 }
 
 static bool
 npnInvokeDefaultTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   bool success = false;
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
- 
+
   NPObject* windowObject;
   NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
   if (!windowObject)
     return false;
 
   NPIdentifier objectIdentifier = variantToIdentifier(args[0]);
   if (!objectIdentifier)
     return false;
 
   NPVariant objectVariant;
   if (NPN_GetProperty(npp, windowObject, objectIdentifier,
       &objectVariant)) {
     if (NPVARIANT_IS_OBJECT(objectVariant)) {
       NPObject* selfObject = NPVARIANT_TO_OBJECT(objectVariant);
       if (selfObject != nullptr) {
         NPVariant resultVariant;
-        if (NPN_InvokeDefault(npp, selfObject, argCount > 1 ? &args[1] : nullptr, 
+        if (NPN_InvokeDefault(npp, selfObject, argCount > 1 ? &args[1] : nullptr,
             argCount - 1, &resultVariant)) {
           *result = resultVariant;
           success = true;
         }
       }
     }
     NPN_ReleaseVariantValue(&objectVariant);
   }
@@ -2292,53 +2299,53 @@ npnInvokeTest(NPObject* npobj, const NPV
   InstanceData* id = static_cast<InstanceData*>(npp->pdata);
   id->err.str("");
   if (argCount < 2)
     return false;
 
   NPIdentifier function = variantToIdentifier(args[0]);
   if (!function)
     return false;
-  
+
   NPObject* windowObject;
   NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
   if (!windowObject)
     return false;
-  
+
   NPVariant invokeResult;
   bool invokeReturn = NPN_Invoke(npp, windowObject, function,
       argCount > 2 ? &args[2] : nullptr, argCount - 2, &invokeResult);
-      
+
   bool compareResult = compareVariants(npp, &invokeResult, &args[1]);
-      
+
   NPN_ReleaseObject(windowObject);
   NPN_ReleaseVariantValue(&invokeResult);
   BOOLEAN_TO_NPVARIANT(invokeReturn && compareResult, *result);
   return true;
 }
 
 static bool
 npnEvaluateTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   bool success = false;
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
-  
+
   if (argCount != 1)
     return false;
-  
+
   if (!NPVARIANT_IS_STRING(args[0]))
     return false;
 
   NPObject* windowObject;
   NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
   if (!windowObject)
     return false;
-  
+
   success = NPN_Evaluate(npp, windowObject, (NPString*)&NPVARIANT_TO_STRING(args[0]), result);
-  
+
   NPN_ReleaseObject(windowObject);
   return success;
 }
 
 static bool
 setUndefinedValueTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
@@ -2575,20 +2582,23 @@ setSlowPaint(NPObject* npobj, const NPVa
 static bool
 getError(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount != 0)
     return false;
 
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
   InstanceData* id = static_cast<InstanceData*>(npp->pdata);
-  if (id->err.str().length() == 0)
-    STRINGZ_TO_NPVARIANT(NPN_StrDup(SUCCESS_STRING), *result);
-  else
-    STRINGZ_TO_NPVARIANT(NPN_StrDup(id->err.str().c_str()), *result);
+  if (id->err.str().length() == 0) {
+    char *outval = NPN_StrDup(SUCCESS_STRING);
+    STRINGZ_TO_NPVARIANT(outval, *result);
+  } else {
+    char *outval = NPN_StrDup(id->err.str().c_str());
+    STRINGZ_TO_NPVARIANT(outval, *result);
+  }
   return true;
 }
 
 static bool
 doInternalConsistencyCheck(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount != 0)
     return false;
@@ -2658,17 +2668,17 @@ convertPointY(NPObject* npobj, const NPV
   double sourceY = static_cast<double>(NPVARIANT_TO_INT32(args[2]));
 
   if (!NPVARIANT_IS_INT32(args[3]))
     return false;
   int32_t destSpace = NPVARIANT_TO_INT32(args[3]);
 
   double resultX, resultY;
   NPN_ConvertPoint(npp, sourceX, sourceY, (NPCoordinateSpace)sourceSpace, &resultX, &resultY, (NPCoordinateSpace)destSpace);
-  
+
   DOUBLE_TO_NPVARIANT(resultY, *result);
   return true;
 }
 
 static bool
 streamTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   // .streamTest(url, doPost, doNull, writeCallback, notifyCallback, redirectCallback, allowRedirects)
@@ -2901,17 +2911,19 @@ static const NPClass kTestSharedNPClass 
 static bool getJavaCodebase(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount != 0) {
     return false;
   }
 
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
   InstanceData* id = static_cast<InstanceData*>(npp->pdata);
-  STRINGZ_TO_NPVARIANT(NPN_StrDup(id->javaCodebase.c_str()), *result);
+
+  char *outval = NPN_StrDup(id->javaCodebase.c_str());
+  STRINGZ_TO_NPVARIANT(outval, *result);
   return true;
 }
 
 static bool getObjectValue(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
 
   NPObject* o = NPN_CreateObject(npp,
@@ -2949,22 +2961,22 @@ static bool enableFPExceptions(NPObject*
 #else
   return false;
 #endif
 }
 
 // caller is responsible for freeing return buffer
 static char* URLForInstanceWindow(NPP instance) {
   char *outString = nullptr;
-  
+
   NPObject* windowObject = nullptr;
   NPError err = NPN_GetValue(instance, NPNVWindowNPObject, &windowObject);
   if (err != NPERR_NO_ERROR || !windowObject)
     return nullptr;
-  
+
   NPIdentifier locationIdentifier = NPN_GetStringIdentifier("location");
   NPVariant locationVariant;
   if (NPN_GetProperty(instance, windowObject, locationIdentifier, &locationVariant)) {
     NPObject *locationObject = locationVariant.value.objectValue;
     if (locationObject) {
       NPIdentifier hrefIdentifier = NPN_GetStringIdentifier("href");
       NPVariant hrefVariant;
       if (NPN_GetProperty(instance, locationObject, hrefIdentifier, &hrefVariant)) {
@@ -2972,64 +2984,64 @@ static char* URLForInstanceWindow(NPP in
         if (hrefString) {
           outString = (char *)malloc(hrefString->UTF8Length + 1);
           if (outString) {
             strcpy(outString, hrefString->UTF8Characters);
             outString[hrefString->UTF8Length] = '\0';
           }
         }
         NPN_ReleaseVariantValue(&hrefVariant);
-      }      
+      }
     }
     NPN_ReleaseVariantValue(&locationVariant);
   }
-  
+
   NPN_ReleaseObject(windowObject);
-  
+
   return outString;
 }
 
 static bool
 setCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount != 1)
     return false;
   if (!NPVARIANT_IS_STRING(args[0]))
     return false;
   const NPString* cookie = &NPVARIANT_TO_STRING(args[0]);
-  
+
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
-  
+
   char* url = URLForInstanceWindow(npp);
   if (!url)
     return false;
   NPError err = NPN_SetValueForURL(npp, NPNURLVCookie, url, cookie->UTF8Characters, cookie->UTF8Length);
   free(url);
-  
+
   return (err == NPERR_NO_ERROR);
 }
 
 static bool
 getCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount != 0)
     return false;
-  
+
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
-  
+
   char* url = URLForInstanceWindow(npp);
   if (!url)
     return false;
   char* cookie = nullptr;
   unsigned int length = 0;
   NPError err = NPN_GetValueForURL(npp, NPNURLVCookie, url, &cookie, &length);
   free(url);
   if (err != NPERR_NO_ERROR || !cookie)
     return false;
-  
+
   STRINGZ_TO_NPVARIANT(cookie, *result);
   return true;
 }
 
 static bool
 getAuthInfo(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount != 5)
@@ -3046,43 +3058,43 @@ getAuthInfo(NPObject* npobj, const NPVar
   const NPString* host = &NPVARIANT_TO_STRING(args[1]);
   uint32_t port = NPVARIANT_TO_INT32(args[2]);
   const NPString* scheme = &NPVARIANT_TO_STRING(args[3]);
   const NPString* realm = &NPVARIANT_TO_STRING(args[4]);
 
   char* username = nullptr;
   char* password = nullptr;
   uint32_t ulen = 0, plen = 0;
-  
-  NPError err = NPN_GetAuthenticationInfo(npp, 
-      protocol->UTF8Characters, 
-      host->UTF8Characters, 
-      port, 
-      scheme->UTF8Characters, 
+
+  NPError err = NPN_GetAuthenticationInfo(npp,
+      protocol->UTF8Characters,
+      host->UTF8Characters,
+      port,
+      scheme->UTF8Characters,
       realm->UTF8Characters,
-      &username, 
-      &ulen, 
-      &password, 
+      &username,
+      &ulen,
+      &password,
       &plen);
-  
+
   if (err != NPERR_NO_ERROR) {
     return false;
   }
 
   char* outstring = (char*)NPN_MemAlloc(ulen + plen + 2);
   memset(outstring, 0, ulen + plen + 2);
   strncpy(outstring, username, ulen);
   strcat(outstring, "|");
   strncat(outstring, password, plen);
 
   STRINGZ_TO_NPVARIANT(outstring, *result);
 
   NPN_MemFree(username);
   NPN_MemFree(password);
-  
+
   return true;
 }
 
 static void timerCallback(NPP npp, uint32_t timerID)
 {
   InstanceData* id = static_cast<InstanceData*>(npp->pdata);
   currentTimerEventCount++;
   timerEvent event = timerEvents[currentTimerEventCount];
@@ -3100,17 +3112,17 @@ static void timerCallback(NPP npp, uint3
   if (currentTimerEventCount == totalTimerEvents - 1) {
     NPVariant arg;
     BOOLEAN_TO_NPVARIANT(id->timerTestResult, arg);
     NPN_Invoke(npp, windowObject, NPN_GetStringIdentifier(id->timerTestScriptCallback.c_str()), &arg, 1, &rval);
     NPN_ReleaseVariantValue(&arg);
   }
 
   NPN_ReleaseObject(windowObject);
-  
+
   if (event.timerIdSchedule > -1) {
     id->timerID[event.timerIdSchedule] = NPN_ScheduleTimer(npp, event.timerInterval, event.timerRepeat, timerCallback);
   }
   if (event.timerIdUnschedule > -1) {
     NPN_UnscheduleTimer(npp, id->timerID[event.timerIdUnschedule]);
   }
 }
 
@@ -3123,19 +3135,19 @@ timerTest(NPObject* npobj, const NPVaria
 
   if (argCount < 1 || !NPVARIANT_IS_STRING(args[0]))
     return false;
   const NPString* argstr = &NPVARIANT_TO_STRING(args[0]);
   id->timerTestScriptCallback = argstr->UTF8Characters;
 
   id->timerTestResult = true;
   timerEvent event = timerEvents[currentTimerEventCount];
-    
+
   id->timerID[event.timerIdSchedule] = NPN_ScheduleTimer(npp, event.timerInterval, event.timerRepeat, timerCallback);
-  
+
   return id->timerID[event.timerIdSchedule] != 0;
 }
 
 #ifdef XP_WIN
 void
 ThreadProc(void* cookie)
 #else
 void*
@@ -3166,17 +3178,17 @@ asyncCallback(void* cookie)
       if (_beginthread(ThreadProc, 0, (void*)npobj) == -1)
         id->asyncCallbackResult = false;
 #else
       pthread_t tid;
       if (pthread_create(&tid, 0, ThreadProc, (void*)npobj))
         id->asyncCallbackResult = false;
 #endif
       break;
-    
+
     // async callback triggered from different thread
     default:
       NPObject* windowObject;
       NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
       if (!windowObject)
         return;
       NPVariant arg, rval;
       BOOLEAN_TO_NPVARIANT(id->asyncCallbackResult, arg);
@@ -3192,21 +3204,21 @@ asyncCallbackTest(NPObject* npobj, const
 {
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
   InstanceData* id = static_cast<InstanceData*>(npp->pdata);
 
   if (argCount < 1 || !NPVARIANT_IS_STRING(args[0]))
     return false;
   const NPString* argstr = &NPVARIANT_TO_STRING(args[0]);
   id->asyncTestScriptCallback = argstr->UTF8Characters;
-  
+
   id->asyncTestPhase = 0;
   id->asyncCallbackResult = true;
   NPN_PluginThreadAsyncCall(npp, asyncCallback, (void*)npobj);
-  
+
   return true;
 }
 
 static bool
 GCRaceInvoke(NPObject*, NPIdentifier, const NPVariant*, uint32_t, NPVariant*)
 {
   return false;
 }
@@ -3282,17 +3294,17 @@ FinishGCRace(void* closure)
 bool
 checkGCRace(NPObject* npobj, const NPVariant* args, uint32_t argCount,
 	    NPVariant* result)
 {
   if (1 != argCount || !NPVARIANT_IS_OBJECT(args[0]))
     return false;
 
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
-  
+
   NPObject* localFunc =
     NPN_CreateObject(npp, const_cast<NPClass*>(&kGCRaceClass));
 
   GCRaceData* rd =
     new GCRaceData(npp, NPVARIANT_TO_OBJECT(args[0]), localFunc);
   NPN_PluginThreadAsyncCall(npp, FinishGCRace, rd);
 
   OBJECT_TO_NPVARIANT(localFunc, *result);
@@ -3305,17 +3317,17 @@ hangPlugin(NPObject* npobj, const NPVari
 {
   mozilla::NoteIntentionalCrash("plugin");
 
   bool busyHang = false;
   if ((argCount == 1) && NPVARIANT_IS_BOOLEAN(args[0])) {
     busyHang = NPVARIANT_TO_BOOLEAN(args[0]);
   }
 
-  if (busyHang) {    
+  if (busyHang) {
     const time_t start = std::time(nullptr);
     while ((std::time(nullptr) - start) < 100000) {
       volatile int dummy = 0;
       for (int i=0; i<1000; ++i) {
         dummy++;
       }
     }
   } else {
@@ -3655,17 +3667,17 @@ bool getWindowPosition(NPObject* npobj, 
 }
 
 bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount == 0 || !NPVARIANT_IS_OBJECT(args[0]))
     return false;
 
   NPObject* ctor = NPVARIANT_TO_OBJECT(args[0]);
-  
+
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
 
   return NPN_Construct(npp, ctor, args + 1, argCount - 1, result);
 }
 
 bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
@@ -3688,22 +3700,22 @@ bool setSitesWithData(NPObject* npobj, c
       next = end;
 
     // Parse out the three tokens into a siteData struct.
     const char* siteEnd = strchr(iterator, ':');
     *((char*) siteEnd) = '\0';
     const char* flagsEnd = strchr(siteEnd + 1, ':');
     *((char*) flagsEnd) = '\0';
     *((char*) next) = '\0';
-    
+
     siteData data;
     data.site = string(iterator);
     data.flags = atoi(siteEnd + 1);
     data.age = atoi(flagsEnd + 1);
-    
+
     sSitesWithData->push_back(data);
 
     if (next == end)
       break;
 
     iterator = next + 1;
   }
 
@@ -3723,17 +3735,19 @@ bool getLastKeyText(NPObject* npobj, con
                     NPVariant* result)
 {
   if (argCount != 0) {
     return false;
   }
 
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
   InstanceData* id = static_cast<InstanceData*>(npp->pdata);
-  STRINGZ_TO_NPVARIANT(NPN_StrDup(id->lastKeyText.c_str()), *result);
+
+  char *outval = NPN_StrDup(id->lastKeyText.c_str());
+  STRINGZ_TO_NPVARIANT(outval, *result);
   return true;
 }
 
 bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args, uint32_t argCount,
                            NPVariant* result)
 {
   if (argCount != 0) {
     return false;
@@ -3751,17 +3765,17 @@ bool getNPNVdocumentOrigin(NPObject* npo
   return true;
 }
 
 bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   if (argCount != 0) {
     return false;
   }
-  
+
   NPP npp = static_cast<TestNPObject*>(npobj)->npp;
   InstanceData* id = static_cast<InstanceData*>(npp->pdata);
   INT32_TO_NPVARIANT(id->mouseUpEventCount, *result);
   return true;
 }
 
 bool queryContentsScaleFactor(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -180,21 +180,20 @@ public:
   }
 };
 
 // Promise
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
-  tmp->MaybeReportRejected();
+  tmp->MaybeReportRejectedOnce();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks);
-  tmp->mResult = JS::UndefinedValue();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
@@ -224,18 +223,17 @@ Promise::Promise(nsIGlobalObject* aGloba
   MOZ_ASSERT(mGlobal);
 
   mozilla::HoldJSObjects(this);
   SetIsDOMBinding();
 }
 
 Promise::~Promise()
 {
-  MaybeReportRejected();
-  mResult = JS::UndefinedValue();
+  MaybeReportRejectedOnce();
   mozilla::DropJSObjects(this);
 }
 
 JSObject*
 Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return PromiseBinding::Wrap(aCx, aScope, this);
 }
@@ -817,16 +815,19 @@ Promise::AppendCallbacks(PromiseCallback
 {
   if (aResolveCallback) {
     mResolveCallbacks.AppendElement(aResolveCallback);
   }
 
   if (aRejectCallback) {
     mHadRejectCallback = true;
     mRejectCallbacks.AppendElement(aRejectCallback);
+
+    // Now that there is a callback, we don't need to report anymore.
+    RemoveFeature();
   }
 
   // If promise's state is resolved, queue a task to process our resolve
   // callbacks with promise's result. If promise's state is rejected, queue a
   // task to process our reject callbacks with promise's result.
   if (mState != Pending && !mTaskPending) {
     if (MOZ_LIKELY(NS_IsMainThread())) {
       nsRefPtr<PromiseTask> task = new PromiseTask(this);
@@ -1053,19 +1054,57 @@ Promise::RunResolveTask(JS::Handle<JS::V
   // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState
   // from asserting.
   if (mState != Pending) {
     return;
   }
 
   SetResult(aValue);
   SetState(aState);
+
+  // If the Promise was rejected, and there is no reject handler already setup,
+  // watch for thread shutdown.
+  if (aState == PromiseState::Rejected &&
+      !mHadRejectCallback &&
+      !NS_IsMainThread()) {
+    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    worker->AssertIsOnWorkerThread();
+
+    mFeature = new PromiseReportRejectFeature(this);
+    if (NS_WARN_IF(!worker->AddFeature(worker->GetJSContext(), mFeature))) {
+      // Worker is shutting down, report rejection immediately since it is
+      // unlikely that reject callbacks will be added after this point.
+      MaybeReportRejected();
+    }
+  }
+
   RunTask();
 }
 
+void
+Promise::RemoveFeature()
+{
+  if (mFeature) {
+    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    worker->RemoveFeature(worker->GetJSContext(), mFeature);
+    mFeature = nullptr;
+  }
+}
+
+bool
+PromiseReportRejectFeature::Notify(JSContext* aCx, workers::Status aStatus)
+{
+  MOZ_ASSERT(aStatus > workers::Running);
+  mPromise->MaybeReportRejectedOnce();
+  // After this point, `this` has been deleted by RemoveFeature!
+  return true;
+}
+
 bool
 Promise::ArgumentToJSValue(const nsAString& aArgument,
                            JSContext* aCx,
                            JSObject* aScope,
                            JS::MutableHandle<JS::Value> aValue)
 {
   // XXXkhuey I'd love to use xpc::NonVoidStringToJsval here, but it requires
   // a non-const nsAString for silly reasons.
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -13,33 +13,53 @@
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/TypedArray.h"
 #include "nsWrapperCache.h"
 #include "nsAutoPtr.h"
 #include "js/TypeDecls.h"
 
+#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class AnyCallback;
 class PromiseCallback;
 class PromiseInit;
 class PromiseNativeHandler;
 
+class Promise;
+class PromiseReportRejectFeature : public workers::WorkerFeature
+{
+  // The Promise that owns this feature.
+  Promise* mPromise;
+
+public:
+  PromiseReportRejectFeature(Promise* aPromise)
+    : mPromise(aPromise)
+  {
+    MOZ_ASSERT(mPromise);
+  }
+
+  virtual bool
+  Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE;
+};
+
 class Promise MOZ_FINAL : public nsISupports,
                           public nsWrapperCache
 {
   friend class NativePromiseCallback;
   friend class PromiseResolverMixin;
   friend class PromiseResolverTask;
   friend class PromiseTask;
+  friend class PromiseReportRejectFeature;
   friend class RejectPromiseCallback;
   friend class ResolvePromiseCallback;
   friend class WorkerPromiseResolverTask;
   friend class WorkerPromiseTask;
   friend class WrapperPromiseCallback;
 
   ~Promise();
 
@@ -152,18 +172,25 @@ private:
                       Promise::PromiseState aState,
                       PromiseTaskSync aAsynchronous);
 
   void AppendCallbacks(PromiseCallback* aResolveCallback,
                        PromiseCallback* aRejectCallback);
 
   // If we have been rejected and our mResult is a JS exception,
   // report it to the error console.
+  // Use MaybeReportRejectedOnce() for actual calls.
   void MaybeReportRejected();
 
+  void MaybeReportRejectedOnce() {
+    MaybeReportRejected();
+    RemoveFeature();
+    mResult = JS::UndefinedValue();
+  }
+
   void MaybeResolveInternal(JSContext* aCx,
                             JS::Handle<JS::Value> aValue,
                             PromiseTaskSync aSync = AsyncTask);
   void MaybeRejectInternal(JSContext* aCx,
                            JS::Handle<JS::Value> aValue,
                            PromiseTaskSync aSync = AsyncTask);
 
   void ResolveInternal(JSContext* aCx,
@@ -266,25 +293,33 @@ private:
   CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise,
                 int32_t aTask);
 
   static JSObject*
   CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask);
 
   void HandleException(JSContext* aCx);
 
+  void RemoveFeature();
+
   nsRefPtr<nsIGlobalObject> mGlobal;
 
   nsTArray<nsRefPtr<PromiseCallback> > mResolveCallbacks;
   nsTArray<nsRefPtr<PromiseCallback> > mRejectCallbacks;
 
   JS::Heap<JS::Value> mResult;
   PromiseState mState;
   bool mTaskPending;
   bool mHadRejectCallback;
 
   bool mResolvePending;
+
+  // If a rejected promise on a worker has no reject callbacks attached, it
+  // needs to know when the worker is shutting down, to report the error on the
+  // console before the worker's context is deleted. This feature is used for
+  // that purpose.
+  nsAutoPtr<PromiseReportRejectFeature> mFeature;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Promise_h
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -305,57 +305,66 @@ int32_t
 gfxMemorySharedReadLock::GetReadCount()
 {
   NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock);
   return mReadCount;
 }
 
 gfxShmSharedReadLock::gfxShmSharedReadLock(ISurfaceAllocator* aAllocator)
   : mAllocator(aAllocator)
+  , mAllocSuccess(false)
 {
   MOZ_COUNT_CTOR(gfxShmSharedReadLock);
-
+  MOZ_ASSERT(mAllocator);
   if (mAllocator) {
 #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
     if (mAllocator->AllocUnsafeShmem(MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)),
                                      mozilla::ipc::SharedMemory::TYPE_BASIC, &mShmem)) {
       ShmReadLockInfo* info = GetShmReadLockInfoPtr();
       info->readCount = 1;
+      mAllocSuccess = true;
     }
   }
 }
 
 gfxShmSharedReadLock::~gfxShmSharedReadLock()
 {
   MOZ_COUNT_DTOR(gfxShmSharedReadLock);
 }
 
 int32_t
 gfxShmSharedReadLock::ReadLock() {
   NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock);
-
+  if (!mAllocSuccess) {
+    return 0;
+  }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   return PR_ATOMIC_INCREMENT(&info->readCount);
 }
 
 int32_t
 gfxShmSharedReadLock::ReadUnlock() {
+  if (!mAllocSuccess) {
+    return 0;
+  }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
   NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock.");
   if (readCount <= 0) {
     mAllocator->DeallocShmem(mShmem);
   }
   return readCount;
 }
 
 int32_t
 gfxShmSharedReadLock::GetReadCount() {
   NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock);
-
+  if (!mAllocSuccess) {
+    return 0;
+  }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   return info->readCount;
 }
 
 // Placeholder
 TileClient::TileClient()
   : mBackBuffer(nullptr)
   , mFrontBuffer(nullptr)
@@ -509,16 +518,19 @@ TileClient::GetBackBuffer(const nsIntReg
     // Create a lock for our newly created back-buffer.
     if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) {
       // If our compositor is in the same process, we can save some cycles by not
       // using shared memory.
       mBackLock = new gfxMemorySharedReadLock();
     } else {
       mBackLock = new gfxShmSharedReadLock(mManager->AsShadowForwarder());
     }
+
+    MOZ_ASSERT(mBackLock->IsValid());
+
     *aCreatedTextureClient = true;
     mInvalidBack = nsIntRect(0, 0, TILEDLAYERBUFFER_TILE_SIZE, TILEDLAYERBUFFER_TILE_SIZE);
   }
 
   ValidateBackBufferFromFront(aDirtyRegion, aCanRerasterizeValidRegion);
 
   return mBackBuffer;
 }
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -50,16 +50,17 @@ class ClientLayerManager;
 class gfxSharedReadLock : public AtomicRefCounted<gfxSharedReadLock> {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(gfxSharedReadLock)
   virtual ~gfxSharedReadLock() {}
 
   virtual int32_t ReadLock() = 0;
   virtual int32_t ReadUnlock() = 0;
   virtual int32_t GetReadCount() = 0;
+  virtual bool IsValid() const = 0;
 
   enum gfxSharedReadLockType {
     TYPE_MEMORY,
     TYPE_SHMEM
   };
   virtual gfxSharedReadLockType GetType() = 0;
 
 protected:
@@ -75,16 +76,18 @@ public:
   virtual int32_t ReadLock() MOZ_OVERRIDE;
 
   virtual int32_t ReadUnlock() MOZ_OVERRIDE;
 
   virtual int32_t GetReadCount() MOZ_OVERRIDE;
 
   virtual gfxSharedReadLockType GetType() MOZ_OVERRIDE { return TYPE_MEMORY; }
 
+  virtual bool IsValid() const MOZ_OVERRIDE { return true; };
+
 private:
   int32_t mReadCount;
 };
 
 class gfxShmSharedReadLock : public gfxSharedReadLock {
 private:
   struct ShmReadLockInfo {
     int32_t readCount;
@@ -96,43 +99,47 @@ public:
   ~gfxShmSharedReadLock();
 
   virtual int32_t ReadLock() MOZ_OVERRIDE;
 
   virtual int32_t ReadUnlock() MOZ_OVERRIDE;
 
   virtual int32_t GetReadCount() MOZ_OVERRIDE;
 
+  virtual bool IsValid() const MOZ_OVERRIDE { return mAllocSuccess; };
+
   virtual gfxSharedReadLockType GetType() MOZ_OVERRIDE { return TYPE_SHMEM; }
 
   mozilla::ipc::Shmem& GetShmem() { return mShmem; }
 
   static already_AddRefed<gfxShmSharedReadLock>
   Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::ipc::Shmem& aShmem)
   {
     nsRefPtr<gfxShmSharedReadLock> readLock = new gfxShmSharedReadLock(aAllocator, aShmem);
     return readLock.forget();
   }
 
 private:
   gfxShmSharedReadLock(ISurfaceAllocator* aAllocator, const mozilla::ipc::Shmem& aShmem)
     : mAllocator(aAllocator)
     , mShmem(aShmem)
+    , mAllocSuccess(true)
   {
     MOZ_COUNT_CTOR(gfxShmSharedReadLock);
   }
 
   ShmReadLockInfo* GetShmReadLockInfoPtr()
   {
     return reinterpret_cast<ShmReadLockInfo*>
       (mShmem.get<char>() + mShmem.Size<char>() - sizeof(ShmReadLockInfo));
   }
 
   RefPtr<ISurfaceAllocator> mAllocator;
   mozilla::ipc::Shmem mShmem;
+  bool mAllocSuccess;
 };
 
 /**
  * Represent a single tile in tiled buffer. The buffer keeps tiles,
  * each tile keeps a reference to a texture client and a read-lock. This
  * read-lock is used to help implement a copy-on-write mechanism. The tile
  * should be locked before being sent to the compositor. The compositor should
  * unlock the read-lock as soon as it has finished with the buffer in the
@@ -165,24 +172,28 @@ struct TileClient
 
   bool IsPlaceholderTile()
   {
     return mBackBuffer == nullptr && mFrontBuffer == nullptr;
   }
 
   void ReadUnlock()
   {
-    NS_ASSERTION(mFrontLock != nullptr, "ReadUnlock with no gfxSharedReadLock");
-    mFrontLock->ReadUnlock();
+    MOZ_ASSERT(mFrontLock, "ReadLock with no gfxSharedReadLock");
+    if (mFrontLock) {
+      mFrontLock->ReadUnlock();
+    }
   }
 
   void ReadLock()
   {
-    NS_ASSERTION(mFrontLock != nullptr, "ReadLock with no gfxSharedReadLock");
-    mFrontLock->ReadLock();
+    MOZ_ASSERT(mFrontLock, "ReadLock with no gfxSharedReadLock");
+    if (mFrontLock) {
+      mFrontLock->ReadLock();
+    }
   }
 
   void Release()
   {
     DiscardFrontBuffer();
     DiscardBackBuffer();
   }
 
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -899,75 +899,74 @@ nsEventStatus AsyncPanZoomController::On
     RequestContentRepaint();
     UpdateSharedCompositorFrameMetrics();
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 bool
-AsyncPanZoomController::ConvertToGecko(const ScreenPoint& aPoint, CSSIntPoint* aOut)
+AsyncPanZoomController::ConvertToGecko(const ScreenPoint& aPoint, CSSPoint* aOut)
 {
   APZCTreeManager* treeManagerLocal = mTreeManager;
   if (treeManagerLocal) {
     gfx3DMatrix transformToApzc;
     gfx3DMatrix transformToGecko;
     treeManagerLocal->GetInputTransforms(this, transformToApzc, transformToGecko);
     gfxPoint result = transformToGecko.Transform(gfxPoint(aPoint.x, aPoint.y));
     // NOTE: This isn't *quite* LayoutDevicePoint, we just don't have a name
     // for this coordinate space and it maps the closest to LayoutDevicePoint.
     LayoutDevicePoint layoutPoint = LayoutDevicePoint(result.x, result.y);
     { // scoped lock to access mFrameMetrics
       ReentrantMonitorAutoEnter lock(mMonitor);
-      CSSPoint cssPoint = layoutPoint / mFrameMetrics.mDevPixelsPerCSSPixel;
-      *aOut = gfx::RoundedToInt(cssPoint);
+      *aOut = layoutPoint / mFrameMetrics.mDevPixelsPerCSSPixel;
     }
     return true;
   }
   return false;
 }
 
 nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a long-press in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
-    CSSIntPoint geckoScreenPoint;
+    CSSPoint geckoScreenPoint;
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
       SetState(WAITING_CONTENT_RESPONSE);
       SetContentResponseTimer();
       controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid());
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
-    CSSIntPoint geckoScreenPoint;
+    CSSPoint geckoScreenPoint;
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
       controller->HandleLongTapUp(geckoScreenPoint, modifiers, GetGuid());
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
   // sending event to content
   if (controller && !mZoomConstraints.mAllowDoubleTapZoom) {
     int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
-    CSSIntPoint geckoScreenPoint;
+    CSSPoint geckoScreenPoint;
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
       // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
       // calling controller->HandleSingleTap directly might mean that content receives
       // the single tap message before the corresponding touch-up. To avoid that we
       // schedule the singletap message to run on the next spin of the event loop.
       // See bug 965381 for the issue this was causing.
       controller->PostDelayedTask(
         NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
@@ -979,17 +978,17 @@ nsEventStatus AsyncPanZoomController::On
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
-    CSSIntPoint geckoScreenPoint;
+    CSSPoint geckoScreenPoint;
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
       // See comment in OnSingleTapUp as to why we do this in PostDelayedTask.
       controller->PostDelayedTask(
         NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
                           geckoScreenPoint, modifiers, GetGuid()),
         0);
       return nsEventStatus_eConsumeNoDefault;
     }
@@ -998,17 +997,17 @@ nsEventStatus AsyncPanZoomController::On
 }
 
 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     if (mZoomConstraints.mAllowDoubleTapZoom) {
       int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
-      CSSIntPoint geckoScreenPoint;
+      CSSPoint geckoScreenPoint;
       if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
         controller->HandleDoubleTap(geckoScreenPoint, modifiers, GetGuid());
       }
     }
 
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
@@ -1680,17 +1679,18 @@ gfx3DMatrix AsyncPanZoomController::GetN
   ReentrantMonitorAutoEnter lock(mMonitor);
   return gfx3DMatrix::ScalingMatrix(mLastContentPaintMetrics.mResolution.scale,
                                     mLastContentPaintMetrics.mResolution.scale,
                                     1.0f);
 }
 
 gfx3DMatrix AsyncPanZoomController::GetTransformToLastDispatchedPaint() {
   ReentrantMonitorAutoEnter lock(mMonitor);
-  CSSPoint scrollChange = mLastContentPaintMetrics.mScrollOffset - mLastDispatchedPaintMetrics.mScrollOffset;
+  LayerPoint scrollChange = (mLastContentPaintMetrics.mScrollOffset - mLastDispatchedPaintMetrics.mScrollOffset)
+                          * mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
   float zoomChange = mLastContentPaintMetrics.mZoom.scale / mLastDispatchedPaintMetrics.mZoom.scale;
   return gfx3DMatrix::Translation(scrollChange.x, scrollChange.y, 0) *
          gfx3DMatrix::ScalingMatrix(zoomChange, zoomChange, 1);
 }
 
 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -596,22 +596,22 @@ private:
   /**
    * Helper to set the current state. Holds the monitor before actually setting
    * it and fires content controller events based on state changes. Always set
    * the state using this call, do not set it directly.
    */
   void SetState(PanZoomState aState);
 
   /**
-   * Convert ScreenPoint relative to this APZC to CSSIntPoint relative
+   * Convert ScreenPoint relative to this APZC to CSSPoint relative
    * to the parent document. This excludes the transient compositor transform.
-   * NOTE: This must be converted to CSSIntPoint relative to the child
+   * NOTE: This must be converted to CSSPoint relative to the child
    * document before sending over IPC.
    */
-  bool ConvertToGecko(const ScreenPoint& aPoint, CSSIntPoint* aOut);
+  bool ConvertToGecko(const ScreenPoint& aPoint, CSSPoint* aOut);
 
   /**
    * Internal helpers for checking general state of this apzc.
    */
   bool IsTransformingState(PanZoomState aState);
   bool IsPanningState(PanZoomState mState);
 
   enum AxisLockMode {
--- a/gfx/layers/ipc/GeckoContentController.h
+++ b/gfx/layers/ipc/GeckoContentController.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_GeckoContentController_h
 #define mozilla_layers_GeckoContentController_h
 
 #include "FrameMetrics.h"               // for FrameMetrics, etc
-#include "Units.h"                      // for CSSIntPoint, CSSRect, etc
+#include "Units.h"                      // for CSSPoint, CSSRect, etc
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "nsISupportsImpl.h"
 
 class Task;
 
 namespace mozilla {
 namespace layers {
 
@@ -37,43 +37,43 @@ public:
                                        const uint32_t& aScrollGeneration) = 0;
 
   /**
    * Requests handling of a double tap. |aPoint| is in CSS pixels, relative to
    * the current scroll offset. This should eventually round-trip back to
    * AsyncPanZoomController::ZoomToRect with the dimensions that we want to zoom
    * to.
    */
-  virtual void HandleDoubleTap(const CSSIntPoint& aPoint,
+  virtual void HandleDoubleTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) = 0;
 
   /**
    * Requests handling a single tap. |aPoint| is in CSS pixels, relative to the
    * current scroll offset. This should simulate and send to content a mouse
    * button down, then mouse button up at |aPoint|.
    */
-  virtual void HandleSingleTap(const CSSIntPoint& aPoint,
+  virtual void HandleSingleTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) = 0;
 
   /**
    * Requests handling a long tap. |aPoint| is in CSS pixels, relative to the
    * current scroll offset.
    */
-  virtual void HandleLongTap(const CSSIntPoint& aPoint,
+  virtual void HandleLongTap(const CSSPoint& aPoint,
                              int32_t aModifiers,
                              const ScrollableLayerGuid& aGuid) = 0;
 
   /**
    * Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
    * relative to the current scroll offset. HandleLongTapUp will always be
    * preceeded by HandleLongTap
    */
-  virtual void HandleLongTapUp(const CSSIntPoint& aPoint,
+  virtual void HandleLongTapUp(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) = 0;
 
   /**
    * Requests sending a mozbrowserasyncscroll domevent to embedder.
    * |aContentRect| is in CSS pixels, relative to the current cssPage.
    * |aScrollableSize| is the current content width/height in CSS pixels.
    */
--- a/gfx/layers/ipc/GestureEventListener.cpp
+++ b/gfx/layers/ipc/GestureEventListener.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GestureEventListener.h"
 #include <math.h>                       // for fabsf
 #include <stddef.h>                     // for size_t
 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController
 #include "mozilla/layers/APZCTreeManager.h"  // for APZCTreeManager
 #include "base/task.h"                  // for CancelableTask, etc
-#include "mozilla/Preferences.h"        // for Preferences
+#include "gfxPrefs.h"                   // for gfxPrefs
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/mozalloc.h"           // for operator new
 #include "nsDebug.h"                    // for NS_WARN_IF_FALSE
 #include "nsMathUtils.h"                // for NS_hypot
 
 namespace mozilla {
 namespace layers {
 
@@ -80,17 +80,17 @@ nsEventStatus GestureEventListener::Hand
       if (mState == GESTURE_NONE) {
         mState = GESTURE_WAITING_SINGLE_TAP;
 
         mLongTapTimeoutTask =
           NewRunnableMethod(this, &GestureEventListener::TimeoutLongTap);
 
         mAsyncPanZoomController->PostDelayedTask(
           mLongTapTimeoutTask,
-          Preferences::GetInt("ui.click_hold_context_menus.delay", 500));
+          gfxPrefs::UiClickHoldContextMenusDelay());
       }
     } else if (length == 2) {
       // Another finger has been added; it can't be a tap anymore.
       HandleTapCancel(aEvent);
     }
 
     break;
   }
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -132,82 +132,16 @@ DrawQuads(GLContext *aGLContext,
   aGLContext->fDisableVertexAttribArray(vertAttribIndex);
   if (texCoords) {
     aGLContext->fDisableVertexAttribArray(texCoordAttribIndex);
   }
 
   aGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 }
 
-#ifdef MOZ_WIDGET_GONK
-CompositorOGLGonkBackendSpecificData::CompositorOGLGonkBackendSpecificData(CompositorOGL* aCompositor)
-  : mCompositor(aCompositor)
-{
-}
-
-CompositorOGLGonkBackendSpecificData::~CompositorOGLGonkBackendSpecificData()
-{
-  // Delete all textures by calling EndFrame twice
-  gl()->MakeCurrent();
-  EndFrame();
-  EndFrame();
-}
-
-GLContext*
-CompositorOGLGonkBackendSpecificData::gl() const
-{
-  return mCompositor->gl();
-}
-
-GLuint
-CompositorOGLGonkBackendSpecificData::GetTexture()
-{
-  GLuint texture = 0;
-
-  if (!mUnusedTextures.IsEmpty()) {
-    // Try to reuse one from the unused pile first
-    texture = mUnusedTextures[0];
-    mUnusedTextures.RemoveElementAt(0);
-  } else if (gl()->MakeCurrent()) {
-    // There isn't one to reuse, create one.
-    gl()->fGenTextures(1, &texture);
-  }
-
-  if (texture) {
-    mCreatedTextures.AppendElement(texture);
-  }
-
-  return texture;
-}
-
-void
-CompositorOGLGonkBackendSpecificData::EndFrame()
-{
-  gl()->MakeCurrent();
-
-  // Some platforms have issues unlocking Gralloc buffers even when they're
-  // rebound.
-  if (gfxPrefs::OverzealousGrallocUnlocking()) {
-    mUnusedTextures.AppendElements(mCreatedTextures);
-    mCreatedTextures.Clear();
-  }
-
-  // Delete unused textures
-  for (size_t i = 0; i < mUnusedTextures.Length(); i++) {
-    GLuint texture = mUnusedTextures[i];
-    gl()->fDeleteTextures(1, &texture);
-  }
-  mUnusedTextures.Clear();
-
-  // Move all created textures into the unused pile
-  mUnusedTextures.AppendElements(mCreatedTextures);
-  mCreatedTextures.Clear();
-}
-#endif
-
 CompositorOGL::CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth,
                              int aSurfaceHeight, bool aUseExternalSurfaceSize)
   : mWidget(aWidget)
   , mWidgetSize(-1, -1)
   , mSurfaceSize(aSurfaceWidth, aSurfaceHeight)
   , mHasBGRA(0)
   , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
   , mFrameInProgress(false)
@@ -241,48 +175,28 @@ CompositorOGL::CreateContext()
 
   if (!context) {
     NS_WARNING("Failed to create CompositorOGL context");
   }
 
   return context.forget();
 }
 
-GLuint
-CompositorOGL::GetTemporaryTexture(GLenum aTextureUnit)
-{
-  size_t index = aTextureUnit - LOCAL_GL_TEXTURE0;
-  // lazily grow the array of temporary textures
-  if (mTextures.Length() <= index) {
-    size_t prevLength = mTextures.Length();
-    mTextures.SetLength(index + 1);
-    for(unsigned int i = prevLength; i <= index; ++i) {
-      mTextures[i] = 0;
-    }
-  }
-  // lazily initialize the temporary textures
-  if (!mTextures[index]) {
-    if (!gl()->MakeCurrent()) {
-      return 0;
-    }
-    gl()->fGenTextures(1, &mTextures[index]);
-  }
-  return mTextures[index];
-}
-
 void
 CompositorOGL::Destroy()
 {
   if (gl() && gl()->MakeCurrent()) {
-    if (mTextures.Length() > 0) {
-      gl()->fDeleteTextures(mTextures.Length(), &mTextures[0]);
-    }
     mVBOs.Flush(gl());
   }
-  mTextures.SetLength(0);
+
+  if (mTexturePool) {
+    mTexturePool->Clear();
+    mTexturePool = nullptr;
+  }
+
   if (!mDestroyed) {
     mDestroyed = true;
     CleanupResources();
   }
 }
 
 void
 CompositorOGL::CleanupResources()
@@ -1361,21 +1275,19 @@ CompositorOGL::EndFrame()
     CopyToTarget(mTarget, mCurrentRenderTarget->GetTransform());
     mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     mCurrentRenderTarget = nullptr;
     return;
   }
 
   mCurrentRenderTarget = nullptr;
 
-#ifdef MOZ_WIDGET_GONK
-  if (mCompositorBackendSpecificData) {
-    static_cast<CompositorOGLGonkBackendSpecificData*>(mCompositorBackendSpecificData.get())->EndFrame();
+  if (mTexturePool) {
+    mTexturePool->EndFrame();
   }
-#endif
 
   mGLContext->SwapBuffers();
   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 }
 
 void
 CompositorOGL::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
 {
@@ -1475,27 +1387,16 @@ CompositorOGL::Resume()
     return false;
 
   // RenewSurface internally calls MakeCurrent.
   return gl()->RenewSurface();
 #endif
   return true;
 }
 
-#ifdef MOZ_WIDGET_GONK
-CompositorBackendSpecificData*
-CompositorOGL::GetCompositorBackendSpecificData()
-{
-  if (!mCompositorBackendSpecificData) {
-    mCompositorBackendSpecificData = new CompositorOGLGonkBackendSpecificData(this);
-  }
-  return mCompositorBackendSpecificData;
-}
-#endif
-
 TemporaryRef<DataTextureSource>
 CompositorOGL::CreateDataTextureSource(TextureFlags aFlags)
 {
   RefPtr<DataTextureSource> result =
     new TextureImageTextureSourceOGL(mGLContext, aFlags);
   return result;
 }
 
@@ -1588,10 +1489,125 @@ CompositorOGL::BindAndDrawQuad(ShaderPro
                                GLuint aDrawMode)
 {
   NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
   BindAndDrawQuad(aProg->AttribLocation(ShaderProgramOGL::VertexCoordAttrib),
                   aProg->AttribLocation(ShaderProgramOGL::TexCoordAttrib),
                   aFlipped, aDrawMode);
 }
 
+GLuint
+CompositorOGL::GetTemporaryTexture(GLenum aUnit)
+{
+  if (!mTexturePool) {
+#ifdef MOZ_WIDGET_GONK
+    mTexturePool = new PerFrameTexturePoolOGL(gl());
+#else
+    mTexturePool = new PerUnitTexturePoolOGL(gl());
+#endif
+  }
+  return mTexturePool->GetTexture(aUnit);
+}
+
+GLuint
+PerUnitTexturePoolOGL::GetTexture(GLenum aTextureUnit)
+{
+  size_t index = aTextureUnit - LOCAL_GL_TEXTURE0;
+  // lazily grow the array of temporary textures
+  if (mTextures.Length() <= index) {
+    size_t prevLength = mTextures.Length();
+    mTextures.SetLength(index + 1);
+    for(unsigned int i = prevLength; i <= index; ++i) {
+      mTextures[i] = 0;
+    }
+  }
+  // lazily initialize the temporary textures
+  if (!mTextures[index]) {
+    if (!mGL->MakeCurrent()) {
+      return 0;
+    }
+    mGL->fGenTextures(1, &mTextures[index]);
+  }
+  return mTextures[index];
+}
+
+void
+PerUnitTexturePoolOGL::DestroyTextures()
+{
+  if (mGL && mGL->MakeCurrent()) {
+    if (mTextures.Length() > 0) {
+      mGL->fDeleteTextures(mTextures.Length(), &mTextures[0]);
+    }
+  }
+  mTextures.SetLength(0);
+}
+
+void
+PerFrameTexturePoolOGL::DestroyTextures()
+{
+  if (!mGL->MakeCurrent()) {
+    return;
+  }
+
+  if (mUnusedTextures.Length() > 0) {
+    mGL->fDeleteTextures(mUnusedTextures.Length(), &mUnusedTextures[0]);
+    mUnusedTextures.Clear();
+  }
+
+  if (mCreatedTextures.Length() > 0) {
+    mGL->fDeleteTextures(mCreatedTextures.Length(), &mCreatedTextures[0]);
+    mCreatedTextures.Clear();
+  }
+}
+
+GLuint
+PerFrameTexturePoolOGL::GetTexture(GLenum)
+{
+  GLuint texture = 0;
+
+  if (!mUnusedTextures.IsEmpty()) {
+    // Try to reuse one from the unused pile first
+    texture = mUnusedTextures[0];
+    mUnusedTextures.RemoveElementAt(0);
+  } else if (mGL->MakeCurrent()) {
+    // There isn't one to reuse, create one.
+    mGL->fGenTextures(1, &texture);
+  }
+
+  if (texture) {
+    mCreatedTextures.AppendElement(texture);
+  }
+
+  return texture;
+}
+
+void
+PerFrameTexturePoolOGL::EndFrame()
+{
+  if (!mGL->MakeCurrent()) {
+    // this means the context got destroyed underneith us somehow, and the driver
+    // already has destroyed the textures.
+    mCreatedTextures.Clear();
+    mUnusedTextures.Clear();
+    return;
+  }
+
+  // Some platforms have issues unlocking Gralloc buffers even when they're
+  // rebound.
+  if (gfxPrefs::OverzealousGrallocUnlocking()) {
+    mUnusedTextures.AppendElements(mCreatedTextures);
+    mCreatedTextures.Clear();
+  }
+
+  // Delete unused textures
+  for (size_t i = 0; i < mUnusedTextures.Length(); i++) {
+    GLuint texture = mUnusedTextures[i];
+    mGL->fDeleteTextures(1, &texture);
+  }
+  mUnusedTextures.Clear();
+
+  // Move all created textures into the unused pile
+  mUnusedTextures.AppendElements(mCreatedTextures);
+  mCreatedTextures.Clear();
+}
+
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -50,16 +50,109 @@ namespace layers {
 class CompositingRenderTarget;
 class CompositingRenderTargetOGL;
 class DataTextureSource;
 class GLManagerCompositor;
 class TextureSource;
 struct Effect;
 struct EffectChain;
 
+/**
+ * Interface for pools of temporary gl textures for the compositor.
+ * The textures are fully owned by the pool, so the latter is responsible
+ * calling fDeleteTextures accordingly.
+ * Users of GetTexture receive a texture that is only valid for the duration
+ * of the current frame.
+ * This is primarily intended for direct texturing APIs that need to attach
+ * shared objects (such as an EGLImage) to a gl texture.
+ */
+class CompositorTexturePoolOGL : public RefCounted<CompositorTexturePoolOGL>
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_TYPENAME(CompositorTexturePoolOGL)
+
+  virtual ~CompositorTexturePoolOGL() {}
+
+  virtual void Clear() = 0;
+
+  virtual GLuint GetTexture(GLenum aUnit) = 0;
+
+  virtual void EndFrame() = 0;
+};
+
+/**
+ * Agressively reuses textures. One gl texture per texture unit in total.
+ * So far this hasn't shown the best results on b2g.
+ */
+class PerUnitTexturePoolOGL : public CompositorTexturePoolOGL
+{
+public:
+  PerUnitTexturePoolOGL(gl::GLContext* aGL)
+  : mGL(aGL)
+  {}
+
+  virtual ~PerUnitTexturePoolOGL()
+  {
+    DestroyTextures();
+  }
+
+  virtual void Clear() MOZ_OVERRIDE
+  {
+    DestroyTextures();
+  }
+
+  virtual GLuint GetTexture(GLenum aUnit) MOZ_OVERRIDE;
+
+  virtual void EndFrame() MOZ_OVERRIDE {}
+
+protected:
+  void DestroyTextures();
+
+  nsTArray<GLuint> mTextures;
+  RefPtr<gl::GLContext> mGL;
+};
+
+/**
+ * Reuse gl textures from a pool of textures that haven't yet been
+ * used during the current frame.
+ * All the textures that are not used at the end of a frame are
+ * deleted.
+ * This strategy seems to work well with gralloc textures because destroying
+ * unused textures which are bound to gralloc buffers let drivers know that it
+ * can unlock the gralloc buffers.
+ */
+class PerFrameTexturePoolOGL : public CompositorTexturePoolOGL
+{
+public:
+  PerFrameTexturePoolOGL(gl::GLContext* aGL)
+  : mGL(aGL)
+  {}
+
+  virtual ~PerFrameTexturePoolOGL()
+  {
+    DestroyTextures();
+  }
+
+  virtual void Clear() MOZ_OVERRIDE
+  {
+    DestroyTextures();
+  }
+
+  virtual GLuint GetTexture(GLenum aUnit) MOZ_OVERRIDE;
+
+  virtual void EndFrame() MOZ_OVERRIDE;
+
+protected:
+  void DestroyTextures();
+
+  RefPtr<gl::GLContext> mGL;
+  nsTArray<GLuint> mCreatedTextures;
+  nsTArray<GLuint> mUnusedTextures;
+};
+
 class CompositorOGL : public Compositor
 {
   typedef mozilla::gl::GLContext GLContext;
   
   friend class GLManagerCompositor;
 
   std::map<ShaderConfigOGL, ShaderProgramOGL*> mPrograms;
 public:
@@ -157,20 +250,16 @@ public:
     return LayersBackend::LAYERS_OPENGL;
   }
 
   virtual void Pause() MOZ_OVERRIDE;
   virtual bool Resume() MOZ_OVERRIDE;
 
   virtual nsIWidget* GetWidget() const MOZ_OVERRIDE { return mWidget; }
 
-#ifdef MOZ_WIDGET_GONK
-  virtual CompositorBackendSpecificData* GetCompositorBackendSpecificData() MOZ_OVERRIDE;
-#endif
-
   GLContext* gl() const { return mGLContext; }
   gfx::SurfaceFormat GetFBOFormat() const {
     return gfx::SurfaceFormat::R8G8B8A8;
   }
 
   /**
    * The compositor provides with temporary textures for use with direct
    * textruing like gralloc texture.
@@ -304,49 +393,23 @@ private:
    *
    * Indeed, the only coordinate system that OpenGL knows has the y-axis
    * pointing upwards, but the layers/compositor coordinate system has the
    * y-axis pointing downwards, for good reason as Web pages are typically
    * scrolled downwards. So, some flipping has to take place; FlippedY does it.
    */
   GLint FlipY(GLint y) const { return mHeight - y; }
 
-  bool mDestroyed;
+  RefPtr<CompositorTexturePoolOGL> mTexturePool;
 
-  // Textures used for direct texturing of buffers like gralloc.
-  // The index of the texture in this array must correspond to the texture unit.
-  nsTArray<GLuint> mTextures;
+  bool mDestroyed;
 
   /**
    * Height of the OpenGL context's primary framebuffer in pixels. Used by
    * FlipY for the y-flipping calculation.
    */
   GLint mHeight;
-
-#ifdef MOZ_WIDGET_GONK
-  RefPtr<CompositorBackendSpecificData> mCompositorBackendSpecificData;
-#endif
 };
 
-#ifdef MOZ_WIDGET_GONK
-class CompositorOGLGonkBackendSpecificData : public CompositorBackendSpecificData
-{
-public:
-  CompositorOGLGonkBackendSpecificData(CompositorOGL* aCompositor);
-  virtual ~CompositorOGLGonkBackendSpecificData();
-
-  GLuint GetTexture();
-  void EndFrame();
-
-private:
-  gl::GLContext* gl() const;
-
-  RefPtr<CompositorOGL> mCompositor;
-
-  nsTArray<GLuint> mCreatedTextures;
-  nsTArray<GLuint> mUnusedTextures;
-};
-#endif
-
 }
 }
 
 #endif /* MOZILLA_GFX_COMPOSITOROGL_H */
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -88,89 +88,73 @@ TextureTargetForAndroidPixelFormat(andro
 
 GrallocTextureSourceOGL::GrallocTextureSourceOGL(CompositorOGL* aCompositor,
                                                  android::GraphicBuffer* aGraphicBuffer,
                                                  gfx::SurfaceFormat aFormat)
   : mCompositor(aCompositor)
   , mGraphicBuffer(aGraphicBuffer)
   , mEGLImage(0)
   , mFormat(aFormat)
-  , mNeedsReset(true)
 {
   MOZ_ASSERT(mGraphicBuffer.get());
 }
 
 GrallocTextureSourceOGL::~GrallocTextureSourceOGL()
 {
   DeallocateDeviceData();
   mCompositor = nullptr;
 }
 
 void
 GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit)
 {
-  /*
-   * The job of this function is to ensure that the texture is tied to the
-   * android::GraphicBuffer, so that texturing will source the GraphicBuffer.
-   *
-   * To this effect we create an EGLImage wrapping this GraphicBuffer,
-   * using EGLImageCreateFromNativeBuffer, and then we tie this EGLImage to our
-   * texture using fEGLImageTargetTexture2D.
-   */
   MOZ_ASSERT(gl());
   if (!IsValid()) {
     return;
   }
   gl()->MakeCurrent();
 
   GLuint tex = GetGLTexture();
   GLuint textureTarget = GetTextureTarget();
 
   gl()->fActiveTexture(aTextureUnit);
   gl()->fBindTexture(textureTarget, tex);
 
-  if (mCompositableBackendData) {
-    // There are two paths for locking/unlocking - if mCompositableBackendData is
-    // set, we use the texture on there, otherwise we use
-    // CompositorBackendSpecificData from the compositor and bind the EGLImage
-    // only in Lock().
-    if (!mEGLImage) {
-      mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer());
-    }
-    gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
-  }
-
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
 }
 
 void GrallocTextureSourceOGL::Lock()
 {
-  if (mCompositableBackendData) return;
-
+  /*
+   * The job of this function is to ensure that the texture is tied to the
+   * android::GraphicBuffer, so that texturing will source the GraphicBuffer.
+   *
+   * To this effect we create an EGLImage wrapping this GraphicBuffer,
+   * using EGLImageCreateFromNativeBuffer, and then we tie this EGLImage to our
+   * texture using fEGLImageTargetTexture2D.
+   */
   MOZ_ASSERT(IsValid());
 
-  CompositorOGLGonkBackendSpecificData* backendData =
-    static_cast<CompositorOGLGonkBackendSpecificData*>(mCompositor->GetCompositorBackendSpecificData());
-  mTexture = backendData->GetTexture();
+  mTexture = mCompositor->GetTemporaryTexture(LOCAL_GL_TEXTURE0);
 
   GLuint textureTarget = GetTextureTarget();
 
   gl()->MakeCurrent();
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
   gl()->fBindTexture(textureTarget, mTexture);
   if (!mEGLImage) {
     mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer());
   }
   gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
 }
 
 bool
 GrallocTextureSourceOGL::IsValid() const
 {
-  return !!gl() && !!mGraphicBuffer.get() && (!!mCompositor || !!mCompositableBackendData);
+  return !!gl() && !!mGraphicBuffer.get();
 }
 
 gl::GLContext*
 GrallocTextureSourceOGL::gl() const
 {
   return mCompositor ? mCompositor->gl() : nullptr;
 }
 
@@ -200,61 +184,16 @@ GrallocTextureSourceOGL::GetFormat() con
     return gfx::SurfaceFormat::UNKNOWN;
   }
   if (GetTextureTarget() == LOCAL_GL_TEXTURE_EXTERNAL) {
     return gfx::SurfaceFormat::R8G8B8A8;
   }
   return mFormat;
 }
 
-void
-GrallocTextureSourceOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
-{
-  if (!aBackendData) {
-    mCompositableBackendData = nullptr;
-    DeallocateDeviceData();
-    return;
-  }
-
-  if (mCompositableBackendData != aBackendData) {
-    mNeedsReset = true;
-  }
-
-  if (!mNeedsReset) {
-    // Update binding to the EGLImage
-    gl()->MakeCurrent();
-    GLuint tex = GetGLTexture();
-    GLuint textureTarget = GetTextureTarget();
-    gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
-    gl()->fBindTexture(textureTarget, tex);
-    gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
-    return;
-  }
-
-  mCompositableBackendData = aBackendData;
-
-  if (!mCompositor) {
-    return;
-  }
-
-  // delete old EGLImage
-  DeallocateDeviceData();
-
-  gl()->MakeCurrent();
-  GLuint tex = GetGLTexture();
-  GLuint textureTarget = GetTextureTarget();
-
-  gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
-  gl()->fBindTexture(textureTarget, tex);
-  // create new EGLImage
-  mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer());
-  gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
-  mNeedsReset = false;
-}
-
 gfx::IntSize
 GrallocTextureSourceOGL::GetSize() const
 {
   if (!IsValid()) {
     NS_WARNING("Trying to access the size of an invalid GrallocTextureSourceOGL");
     return gfx::IntSize(0, 0);
   }
   return gfx::IntSize(mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight());
@@ -413,21 +352,16 @@ GrallocTextureSourceOGL::GetAsSurface() 
 
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
   return surf.forget();
 }
 
 GLuint
 GrallocTextureSourceOGL::GetGLTexture()
 {
-  if (mCompositableBackendData) {
-    mCompositableBackendData->SetCompositor(mCompositor);
-    return static_cast<CompositableDataGonkOGL*>(mCompositableBackendData.get())->GetTexture();
-  }
-
   return mTexture;
 }
 
 void
 GrallocTextureHostOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
 {
   mCompositableBackendData = aBackendData;
   if (mTextureSource) {
--- a/gfx/layers/opengl/GrallocTextureHost.h
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -40,18 +40,16 @@ public:
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
 
   virtual GLenum GetWrapMode() const MOZ_OVERRIDE
   {
     return LOCAL_GL_CLAMP_TO_EDGE;
   }
 
-  virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE;
-
   void DeallocateDeviceData();
 
   gl::GLContext* gl() const;
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   void ForgetBuffer()
   {
@@ -65,17 +63,16 @@ public:
   void Lock();
 
 protected:
   CompositorOGL* mCompositor;
   android::sp<android::GraphicBuffer> mGraphicBuffer;
   EGLImage mEGLImage;
   GLuint mTexture;
   gfx::SurfaceFormat mFormat;
-  bool mNeedsReset;
 };
 
 class GrallocTextureHostOGL : public TextureHost
 #if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
                             , public TextureHostOGL
 #endif
 {
   friend class GrallocBufferActor;
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -150,60 +150,36 @@ WrapMode(gl::GLContext *aGl, bool aAllow
       (aGl->IsExtensionSupported(GLContext::ARB_texture_non_power_of_two) ||
        aGl->IsExtensionSupported(GLContext::OES_texture_npot))) {
     return LOCAL_GL_REPEAT;
   }
   return LOCAL_GL_CLAMP_TO_EDGE;
 }
 
 CompositableDataGonkOGL::CompositableDataGonkOGL()
- : mTexture(0)
 {
 }
 CompositableDataGonkOGL::~CompositableDataGonkOGL()
 {
-  DeleteTextureIfPresent();
 }
 
 gl::GLContext*
 CompositableDataGonkOGL::gl() const
 {
   return mCompositor ? mCompositor->gl() : nullptr;
 }
 
 void CompositableDataGonkOGL::SetCompositor(Compositor* aCompositor)
 {
   mCompositor = static_cast<CompositorOGL*>(aCompositor);
 }
 
 void CompositableDataGonkOGL::ClearData()
 {
   CompositableBackendSpecificData::ClearData();
-  DeleteTextureIfPresent();
-}
-
-GLuint CompositableDataGonkOGL::GetTexture()
-{
-  if (!mTexture) {
-    if (gl()->MakeCurrent()) {
-      gl()->fGenTextures(1, &mTexture);
-    }
-  }
-  return mTexture;
-}
-
-void
-CompositableDataGonkOGL::DeleteTextureIfPresent()
-{
-  if (mTexture) {
-    if (gl()->MakeCurrent()) {
-      gl()->fDeleteTextures(1, &mTexture);
-    }
-    mTexture = 0;
-  }
 }
 
 #if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 bool
 TextureHostOGL::SetReleaseFence(const android::sp<android::Fence>& aReleaseFence)
 {
   if (!aReleaseFence.get() || !aReleaseFence->isValid()) {
     return false;
@@ -726,29 +702,16 @@ TextureImageDeprecatedTextureHostOGL::Up
                                        nsIntRegion* aRegion,
                                        nsIntPoint* aOffset)
 {
   if (!mGL) {
     NS_WARNING("trying to update TextureImageDeprecatedTextureHostOGL without a compositor?");
     return;
   }
 
-#ifdef MOZ_WIDGET_GONK
-  if (mCompositableBackendData) {
-    // on gonk, this class is used as a fallback from gralloc buffer.
-    // There is a case this class is used with GrallocDeprecatedTextureHostOGL
-    // under same CompositableHost. if it happens, a gralloc buffer of
-    // GrallocDeprecatedTextureHostOGL needs to be unbounded from a texture,
-    // when the gralloc buffer is not rendered.
-    // Establish the unbound by deleting the texture.
-    // See Bug 916264.
-    static_cast<CompositableDataGonkOGL*>(mCompositableBackendData.get())->DeleteTextureIfPresent();
-  }
-#endif
-
   AutoOpenSurface surf(OPEN_READ_ONLY, aImage);
   gfx::IntSize size = surf.Size();
   TextureImage::ImageFormat format = surf.ImageFormat();
 
   if (!mTexture ||
       (mTexture->GetSize() != size && !aOffset) ||
       mTexture->GetContentType() != surf.ContentType() ||
       (mTexture->GetImageFormat() != format &&
@@ -1191,17 +1154,17 @@ GrallocDeprecatedTextureHostOGL::GetRend
 
   return LayerRenderState();
 }
 
 GLuint
 GrallocDeprecatedTextureHostOGL::GetGLTexture()
 {
   mCompositableBackendData->SetCompositor(mCompositor);
-  return static_cast<CompositableDataGonkOGL*>(mCompositableBackendData.get())->GetTexture();
+  return mCompositor->GetTemporaryTexture(LOCAL_GL_TEXTURE0);
 }
 
 #endif // MOZ_WIDGET_GONK
 
 TemporaryRef<gfx::DataSourceSurface>
 TextureImageDeprecatedTextureHostOGL::GetAsSurface() {
   RefPtr<gfx::DataSourceSurface> surf =
     IsValid() ? ReadBackSurface(mGL, mTexture->GetTextureID(),
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -67,22 +67,19 @@ class TextureImageDeprecatedTextureHostO
 class CompositableDataGonkOGL : public CompositableBackendSpecificData
 {
 public:
   CompositableDataGonkOGL();
   virtual ~CompositableDataGonkOGL();
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
   virtual void ClearData() MOZ_OVERRIDE;
-  GLuint GetTexture();
-  void DeleteTextureIfPresent();
   gl::GLContext* gl() const;
 protected:
   RefPtr<CompositorOGL> mCompositor;
-  GLuint mTexture;
 };
 
 /*
  * TextureHost implementations for the OpenGL backend.
  *
  * Note that it is important to be careful about the ownership model with
  * the OpenGL backend, due to some widget limitation on Linux: before
  * the nsBaseWidget associated with our OpenGL context has been completely
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -49,20 +49,20 @@ protected:
     gfxPrefs::DestroySingleton();
   }
 };
 
 class MockContentController : public GeckoContentController {
 public:
   MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
   MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
-  MOCK_METHOD3(HandleDoubleTap, void(const CSSIntPoint&, int32_t, const ScrollableLayerGuid&));
-  MOCK_METHOD3(HandleSingleTap, void(const CSSIntPoint&, int32_t, const ScrollableLayerGuid&));
-  MOCK_METHOD3(HandleLongTap, void(const CSSIntPoint&, int32_t, const ScrollableLayerGuid&));
-  MOCK_METHOD3(HandleLongTapUp, void(const CSSIntPoint&, int32_t, const ScrollableLayerGuid&));
+  MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
+  MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
+  MOCK_METHOD3(HandleLongTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
+  MOCK_METHOD3(HandleLongTapUp, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
   MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
   MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
 };
 
 class MockContentControllerDelayed : public MockContentController {
 public:
   MockContentControllerDelayed()
     : mCurrentTask(nullptr)
@@ -681,17 +681,17 @@ TEST_F(AsyncPanZoomControllerTester, Ove
   ViewTransform viewTransformOut;
 
   // Pan down
   ApzcPan(apzc, tm, time, touchStart, touchEnd);
   apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(1000), &viewTransformOut, pointOut);
   EXPECT_EQ(pointOut, ScreenPoint(0, 90));
 }
 
-TEST(AsyncPanZoomController, ShortPress) {
+TEST_F(AsyncPanZoomControllerTester, ShortPress) {
   nsRefPtr<MockContentControllerDelayed> mcc = new NiceMock<MockContentControllerDelayed>();
   nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
     0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
   apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
@@ -699,23 +699,23 @@ TEST(AsyncPanZoomController, ShortPress)
   int time = 0;
   nsEventStatus status = ApzcTap(apzc, 10, 10, time, 100, mcc.get());
   EXPECT_EQ(nsEventStatus_eIgnore, status);
 
   // This verifies that the single tap notification is sent after the
   // touchdown is fully processed. The ordering here is important.
   mcc->CheckHasDelayedTask();
 
-  EXPECT_CALL(*mcc, HandleSingleTap(CSSIntPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   mcc->RunDelayedTask();
 
   apzc->Destroy();
 }
 
-TEST(AsyncPanZoomController, MediumPress) {
+TEST_F(AsyncPanZoomControllerTester, MediumPress) {
   nsRefPtr<MockContentControllerDelayed> mcc = new NiceMock<MockContentControllerDelayed>();
   nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
     0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
   apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
@@ -723,17 +723,17 @@ TEST(AsyncPanZoomController, MediumPress
   int time = 0;
   nsEventStatus status = ApzcTap(apzc, 10, 10, time, 400, mcc.get());
   EXPECT_EQ(nsEventStatus_eIgnore, status);
 
   // This verifies that the single tap notification is sent after the
   // touchdown is fully processed. The ordering here is important.
   mcc->CheckHasDelayedTask();
 
-  EXPECT_CALL(*mcc, HandleSingleTap(CSSIntPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   mcc->RunDelayedTask();
 
   apzc->Destroy();
 }
 
 void
 DoLongPressTest(bool aShouldUseTouchAction, uint32_t aBehavior) {
   nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed();
@@ -756,21 +756,21 @@ DoLongPressTest(bool aShouldUseTouchActi
   EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
 
   MockFunction<void(std::string checkPointName)> check;
 
   {
     InSequence s;
 
     EXPECT_CALL(check, Call("preHandleLongTap"));
-    EXPECT_CALL(*mcc, HandleLongTap(CSSIntPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
     EXPECT_CALL(check, Call("postHandleLongTap"));
 
     EXPECT_CALL(check, Call("preHandleLongTapUp"));
-    EXPECT_CALL(*mcc, HandleLongTapUp(CSSIntPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
     EXPECT_CALL(check, Call("postHandleLongTapUp"));
   }
 
   mcc->CheckHasDelayedTask();
 
   // Manually invoke the longpress while the touch is currently down.
   check.Call("preHandleLongTap");
   mcc->RunDelayedTask();
@@ -786,17 +786,17 @@ DoLongPressTest(bool aShouldUseTouchActi
   // mechanism.
   check.Call("preHandleLongTapUp");
   apzc->ContentReceivedTouch(false);
   check.Call("postHandleLongTapUp");
 
   apzc->Destroy();
 }
 
-TEST(AsyncPanZoomController, LongPressPreventDefault) {
+TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefault) {
   // We have to initialize both an integer time and TimeStamp time because
   // TimeStamp doesn't have any ToXXX() functions for converting back to
   // primitives.
   TimeStamp testStartTime = TimeStamp::Now();
   int time = 0;
   AsyncPanZoomController::SetFrameTime(testStartTime);
 
   nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed();
@@ -819,17 +819,17 @@ TEST(AsyncPanZoomController, LongPressPr
   EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
 
   MockFunction<void(std::string checkPointName)> check;
 
   {
     InSequence s;
 
     EXPECT_CALL(check, Call("preHandleLongTap"));
-    EXPECT_CALL(*mcc, HandleLongTap(CSSIntPoint(touchX, touchStartY), 0, apzc->GetGuid())).Times(1);
+    EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid())).Times(1);
     EXPECT_CALL(check, Call("postHandleLongTap"));
   }
 
   mcc->CheckHasDelayedTask();
 
   // Manually invoke the longpress while the touch is currently down.
   check.Call("preHandleLongTap");
   mcc->RunDelayedTask();
@@ -861,21 +861,21 @@ TEST(AsyncPanZoomController, LongPressPr
   apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
   EXPECT_EQ(pointOut, ScreenPoint());
   EXPECT_EQ(viewTransformOut, ViewTransform());
 
   apzc->Destroy();
 }
 
-TEST(AsyncPanZoomController, LongPress) {
+TEST_F(AsyncPanZoomControllerTester, LongPress) {
   DoLongPressTest(false, mozilla::layers::AllowedTouchBehavior::NONE);
 }
 
-TEST(AsyncPanZoomController, LongPressPanAndZoom) {
+TEST_F(AsyncPanZoomControllerTester, LongPressPanAndZoom) {
   DoLongPressTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
                       | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
                       | mozilla::layers::AllowedTouchBehavior::ZOOM);
 }
 
 
 // Layer tree for HitTesting1
 static already_AddRefed<mozilla::layers::Layer>
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -173,16 +173,18 @@ private:
   DECL_GFX_PREF(Once, "layers.prefer-opengl",                  LayersPreferOpenGL, bool, false);
   DECL_GFX_PREF(Once, "layers.progressive-paint",              UseProgressiveTilePainting, bool, false);
   DECL_GFX_PREF(Once, "layers.scroll-graph",                   LayersScrollGraph, bool, false);
 
   DECL_GFX_PREF(Once, "layout.frame_rate",                     LayoutFrameRate, int32_t, -1);
 
   DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
 
+  DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay",     UiClickHoldContextMenusDelay, int32_t, 500);
+
   DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
 
 public:
   // Manage the singleton:
   static gfxPrefs& GetSingleton()
   {
     if (!sInstance) {
       sInstance = new gfxPrefs;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug977674.js
@@ -0,0 +1,39 @@
+
+function testReduce() {
+  function sum(a, b) {
+    var r = a + b;
+  }
+  var array = build(8 * 0X0aaec , function() { return 1; });
+  var parResult = array.reducePar(sum);
+}
+for (var ix = 0; ix < 3; ++ix) {
+  testReduce();
+}
+function build(n, f) {
+  var result = [];
+  for (var i = 0; i < n; i++)
+    result.push(f(i));
+  return result;
+}
+function seq_scan(array, f) {
+  for (var i = 1; i < array.length; i++) {
+  }
+}
+function assertAlmostEq(v1, v2) {
+    if (e1 instanceof Array && e2 instanceof Array) {
+      for (prop in e1) {
+        if (e1.hasOwnProperty(prop)) {        }
+      }
+    }
+}
+function assertEqArray(a, b) {
+    for (var i = 0, l = a.length; i < l; i++) {
+      try {      } catch (e) {      }
+    }
+}
+function assertParallelExecWillRecover(opFunction) {
+assertParallelExecSucceeds(
+    function(m) {},
+    function(r) {}
+);
+}
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -501,18 +501,19 @@ JitCompartment::ensureIonStubsExist(JSCo
     return true;
 }
 
 void
 jit::FinishOffThreadBuilder(IonBuilder *builder)
 {
     ExecutionMode executionMode = builder->info().executionMode();
 
-    // Clear the recompiling flag if it would have failed.
-    if (builder->script()->hasIonScript())
+    // Clear the recompiling flag of the old ionScript, since we continue to
+    // use the old ionScript if recompiling fails.
+    if (executionMode == SequentialExecution && builder->script()->hasIonScript())
         builder->script()->ionScript()->clearRecompiling();
 
     // Clean up if compilation did not succeed.
     if (CompilingOffThread(builder->script(), executionMode))
         SetIonScript(builder->script(), executionMode, nullptr);
 
     // The builder is allocated into its LifoAlloc, so destroying that will
     // destroy the builder and all other data accumulated during compilation,
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -534,17 +534,17 @@ public:
       return;
     }
     if (mRenderFrame) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
       browser->AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
     }
   }
 
-  virtual void HandleDoubleTap(const CSSIntPoint& aPoint,
+  virtual void HandleDoubleTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE
   {
     if (MessageLoop::current() != mUILoop) {
       // We have to send this message from the "UI thread" (main
       // thread).
       mUILoop->PostTask(
         FROM_HERE,
@@ -553,17 +553,17 @@ public:
       return;
     }
     if (mRenderFrame) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
       browser->HandleDoubleTap(aPoint, aModifiers, aGuid);
     }
   }
 
-  virtual void HandleSingleTap(const CSSIntPoint& aPoint,
+  virtual void HandleSingleTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE
   {
     if (MessageLoop::current() != mUILoop) {
       // We have to send this message from the "UI thread" (main
       // thread).
       mUILoop->PostTask(
         FROM_HERE,
@@ -572,17 +572,17 @@ public:
       return;
     }
     if (mRenderFrame) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
       browser->HandleSingleTap(aPoint, aModifiers, aGuid);
     }
   }
 
-  virtual void HandleLongTap(const CSSIntPoint& aPoint,
+  virtual void HandleLongTap(const CSSPoint& aPoint,
                              int32_t aModifiers,
                              const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE
   {
     if (MessageLoop::current() != mUILoop) {
       // We have to send this message from the "UI thread" (main
       // thread).
       mUILoop->PostTask(
         FROM_HERE,
@@ -591,17 +591,17 @@ public:
       return;
     }
     if (mRenderFrame) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
       browser->HandleLongTap(aPoint, aModifiers, aGuid);
     }
   }
 
-  virtual void HandleLongTapUp(const CSSIntPoint& aPoint,
+  virtual void HandleLongTapUp(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE
   {
     if (MessageLoop::current() != mUILoop) {
       // We have to send this message from the "UI thread" (main
       // thread).
       mUILoop->PostTask(
         FROM_HERE,
--- a/layout/style/test/Makefile.in
+++ b/layout/style/test/Makefile.in
@@ -15,11 +15,13 @@
 #LIBS += ../nsCSSKeywords.$(OBJ_SUFFIX) ../nsCSSProps.$(OBJ_SUFFIX) $(XPCOM_LIBS)
 
 ifdef COMPILE_ENVIRONMENT
 css_properties.js: host_ListCSSProperties$(HOST_BIN_SUFFIX) css_properties_like_longhand.js Makefile
 	$(RM) $@
 	./host_ListCSSProperties$(HOST_BIN_SUFFIX) > $@
 	cat $(srcdir)/css_properties_like_longhand.js >> $@
 
-GARBAGE += css_properties.jsm
-MOCHITEST_FILES += css_properties.js
+GARBAGE += css_properties.js
+TEST_FILES := css_properties.js
+TEST_DEST = $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+INSTALL_TARGETS += TEST
 endif
--- a/layout/xul/test/mochitest.ini
+++ b/layout/xul/test/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = buildapp == 'b2g'
 
 [test_bug386386.html]
 [test_bug394800.xhtml]
 [test_bug511075.html]
 skip-if = toolkit == 'android' #bug 798806
 [test_bug563416.html]
 [test_resizer_incontent.xul]
 [test_splitter.xul]
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -822,27 +822,25 @@ pref("browser.snippets.geoUrl", "https:/
 pref("browser.snippets.statsUrl", "https://snippets-stats.mozilla.org/mobile");
 
 // These prefs require a restart to take effect.
 pref("browser.snippets.enabled", true);
 pref("browser.snippets.syncPromo.enabled", true);
 
 #ifdef MOZ_ANDROID_SYNTHAPKS
 // The URL of the APK factory from which we obtain APKs for webapps.
-// This currently points to the development server.
-pref("browser.webapps.apkFactoryUrl", "http://dapk.net/application.apk");
+pref("browser.webapps.apkFactoryUrl", "https://controller.apk.firefox.com/application.apk");
 
 // How frequently to check for webapp updates, in seconds (86400 is daily).
 pref("browser.webapps.updateInterval", 86400);
 
 // The URL of the service that checks for updates.
-// This currently points to the development server.
 // To test updates, set this to http://apk-update-checker.paas.allizom.org,
 // which is a test server that always reports all apps as having updates.
-pref("browser.webapps.updateCheckUrl", "http://dapk.net/app_updates");
+pref("browser.webapps.updateCheckUrl", "https://controller.apk.firefox.com/app_updates");
 
 #endif
 
 // The mode of home provider syncing.
 // 0: Sync always
 // 1: Sync only when on wifi
 pref("home.sync.updateMode", 0);
 
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -65,18 +65,16 @@ skip-if = android_version == "10"
 [testOverscroll]
 [testPanCorrectness]
 # disabled on x86 only; bug 927476
 skip-if = processor == "x86"
 # [testPasswordEncrypt] # see bug 824067
 [testPasswordProvider]
 # [testPermissions] # see bug 757475
 [testPictureLinkContextMenu]
-# disabled on Android 2.3; bug 979612
-skip-if = android_version == "10"
 [testPrefsObserver]
 [testPrivateBrowsing]
 [testPromptGridInput]
 # disabled on x86 only; bug 957185
 skip-if = processor == "x86"
 # [testReaderMode] # see bug 913254, 936224
 [testReadingListProvider]
 [testSearchSuggestions]
--- a/mobile/android/base/tests/testPictureLinkContextMenu.java
+++ b/mobile/android/base/tests/testPictureLinkContextMenu.java
@@ -15,18 +15,18 @@ public class testPictureLinkContextMenu 
         return TEST_MOCHITEST;
     }
 
     public void testPictureLinkContextMenu() {
         blockForGeckoReady();
 
         PICTURE_PAGE_URL=getAbsoluteUrl("/robocop/robocop_picture_link.html");
         BLANK_PAGE_URL=getAbsoluteUrl("/robocop/robocop_blank_02.html");
-        inputAndLoadUrl(PICTURE_PAGE_URL);
-        waitForText(PICTURE_PAGE_TITLE);
+        loadAndPaint(PICTURE_PAGE_URL);
+        verifyPageTitle(PICTURE_PAGE_TITLE);
 
         verifyContextMenuItems(photoMenuItems);
         verifyCopyOption(photoMenuItems[0], "Firefox.jpg"); // Test the "Copy Image Location" option
         verifyShareOption(photoMenuItems[1], PICTURE_PAGE_TITLE); // Test the "Share Image" option
         openTabFromContextMenu(photoMenuItems[4],2); // Test the "Open in New Tab" option - expecting 2 tabs: the original and the new one
         openTabFromContextMenu(photoMenuItems[5],2); // Test the "Open in Private Tab" option - expecting only 2 tabs in normal mode
         verifyCopyOption(photoMenuItems[6], BLANK_PAGE_URL); // Test the "Copy Link" option
         verifyShareOption(photoMenuItems[7], PICTURE_PAGE_TITLE); // Test the "Share Link" option
--- a/mobile/android/base/webapp/EventListener.java
+++ b/mobile/android/base/webapp/EventListener.java
@@ -107,17 +107,17 @@ public class EventListener implements Ge
             } else if (!AppConstants.MOZ_ANDROID_SYNTHAPKS && event.equals("Webapps:Preinstall")) {
                 String name = message.getString("name");
                 String manifestURL = message.getString("manifestURL");
                 String origin = message.getString("origin");
 
                 JSONObject obj = new JSONObject();
                 obj.put("profile", preInstallWebapp(name, manifestURL, origin).toString());
                 EventDispatcher.sendResponse(message, obj);
-            } else if (event.equals("WebApps:GetApkVersions")) {
+            } else if (event.equals("Webapps:GetApkVersions")) {
                 JSONObject obj = new JSONObject();
                 obj.put("versions", getApkVersions(GeckoAppShell.getGeckoInterface().getActivity(),
                                                    message.getJSONArray("packageNames")).toString());
                 EventDispatcher.sendResponse(message, obj);
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
--- a/mobile/android/modules/WebappManager.jsm
+++ b/mobile/android/modules/WebappManager.jsm
@@ -18,36 +18,33 @@ Cu.import("resource://gre/modules/FileUt
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
 Cu.import("resource://gre/modules/Webapps.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "sendMessageToJava", "resource://gre/modules/Messaging.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "Strings", function() {
   return Services.strings.createBundle("chrome://browser/locale/webapp.properties");
 });
 
 function debug(aMessage) {
   // We use *dump* instead of Services.console.logStringMessage so the messages
   // have the INFO level of severity instead of the ERROR level.  And we don't
   // append a newline character to the end of the message because *dump* spills
   // into the Android native logging system, which strips newlines from messages
   // and breaks messages into lines automatically at display time (i.e. logcat).
-#ifdef MOZ_DEBUG
+#ifdef DEBUG
   dump(aMessage);
 #endif
 }
 
-function sendMessageToJava(aMessage) {
-  return Services.androidBridge.handleGeckoMessage(JSON.stringify(aMessage));
-}
-
 this.WebappManager = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   get _testing() {
     try {
       return Services.prefs.getBoolPref("browser.webapps.testing");
     } catch(ex) {
       return false;
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -267,17 +267,18 @@ CertVerifier::InsanityVerifyCert(
   // TODO(bug 915931): Pass in stapled OCSP response in all calls to
   //                   BuildCertChain.
 
   insanity::pkix::ScopedCERTCertList builtChain;
   switch (usage) {
     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, ocspFetching, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
+                                       pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
@@ -291,17 +292,17 @@ CertVerifier::InsanityVerifyCert(
       SECOidTag evPolicy = SEC_OID_UNKNOWN;
       rv = GetFirstEVPolicy(cert, evPolicy);
       if (rv == SECSuccess && evPolicy != SEC_OID_UNKNOWN) {
         NSSCertDBTrustDomain
           trustDomain(trustSSL,
                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                         : NSSCertDBTrustDomain::FetchOCSPForEV,
-                      pinArg);
+                      mOCSPCache, 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,
                                           evPolicy, stapledOCSPResponse,
                                           builtChain);
         if (rv == SECSuccess) {
@@ -316,65 +317,69 @@ CertVerifier::InsanityVerifyCert(
 
       if (flags & FLAG_MUST_BE_EV) {
         PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
         rv = SECFailure;
         break;
       }
 
       // Now try non-EV.
-      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
+                                       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,
                                         SEC_OID_X509_ANY_POLICY,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLCA: {
-      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
+                                       pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeCA,
                           KU_KEY_CERT_SIGN,
                           SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailSigner: {
-      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
+                                       pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
                           SEC_OID_X509_ANY_POLICY,
                           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, ocspFetching, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
+                                       pinArg);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         0,
                                         SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
                                         SEC_OID_X509_ANY_POLICY,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
-                                       pinArg);
+                                       mOCSPCache, pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
@@ -392,28 +397,31 @@ CertVerifier::InsanityVerifyCert(
         keyUsage = KU_KEY_CERT_SIGN;
         eku = SEC_OID_UNKNOWN;
       } else {
         endEntityOrCA = MustBeEndEntity;
         keyUsage = KU_DIGITAL_SIGNATURE;
         eku = SEC_OID_OCSP_RESPONDER;
       }
 
-      NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, pinArg);
+      NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
+                                    pinArg);
       rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
                           keyUsage, eku, SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
-        NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, pinArg);
+        NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
+                                        pinArg);
         rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
                             eku, SEC_OID_X509_ANY_POLICY,
                             stapledOCSPResponse, builtChain);
         if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
-                                                  ocspFetching, pinArg);
+                                                  ocspFetching, mOCSPCache,
+                                                  pinArg);
           rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
                               keyUsage, eku, SEC_OID_X509_ANY_POLICY,
                               stapledOCSPResponse, builtChain);
         }
       }
 
       break;
     }
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_psm__CertVerifier_h
 #define mozilla_psm__CertVerifier_h
 
 #include "insanity/pkixtypes.h"
+#include "OCSPCache.h"
 
 namespace mozilla { namespace psm {
 
 class CertVerifier
 {
 public:
   typedef unsigned int Flags;
   // XXX: FLAG_LOCAL_ONLY is ignored in the classic verification case
@@ -62,16 +63,18 @@ public:
   CertVerifier(implementation_config ic,
 #ifndef NSS_NO_LIBPKIX
                missing_cert_download_config ac, crl_download_config cdc,
 #endif
                ocsp_download_config odc, ocsp_strict_config osc,
                ocsp_get_config ogc);
   ~CertVerifier();
 
+  void ClearOCSPCache() { mOCSPCache.Clear(); }
+
   const implementation_config mImplementation;
 #ifndef NSS_NO_LIBPKIX
   const bool mMissingCertDownloadEnabled;
   const bool mCRLDownloadEnabled;
 #endif
   const bool mOCSPDownloadEnabled;
   const bool mOCSPStrict;
   const bool mOCSPGETEnabled;
@@ -80,14 +83,16 @@ private:
   SECStatus InsanityVerifyCert(CERTCertificate* cert,
       const SECCertificateUsage usage,
       const PRTime time,
       void* pinArg,
       const Flags flags,
       /*optional*/ const SECItem* stapledOCSPResponse,
       /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain,
       /*optional out*/ SECOidTag* evOidPolicy);
+
+  OCSPCache mOCSPCache;
 };
 
 void InitCertVerifierLog();
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__CertVerifier_h
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -4,18 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "NSSCertDBTrustDomain.h"
 
 #include <stdint.h>
 
 #include "ExtendedValidation.h"
+#include "certdb.h"
 #include "insanity/pkix.h"
-#include "certdb.h"
 #include "nss.h"
 #include "ocsp.h"
 #include "pk11pub.h"
 #include "prerror.h"
 #include "prmem.h"
 #include "prprf.h"
 #include "secerr.h"
 #include "secmod.h"
@@ -25,29 +25,31 @@ using namespace insanity::pkix;
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gCertVerifierLog;
 #endif
 
 namespace mozilla { namespace psm {
 
 const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[] = "Builtin Roots Module";
 
-namespace {
+void PORT_Free_string(char* str) { PORT_Free(str); }
 
-inline void PORT_Free_string(char* str) { PORT_Free(str); }
+namespace {
 
 typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
 
 } // unnamed namespace
 
 NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
                                            OCSPFetching ocspFetching,
+                                           OCSPCache& ocspCache,
                                            void* pinArg)
   : mCertDBTrustType(certDBTrustType)
   , mOCSPFetching(ocspFetching)
+  , mOCSPCache(ocspCache)
   , mPinArg(pinArg)
 {
 }
 
 SECStatus
 NSSCertDBTrustDomain::FindPotentialIssuers(
   const SECItem* encodedIssuerName, PRTime time,
   /*out*/ insanity::pkix::ScopedCERTCertList& results)
@@ -156,54 +158,116 @@ NSSCertDBTrustDomain::CheckRevocation(
   }
 
   // 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);
+    SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert,
+                                                          time,
+                                                          stapledOCSPResponse);
     if (rv == SECSuccess) {
+      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+             ("NSSCertDBTrustDomain: stapled OCSP response: good"));
       return rv;
     }
     if (PR_GetError() != SEC_ERROR_OCSP_OLD_RESPONSE) {
+      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+             ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
       return rv;
     }
+  } else {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("NSSCertDBTrustDomain: no stapled OCSP response"));
   }
 
-  // TODO(bug 915932): Need to change this when we add the OCSP cache.
+  PRErrorCode cachedResponseErrorCode = 0;
+  PRTime cachedResponseValidThrough = 0;
+  bool cachedResponsePresent = mOCSPCache.Get(cert, issuerCert,
+                                              cachedResponseErrorCode,
+                                              cachedResponseValidThrough);
+  if (cachedResponsePresent) {
+    if (cachedResponseErrorCode == 0 && cachedResponseValidThrough >= time) {
+      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+             ("NSSCertDBTrustDomain: cached OCSP response: good"));
+      return SECSuccess;
+    }
+    // If we have a cached revoked response, use it.
+    if (cachedResponseErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) {
+      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+             ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
+      PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
+      return SECFailure;
+    }
+    // The cached response may indicate an unknown certificate or it may be
+    // expired. Don't return with either of these statuses yet - we may be
+    // able to fetch a more recent one.
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("NSSCertDBTrustDomain: cached OCSP response: error %ld valid "
+           "until %lld", cachedResponseErrorCode, cachedResponseValidThrough));
+    // When a good cached response has expired, it is more convenient
+    // to convert that to an error code and just deal with
+    // cachedResponseErrorCode from here on out.
+    if (cachedResponseErrorCode == 0 && cachedResponseValidThrough < time) {
+      cachedResponseErrorCode = SEC_ERROR_OCSP_OLD_RESPONSE;
+    }
+  } else {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("NSSCertDBTrustDomain: no cached OCSP response"));
+  }
+  // At this point, if and only if cachedErrorResponseCode is 0, there was no
+  // cached response.
+  PR_ASSERT((!cachedResponsePresent && cachedResponseErrorCode == 0) ||
+            (cachedResponsePresent && cachedResponseErrorCode != 0));
 
   // TODO: We still need to handle the fallback for expired responses. But,
   // if/when we disable OCSP fetching by default, it would be ambiguous whether
   // security.OCSP.enable==0 means "I want the default" or "I really never want
   // you to ever fetch OCSP."
 
   if ((mOCSPFetching == NeverFetchOCSP) ||
       (endEntityOrCA == MustBeCA && (mOCSPFetching == FetchOCSPForDVHardFail ||
                                      mOCSPFetching == FetchOCSPForDVSoftFail))) {
+    // We're not going to be doing any fetching, so if there was a cached
+    // "unknown" response, say so.
+    if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
+      PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
+      return SECFailure;
+    }
+    // If we're doing hard-fail, we want to know if we have a cached response
+    // that has expired.
+    if (mOCSPFetching == FetchOCSPForDVHardFail &&
+        cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
+      PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
+      return SECFailure;
+    }
+
     return SECSuccess;
   }
 
   if (mOCSPFetching == LocalOnlyOCSPForEV) {
-    PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
+    PR_SetError(cachedResponseErrorCode != 0 ? cachedResponseErrorCode
+                                             : SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
     return SECFailure;
   }
 
   ScopedPtr<char, PORT_Free_string>
     url(CERT_GetOCSPAuthorityInfoAccessLocation(cert));
 
   if (!url) {
-    if (stapledOCSPResponse) {
-      PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
+    if (mOCSPFetching == FetchOCSPForEV ||
+        cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
+      PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
       return SECFailure;
     }
-    if (mOCSPFetching == FetchOCSPForEV) {
-      PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
+    if (stapledOCSPResponse ||
+        cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
+      PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
       return SECFailure;
     }
 
     // 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.
     return SECSuccess;
@@ -224,25 +288,32 @@ NSSCertDBTrustDomain::CheckRevocation(
                                                request));
   if (!response) {
     if (mOCSPFetching != FetchOCSPForDVSoftFail) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure after "
               "CERT_PostOCSPRequest failure"));
       return SECFailure;
     }
+    if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
+      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+             ("NSSCertDBTrustDomain: returning SECFailure from cached "
+              "response after CERT_PostOCSPRequest failure"));
+      PR_SetError(cachedResponseErrorCode, 0);
+      return SECFailure;
+    }
 
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: returning SECSuccess after "
             "CERT_PostOCSPRequest failure"));
     return SECSuccess; // Soft fail -> success :(
   }
 
-  SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time,
-                                           response);
+  SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time,
+                                                        response);
   if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) {
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
       ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
     return rv;
   }
 
   PRErrorCode error = PR_GetError();
   if (error == SEC_ERROR_OCSP_UNKNOWN_CERT ||
@@ -251,16 +322,41 @@ NSSCertDBTrustDomain::CheckRevocation(
   }
 
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
          ("NSSCertDBTrustDomain: end of CheckRevocation"));
 
   return SECSuccess;
 }
 
+SECStatus
+NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
+  const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
+  const SECItem* encodedResponse)
+{
+  PRTime thisUpdate = 0;
+  PRTime validThrough = 0;
+  SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time,
+                                           encodedResponse, &thisUpdate,
+                                           &validThrough);
+  PRErrorCode error = (rv == SECSuccess ? 0 : PR_GetError());
+  if (rv == SECSuccess || error == SEC_ERROR_REVOKED_CERTIFICATE ||
+      error == SEC_ERROR_OCSP_UNKNOWN_CERT) {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("NSSCertDBTrustDomain: caching OCSP response"));
+    if (mOCSPCache.Put(cert, issuerCert, error, thisUpdate, validThrough)
+          != SECSuccess) {
+      return SECFailure;
+    }
+    // The call to Put may have un-set the error. Re-set it.
+    PR_SetError(error, 0);
+  }
+  return rv;
+}
+
 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
@@ -14,16 +14,18 @@
 namespace mozilla { namespace psm {
 
 SECStatus InitializeNSS(const char* dir, bool readOnly);
 
 void DisableMD5();
 
 extern const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[];
 
+void PORT_Free_string(char* str);
+
 // The dir parameter is the path to the directory containing the NSS builtin
 // roots module. Usually this is the same as the path to the other NSS shared
 // libraries. If it is null then the (library) path will be searched.
 //
 // The modNameUTF8 parameter should usually be
 // BUILTIN_ROOTS_MODULE_DEFAULT_NAME.
 SECStatus LoadLoadableRoots(/*optional*/ const char* dir,
                             const char* modNameUTF8);
@@ -50,17 +52,17 @@ public:
   enum OCSPFetching {
     NeverFetchOCSP = 0,
     FetchOCSPForDVSoftFail = 1,
     FetchOCSPForDVHardFail = 2,
     FetchOCSPForEV = 3,
     LocalOnlyOCSPForEV = 4,
   };
   NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
-                       void* pinArg);
+                       OCSPCache& ocspCache, void* pinArg);
 
   virtual SECStatus FindPotentialIssuers(
                         const SECItem* encodedIssuerName,
                         PRTime time,
                 /*out*/ insanity::pkix::ScopedCERTCertList& results);
 
   virtual SECStatus GetCertTrust(insanity::pkix::EndEntityOrCA endEntityOrCA,
                                  SECOidTag policy,
@@ -72,16 +74,21 @@ public:
 
   virtual SECStatus CheckRevocation(insanity::pkix::EndEntityOrCA endEntityOrCA,
                                     const CERTCertificate* cert,
                           /*const*/ CERTCertificate* issuerCert,
                                     PRTime time,
                        /*optional*/ const SECItem* stapledOCSPResponse);
 
 private:
+  SECStatus VerifyAndMaybeCacheEncodedOCSPResponse(
+    const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
+    const SECItem* encodedResponse);
+
   const SECTrustType mCertDBTrustType;
   const OCSPFetching mOCSPFetching;
+  OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__NSSCertDBTrustDomain_h
new file mode 100644
--- /dev/null
+++ b/security/certverifier/OCSPCache.cpp
@@ -0,0 +1,289 @@
+/* -*- 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 "OCSPCache.h"
+
+#include "NSSCertDBTrustDomain.h"
+#include "pk11pub.h"
+#include "secerr.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gCertVerifierLog;
+#endif
+
+namespace mozilla { namespace psm {
+
+void
+Insanity_PK11_DestroyContext_true(PK11Context* context)
+{
+  PK11_DestroyContext(context, true);
+}
+
+typedef insanity::pkix::ScopedPtr<PK11Context,
+                                  Insanity_PK11_DestroyContext_true>
+                                  ScopedPK11Context;
+
+// Let derIssuer be the DER encoding of the issuer of aCert.
+// Let derPublicKey be the DER encoding of the public key of aIssuerCert.
+// Let serialNumber be the bytes of the serial number of aCert.
+// The value calculated is SHA384(derIssuer || derPublicKey || serialNumber).
+// Because the DER encodings include the length of the data encoded,
+// there do not exist A(derIssuerA, derPublicKeyA, serialNumberA) and
+// B(derIssuerB, derPublicKeyB, serialNumberB) such that the concatenation of
+// each triplet results in the same string of bytes but where each part in A is
+// not equal to its counterpart in B. This is important because as a result it
+// is computationally infeasible to find collisions that would subvert this
+// cache (given that SHA384 is a cryptographically-secure hash function).
+static SECStatus
+CertIDHash(SHA384Buffer& buf, const CERTCertificate* aCert,
+       const CERTCertificate* aIssuerCert)
+{
+  ScopedPK11Context context(PK11_CreateDigestContext(SEC_OID_SHA384));
+  if (!context) {
+    return SECFailure;
+  }
+  SECStatus rv = PK11_DigestBegin(context.get());
+  if (rv != SECSuccess) {
+    return rv;
+  }
+  rv = PK11_DigestOp(context.get(), aCert->derIssuer.data,
+                     aCert->derIssuer.len);
+  if (rv != SECSuccess) {
+    return rv;
+  }
+  rv = PK11_DigestOp(context.get(), aIssuerCert->derPublicKey.data,
+                     aIssuerCert->derPublicKey.len);
+  if (rv != SECSuccess) {
+    return rv;
+  }
+  rv = PK11_DigestOp(context.get(), aCert->serialNumber.data,
+                     aCert->serialNumber.len);
+  if (rv != SECSuccess) {
+    return rv;
+  }
+  uint32_t outLen = 0;
+  rv = PK11_DigestFinal(context.get(), buf, &outLen, SHA384_LENGTH);
+  if (outLen != SHA384_LENGTH) {
+    return SECFailure;
+  }
+  return rv;
+}
+
+SECStatus
+OCSPCache::Entry::Init(const CERTCertificate* aCert,
+                       const CERTCertificate* aIssuerCert,
+                       PRErrorCode aErrorCode,
+                       PRTime aThisUpdate,
+                       PRTime aValidThrough)
+{
+  mErrorCode = aErrorCode;
+  mThisUpdate = aThisUpdate;
+  mValidThrough = aValidThrough;
+  return CertIDHash(mIDHash, aCert, aIssuerCert);
+}
+
+OCSPCache::OCSPCache()
+  : mMutex("OCSPCache-mutex")
+{
+}
+
+OCSPCache::~OCSPCache()
+{
+  Clear();
+}
+
+// Returns -1 if no entry is found for the given (cert, issuer) pair.
+int32_t
+OCSPCache::FindInternal(const CERTCertificate* aCert,
+                        const CERTCertificate* aIssuerCert,
+                        const MutexAutoLock& /* aProofOfLock */)
+{
+  if (mEntries.length() == 0) {
+    return -1;
+  }
+
+  SHA384Buffer idHash;
+  SECStatus rv = CertIDHash(idHash, aCert, aIssuerCert);
+  if (rv != SECSuccess) {
+    return -1;
+  }
+
+  // mEntries is sorted with the most-recently-used entry at the end.
+  // Thus, searching from the end will often be fastest.
+  for (int32_t i = mEntries.length() - 1; i >= 0; i--) {
+    if (memcmp(mEntries[i]->mIDHash, idHash, SHA384_LENGTH) == 0) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+void
+OCSPCache::LogWithCerts(const char* aMessage, const CERTCertificate* aCert,
+                        const CERTCertificate* aIssuerCert)
+{
+#ifdef PR_LOGGING
+  if (PR_LOG_TEST(gCertVerifierLog, PR_LOG_DEBUG)) {
+    insanity::pkix::ScopedPtr<char, mozilla::psm::PORT_Free_string>
+      cn(CERT_GetCommonName(&aCert->subject));
+    insanity::pkix::ScopedPtr<char, mozilla::psm::PORT_Free_string>
+      cnIssuer(CERT_GetCommonName(&aIssuerCert->subject));
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, (aMessage, cn.get(), cnIssuer.get()));
+  }
+#endif
+}
+
+void
+OCSPCache::MakeMostRecentlyUsed(size_t aIndex,
+                                const MutexAutoLock& /* aProofOfLock */)
+{
+  Entry* entry = mEntries[aIndex];
+  // Since mEntries is sorted with the most-recently-used entry at the end,
+  // aIndex is likely to be near the end, so this is likely to be fast.
+  mEntries.erase(mEntries.begin() + aIndex);
+  mEntries.append(entry);
+}
+
+bool
+OCSPCache::Get(const CERTCertificate* aCert,
+               const CERTCertificate* aIssuerCert,
+               PRErrorCode& aErrorCode,
+               PRTime& aValidThrough)
+{
+  PR_ASSERT(aCert);
+  PR_ASSERT(aIssuerCert);
+
+  MutexAutoLock lock(mMutex);
+
+  int32_t index = FindInternal(aCert, aIssuerCert, lock);
+  if (index < 0) {
+    LogWithCerts("OCSPCache::Get(%s, %s) not in cache", aCert, aIssuerCert);
+    return false;
+  }
+  LogWithCerts("OCSPCache::Get(%s, %s) in cache", aCert, aIssuerCert);
+  aErrorCode = mEntries[index]->mErrorCode;
+  aValidThrough = mEntries[index]->mValidThrough;
+  MakeMostRecentlyUsed(index, lock);
+  return true;
+}
+
+SECStatus
+OCSPCache::Put(const CERTCertificate* aCert,
+               const CERTCertificate* aIssuerCert,
+               PRErrorCode aErrorCode,
+               PRTime aThisUpdate,
+               PRTime aValidThrough)
+{
+  PR_ASSERT(aCert);
+  PR_ASSERT(aIssuerCert);
+
+  MutexAutoLock lock(mMutex);
+
+  int32_t index = FindInternal(aCert, aIssuerCert, lock);
+
+  if (index >= 0) {
+    // Never replace an entry indicating a revoked certificate.
+    if (mEntries[index]->mErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) {
+      LogWithCerts("OCSPCache::Put(%s, %s) already in cache as revoked - "
+                   "not replacing", aCert, aIssuerCert);
+      MakeMostRecentlyUsed(index, lock);
+      return SECSuccess;
+    }
+
+    // Never replace a newer entry with an older one unless the older entry
+    // indicates a revoked certificate, which we want to remember.
+    if (mEntries[index]->mThisUpdate > aThisUpdate &&
+        aErrorCode != SEC_ERROR_REVOKED_CERTIFICATE) {
+      LogWithCerts("OCSPCache::Put(%s, %s) already in cache with more recent "
+                   "validity - not replacing", aCert, aIssuerCert);
+      MakeMostRecentlyUsed(index, lock);
+      return SECSuccess;
+    }
+
+    LogWithCerts("OCSPCache::Put(%s, %s) already in cache - replacing",
+                 aCert, aIssuerCert);
+    mEntries[index]->mErrorCode = aErrorCode;
+    mEntries[index]->mThisUpdate = aThisUpdate;
+    mEntries[index]->mValidThrough = aValidThrough;
+    MakeMostRecentlyUsed(index, lock);
+    return SECSuccess;
+  }
+
+  if (mEntries.length() == MaxEntries) {
+    LogWithCerts("OCSPCache::Put(%s, %s) too full - evicting an entry", aCert,
+                 aIssuerCert);
+    for (Entry** toEvict = mEntries.begin(); toEvict != mEntries.end();
+         toEvict++) {
+      // Never evict an entry that indicates a revoked or unknokwn certificate,
+      // because revoked responses are more security-critical to remember.
+      if ((*toEvict)->mErrorCode != SEC_ERROR_REVOKED_CERTIFICATE &&
+          (*toEvict)->mErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT) {
+        delete *toEvict;
+        mEntries.erase(toEvict);
+        break;
+      }
+    }
+    // Well, we tried, but apparently everything is revoked or unknown.
+    // We don't want to remove a cached revoked or unknown response. If we're
+    // trying to insert a good response, we can just return "successfully"
+    // without doing so. This means we'll lose some speed, but it's not a
+    // security issue. If we're trying to insert a revoked or unknown response,
+    // we can't. We should return with an error that causes the current
+    // verification to fail.
+    if (mEntries.length() == MaxEntries) {
+      if (aErrorCode != 0) {
+        PR_SetError(aErrorCode, 0);
+        return SECFailure;
+      }
+      return SECSuccess;
+    }
+  }
+
+  Entry* newEntry = new Entry();
+  // Normally we don't have to do this in Gecko, because OOM is fatal.
+  // However, if we want to embed this in another project, OOM might not
+  // be fatal, so handle this case.
+  if (!newEntry) {
+    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+    return SECFailure;
+  }
+  SECStatus rv = newEntry->Init(aCert, aIssuerCert, aErrorCode, aThisUpdate,
+                                aValidThrough);
+  if (rv != SECSuccess) {
+    return rv;
+  }
+  mEntries.append(newEntry);
+  LogWithCerts("OCSPCache::Put(%s, %s) added to cache", aCert, aIssuerCert);
+  return SECSuccess;
+}
+
+void
+OCSPCache::Clear()
+{
+  MutexAutoLock lock(mMutex);
+  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("OCSPCache::Clear: clearing cache"));
+  // First go through and delete the memory being pointed to by the pointers
+  // in the vector.
+  for (Entry** entry = mEntries.begin(); entry < mEntries.end();
+       entry++) {
+    delete *entry;
+  }
+  // Then remove the pointers themselves.
+  mEntries.clearAndFree();
+}
+
+} } // namespace mozilla::psm
new file mode 100644
--- /dev/null
+++ b/security/certverifier/OCSPCache.h
@@ -0,0 +1,107 @@
+/* -*- 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.
+ */
+
+#ifndef mozilla_psm_OCSPCache_h
+#define mozilla_psm_OCSPCache_h
+
+#include "certt.h"
+#include "hasht.h"
+#include "insanity/pkixtypes.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Vector.h"
+#include "prerror.h"
+
+namespace mozilla { namespace psm {
+
+// make SHA384Buffer be of type "array of uint8_t of length SHA384_LENGTH"
+typedef uint8_t SHA384Buffer[SHA384_LENGTH];
+
+// OCSPCache can store and retrieve OCSP response verification results. Each
+// result is keyed on the certificate that purportedly corresponds to it (where
+// certificates are distinguished based on serial number, issuer, and
+// issuer public key, much like in an encoded OCSP response itself). A maximum
+// of 1024 distinct entries can be stored.
+// OCSPCache is thread-safe.
+class OCSPCache
+{
+public:
+  OCSPCache();
+  ~OCSPCache();
+
+  // Returns true if the status of the given certificate (issued by the given
+  // issuer) is in the cache, and false otherwise.
+  // If it is in the cache, returns by reference the error code of the cached
+  // status and the time through which the status is considered trustworthy.
+  bool Get(const CERTCertificate* aCert, const CERTCertificate* aIssuerCert,
+           /* out */ PRErrorCode& aErrorCode, /* out */ PRTime& aValidThrough);
+
+  // Caches the status of the given certificate (issued by the given issuer).
+  // The status is considered trustworthy through the given time.
+  // A status with an error code of SEC_ERROR_REVOKED_CERTIFICATE will not
+  // be replaced or evicted.
+  // A status with an error code of SEC_ERROR_OCSP_UNKNOWN_CERT will not
+  // be evicted when the cache is full.
+  // A status with a more recent thisUpdate will not be replaced with a
+  // status with a less recent thisUpdate unless the less recent status
+  // indicates the certificate is revoked.
+  SECStatus Put(const CERTCertificate* aCert,
+                const CERTCertificate* aIssuerCert,
+                PRErrorCode aErrorCode,
+                PRTime aThisUpdate,
+                PRTime aValidThrough);
+
+  // Removes everything from the cache.
+  void Clear();
+
+private:
+  class Entry
+  {
+  public:
+    SECStatus Init(const CERTCertificate* aCert,
+                   const CERTCertificate* aIssuerCert,
+                   PRErrorCode aErrorCode, PRTime aThisUpdate,
+                   PRTime aValidThrough);
+
+    PRErrorCode mErrorCode;
+    PRTime mThisUpdate;
+    PRTime mValidThrough;
+    // The SHA-384 hash of the concatenation of the DER encodings of the
+    // issuer name and issuer key, followed by the serial number.
+    // See the documentation for CertIDHash in OCSPCache.cpp.
+    SHA384Buffer mIDHash;
+  };
+
+  int32_t FindInternal(const CERTCertificate* aCert,
+                       const CERTCertificate* aIssuerCert,
+                       const MutexAutoLock& aProofOfLock);
+  void MakeMostRecentlyUsed(size_t aIndex, const MutexAutoLock& aProofOfLock);
+  void LogWithCerts(const char* aMessage, const CERTCertificate* aCert,
+                    const CERTCertificate* aIssuerCert);
+
+  Mutex mMutex;
+  static const size_t MaxEntries = 1024;
+  // Sorted with the most-recently-used entry at the end.
+  // Using 256 here reserves as much possible inline storage as the vector
+  // implementation will give us. 1024 bytes is the maximum it allows,
+  // which results in 256 Entry pointers or 128 Entry pointers, depending
+  // on the size of a pointer.
+  Vector<Entry*, 256> mEntries;
+};
+
+} } // namespace mozilla::psm
+
+#endif // mozilla_psm_OCSPCache_h
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -1,34 +1,30 @@
 # -*- 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/.
 
 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',
+    'OCSPCache.cpp',
 ]
 
 if not CONFIG['NSS_NO_EV_CERTS']:
     UNIFIED_SOURCES += [
         'ExtendedValidation.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '../insanity/include',
 ]
 
 DIRS += [
-    '../insanity/test/lib',
+    '../insanity',
 ]
 
 FAIL_ON_WARNINGS = True
 
 LIBRARY_NAME = 'certverifier'
 
 FINAL_LIBRARY = 'xul'
--- a/security/insanity/include/insanity/pkix.h
+++ b/security/insanity/include/insanity/pkix.h
@@ -98,17 +98,26 @@ SECStatus VerifySignedData(const CERTSig
                            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);
 
+// The optional parameter thisUpdate will be the thisUpdate value of
+// the encoded response if it is considered trustworthy. Only
+// good, unknown, or revoked responses that verify correctly are considered
+// trustworthy. If the response is not trustworthy, thisUpdate will be 0.
+// Similarly, the optional parameter validThrough will be the time through
+// which the encoded response is considered trustworthy (that is, if a response had a
+// thisUpdate time of validThrough, it would be considered trustworthy).
 SECStatus VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
                                     const CERTCertificate* cert,
                                     CERTCertificate* issuerCert,
                                     PRTime time,
-                                    const SECItem* encodedResponse);
+                                    const SECItem* encodedResponse,
+                 /* optional out */ PRTime* thisUpdate,
+                 /* optional out */ PRTime* validThrough);
 
 } } // namespace insanity::pkix
 
 #endif // insanity_pkix__pkix_h
--- a/security/insanity/lib/pkixocsp.cpp
+++ b/security/insanity/lib/pkixocsp.cpp
@@ -50,30 +50,42 @@ ENUM_CLASS CertStatus : uint8_t {
 };
 
 class Context
 {
 public:
   Context(TrustDomain& trustDomain,
           const CERTCertificate& cert,
           CERTCertificate& issuerCert,
-          PRTime time)
+          PRTime time,
+          PRTime* thisUpdate,
+          PRTime* validThrough)
     : trustDomain(trustDomain)
     , cert(cert)
     , issuerCert(issuerCert)
     , time(time)
     , certStatus(CertStatus::Unknown)
+    , thisUpdate(thisUpdate)
+    , validThrough(validThrough)
   {
+    if (thisUpdate) {
+      *thisUpdate = 0;
+    }
+    if (validThrough) {
+      *validThrough = 0;
+    }
   }
 
   TrustDomain& trustDomain;
   const CERTCertificate& cert;
   CERTCertificate& issuerCert;
   const PRTime time;
   CertStatus certStatus;
+  PRTime* thisUpdate;
+  PRTime* validThrough;
 
 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.
@@ -298,33 +310,36 @@ VerifySignature(Context& context, Respon
 
   return SECSuccess;
 }
 
 SECStatus
 VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
                           const CERTCertificate* cert,
                           CERTCertificate* issuerCert, PRTime time,
-                          const SECItem* encodedResponse)
+                          const SECItem* encodedResponse,
+                          PRTime* thisUpdate,
+                          PRTime* validThrough)
 {
   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);
+  Context context(trustDomain, *cert, *issuerCert, time, thisUpdate,
+                  validThrough);
 
   if (der::Nested(input, der::SEQUENCE,
                   bind(OCSPResponse, _1, ref(context))) != der::Success) {
     return SECFailure;
   }
 
   if (der::End(input) != der::Success) {
     return SECFailure;
@@ -679,16 +694,23 @@ SingleResponse(der::Input& input, Contex
 
 
   if (!input.AtEnd()) {
     if (CheckExtensionsForCriticality(input) != der::Success) {
       return der::Failure;
     }
   }
 
+  if (context.thisUpdate) {
+    *context.thisUpdate = thisUpdate;
+  }
+  if (context.validThrough) {
+    *context.validThrough = notAfter;
+  }
+
   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 }
copy from security/certverifier/moz.build
copy to security/insanity/moz.build
--- a/security/certverifier/moz.build
+++ b/security/insanity/moz.build
@@ -1,34 +1,27 @@
 # -*- 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/.
 
 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',
+    'lib/pkixbuild.cpp',
+    'lib/pkixcheck.cpp',
+    'lib/pkixder.cpp',
+    'lib/pkixkey.cpp',
+    'lib/pkixocsp.cpp',
 ]
 
-if not CONFIG['NSS_NO_EV_CERTS']:
-    UNIFIED_SOURCES += [
-        'ExtendedValidation.cpp',
-    ]
-
 LOCAL_INCLUDES += [
-    '../insanity/include',
+    'include',
 ]
 
 DIRS += [
-    '../insanity/test/lib',
+    'test/lib',
 ]
 
 FAIL_ON_WARNINGS = True
 
-LIBRARY_NAME = 'certverifier'
+LIBRARY_NAME = 'insanitypkix'
 
 FINAL_LIBRARY = 'xul'
--- a/security/manager/ssl/public/nsIX509CertDB.idl
+++ b/security/manager/ssl/public/nsIX509CertDB.idl
@@ -16,17 +16,17 @@ interface nsIRecentBadCerts;
 interface nsIX509CertList;
 
 %{C++
 #define NS_X509CERTDB_CONTRACTID "@mozilla.org/security/x509certdb;1"
 %}
 
 typedef uint32_t AppTrustedRoot;
 
-[scriptable, function, uuid(e12aec59-7153-4e84-9376-2e851311b7a3)]
+[scriptable, function, uuid(0927baea-622d-4e41-a76d-255af426e7fb)]
 interface nsIOpenSignedAppFileCallback : nsISupports
 {
   void openSignedAppFileFinished(in nsresult rv,
                                  in nsIZipReader aZipReader,
                                  in nsIX509Cert3 aSignerCert);
 };
 
 /**
@@ -353,9 +353,12 @@ interface nsIX509CertDB : nsISupports {
    */
   int32_t /*PRErrorCode*/
     verifyCertNow(in nsIX509Cert aCert,
                   in int64_t /*SECCertificateUsage*/ aUsage,
                   in uint32_t aFlags,
                   out nsIX509CertList verifiedChain,
                   out bool aHasEVPolicy);
 
+  // Clears the OCSP cache for the current certificate verification
+  // implementation.
+  void clearOCSPCache();
 };
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -904,61 +904,63 @@ AuthCertificate(CertVerifier& certVerifi
 {
   MOZ_ASSERT(infoObject);
   MOZ_ASSERT(cert);
 
   SECStatus rv;
 
   // TODO: Remove this after we switch to insanity::pkix as the
   // only option
-  if (stapledOCSPResponse) {
-    CERTCertDBHandle* handle = CERT_GetDefaultCertDB();
-    rv = CERT_CacheOCSPResponseFromSideChannel(handle, cert, PR_Now(),
-                                               stapledOCSPResponse,
-                                               infoObject);
-    if (rv != SECSuccess) {
-      // Due to buggy servers that will staple expired OCSP responses
-      // (see for example http://trac.nginx.org/nginx/ticket/425),
-      // don't terminate the connection if the stapled response is expired.
-      // We will fall back to fetching revocation information.
-      PRErrorCode ocspErrorCode = PR_GetError();
-      if (ocspErrorCode != SEC_ERROR_OCSP_OLD_RESPONSE) {
-        // stapled OCSP response present but invalid for some reason
-        Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4);
-        return rv;
+  if (certVerifier.mImplementation == CertVerifier::classic) {
+    if (stapledOCSPResponse) {
+      CERTCertDBHandle* handle = CERT_GetDefaultCertDB();
+      rv = CERT_CacheOCSPResponseFromSideChannel(handle, cert, PR_Now(),
+                                                 stapledOCSPResponse,
+                                                 infoObject);
+      if (rv != SECSuccess) {
+        // Due to buggy servers that will staple expired OCSP responses
+        // (see for example http://trac.nginx.org/nginx/ticket/425),
+        // don't terminate the connection if the stapled response is expired.
+        // We will fall back to fetching revocation information.
+        PRErrorCode ocspErrorCode = PR_GetError();
+        if (ocspErrorCode != SEC_ERROR_OCSP_OLD_RESPONSE) {
+          // stapled OCSP response present but invalid for some reason
+          Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4);
+          return rv;
+        } else {
+          // stapled OCSP response present but expired
+          Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3);
+        }
       } else {
-        // stapled OCSP response present but expired
-        Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3);
+        // stapled OCSP response present and good
+        Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1);
       }
     } else {
-      // stapled OCSP response present and good
-      Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1);
-    }
-  } else {
-    // no stapled OCSP response
-    Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2);
+      // no stapled OCSP response
+      Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2);
 
-    uint32_t reasonsForNotFetching = 0;
+      uint32_t reasonsForNotFetching = 0;
 
-    char* ocspURI = CERT_GetOCSPAuthorityInfoAccessLocation(cert);
-    if (!ocspURI) {
-      reasonsForNotFetching |= 1; // invalid/missing OCSP URI
-    } else {
-      if (std::strncmp(ocspURI, "http://", 7)) { // approximation
+      char* ocspURI = CERT_GetOCSPAuthorityInfoAccessLocation(cert);
+      if (!ocspURI) {
         reasonsForNotFetching |= 1; // invalid/missing OCSP URI
+      } else {
+        if (std::strncmp(ocspURI, "http://", 7)) { // approximation
+          reasonsForNotFetching |= 1; // invalid/missing OCSP URI
+        }
+        PORT_Free(ocspURI);
       }
-      PORT_Free(ocspURI);
-    }
 
-    if (!certVerifier.mOCSPDownloadEnabled) {
-      reasonsForNotFetching |= 2;
+      if (!certVerifier.mOCSPDownloadEnabled) {
+        reasonsForNotFetching |= 2;
+      }
+
+      Telemetry::Accumulate(Telemetry::SSL_OCSP_MAY_FETCH,
+                            reasonsForNotFetching);
     }
-
-    Telemetry::Accumulate(Telemetry::SSL_OCSP_MAY_FETCH,
-                          reasonsForNotFetching);
   }
 
   // We want to avoid storing any intermediate cert information when browsing
   // in private, transient contexts.
   bool saveIntermediates =
     !(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE);
 
   insanity::pkix::ScopedCERTCertList certList;
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -1795,8 +1795,30 @@ nsNSSCertificateDB::VerifyCertNow(nsIX50
     NS_ENSURE_TRUE(evOidPolicy == SEC_OID_UNKNOWN, NS_ERROR_FAILURE);
     NS_ENSURE_TRUE(error != 0, NS_ERROR_FAILURE);
     *_retval = error;
   }
   nssCertList.forget(verifiedChain);
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsNSSCertificateDB::ClearOCSPCache()
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
+  NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
+  if (certVerifier->mImplementation == CertVerifier::insanity) {
+    certVerifier->ClearOCSPCache();
+  } else {
+    SECStatus srv = CERT_ClearOCSPCache();
+    if (srv != SECSuccess) {
+      return MapSECStatus(srv);
+    }
+  }
+
+  return NS_OK;
+}
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -992,16 +992,28 @@ void nsNSSComponent::setValidationOption
 #ifndef NSS_NO_LIBPKIX
       aiaDownloadEnabled ?
         CertVerifier::missing_cert_download_on : CertVerifier::missing_cert_download_off,
       crlDownloading ?
         CertVerifier::crl_download_allowed : CertVerifier::crl_local_only,
 #endif
       odc, osc, ogc);
 
+  // insanity::pkix has its own OCSP cache, so disable the NSS cache
+  // if appropriate.
+  if (certVerifierImplementation == CertVerifier::insanity) {
+    // Using -1 disables the cache. The other arguments are the default
+    // values and aren't exposed by the API.
+    CERT_OCSPCacheSettings(-1, 1*60*60L, 24*60*60L);
+  } else {
+    // Using 1000 enables the cache with the default size of 1000. Again,
+    // these values are not exposed by the API.
+    CERT_OCSPCacheSettings(1000, 1*60*60L, 24*60*60L);
+  }
+
   CERT_ClearOCSPCache();
 }
 
 // Enable the TLS versions given in the prefs, defaulting to SSL 3.0 (min
 // version) and TLS 1.2 (max version) when the prefs aren't set or set to
 // invalid values.
 nsresult
 nsNSSComponent::setEnabledTLSVersions()
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CertVerifier.h"
+#include "OCSPCache.h"
+#include "nss.h"
+#include "prprf.h"
+#include "secerr.h"
+
+#include "gtest/gtest.h"
+
+const int MaxCacheEntries = 1024;
+
+class OCSPCacheTest : public ::testing::Test
+{
+  protected:
+    static void SetUpTestCase()
+    {
+      NSS_NoDB_Init(nullptr);
+      mozilla::psm::InitCertVerifierLog();
+    }
+
+    mozilla::psm::OCSPCache cache;
+};
+
+// Makes a fake certificate with just the fields we need for testing here.
+// (And those values are almost entirely bogus.)
+// stackCert should be stack-allocated memory.
+static void
+MakeFakeCert(CERTCertificate* stackCert, const char* subjectValue,
+             const char* issuerValue, const char* serialNumberValue,
+             const char* publicKeyValue)
+{
+  stackCert->derSubject.data = (unsigned char*)subjectValue;
+  stackCert->derSubject.len = strlen(subjectValue);
+  stackCert->derIssuer.data = (unsigned char*)issuerValue;
+  stackCert->derIssuer.len = strlen(issuerValue);
+  stackCert->serialNumber.data = (unsigned char*)serialNumberValue;
+  stackCert->serialNumber.len = strlen(serialNumberValue);
+  stackCert->derPublicKey.data = (unsigned char*)publicKeyValue;
+  stackCert->derPublicKey.len = strlen(publicKeyValue);
+  CERTName *subject = CERT_AsciiToName(subjectValue); // TODO: this will leak...
+  ASSERT_TRUE(subject);
+  stackCert->subject.arena = subject->arena;
+  stackCert->subject.rdns = subject->rdns;
+}
+
+static void
+PutAndGet(mozilla::psm::OCSPCache& cache, CERTCertificate* subject,
+          CERTCertificate* issuer,
+          PRErrorCode error, PRTime time)
+{
+  // The first time is thisUpdate. The second is validUntil.
+  // The caller is expecting the validUntil returned with Get
+  // to be equal to the passed-in time. Since these values will
+  // be different in practice, make thisUpdate less than validUntil.
+  ASSERT_TRUE(time >= 10);
+  SECStatus rv = cache.Put(subject, issuer, error, time - 10, time);
+  ASSERT_TRUE(rv == SECSuccess);
+  PRErrorCode errorOut;
+  PRTime timeOut;
+  ASSERT_TRUE(cache.Get(subject, issuer, errorOut, timeOut));
+  ASSERT_TRUE(error == errorOut && time == timeOut);
+}
+
+TEST_F(OCSPCacheTest, TestPutAndGet)
+{
+  SCOPED_TRACE("");
+  CERTCertificate subject;
+  MakeFakeCert(&subject, "CN=subject1", "CN=issuer1", "001", "key001");
+  CERTCertificate issuer;
+  MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
+  PutAndGet(cache, &subject, &issuer, 0, PR_Now());
+  PRErrorCode errorOut;
+  PRTime timeOut;
+  ASSERT_FALSE(cache.Get(&issuer, &issuer, errorOut, timeOut));
+}
+
+TEST_F(OCSPCacheTest, TestVariousGets)
+{
+  SCOPED_TRACE("");
+  CERTCertificate issuer;
+  MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
+  PRTime timeIn = PR_Now();
+  for (int i = 0; i < MaxCacheEntries; i++) {
+    CERTCertificate subject;
+    char subjectBuf[64];
+    PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
+    char serialBuf[8];
+    PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
+    MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
+    PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
+  }
+  CERTCertificate subject;
+  // This will be at the end of the list in the cache
+  PRErrorCode errorOut;
+  PRTime timeOut;
+  MakeFakeCert(&subject, "CN=subject0000", "CN=issuer1", "0000", "key000");
+  ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
+  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
+  // Once we access it, it goes to the front
+  ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
+  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
+  MakeFakeCert(&subject, "CN=subject0512", "CN=issuer1", "0512", "key000");
+  // This will be in the middle
+  ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
+  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512);
+  ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
+  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512);
+  // We've never seen this certificate
+  MakeFakeCert(&subject, "CN=subject1111", "CN=issuer1", "1111", "key000");
+  ASSERT_FALSE(cache.Get(&subject, &issuer, errorOut, timeOut));
+}
+
+TEST_F(OCSPCacheTest, TestEviction)
+{
+  SCOPED_TRACE("");
+  CERTCertificate issuer;
+  PRTime timeIn = PR_Now();
+  MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
+  // By putting more distinct entries in the cache than it can hold,
+  // we cause the least recently used entry to be evicted.
+  for (int i = 0; i < MaxCacheEntries + 1; i++) {
+    CERTCertificate subject;
+    char subjectBuf[64];
+    PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
+    char serialBuf[8];
+    PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
+    MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
+    PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
+  }
+  CERTCertificate evictedSubject;
+  MakeFakeCert(&evictedSubject, "CN=subject0000", "CN=issuer1", "0000", "key000");
+  PRErrorCode errorOut;
+  PRTime timeOut;
+  ASSERT_FALSE(cache.Get(&evictedSubject, &issuer, errorOut, timeOut));
+}
+
+TEST_F(OCSPCacheTest, TestNoEvictionForRevokedResponses)
+{
+  SCOPED_TRACE("");
+  CERTCertificate issuer;
+  MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
+  CERTCertificate notEvictedSubject;
+  MakeFakeCert(&notEvictedSubject, "CN=subject0000", "CN=issuer1", "0000", "key000");
+  PRTime timeIn = PR_Now();
+  PutAndGet(cache, &notEvictedSubject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, timeIn);
+  // By putting more distinct entries in the cache than it can hold,
+  // we cause the least recently used entry that isn't revoked to be evicted.
+  for (int i = 1; i < MaxCacheEntries + 1; i++) {
+    CERTCertificate subject;
+    char subjectBuf[64];
+    PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
+    char serialBuf[8];
+    PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
+    MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
+    PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
+  }
+  PRErrorCode errorOut;
+  PRTime timeOut;
+  ASSERT_TRUE(cache.Get(&notEvictedSubject, &issuer, errorOut, timeOut));
+  ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == timeIn);
+  CERTCertificate evictedSubject;
+  MakeFakeCert(&evictedSubject, "CN=subject0001", "CN=issuer1", "0001", "key000");
+  ASSERT_FALSE(cache.Get(&evictedSubject, &issuer, errorOut, timeOut));
+}
+
+TEST_F(OCSPCacheTest, TestEverythingIsRevoked)
+{
+  SCOPED_TRACE("");
+  CERTCertificate issuer;
+  MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
+  PRTime timeIn = PR_Now();
+  // Fill up the cache with revoked responses.
+  for (int i = 0; i < MaxCacheEntries; i++) {
+    CERTCertificate subject;
+    char subjectBuf[64];
+    PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
+    char serialBuf[8];
+    PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
+    MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
+    PutAndGet(cache, &subject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, timeIn + i);
+  }
+  CERTCertificate goodSubject;
+  MakeFakeCert(&goodSubject, "CN=subject1025", "CN=issuer1", "1025", "key000");
+  // This will "succeed", allowing verification to continue. However,
+  // nothing was actually put in the cache.
+  SECStatus result = cache.Put(&goodSubject, &issuer, 0, timeIn + 1025 - 50,
+                               timeIn + 1025);
+  ASSERT_TRUE(result == SECSuccess);
+  PRErrorCode errorOut;
+  PRTime timeOut;
+  ASSERT_FALSE(cache.Get(&goodSubject, &issuer, errorOut, timeOut));
+
+  CERTCertificate revokedSubject;
+  MakeFakeCert(&revokedSubject, "CN=subject1026", "CN=issuer1", "1026", "key000");
+  // This will fail, causing verification to fail.
+  result = cache.Put(&revokedSubject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE,
+                     timeIn + 1026 - 50, timeIn + 1026);
+  PRErrorCode error = PR_GetError();
+  ASSERT_TRUE(result == SECFailure);
+  ASSERT_TRUE(error == SEC_ERROR_REVOKED_CERTIFICATE);
+}
+
+TEST_F(OCSPCacheTest, VariousIssuers)
+{
+  SCOPED_TRACE("");
+  CERTCertificate issuer1;
+  MakeFakeCert(&issuer1, "CN=issuer1", "CN=issuer1", "000", "key000");
+  CERTCertificate issuer2;
+  MakeFakeCert(&issuer2, "CN=issuer2", "CN=issuer2", "000", "key001");
+  CERTCertificate issuer3;
+  // Note: same CN as issuer1
+  MakeFakeCert(&issuer3, "CN=issuer1", "CN=issuer3", "000", "key003");
+  CERTCertificate subject;
+  MakeFakeCert(&subject, "CN=subject", "CN=issuer1", "001", "key002");
+  PRTime timeIn = PR_Now();
+  PutAndGet(cache, &subject, &issuer1, 0, timeIn);
+  PRErrorCode errorOut;
+  PRTime timeOut;
+  ASSERT_TRUE(cache.Get(&subject, &issuer1, errorOut, timeOut));
+  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
+  ASSERT_FALSE(cache.Get(&subject, &issuer2, errorOut, timeOut));
+  ASSERT_FALSE(cache.Get(&subject, &issuer3, errorOut, timeOut));
+}
+
+TEST_F(OCSPCacheTest, Times)
+{
+  SCOPED_TRACE("");
+  CERTCertificate subject;
+  MakeFakeCert(&subject, "CN=subject1", "CN=issuer1", "001", "key001");
+  CERTCertificate issuer;
+  MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
+  PutAndGet(cache, &subject, &issuer, SEC_ERROR_OCSP_UNKNOWN_CERT, 100);
+  PutAndGet(cache, &subject, &issuer, 0, 200);
+  // This should not override the more recent entry.
+  SECStatus rv = cache.Put(&subject, &issuer, SEC_ERROR_OCSP_UNKNOWN_CERT, 100, 100);
+  ASSERT_TRUE(rv == SECSuccess);
+  PRErrorCode errorOut;
+  PRTime timeOut;
+  ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
+  // Here we see the more recent time.
+  ASSERT_TRUE(errorOut == 0 && timeOut == 200);
+
+  // SEC_ERROR_REVOKED_CERTIFICATE overrides everything
+  PutAndGet(cache, &subject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, 50);
+}
--- a/security/manager/ssl/tests/gtest/moz.build
+++ b/security/manager/ssl/tests/gtest/moz.build
@@ -4,21 +4,19 @@
 # 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/.
 
 LIBRARY_NAME = 'ssltest'
 
 LIBXUL_LIBRARY = True
 
 SOURCES += [
-	'TLSIntoleranceTest.cpp',
+    'OCSPCacheTest.cpp',
+    'TLSIntoleranceTest.cpp',
 ]
 
 LOCAL_INCLUDES += [
+    '../../../../certverifier',
     '../../../../insanity/include',
+    '/security/manager/ssl/src',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
-
-LOCAL_INCLUDES += [
-    '/security/manager/ssl/src',
-]
-
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -17,16 +17,17 @@ let gIsWindows = ("@mozilla.org/windows-
 
 const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"]
                        .getService(Ci.nsIDebug2).isDebugBuild;
 
 const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
 const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
 
 // Sort in numerical order
+const SEC_ERROR_BAD_DER                                 = SEC_ERROR_BASE +   9;
 const SEC_ERROR_EXPIRED_CERTIFICATE                     = SEC_ERROR_BASE +  11;
 const SEC_ERROR_REVOKED_CERTIFICATE                     = SEC_ERROR_BASE +  12; // -8180
 const SEC_ERROR_UNKNOWN_ISSUER                          = SEC_ERROR_BASE +  13;
 const SEC_ERROR_BAD_DATABASE                            = SEC_ERROR_BASE +  18;
 const SEC_ERROR_UNTRUSTED_ISSUER                        = SEC_ERROR_BASE +  20; // -8172
 const SEC_ERROR_UNTRUSTED_CERT                          = SEC_ERROR_BASE +  21; // -8171
 const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE              = SEC_ERROR_BASE +  30; // -8162
 const SEC_ERROR_EXTENSION_NOT_FOUND                     = SEC_ERROR_BASE +  35; // -8157
@@ -113,21 +114,19 @@ function _getLibraryFunctionWithNoArgume
   }
 
   let SECStatus = ctypes.int;
   let func = nsslib.declare(functionName, ctypes.default_abi, SECStatus);
   return func;
 }
 
 function clearOCSPCache() {
-  let CERT_ClearOCSPCache =
-    _getLibraryFunctionWithNoArguments("CERT_ClearOCSPCache", "nss3");
-  if (CERT_ClearOCSPCache() != 0) {
-    throw "Failed to clear OCSP cache";
-  }
+  let certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                 .getService(Ci.nsIX509CertDB);
+  certdb.clearOCSPCache();
 }
 
 function clearSessionCache() {
   let SSL_ClearSessionCache = null;
   try {
     SSL_ClearSessionCache =
       _getLibraryFunctionWithNoArguments("SSL_ClearSessionCache", "ssl3");
   } catch (e) {
--- a/security/manager/ssl/tests/unit/test_ev_certs.js
+++ b/security/manager/ssl/tests/unit/test_ev_certs.js
@@ -218,22 +218,20 @@ function add_tests_in_mode(useInsanity)
       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);
-      // XXX(bug 915932): Without an OCSP cache, local-only validation of EV
-      //                  certs will always fail due to lack of an OCSP.
-      do_check_eq(hasEVPolicy.value, isDebugBuild && !useInsanity);
+      do_check_eq(hasEVPolicy.value, isDebugBuild);
       do_check_eq(error,
-                  useInsanity ? SEC_ERROR_POLICY_VALIDATION_FAILED
-                              : (isDebugBuild ? 0
+                  isDebugBuild ? 0
+                               : (useInsanity ? SEC_ERROR_POLICY_VALIDATION_FAILED
                                               : 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.
--- a/security/manager/ssl/tests/unit/test_ocsp_caching.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_caching.js
@@ -35,16 +35,30 @@ function run_test() {
     let goodResponse = ocspResponses[0];
 
     response.setStatusLine(request.httpVersion, 200, "OK");
     response.setHeader("Content-Type", "application/ocsp-response");
     response.bodyOutputStream.write(goodResponse, goodResponse.length);
   });
   ocspResponder.start(8080);
 
+  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();
+  });
+
   // This test assumes that OCSPStaplingServer uses the same cert for
   // ocsp-stapling-unknown.example.com and ocsp-stapling-none.example.com.
 
   // Get an Unknown response for the *.exmaple.com cert and put it in the
   // OCSP cache.
   add_connection_test("ocsp-stapling-unknown.example.com",
                       getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNKNOWN_CERT),
                       clearSessionCache);
@@ -88,26 +102,28 @@ function run_test() {
   add_test(function() { clearOCSPCache(); gFetchCount = 0; run_next_test(); });
 
   // A failure to retrieve an OCSP response will result in an error entry being
   // added to the cache.
   add_connection_test("ocsp-stapling-none.example.com", Cr.NS_OK,
                       clearSessionCache);
   add_test(function() { do_check_eq(gFetchCount, 1); run_next_test(); });
 
-  // The error entry will prevent a fetch from happening for a while.
-  add_connection_test("ocsp-stapling-none.example.com", Cr.NS_OK,
-                      clearSessionCache);
-  add_test(function() { do_check_eq(gFetchCount, 1); run_next_test(); });
+  // TODO(bug 977865): implement this for insanity
+  if (!useInsanity) {
+    // The error entry will prevent a fetch from happening for a while.
+    add_connection_test("ocsp-stapling-none.example.com", Cr.NS_OK,
+                        clearSessionCache);
+    add_test(function() { do_check_eq(gFetchCount, 1); run_next_test(); });
+  }
 
   // The error entry must not prevent a stapled OCSP response from being
   // 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();
+  // Reset state
+  add_test(function() { clearOCSPCache(); gFetchCount = 0; run_next_test(); });
 }
--- a/security/manager/ssl/tests/unit/test_ocsp_required.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_required.js
@@ -48,15 +48,15 @@ function add_tests_in_mode(useInsanity)
     run_next_test();
   });
 
   add_connection_test("ocsp-stapling-none.example.com",
                       getXPCOMStatusFromNSS(SEC_ERROR_OCSP_BAD_SIGNATURE));
   add_connection_test("ocsp-stapling-none.example.com",
                       getXPCOMStatusFromNSS(SEC_ERROR_OCSP_BAD_SIGNATURE));
   add_test(function () {
-    // XXX(bug 915932): special case for insanity::pkix due to the temporary
-    // lack of an OCSP cache.
+    // TODO(bug 977865): insanity::pkix keeps requesting responses from
+    // failing responders
     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
@@ -47,17 +47,19 @@ function add_tests_in_mode(useInsanity, 
   // 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.
+  // depends on whether the CA that signed the response is a trusted CA
+  // (but only with the classic implementation - insanity::pkix always
+  // results in the error SEC_ERROR_OCSP_INVALID_SIGNING_CERT).
 
   // This stapled response is from a CA that is untrusted and did not issue
   // the server's certificate.
   add_test(function() {
     certDB.setCertTrust(otherTestCA, Ci.nsIX509Cert.CA_CERT,
                         Ci.nsIX509CertDB.UNTRUSTED);
     run_next_test();
   });
@@ -88,46 +90,52 @@ function add_tests_in_mode(useInsanity, 
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_TRY_SERVER_LATER), true);
   add_ocsp_test("ocsp-stapling-needssig.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG), true);
   add_ocsp_test("ocsp-stapling-unauthorized.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST),
                 true);
   add_ocsp_test("ocsp-stapling-unknown.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNKNOWN_CERT), true);
+  // TODO(bug 977870): this should not result in SEC_ERROR_BAD_DER
   add_ocsp_test("ocsp-stapling-good-other.example.com",
-                getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNKNOWN_CERT), true);
+                getXPCOMStatusFromNSS(
+                  useInsanity ? SEC_ERROR_BAD_DER
+                              : SEC_ERROR_OCSP_UNKNOWN_CERT), true);
   // If the server doesn't staple an OCSP response, we continue as normal
   // (this means that even though stapling is enabled, we expect an OCSP
   // request).
   add_connection_test("ocsp-stapling-none.example.com", Cr.NS_OK,
     function() {
       gExpectOCSPRequest = true;
       clearOCSPCache();
       clearSessionCache();
       Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
     }
   );
+  // TODO(bug 977870): this should not result in SEC_ERROR_BAD_DER
   add_ocsp_test("ocsp-stapling-empty.example.com",
-                getXPCOMStatusFromNSS(SEC_ERROR_OCSP_MALFORMED_RESPONSE), true);
+                getXPCOMStatusFromNSS(
+                  useInsanity ? SEC_ERROR_BAD_DER
+                              : 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
 }
 
 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], 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
+  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
   run_next_test();
 }
 
 function run_test() {
   do_get_profile();
 
   let certDB = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -102,15 +102,15 @@ function add_tests_in_mode(useInsanity)
                 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], 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
+  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], 9); // 9 connections with an expired response
+  do_check_eq(histogram.counts[4], 0); // 0 connections with bad responses
   run_next_test();
 }
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/Makefile.in
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/Makefile.in
@@ -5,15 +5,14 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include $(topsrcdir)/config/config.mk
 
 LIBS = \
   $(NSPR_LIBS) \
   $(NSS_LIBS) \
   $(MOZALLOC_LIB) \
-  ../../../../../../certverifier/$(LIB_PREFIX)certverifier.$(LIB_SUFFIX) \
+  ../../../../../../insanity/$(LIB_PREFIX)insanitypkix.$(LIB_SUFFIX) \
   ../../../../../../insanity/test/lib/$(LIB_PREFIX)pkixtestutil.$(LIB_SUFFIX) \
   ../lib/$(LIB_PREFIX)tlsserver.$(LIB_SUFFIX) \
   $(NULL)
 
 DEFINES += $(TK_CFLAGS)
-
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -38,16 +38,17 @@ SERV_FILES = 	\
 		chrome-harness.js \
 		browser-harness.xul \
 		redirect.html \
 		$(topsrcdir)/build/pgo/server-locations.txt \
 		$(topsrcdir)/netwerk/test/httpserver/httpd.js \
 		pywebsocket_wrapper.py \
 		android.json \
 		androidx86.json \
+		android23.json \
 		b2g.json \
 		b2g-desktop.json \
 		b2g-debug.json \
 		gl.json \
 		b2g_start_script.js \
 		$(NULL)	
 
 ifeq ($(MOZ_BUILD_APP),mobile/android)
--- a/testing/mochitest/b2g-debug.json
+++ b/testing/mochitest/b2g-debug.json
@@ -4,34 +4,10 @@
     "caps": "",
     "content": "",
     "docshell": "",
     "dom": "",
     "layout": "",
     "toolkit/devtools/apps": ""
   },
 "excludetests": {
-    "dom/imptests/html/webgl": "",
-    "dom/imptests/editing/selecttest/test_addRange.html": "oom?, bug 775227",
-    "dom/imptests/html/html/": "debug-only failure",
-    "dom/imptests/html/dom/": "debug-only failure",
-    "content/media/webaudio": "bug 916135",
-    "dom/imptests/html/dom/ranges/test_Range-deleteContents.html": "",
-    "dom/imptests/editing/selecttest/test_selectAllChildren.html": "debug-only failure",
-    "content/base/test/test_copypaste.xul": "bug 904183",
-    "dom/imptests/html/dom/ranges/test_Range-extractContents.html": "",
-    "content/xul": "tests that use xul",
-    "layout/xul": "",
-    "dom/imptests/html/dom/ranges/test_Range-mutations.html": "Test timed out.",
-    "dom/apps": "bug 972927, nearly perma-fail",
-    "dom/datastore": "bug 974270, frequent failures",
-    "dom/imptests/editing/conformancetest/test_runtest.html": "takes too long",
-    "content/base/test/test_messagemanager_assertpermission.html": "specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE",
-    "dom/imptests/html/dom/ranges/test_Range-set.html": "",
-    "dom/imptests/editing/selecttest/test_extend.html": "debug-only failure; time out",
-    "dom/imptests/html/dom/ranges/test_Range-cloneContents.html": "",
-    "dom/imptests/html/dom/ranges/test_Range-compareBoundaryPoints.html": "times out, bug 862196",
-    "dom/imptests/html/dom/ranges/test_Range-insertNode.html": "oom?, bug 775227",
-    "dom/browser-element/": "",
-    "content/base/test/test_child_process_shutdown_message.html": "specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE",
-    "dom/imptests/html/dom/ranges/test_Range-surroundContents.html": ""
   }
 }
--- a/testing/mochitest/b2g-desktop.json
+++ b/testing/mochitest/b2g-desktop.json
@@ -4,31 +4,10 @@
     "caps": "",
     "content": "",
     "docshell": "",
     "dom": "",
     "layout": "",
     "toolkit/devtools/apps": ""
   },
 "excludetests": {
-    "dom/imptests/html/webgl": "",
-    "dom/imptests/editing/selecttest/test_addRange.html": "oom?, bug 775227",
-    "content/base/test/test_copyimage.html": "Bug 931116, b2g desktop specific, initial triage",
-    "dom/imptests/html/dom/ranges/test_Range-deleteContents.html": "",
-    "content/base/test/test_copypaste.xul": "bug 904183",
-    "dom/imptests/html/dom/ranges/test_Range-extractContents.html": "",
-    "content/xul": "tests that use xul",
-    "layout/xul": "",
-    "dom/imptests/html/dom/ranges/test_Range-mutations.html": "Test timed out.",
-    "dom/permission/tests/test_wifi-manage.html": "Bug 931116, b2g desktop specific, initial triage",
-    "content/media/webaudio": "bug 916135",
-    "dom/imptests/editing/conformancetest/test_runtest.html": "takes too long",
-    "content/base/test/test_messagemanager_assertpermission.html": "specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE",
-    "dom/imptests/html/dom/ranges/test_Range-set.html": "",
-    "dom/imptests/html/dom/ranges/test_Range-cloneContents.html": "",
-    "dom/imptests/html/dom/ranges/test_Range-compareBoundaryPoints.html": "times out, bug 862196",
-    "dom/imptests/html/dom/ranges/test_Range-insertNode.html": "oom?, bug 775227",
-    "dom/plugins": "tests that use plugins",
-    "dom/browser-element/": "",
-    "content/base/test/test_child_process_shutdown_message.html": "specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE",
-    "dom/imptests/html/dom/ranges/test_Range-surroundContents.html": ""
   }
 }
--- a/testing/mochitest/b2g.json
+++ b/testing/mochitest/b2g.json
@@ -4,48 +4,10 @@
     "caps": "",
     "content": "",
     "docshell": "",
     "dom": "",
     "layout": "",
     "toolkit/devtools/apps": ""
   },
 "excludetests": {
-    "dom/browser-element/mochitest/test_browserElement_oop_FrameWrongURI.html": "",
-    "dom/imptests/html/webgl": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_TargetBlank.html": "",
-    "dom/imptests/editing/selecttest/test_addRange.html": "oom?, bug 775227",
-    "content/media/test/test_media_selection.html": "timed out",
-    "dom/browser-element/mochitest/test_browserElement_oop_OpenWindowRejected.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_OpenWindowDifferentOrigin.html": "",
-    "dom/imptests/html/dom/ranges/test_Range-deleteContents.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_SecurityChange.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_OpenMixedProcess.html": "",
-    "content/base/test/test_copypaste.xul": "bug 904183",
-    "dom/imptests/html/dom/ranges/test_Range-extractContents.html": "",
-    "content/xul": "tests that use xul",
-    "dom/browser-element/mochitest/test_browserElement_inproc_OpenMixedProcess.html": "",
-    "layout/xul": "",
-    "dom/imptests/html/dom/ranges/test_Range-mutations.html": "Test timed out.",
-    "content/media/test/test_seek.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_BrowserWindowNamespace.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_OpenWindow.html": "",
-    "dom/apps": "bug 972927, nearly perma-fail",
-    "dom/datastore": "bug 974270, frequent failures",
-    "dom/imptests/editing/conformancetest/test_runtest.html": "takes too long",
-    "content/media/test/test_buffered.html": "assertion failures",
-    "content/media/test/test_bug493187.html": "",
-    "content/base/test/test_messagemanager_assertpermission.html": "specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE",
-    "content/media/test/test_bug465498.html": "",
-    "dom/imptests/html/dom/ranges/test_Range-set.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_AppWindowNamespace.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_AppFramePermission.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_ErrorSecurity.html": "",
-    "dom/imptests/html/dom/ranges/test_Range-cloneContents.html": "",
-    "dom/imptests/html/dom/ranges/test_Range-compareBoundaryPoints.html": "times out, bug 862196",
-    "dom/imptests/html/dom/ranges/test_Range-insertNode.html": "oom?, bug 775227",
-    "dom/browser-element/mochitest/test_browserElement_oop_OpenWindowInFrame.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_OpenNamed.html": "",
-    "dom/browser-element/mochitest/test_browserElement_oop_Auth.html": "",
-    "content/base/test/test_child_process_shutdown_message.html": "specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE",
-    "dom/imptests/html/dom/ranges/test_Range-surroundContents.html": ""
   }
 }
--- a/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
+++ b/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
@@ -288,10 +288,16 @@ function addTest(test) {
   _tests.push(test);
 }
 
 function runNextTest() {
   if (_tests.length == 0) {
     SimpleTest.finish()
     return;
   }
-  _tests.shift()();
+  var fn = _tests.shift();
+  try {
+    fn();
+  } catch (ex) {
+    info("Test function " + (fn.name ? "'" + fn.name + "' " : "") +
+         "threw an exception: " + ex);
+  }
 }
--- a/toolkit/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionScript.html
+++ b/toolkit/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionScript.html
@@ -76,17 +76,17 @@ window.onload = function () {
        "eval frame's introduction offset is current offset in older frame");
     ok(source.introductionScript.source.element === script2DO,
        "eval frame's introducer belongs to script2 element");
 
     // The frame that called eval, in turn, should have no introduction
     // information. (In the future, we certainly could point at the call
     // that inserted the script element into the document; if that happens,
     // we can update this test.)
-    ok(frame2.script.source.introductionType === undefined,
+    ok(frame2.script.source.introductionType === 'scriptElement',
        "older frame has no introduction type");
     ok(frame2.script.source.introductionScript === undefined,
        "older frame has no introduction script");
     ok(frame2.script.source.introductionOffset === undefined,
        "older frame has no introduction offset");
 
     SimpleTest.finish();
   }
--- a/toolkit/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionType.html
+++ b/toolkit/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionType.html
@@ -5,81 +5,177 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 Debugger.Source.prototype.introductionType should return 'eventHandler' for
 JavaScrip appearing in an inline event handler attribute.
 -->
 <head>
   <meta charset="utf-8">
   <title>Debugger.Source.prototype.introductionType should identify event handlers</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script>
 
 Components.utils.import("resource://gre/modules/jsdebugger.jsm");
 addDebuggerToGlobal(this);
 
+var dbg;
+var iframeDO, doc;
+var Tootles, TootlesDO;
+
 window.onload = function () {
   SimpleTest.waitForExplicitFinish();
+  runNextTest();
+};
 
-  var log = ''
-  var dbg;
-  var iframeDO, doc;
-  var Tootles, TootlesDO;
-
+addTest(function setup() {
   // Create an iframe to debug.
   var iframe = document.createElement("iframe");
-  iframe.src = "data:text/html,<div id='Tootles' onclick='debugger;'>Auddie</div>";
+  iframe.src = "data:text/html," +
+               "<div id='Tootles' onclick='debugger;'>I'm a DIV!</div>" +
+               "<script id='Auddie'>function auddie() { debugger; }<\/script>";
   iframe.onload = onLoadHandler;
   document.body.appendChild(iframe);
 
   function onLoadHandler() {
-    log += 'l';
     // Now that the iframe's window has been created, we can add
     // it as a debuggee.
     dbg = new Debugger;
-    dbg.onDebuggerStatement = TootlesClickDebugger;
     iframeDO = dbg.addDebuggee(iframe.contentWindow);
     doc = iframe.contentWindow.document;
-
     Tootles = doc.getElementById('Tootles');
     TootlesDO = iframeDO.makeDebuggeeValue(Tootles);
 
-    // Send a click event to Tootles.
-    Tootles.dispatchEvent(new Event('click'));
+    runNextTest();
   }
+});
+
+
+// Check the introduction type of in-markup event handler code.
+// Send a click event to Tootles, whose handler has a 'debugger' statement,
+// and check that script's introduction type.
+addTest(function ClickOnTootles() {
+  dbg.onDebuggerStatement = TootlesClickDebugger;
+  Tootles.dispatchEvent(new Event('click'));
 
   function TootlesClickDebugger(frame) {
-    log += 'TC';
+    // some sanity checks
     ok(frame.script.source.element === TootlesDO,
        "top frame source belongs to element 'Tootles'");
-    ok(frame.script.source.elementAttributeName === 'onclick',
+    is(frame.script.source.elementAttributeName, 'onclick',
        "top frame source belongs to 'onclick' attribute");
+
     // And, the actual point of this test:
-    ok(frame.script.source.introductionType === 'eventHandler',
+    is(frame.script.source.introductionType, 'eventHandler',
+       "top frame source's introductionType is 'eventHandler'");
+
+    runNextTest();
+  }
+});
+
+
+// Check the introduction type of dynamically added event handler code.
+// Add a drag event handler to Tootles as a string, and then send
+// Tootles a drag event.
+addTest(function DragTootles() {
+  dbg.onDebuggerStatement = TootlesDragDebugger;
+  Tootles.setAttribute('ondrag', 'debugger;');
+  Tootles.dispatchEvent(new Event('drag'));
+
+  function TootlesDragDebugger(frame) {
+    // sanity checks
+    ok(frame.script.source.element === TootlesDO,
+       "top frame source belongs to element 'Tootles'");
+    is(frame.script.source.elementAttributeName, 'ondrag',
+       "top frame source belongs to 'ondrag' attribute");
+
+    // And, the actual point of this test:
+    is(frame.script.source.introductionType, 'eventHandler',
        "top frame source's introductionType is 'eventHandler'");
 
-    // Dynamically assign some text as a handler, and invoke that handler.
-    dbg.onDebuggerStatement = TootlesDragDebugger;
-    Tootles.setAttribute('ondrag', 'debugger;');
-    Tootles.dispatchEvent(new Event('drag'));
+    runNextTest();
   }
+});
+
+
+// Check the introduction type of an in-markup script element.
+addTest(function checkAuddie() {
+  var fnDO = iframeDO.getOwnPropertyDescriptor('auddie').value;
+  var AuddieDO = iframeDO.makeDebuggeeValue(doc.getElementById('Auddie'));
+
+  ise(fnDO.class, 'Function',
+      "Script element 'Auddie' defined function 'auddie'.");
+  ok(fnDO.script.source.element === AuddieDO,
+     "Function auddie's script belongs to script element 'Auddie'");
+  is(fnDO.script.source.elementAttributeName, undefined,
+     "Function auddie's script doesn't belong to any attribute of 'Auddie'");
+  is(fnDO.script.source.introductionType, 'scriptElement',
+     "Function auddie's script's source was introduced by a script element");
+
+  runNextTest();
+});
+
+
+// Check the introduction type of a dynamically inserted script element.
+addTest(function InsertRover() {
+  dbg.onDebuggerStatement = RoverDebugger;
+  var rover = doc.createElement('script');
+  var roverDO = iframeDO.makeDebuggeeValue(rover);
+  rover.text = 'debugger;';
+  doc.body.appendChild(rover);
+
+  function RoverDebugger(frame) {
+    // sanity checks
+    ok(frame.script.source.element === roverDO,
+       "Rover script belongs to Rover");
+    ok(frame.script.source.elementAttributeName === undefined,
+       "Rover script doesn't belong to an attribute of Rover");
 
-  function TootlesDragDebugger(frame) {
-    log += 'TD';
-    ok(frame.script.source.element === TootlesDO,
-       "top frame source belongs to element 'Tootles'");
-    ok(frame.script.source.elementAttributeName === 'ondrag',
-       "top frame source belongs to 'ondrag' attribute");
-    // And, the actual point of this test:
-    ok(frame.script.source.introductionType === 'eventHandler',
-       "top frame source's introductionType is 'eventHandler'");
+    // Check the introduction type.
+    ok(frame.script.source.introductionType === 'scriptElement',
+       "Rover script's introduction type is 'scriptElement'");
+
+    runNextTest();
+  }
+});
+
+
+// Create a XUL document with a script element, and check its introduction type.
+addTest(function XULDocumentScript() {
+  var xulFrame = document.createElement('iframe');
+  xulFrame.src = "data:application/vnd.mozilla.xul+xml;charset=utf-8," +
+                 "<?xml version=\"1.0\"?>" +
+                 "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'>" +
+                 "<script id='xulie'>function xulScriptFunc() { debugger; }<\/script>" +
+                 "</window>";
+  xulFrame.onload = xulLoaded;
+  info("Appending iframe containing XUL document");
+  document.body.appendChild(xulFrame);
 
-    ok(log === 'lTCTD', "All tests ran.");
-    SimpleTest.finish();
+  function xulLoaded() {
+    info("Loaded XUL document");
+    var xulFrameDO = dbg.addDebuggee(xulFrame.contentWindow);
+    var xulDoc = xulFrame.contentWindow.document;
+    var xulieDO = xulFrameDO.makeDebuggeeValue(xulDoc.getElementById('xulie'));
+    var xulFnDO = xulFrameDO.getOwnPropertyDescriptor('xulScriptFunc').value;
+    ise(typeof xulFnDO, 'object', "XUL script element defined 'xulScriptFunc'");
+    ise(xulFnDO.class, 'Function',
+        "XUL global 'xulScriptFunc' is indeed a function");
+
+    // A XUL document's script elements' code gets shared amongst all
+    // instantiations of the document, so there's no specific DOM element
+    // we can attribute the code to.
+    ise(xulFnDO.script.source.element, undefined,
+        "XUL script code should not be attributed to any individual element");
+
+    ise(xulFnDO.script.source.introductionType, 'scriptElement',
+        "xulScriptFunc's introduction type is 'scriptElement'");
+    runNextTest();
   }
-}
+});
+
 </script>
 </pre>
 </body>
 </html>
--- a/tools/update-packaging/Makefile.in
+++ b/tools/update-packaging/Makefile.in
@@ -58,22 +58,22 @@ ifeq ($(OS_TARGET), WINNT)
 	test -f $(UNPACKAGE)
 	$(RM) -rf '$(PACKAGE_DIR)'
 	cd $(PACKAGE_BASE_DIR) && $(INNER_UNMAKE_PACKAGE)
 endif
 	MAR=$(MAR_BIN) \
 	  $(srcdir)/make_full_update.sh \
 	  '$(STAGE_DIR)/$(PKG_UPDATE_BASENAME).complete.mar' \
 	  '$(PACKAGE_DIR)'
-ifdef MOZ_SIGN_PACKAGE_CMD
-	$(MOZ_SIGN_PACKAGE_CMD) '$(STAGE_DIR)/$(PKG_UPDATE_BASENAME).complete.mar'
+ifdef MOZ_SIGN_CMD
+	$(MOZ_SIGN_CMD) -f mar '$(STAGE_DIR)/$(PKG_UPDATE_BASENAME).complete.mar'
 endif
 
 partial-patch:: $(dir-stage)
 	MAR=$(MAR_BIN) \
 	MBSDIFF=$(MBSDIFF_BIN) \
 	  $(srcdir)/make_incremental_update.sh \
 	  '$(STAGE_DIR)/$(PKG_UPDATE_BASENAME).partial.$(SRC_BUILD_ID)-$(DST_BUILD_ID).mar' \
 	  '$(SRC_BUILD)' \
 	  '$(DST_BUILD)'
-ifdef MOZ_SIGN_PACKAGE_CMD
-	$(MOZ_SIGN_PACKAGE_CMD) '$(STAGE_DIR)/$(PKG_UPDATE_BASENAME).partial.$(SRC_BUILD_ID)-$(DST_BUILD_ID).mar'
+ifdef MOZ_SIGN_CMD
+	$(MOZ_SIGN_CMD) -f mar '$(STAGE_DIR)/$(PKG_UPDATE_BASENAME).partial.$(SRC_BUILD_ID)-$(DST_BUILD_ID).mar'
 endif
--- a/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
+++ b/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
@@ -43,32 +43,26 @@ function make_test(param, expected) {
     param: param,
     expected: expected,
   });
 }
 
 SimpleTest.waitForExplicitFinish();
 
 function load() {
-  info("Started the load handler");
   var iframe = document.getElementById("test");
   var gCallback = null;
   function run_test(test, cb) {
     var url = "unsafeBidiFileName.sjs?name=" + encodeURIComponent(test.param);
-    info("Loading test " + gCounter + " (" + test.param + ") from " + url);
-    iframe.onload = function() {
-      ok(false, "Received an unexpected load event from the iframe");
-    };
     gCallback = cb;
     iframe.src = url;
   }
 
   var gCounter = -1;
   function run_next_test() {
-    info("run_next_test called, gCounter = " + gCounter);
     if (++gCounter == gTests.length)
       finish_test();
     else
       run_test(gTests[gCounter], run_next_test);
   }
 
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
 
@@ -79,56 +73,50 @@ function load() {
     getService(SpecialPowers.Ci.nsIUUIDGenerator).generateUUID();
 
   function HelperAppLauncherDialog() {}
   HelperAppLauncherDialog.prototype = {
     REASON_CANTHANDLE: 0,
     REASON_SERVERREQUEST: 1,
     REASON_TYPESNIFFED: 2,
     show: function(aLauncher, aWindowContext, aReason) {
-      info("show() called");
       netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
       var test = gTests[gCounter];
       is(aLauncher.suggestedFileName, test.expected,
          "The filename should be correctly sanitized");
       gCallback();
     },
     promptForSaveToFile: function(aLauncher, aWindowContext, aDefaultFileName, aSuggestedFileExtension, aForcePrompt) {
-      info("promptForSaveToFile called");
       return null;
     },
     QueryInterface: function(aIID) {
-      info("QueryInterface called");
       netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
       if (aIID.equals(SpecialPowers.Ci.nsISupports) ||
           aIID.equals(SpecialPowers.Ci.nsIHelperAppLauncherDialog))
         return this;
       throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
     }
   };
 
   var factory = {
     createInstance: function(aOuter, aIID) {
-      info("createInstance called");
       netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
       if (aOuter != null)
         throw SpecialPowers.Cr.NS_ERROR_NO_AGGREGATION;
       return new HelperAppLauncherDialog().QueryInterface(aIID);
     }
   };
 
-  info("RegisterFactory...\n");
   SpecialPowers.wrap(SpecialPowers.Components).manager
             .QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
             .registerFactory(FAKE_CID, "",
                              HELPERAPP_DIALOG_CONTRACT,
                              factory);
 
   function finish_test() {
-    info("UnregisterFactory...\n");
     SpecialPowers.wrap(SpecialPowers.Components).manager
               .QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
               .registerFactory(HELPERAPP_DIALOG_CID, "",
                                HELPERAPP_DIALOG_CONTRACT,
                                null);
     SimpleTest.finish();
   }
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1889,48 +1889,48 @@ AndroidBridge::RequestContentRepaint(con
 void
 AndroidBridge::AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId,
                                        const uint32_t& aScrollGeneration)
 {
     // FIXME implement this
 }
 
 void
-AndroidBridge::HandleDoubleTap(const CSSIntPoint& aPoint,
+AndroidBridge::HandleDoubleTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid)
 {
-    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
+    nsCString data = nsPrintfCString("{ \"x\": %f, \"y\": %f }", aPoint.x, aPoint.y);
     nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
             NS_LITERAL_CSTRING("Gesture:DoubleTap"), data));
 }
 
 void
-AndroidBridge::HandleSingleTap(const CSSIntPoint& aPoint,
+AndroidBridge::HandleSingleTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid)
 {
     // TODO Send the modifier data to Gecko for use in mouse events.
-    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
+    nsCString data = nsPrintfCString("{ \"x\": %f, \"y\": %f }", aPoint.x, aPoint.y);
     nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
             NS_LITERAL_CSTRING("Gesture:SingleTap"), data));
 }
 
 void
-AndroidBridge::HandleLongTap(const CSSIntPoint& aPoint,
+AndroidBridge::HandleLongTap(const CSSPoint& aPoint,
                              int32_t aModifiers,
                              const mozilla::layers::ScrollableLayerGuid& aGuid)
 {
-    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
+    nsCString data = nsPrintfCString("{ \"x\": %f, \"y\": %f }", aPoint.x, aPoint.y);
     nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
             NS_LITERAL_CSTRING("Gesture:LongPress"), data));
 }
 
 void
-AndroidBridge::HandleLongTapUp(const CSSIntPoint& aPoint,
+AndroidBridge::HandleLongTapUp(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid)
 {
 }
 
 void
 AndroidBridge::SendAsyncScrollDOMEvent(bool aIsRoot,
                                        const CSSRect& aContentRect,
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -428,26 +428,26 @@ private:
     nsTArray<DelayedTask*> mDelayedTaskQueue;
 
 public:
     NativePanZoomController* SetNativePanZoomController(jobject obj);
     // GeckoContentController methods
     void RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
     void AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId,
                                  const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
-    void HandleDoubleTap(const CSSIntPoint& aPoint,
+    void HandleDoubleTap(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    void HandleSingleTap(const CSSIntPoint& aPoint,
+    void HandleSingleTap(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    void HandleLongTap(const CSSIntPoint& aPoint,
+    void HandleLongTap(const CSSPoint& aPoint,
                        int32_t aModifiers,
                        const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    void HandleLongTapUp(const CSSIntPoint& aPoint,
+    void HandleLongTapUp(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     void SendAsyncScrollDOMEvent(bool aIsRoot,
                                  const CSSRect& aContentRect,
                                  const CSSSize& aScrollableSize) MOZ_OVERRIDE;
     void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE;
     int64_t RunDelayedTasks();
 };
--- a/widget/gonk/ParentProcessController.h
+++ b/widget/gonk/ParentProcessController.h
@@ -18,26 +18,26 @@ class ParentProcessController : public m
 
 public:
     virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
     virtual void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aViewId,
                                          const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
     virtual void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE;
 
     // No-ops
-    virtual void HandleDoubleTap(const CSSIntPoint& aPoint,
+    virtual void HandleDoubleTap(const CSSPoint& aPoint,
                                  int32_t aModifiers,
                                  const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
-    virtual void HandleSingleTap(const CSSIntPoint& aPoint,
+    virtual void HandleSingleTap(const CSSPoint& aPoint,
                                  int32_t aModifiers,
                                  const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
-    virtual void HandleLongTap(const CSSIntPoint& aPoint,
+    virtual void HandleLongTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
-    virtual void HandleLongTapUp(const CSSIntPoint& aPoint,
+    virtual void HandleLongTapUp(const CSSPoint& aPoint,
                                  int32_t aModifiers,
                                  const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
 
     virtual void SendAsyncScrollDOMEvent(bool aIsRoot,
                                          const CSSRect &aContentRect,
                                          const CSSSize &aScrollableSize) MOZ_OVERRIDE {}
 };
 
--- a/widget/windows/winrt/APZController.cpp
+++ b/widget/windows/winrt/APZController.cpp
@@ -223,38 +223,38 @@ APZController::AcknowledgeScrollUpdate(c
 #ifdef DEBUG_CONTROLLER
   WinUtils::Log("APZController::AcknowledgeScrollUpdate scrollid=%I64d gen=%lu",
     aScrollId, aScrollGeneration);
 #endif
   APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
 }
 
 void
-APZController::HandleDoubleTap(const CSSIntPoint& aPoint,
+APZController::HandleDoubleTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid)
 {
 }
 
 void
-APZController::HandleSingleTap(const CSSIntPoint& aPoint,
+APZController::HandleSingleTap(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid)
 {
 }
 
 void
-APZController::HandleLongTap(const CSSIntPoint& aPoint,
+APZController::HandleLongTap(const CSSPoint& aPoint,
                              int32_t aModifiers,
                              const ScrollableLayerGuid& aGuid)
 {
 }
 
 void
-APZController::HandleLongTapUp(const CSSIntPoint& aPoint,
+APZController::HandleLongTapUp(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid)
 {
 }
 
 // requests that we send a mozbrowserasyncscroll domevent. not in use.
 void
 APZController::SendAsyncScrollDOMEvent(bool aIsRoot,
--- a/widget/windows/winrt/APZController.h
+++ b/widget/windows/winrt/APZController.h
@@ -29,26 +29,26 @@ public:
   APZController() :
     mWidgetListener(nullptr)
   {
   }
 
   // GeckoContentController interface
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics);
   virtual void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId, const uint32_t& aScrollGeneration);
-  virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint,
+  virtual void HandleDoubleTap(const mozilla::CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid);
-  virtual void HandleSingleTap(const mozilla::CSSIntPoint& aPoint,
+  virtual void HandleSingleTap(const mozilla::CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid);
-  virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint,
+  virtual void HandleLongTap(const mozilla::CSSPoint& aPoint,
                              int32_t aModifiers,
                              const mozilla::layers::ScrollableLayerGuid& aGuid);
-  virtual void HandleLongTapUp(const mozilla::CSSIntPoint& aPoint,
+  virtual void HandleLongTapUp(const mozilla::CSSPoint& aPoint,
                                int32_t aModifiers,
                                const mozilla::layers::ScrollableLayerGuid& aGuid);
   virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
   virtual void PostDelayedTask(Task* aTask, int aDelayMs);
   virtual bool GetRootZoomConstraints(ZoomConstraints* aOutConstraints);
   virtual void NotifyTransformBegin(const ScrollableLayerGuid& aGuid);
   virtual void NotifyTransformEnd(const ScrollableLayerGuid& aGuid);
   
--- a/widget/xpwidgets/APZCCallbackHelper.cpp
+++ b/widget/xpwidgets/APZCCallbackHelper.cpp
@@ -318,10 +318,67 @@ APZCCallbackHelper::AcknowledgeScrollUpd
     nsCOMPtr<nsIRunnable> r1 = new AcknowledgeScrollUpdateEvent(aScrollId, aScrollGeneration);
     if (!NS_IsMainThread()) {
         NS_DispatchToMainThread(r1);
     } else {
         r1->Run();
     }
 }
 
+static void
+DestroyCSSPoint(void* aObject, nsIAtom* aPropertyName,
+                void* aPropertyValue, void* aData)
+{
+  CSSPoint* point = static_cast<CSSPoint*>(aPropertyValue);
+  delete point;
+}
+
+void
+APZCCallbackHelper::UpdateCallbackTransform(const FrameMetrics& aApzcMetrics, const FrameMetrics& aActualMetrics)
+{
+    nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aApzcMetrics.mScrollId);
+    if (!content) {
+        return;
+    }
+    CSSPoint scrollDelta = aApzcMetrics.mScrollOffset - aActualMetrics.mScrollOffset;
+    content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta), DestroyCSSPoint);
+}
+
+CSSPoint
+APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput, const ScrollableLayerGuid& aGuid)
+{
+    // XXX: technically we need to walk all the way up the layer tree from the layer
+    // represented by |aGuid.mScrollId| up to the root of the layer tree and apply
+    // the input transforms at each level in turn. However, it is quite difficult
+    // to do this given that the structure of the layer tree may be different from
+    // the structure of the content tree. Also it may be impossible to do correctly
+    // at this point because there are other CSS transforms and such interleaved in
+    // between so applying the inputTransforms all in a row at the end may leave
+    // some things transformed improperly. In practice we should rarely hit scenarios
+    // where any of this matters, so I'm skipping it for now and just doing the single
+    // transform for the layer that the input hit.
+
+    if (aGuid.mScrollId != FrameMetrics::NULL_SCROLL_ID) {
+        nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
+        if (content) {
+            void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform);
+            if (property) {
+                CSSPoint delta = (*static_cast<CSSPoint*>(property));
+                return aInput + delta;
+            }
+        }
+    }
+    return aInput;
+}
+
+nsIntPoint
+APZCCallbackHelper::ApplyCallbackTransform(const nsIntPoint& aPoint,
+                                        const ScrollableLayerGuid& aGuid,
+                                        const CSSToLayoutDeviceScale& aScale)
+{
+    LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
+    point = ApplyCallbackTransform(point / aScale, aGuid) * aScale;
+    LayoutDeviceIntPoint ret = gfx::RoundedToInt(point);
+    return nsIntPoint(ret.x, ret.y);
+}
+
 }
 }
--- a/widget/xpwidgets/APZCCallbackHelper.h
+++ b/widget/xpwidgets/APZCCallbackHelper.h
@@ -18,16 +18,17 @@ namespace widget {
    GeckoContentController callback interface required by the AsyncPanZoomController.
    Since different platforms need to implement this interface in similar-but-
    not-quite-the-same ways, this utility class provides some helpful methods
    to hold code that can be shared across the different platform implementations.
  */
 class APZCCallbackHelper
 {
     typedef mozilla::layers::FrameMetrics FrameMetrics;
+    typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
 
 public:
     /* Checks to see if the pres shell that the given FrameMetrics object refers
        to is still the valid pres shell for the DOMWindowUtils. This can help
        guard against apply stale updates (updates meant for a pres shell that has
        since been torn down and replaced). */
     static bool HasValidPresShellId(nsIDOMWindowUtils* aUtils,
                                     const FrameMetrics& aMetrics);
@@ -62,14 +63,41 @@ public:
     static bool GetScrollIdentifiers(const nsIContent* aContent,
                                      uint32_t* aPresShellIdOut,
                                      FrameMetrics::ViewID* aViewIdOut);
 
     /* Tell layout that we received the scroll offset update for the given view ID, so
        that it accepts future scroll offset updates from APZ. */
     static void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId,
                                         const uint32_t& aScrollGeneration);
+
+    /* Save an "input transform" property on the content element corresponding to
+       the scrollable content. This is needed because in some cases when the APZ code
+       sends a paint request via the GeckoContentController interface, we don't always
+       apply the scroll offset that was requested. Since the APZ code doesn't know
+       that we didn't apply it, it will transform inputs assuming that we had applied
+       it, and so we need to apply a fixup to the input to account for the fact that
+       we didn't.
+       The |aApzcMetrics| argument are the metrics that the APZ sent us, and the
+       |aActualMetrics| argument are the metrics representing the gecko state after we
+       applied some or all of the APZ metrics. */
+    static void UpdateCallbackTransform(const FrameMetrics& aApzcMetrics,
+                                        const FrameMetrics& aActualMetrics);
+
+    /* Apply an "input transform" to the given |aInput| and return the transformed value.
+       The input transform applied is the one for the content element corresponding to
+       |aGuid|; this is populated in a previous call to UpdateCallbackTransform. See that
+       method's documentations for details. */
+    static CSSPoint ApplyCallbackTransform(const CSSPoint& aInput,
+                                           const ScrollableLayerGuid& aGuid);
+
+    /* Same as above, but operates on nsIntPoint that are assumed to be in LayoutDevice
+       pixel space. Requires an additonal |aScale| parameter to convert between CSS and
+       LayoutDevice space. */
+    static nsIntPoint ApplyCallbackTransform(const nsIntPoint& aPoint,
+                                             const ScrollableLayerGuid& aGuid,
+                                             const CSSToLayoutDeviceScale& aScale);
 };
 
 }
 }
 
 #endif /*__mozilla_widget_APZCCallbackHelper_h__ */