Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 08 Jun 2014 22:10:12 -0400
changeset 207823 9305a8ec77fe3efc902b73c26807558151e23cdb
parent 207782 5f46ba7c1a82d68cf0d939c1ea842c91234e9508 (current diff)
parent 207822 6cc9ac1f57fabc4466b5beb0889f731fa804e5b1 (diff)
child 207824 bd5d24cb324684d758706eadc74ba1f2bf52dae9
child 207851 96e6a5f9fa3f2affa3cf6b3e412ec2d8d140a8ef
child 207894 b073a21b0c5a6ae17f02a4555f3c2af9efa86f8c
child 209661 6b68004e4b859b126165c8a0f0604dd46c519376
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone32.0a1
first release with
nightly linux32
9305a8ec77fe / 32.0a1 / 20140609030202 / files
nightly linux64
9305a8ec77fe / 32.0a1 / 20140609030202 / files
nightly mac
9305a8ec77fe / 32.0a1 / 20140609030202 / files
nightly win32
9305a8ec77fe / 32.0a1 / 20140609030202 / files
nightly win64
9305a8ec77fe / 32.0a1 / 20140609030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
--- a/browser/components/translation/TranslationDocument.jsm
+++ b/browser/components/translation/TranslationDocument.jsm
@@ -132,42 +132,53 @@ this.TranslationDocument.prototype = {
     if (item.isSimpleRoot) {
       let text = item.nodeRef.firstChild.nodeValue.trim();
       item.original = [text];
       return text;
     }
 
     let str = "";
     item.original = [];
+    let wasLastItemPlaceholder = false;
 
     for (let child of item.nodeRef.childNodes) {
       if (child.nodeType == TEXT_NODE) {
         let x = child.nodeValue.trim();
-        str += x;
-        item.original.push(x);
+        if (x != "") {
+          item.original.push(x);
+          str += x;
+          wasLastItemPlaceholder = false;
+        }
         continue;
       }
 
       let objInMap = this.itemsMap.get(child);
       if (objInMap && !objInMap.isRoot) {
         // If this childNode is present in the itemsMap, it means
         // it's a translation node: it has useful content for translation.
         // In this case, we need to stringify this node.
         // However, if this item is a root, we should skip it here in this
         // object's child list (and just add a placeholder for it), because
         // it will be stringfied separately for being a root.
         item.original.push(objInMap);
         str += this.generateTextForItem(objInMap);
+        wasLastItemPlaceholder = false;
       } else {
         // Otherwise, if this node doesn't contain any useful content,
         // or if it is a root itself, we can replace it with a placeholder node.
         // We can't simply eliminate this node from our string representation
         // because that could change the HTML structure (e.g., it would
         // probably merge two separate text nodes).
-        str += '<br/>';
+        // It's not necessary to add more than one placeholder in sequence;
+        // we can optimize them away.
+        if (!wasLastItemPlaceholder) {
+          item.original.push(TranslationItem_NodePlaceholder);
+          str += '<br>';
+          wasLastItemPlaceholder = true;
+        }
       }
     }
 
     return generateTranslationHtmlForItem(item, str);
   },
 
   /**
    * Changes the document to display its translated
@@ -254,18 +265,18 @@ function TranslationItem(node, id, isRoo
   this.children = [];
 }
 
 TranslationItem.prototype = {
   isRoot: false,
   isSimpleRoot: false,
 
   toString: function() {
-    let rootType = this._isRoot
-                   ? (this._isSimpleRoot ? ' (simple root)' : ' (non simple root)')
+    let rootType = this.isRoot
+                   ? (this.isSimpleRoot ? ' (simple root)' : ' (non simple root)')
                    : '';
     return "[object TranslationItem: <" + this.nodeRef.localName + ">"
            + rootType + "]";
   },
 
   /**
    * This function will parse the result of the translation of one translation
    * item. If this item was a simple root, all we sent was a plain-text version
@@ -320,26 +331,39 @@ TranslationItem.prototype = {
    *                 or "original".
    */
   swapText: function(target) {
     swapTextForItem(this, target);
   }
 };
 
 /**
+ * This object represents a placeholder item for translation. It's similar to
+ * the TranslationItem class, but it represents nodes that have no meaningful
+ * content for translation. These nodes will be replaced by "<br>" in a
+ * translation request. It's necessary to keep them to use it as a mark
+ * for correct positioning and spliting of text nodes.
+ */
+const TranslationItem_NodePlaceholder = {
+  toString: function() {
+    return "[object TranslationItem_NodePlaceholder]";
+  }
+};
+
+/**
  * Generate the outer HTML representation for a given item.
  *
  * @param   item       A TranslationItem object.
  * param    content    The inner content for this item.
  * @returns string     The outer HTML needed for translation
  *                     of this item.
  */
 function generateTranslationHtmlForItem(item, content) {
   let localName = item.isRoot ? "div" : "b";
-  return '<' + localName + ' id="n' + item.id + '">' +
+  return '<' + localName + ' id=n' + item.id + '>' +
          content +
          "</" + localName + ">";
 }
 
  /**
  * Regenerate the text string that represents a TranslationItem object,
  * with data from its "original" array. The array must have already
  * been created by TranslationDocument.generateTextForItem().
@@ -349,32 +373,23 @@ function generateTranslationHtmlForItem(
  * @returns        A string representation of the TranslationItem.
  */
 function regenerateTextFromOriginalHelper(item) {
   if (item.isSimpleRoot) {
     return item.original[0];
   }
 
   let str = "";
-
-  let wasLastItemText = false;
   for (let child of item.original) {
     if (child instanceof TranslationItem) {
       str += regenerateTextFromOriginalHelper(child);
-      wasLastItemText = false;
+    } else if (child === TranslationItem_NodePlaceholder) {
+      str += "<br>";
     } else {
-      // The non-significant elements (which were replaced with <br/>
-      // during the first call to generateTextForItem) are not stored
-      // in the original array. If they are not properly re-generated,
-      // two adjacent text nodes would be merged into one.
-      if (wasLastItemText) {
-        str += "<br/>";
-      }
       str += child;
-      wasLastItemText = true;
     }
   }
 
   return generateTranslationHtmlForItem(item, str);
 }
 
 /**
  * Helper function to parse a HTML doc result.
@@ -390,30 +405,89 @@ function regenerateTextFromOriginalHelpe
  *
  * For text nodes we simply add it as a string.
  */
 function parseResultNode(item, node) {
   item.translation = [];
   for (let child of node.childNodes) {
     if (child.nodeType == TEXT_NODE) {
       item.translation.push(child.nodeValue);
+    } else if (child.localName == "br") {
+      item.translation.push(TranslationItem_NodePlaceholder);
     } else {
       let translationItemChild = item.getChildById(child.id);
 
       if (translationItemChild) {
         item.translation.push(translationItemChild);
         parseResultNode(translationItemChild, child);
       }
     }
   }
 }
 
 /**
  * Helper function to swap the text of a TranslationItem
  * between its original and translated states.
+ * How it works:
+ *
+ * The function iterates through the target array (either the `original` or
+ * `translation` array from the TranslationItem), while also keeping a pointer
+ * to a current position in the child nodes from the actual DOM node that we
+ * are modifying. This pointer is moved forward after each item of the array
+ * is translated. If, at any given time, the pointer doesn't match the expected
+ * node that was supposed to be seen, it means that the original and translated
+ * contents have a different ordering, and thus we need to adjust that.
+ *
+ * A full example of the reordering process, swapping from Original to
+ * Translation:
+ *
+ *    Original (en): <div>I <em>miss</em> <b>you</b></div>
+ *
+ * Translation (fr): <div><b>Tu</b> me <em>manques</em></div>
+ *
+ * Step 1:
+ *   pointer points to firstChild of the DOM node, textnode "I "
+ *   first item in item.translation is [object TranslationItem <b>]
+ *
+ *   pointer does not match the expected element, <b>. So let's move <b> to the
+ *   pointer position.
+ *
+ *   Current state of the DOM:
+ *     <div><b>you</b>I <em>miss</em> </div>
+ *
+ * Step 2:
+ *   pointer moves forward to nextSibling, textnode "I " again.
+ *   second item in item.translation is the string " me "
+ *
+ *   pointer points to a text node, and we were expecting a text node. Match!
+ *   just replace the text content.
+ *
+ *   Current state of the DOM:
+ *     <div><b>you</b> me <em>miss</em> </div>
+ *
+ * Step 3:
+ *   pointer moves forward to nextSibling, <em>miss</em>
+ *   third item in item.translation is [object TranslationItem <em>]
+ *
+ *   pointer points to the expected node. Match! Nothing to do.
+ *
+ * Step 4:
+ *   all items in this item.translation were transformed. The remaining
+ *   text nodes are cleared to "", and domNode.normalize() removes them.
+ *
+ *   Current state of the DOM:
+ *     <div><b>you</b> me <em>miss</em></div>
+ *
+ * Further steps:
+ *   After that, the function will visit the child items (from the visitStack),
+ *   and the text inside the <b> and <em> nodes will be swapped as well,
+ *   yielding the final result:
+ *
+ *     <div><b>Tu</b> me <em>manques</em></div>
+ *
  *
  * @param item     A TranslationItem object
  * @param target   A string that is either "translation"
  *                 or "original".
  */
 function swapTextForItem(item, target) {
   // visitStack is the stack of items that we still need to visit.
   // Let's start the process by adding the root item.
@@ -424,76 +498,176 @@ function swapTextForItem(item, target) {
     let curItem = visitStack.shift();
 
     let domNode = curItem.nodeRef;
     if (!domNode) {
       // Skipping this item due to a missing node.
       continue;
     }
 
-    let sourceNodeCount = 0;
-
     if (!curItem[target]) {
       // Translation not found for this item. This could be due to
       // an error in the server response. For example, if a translation
       // was broken in various chunks, and one of the chunks failed,
       // the items from that chunk will be missing its "translation"
       // field.
       continue;
     }
 
+    domNode.normalize();
+
+    // curNode points to the child nodes of the DOM node that we are
+    // modifying. During most of the process, while the target array is
+    // being iterated (in the for loop below), it should walk together with
+    // the array and be pointing to the correct node that needs to modified.
+    // If it's not pointing to it, that means some sort of node reordering
+    // will be necessary to produce the correct translation.
+    // Note that text nodes don't need to be reordered, as we can just replace
+    // the content of one text node with another.
+    //
+    // curNode starts in the firstChild...
+    let curNode = domNode.firstChild;
+
+    // ... actually, let's make curNode start at the first useful node (either
+    // a non-blank text node or something else). This is not strictly necessary,
+    // as the reordering algorithm would correctly handle this case. However,
+    // this better aligns the resulting translation with the DOM content of the
+    // page, avoiding cases that would need to be unecessarily reordered.
+    //
+    // An example of how this helps:
+    //
+    // ---- Original: <div>                <b>Hello </b> world.</div>
+    //                       ^textnode 1      ^item 1      ^textnode 2
+    //
+    // - Translation: <div><b>Hallo </b> Welt.</div>
+    //
+    // Transformation process without this optimization:
+    //   1 - start pointer at textnode 1
+    //   2 - move item 1 to first position inside the <div>
+    //
+    //       Node now looks like: <div><b>Hello </b>[         ][ world.]</div>
+    //                                         textnode 1^       ^textnode 2
+    //
+    //   3 - replace textnode 1 with " Welt."
+    //   4 - clear remaining text nodes (in this case, textnode 2)
+    //
+    // Transformation process with this optimization:
+    //   1 - start pointer at item 1
+    //   2 - item 1 is already in position
+    //   3 - replace textnode 2 with " Welt."
+    //
+    // which completely avoids any node reordering, and requires only one
+    // text change instead of two (while also leaving the page closer to
+    // its original state).
+    while (curNode &&
+           curNode.nodeType == TEXT_NODE &&
+           curNode.nodeValue.trim() == "") {
+      curNode = curNode.nextSibling;
+    }
+
     // Now let's walk through all items in the `target` array of the
     // TranslationItem. This means either the TranslationItem.original or
     // TranslationItem.translation array.
-    for (let child of curItem[target]) {
-      // If the array element is another TranslationItem object, let's
-      // add it to the stack to be visited
-      if (child instanceof TranslationItem) {
-        // Adding this child to the stack.
-        visitStack.push(child);
-        continue;
-      }
+    for (let targetItem of curItem[target]) {
+
+      if (targetItem instanceof TranslationItem) {
+        // If the array element is another TranslationItem object, let's
+        // add it to the stack to be visited.
+        visitStack.push(targetItem);
+
+        let targetNode = targetItem.nodeRef;
+
+            // If the node is not in the expected position, let's reorder
+            // it into position...
+        if (curNode != targetNode &&
+            // ...unless the page has reparented this node under a totally
+            // different node (or removed it). In this case, all bets are off
+            // on being able to do anything correctly, so it's better not to
+            // bring back the node to this parent.
+            targetNode.parentNode == domNode) {
+
+          // We don't need to null-check curNode because insertBefore(..., null)
+          // does what we need in that case: reorder this node to the end
+          // of child nodes.
+          domNode.insertBefore(targetNode, curNode);
+          curNode = targetNode;
+        }
+
+        // Move pointer forward. Since we do not add empty text nodes to the
+        // list of translation items, we must skip them here too while
+        // traversing the DOM in order to get better alignment between the
+        // text nodes and the translation items.
+        if (curNode) {
+          curNode = getNextSiblingSkippingEmptyTextNodes(curNode);
+        }
 
-      // If it's a string, say, the Nth string of the `target` array, let's
-      // replace the Nth child TextNode of this element with this string.
-      // During our translation process we skipped all empty text nodes, so we
-      // must also skip them here. If there are not enough text nodes to be used,
-      // a new text node will be created and appended to the end of the element.
-      let targetTextNode = getNthNonEmptyTextNodeFromElement(sourceNodeCount++, domNode);
+      } else if (targetItem === TranslationItem_NodePlaceholder) {
+        // If the current item is a placeholder node, we need to move
+        // our pointer "past" it, jumping from one side of a block of
+        // elements + empty text nodes to the other side. Even if
+        // non-placeholder elements exists inside the jumped block,
+        // they will be pulled correctly later in the process when the
+        // targetItem for those nodes are handled.
+
+        while (curNode &&
+               (curNode.nodeType != TEXT_NODE ||
+                curNode.nodeValue.trim() == "")) {
+          curNode = curNode.nextSibling;
+        }
 
-      // A trailing and a leading space must be preserved because they are meaningful in HTML.
-      let preSpace = targetTextNode.nodeValue.startsWith(" ") ? " " : "";
-      let endSpace = targetTextNode.nodeValue.endsWith(" ") ? " " : "";
-      targetTextNode.nodeValue = preSpace + child + endSpace;
+      } else {
+        // Finally, if it's a text item, we just need to find the next
+        // text node to use. Text nodes don't need to be reordered, so
+        // the first one found can be used.
+        while (curNode && curNode.nodeType != TEXT_NODE) {
+          curNode = curNode.nextSibling;
+        }
+
+        // If none was found and we reached the end of the child nodes,
+        // let's create a new one.
+        if (!curNode) {
+          // We don't know if the original content had a space or not,
+          // so the best bet is to create the text node with " " which
+          // will add one space at the beginning and one at the end.
+          curNode = domNode.appendChild(domNode.ownerDocument.createTextNode(" "));
+        }
+
+        // A trailing and a leading space must be preserved because
+        // they are meaningful in HTML.
+        let preSpace = /^\s/.test(curNode.nodeValue) ? " " : "";
+        let endSpace = /\s$/.test(curNode.nodeValue) ? " " : "";
+
+        curNode.nodeValue = preSpace + targetItem + endSpace;
+        curNode = getNextSiblingSkippingEmptyTextNodes(curNode);
+      }
     }
 
-    // The translated version of a node might have less text nodes than its original
-    // version. If that's the case, let's clear the remaining nodes.
-    if (sourceNodeCount > 0) {
-      clearRemainingNonEmptyTextNodesFromElement(sourceNodeCount, domNode);
+    // The translated version of a node might have less text nodes than its
+    // original version. If that's the case, let's clear the remaining nodes.
+    if (curNode) {
+      clearRemainingNonEmptyTextNodesFromElement(curNode);
     }
+
+    // And remove any garbage "" nodes left after clearing.
+    domNode.normalize();
   }
 }
 
-function getNthNonEmptyTextNodeFromElement(n, element) {
-  for (let childNode of element.childNodes) {
-    if (childNode.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
-        childNode.nodeValue.trim() != "") {
-      if (n-- == 0)
-        return childNode;
-    }
+function getNextSiblingSkippingEmptyTextNodes(startSibling) {
+  let item = startSibling.nextSibling;
+  while (item &&
+         item.nodeType == TEXT_NODE &&
+         item.nodeValue.trim() == "") {
+    item = item.nextSibling;
   }
-
-  // If there are not enough DOM nodes, let's create a new one.
-  return element.appendChild(element.ownerDocument.createTextNode(""));
+  return item;
 }
 
-function clearRemainingNonEmptyTextNodesFromElement(start, element) {
-  let count = 0;
-  for (let childNode of element.childNodes) {
-    if (childNode.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
-        childNode.nodeValue.trim() != "") {
-      if (count++ >= start) {
-        childNode.nodeValue = "";
-      }
+function clearRemainingNonEmptyTextNodesFromElement(startSibling) {
+  let item = startSibling;
+  while (item) {
+    if (item.nodeType == TEXT_NODE &&
+        item.nodeValue != "") {
+      item.nodeValue = "";
     }
+    item = item.nextSibling;
   }
 }
--- a/build/autoconf/toolchain.m4
+++ b/build/autoconf/toolchain.m4
@@ -5,25 +5,50 @@ dnl file, You can obtain one at http://m
 AC_DEFUN([MOZ_TOOL_VARIABLES],
 [
 GNU_AS=
 GNU_LD=
 GNU_CC=
 GNU_CXX=
 CC_VERSION='N/A'
 CXX_VERSION='N/A'
-if test "$GCC" = "yes"; then
-    GNU_CC=1
-    CC_VERSION=`$CC -v 2>&1 | grep 'gcc version'`
+cat <<EOF > conftest.c
+#if defined(_MSC_VER)
+#if defined(__clang__)
+COMPILER clang-cl _MSC_VER
+#else
+COMPILER msvc _MSC_VER
+#endif
+#elif defined(__clang__)
+COMPILER clang __clang_major__.__clang_minor__.__clang_patchlevel__
+#elif defined(__GNUC__)
+COMPILER gcc __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
+#elif defined(__INTEL_COMPILER)
+COMPILER icc __INTEL_COMPILER
+#endif
+EOF
+read dummy compiler CC_VERSION <<EOF
+$($CC -E conftest.c 2>/dev/null | grep COMPILER)
+EOF
+read dummy cxxcompiler CXX_VERSION <<EOF
+$($CXX -E conftest.c 2>/dev/null | grep COMPILER)
+EOF
+if test "$compiler" != "$cxxcompiler"; then
+    AC_MSG_ERROR([Your C and C++ compilers are different.  You need to use the same compiler.])
 fi
-if test "$GXX" = "yes"; then
+CC_VERSION=`echo "$CC_VERSION" | sed 's/ //g'`
+CXX_VERSION=`echo "$CXX_VERSION" | sed 's/ //g'`
+echo "$CC_VERSION"
+echo "$CXX_VERSION"
+ecit 1
+if test "$compiler" = "gcc"; then
+    GNU_CC=1
     GNU_CXX=1
-    CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'`
     changequote(<<,>>)
-    GCC_VERSION_FULL=`echo "$CXX_VERSION" | $PERL -pe 's/^.*gcc version ([^ ]*).*/<<$>>1/'`
+    GCC_VERSION_FULL="$CXX_VERSION"
     GCC_VERSION=`echo "$GCC_VERSION_FULL" | $PERL -pe '(split(/\./))[0]>=4&&s/(^\d*\.\d*).*/<<$>>1/;'`
 
     GCC_MAJOR_VERSION=`echo ${GCC_VERSION} | $AWK -F\. '{ print <<$>>1 }'`
     GCC_MINOR_VERSION=`echo ${GCC_VERSION} | $AWK -F\. '{ print <<$>>2 }'`
     changequote([,])
 fi
 
 if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then
@@ -36,38 +61,36 @@ fi
 if test "$GNU_CC"; then
     if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then
         GCC_USE_GNU_LD=1
     fi
 fi
 
 INTEL_CC=
 INTEL_CXX=
-if test "$GCC" = yes; then
-   if test "`$CC -help 2>&1 | grep -c 'Intel(R) C++ Compiler'`" != "0"; then
-     INTEL_CC=1
-   fi
-fi
-
-if test "$GXX" = yes; then
-   if test "`$CXX -help 2>&1 | grep -c 'Intel(R) C++ Compiler'`" != "0"; then
-     INTEL_CXX=1
-   fi
+if test "$compiler" = "icc"; then
+   INTEL_CC=1
+   INTEL_CXX=1
 fi
 
 CLANG_CC=
 CLANG_CXX=
-if test "`$CC -v 2>&1 | egrep -c '(clang version|Apple.*clang)'`" != "0"; then
-   CLANG_CC=1
+CLANG_CL=
+if test "$compiler" = "clang"; then
+    GNU_CC=1
+    GNU_CXX=1
+    CLANG_CC=1
+    CLANG_CXX=1
+fi
+if test "$compiler" = "clang-cl"; then
+    CLANG_CL=1
 fi
 
-if test "`$CXX -v 2>&1 | egrep -c '(clang version|Apple.*clang)'`" != "0"; then
-   CLANG_CXX=1
-fi
 AC_SUBST(CLANG_CXX)
+AC_SUBST(CLANG_CL)
 ])
 
 AC_DEFUN([MOZ_CROSS_COMPILER],
 [
 echo "cross compiling from $host to $target"
 
 _SAVE_CC="$CC"
 _SAVE_CFLAGS="$CFLAGS"
--- a/configure.in
+++ b/configure.in
@@ -460,25 +460,21 @@ case "$target" in
             AC_MSG_ERROR([\$(CXX) test failed.  You must have MS VC++ in your path to build.]) )
         AC_LANG_RESTORE
 
         changequote(,)
         _MSVC_VER_FILTER='s|.*[^!-~]([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?).*|\1|p'
         changequote([,])
 
         # Determine compiler version
-        CC_VERSION=`${CC} -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
-        _CC_MAJOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $1 }'`
-        _CC_MINOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $2 }'`
-        _CC_RELEASE=`echo ${CC_VERSION} | $AWK -F\. '{ print $3 }'`
-        _CC_BUILD=`echo ${CC_VERSION} | $AWK -F\. '{ print $4 }'`
-        _MSC_VER=${_CC_MAJOR_VERSION}${_CC_MINOR_VERSION}
-
-        CXX_VERSION=`${CXX} -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
-        _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | $AWK -F\. '{ print $1 }'`
+        _CC_MAJOR_VERSION=`echo ${CC_VERSION} | cut -c 1-2`
+        _CC_MINOR_VERSION=`echo ${CC_VERSION} | cut -c 3-4`
+        _MSC_VER=${CC_VERSION}
+
+        _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | cut -c 1-2`
 
         if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then
             AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.])
         fi
 
         AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
         AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
 
@@ -491,17 +487,20 @@ case "$target" in
         elif test "$_CC_MAJOR_VERSION" = "18"; then
             _CC_SUITE=12
             MSVS_VERSION=2013
         else
             AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
         fi
         AC_SUBST(MSVS_VERSION)
 
-        AC_DEFINE(HAVE_SEH_EXCEPTIONS)
+        # Disable SEH on clang-cl because it doesn't implement them yet.
+        if test -z "$CLANG_CL"; then
+            AC_DEFINE(HAVE_SEH_EXCEPTIONS)
+        fi
 
         if test -n "$WIN32_REDIST_DIR"; then
           if test ! -d "$WIN32_REDIST_DIR"; then
             AC_MSG_ERROR([Invalid Win32 Redist directory: ${WIN32_REDIST_DIR}])
           fi
           WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd`
         fi
 
@@ -612,18 +611,20 @@ case "$target" in
                 MOZ_MSVC_STL_WRAP__RAISE=1
                 AC_DEFINE(MOZ_MSVC_STL_WRAP__RAISE)
             else
                 AC_MSG_ERROR([Gecko exception wrapping doesn't understand your your MSVC/SDK.  Please file a bug describing this error and your build configuration.])
             fi
         fi
 
         if test "$WRAP_STL_INCLUDES" = "1"; then
-            STL_FLAGS='-D_HAS_EXCEPTIONS=0 -I$(DIST)/stl_wrappers'
+            STL_FLAGS='-I$(DIST)/stl_wrappers'
         fi
+        CFLAGS="$CFLAGS -D_HAS_EXCEPTIONS=0"
+        CXXFLAGS="$CXXFLAGS -D_HAS_EXCEPTIONS=0"
     elif test -z "$CLANG_CC"; then
         # Check w32api version
         _W32API_MAJOR_VERSION=`echo $W32API_VERSION | $AWK -F\. '{ print $1 }'`
         _W32API_MINOR_VERSION=`echo $W32API_VERSION | $AWK -F\. '{ print $2 }'`
         AC_MSG_CHECKING([for w32api version >= $W32API_VERSION])
         AC_TRY_COMPILE([#include <w32api.h>],
             #if (__W32API_MAJOR_VERSION < $_W32API_MAJOR_VERSION) || \
                 (__W32API_MAJOR_VERSION == $_W32API_MAJOR_VERSION && \
@@ -894,21 +895,16 @@ tools are selected during the Xcode/Deve
 
   if test "$result" = "no" ; then
     AC_MSG_ERROR([The selected compiler and Mac OS X SDK are incompatible.])
   fi
 fi
 
 fi # COMPILE_ENVIRONMENT
 
-AC_MSG_CHECKING([compiler version])
-# Just print it so it shows up in the logs.
-cc_version=$($CC --version)
-AC_MSG_RESULT([$cc_version])
-
 if test -n "$MAKE"; then
   if test `echo $MAKE | grep -c make.py` != 1; then
      NOT_PYMAKE=$MAKE
   fi
 fi
 
 case "$host_os" in
 mingw*)
@@ -2161,17 +2157,21 @@ ia64*-hpux*)
         CFLAGS="$CFLAGS -we4553"
         CXXFLAGS="$CXXFLAGS -we4553"
         LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib secur32.lib netapi32.lib"
         MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
         WARNINGS_AS_ERRORS='-WX'
         MOZ_OPTIMIZE_FLAGS='-O1 -Oi'
         MOZ_FIX_LINK_PATHS=
         MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)'
-        LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
+        # Disable these flags on clang-cl since it doesn't ignore unknown arguments by default, and
+        # autoconf insists on passing $LDFLAGS to the compiler.
+        if test -z "$CLANG_CL"; then
+            LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
+        fi
         if test -z "$DEVELOPER_OPTIONS"; then
             LDFLAGS="$LDFLAGS -RELEASE"
         fi
         dnl For profile-guided optimization
         PROFILE_GEN_CFLAGS="-GL"
         PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT"
         dnl XXX: PGO builds can fail with warnings treated as errors,
         dnl specifically "no profile data available" appears to be
@@ -2249,17 +2249,21 @@ ia64*-hpux*)
         fi
 
         if test -n "$GNU_CC"; then
             CFLAGS="$CFLAGS -mstackrealign -fno-keep-inline-dllexport"
             CXXFLAGS="$CXXFLAGS -mstackrealign -fno-keep-inline-dllexport"
             LDFLAGS="$LDFLAGS -Wl,--enable-stdcall-fixup -Wl,--large-address-aware"
         else
             DSO_LDOPTS="$DSO_LDOPTS -MACHINE:X86"
-            LDFLAGS="$LDFLAGS -SAFESEH"
+            # Disable this flag on clang-cl since it doesn't ignore unknown arguments by default, and
+            # autoconf insists on passing $LDFLAGS to the compiler.
+            if test -z "$CLANG_CL"; then
+                LDFLAGS="$LDFLAGS -SAFESEH"
+            fi
         fi
 
         AC_DEFINE(_X86_)
         ;;
     x86_64-*)
         if test -n "$_WIN32_MSVC"; then
             DSO_LDOPTS="$DSO_LDOPTS -MACHINE:X64"
         fi
--- a/content/media/gmp/gmp-api/gmp-video-codec.h
+++ b/content/media/gmp/gmp-api/gmp-video-codec.h
@@ -30,16 +30,17 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef GMP_VIDEO_CODEC_h_
 #define GMP_VIDEO_CODEC_h_
 
 #include <stdint.h>
+#include <stddef.h>
 
 enum { kGMPPayloadNameSize = 32};
 enum { kGMPMaxSimulcastStreams = 4};
 
 enum GMPVideoCodecComplexity
 {
   kGMPComplexityNormal = 0,
   kGMPComplexityHigh = 1,
@@ -70,25 +71,43 @@ struct GMPVideoCodecVP8
   uint32_t mNumberOfTemporalLayers;
   bool mDenoisingOn;
   bool mErrorConcealmentOn;
   bool mAutomaticResizeOn;
   bool mFrameDroppingOn;
   int32_t mKeyFrameInterval;
 };
 
+// H264 specific
+struct GMPVideoCodecH264
+{
+  uint8_t        mProfile;
+  uint8_t        mConstraints;
+  uint8_t        mLevel;
+  uint8_t        mPacketizationMode; // 0 or 1
+  bool           mFrameDroppingOn;
+  int32_t        mKeyFrameInterval;
+  // These are null/0 if not externally negotiated
+  const uint8_t* mSPSData;
+  size_t         mSPSLen;
+  const uint8_t* mPPSData;
+  size_t         mPPSLen;
+};
+
 enum GMPVideoCodecType
 {
   kGMPVideoCodecVP8,
+  kGMPVideoCodecH264,
   kGMPVideoCodecInvalid // Should always be last.
 };
 
 union GMPVideoCodecUnion
 {
   GMPVideoCodecVP8 mVP8;
+  GMPVideoCodecH264 mH264;
 };
 
 // Simulcast is when the same stream is encoded multiple times with different
 // settings such as resolution.
 struct GMPSimulcastStream
 {
   uint32_t mWidth;
   uint32_t mHeight;
@@ -123,20 +142,36 @@ struct GMPVideoCodec
 
   uint32_t mQPMax;
   uint32_t mNumberOfSimulcastStreams;
   GMPSimulcastStream mSimulcastStream[kGMPMaxSimulcastStreams];
 
   GMPVideoCodecMode mMode;
 };
 
+// Either single encoded unit, or multiple units separated by 8/16/24/32
+// bit lengths, all with the same timestamp.  Note there is no final 0-length
+// entry; one should check the overall end-of-buffer against where the next
+// length would be.
+enum GMPBufferType {
+  GMP_BufferSingle = 0,
+  GMP_BufferLength8,
+  GMP_BufferLength16,
+  GMP_BufferLength24,
+  GMP_BufferLength32,
+};
+
 struct GMPCodecSpecificInfoGeneric {
   uint8_t mSimulcastIdx;
 };
 
+struct GMPCodecSpecificInfoH264 {
+  uint8_t mSimulcastIdx;
+};
+
 // Note: if any pointers are added to this struct, it must be fitted
 // with a copy-constructor. See below.
 struct GMPCodecSpecificInfoVP8
 {
   bool mHasReceivedSLI;
   uint8_t mPictureIdSLI;
   bool mHasReceivedRPSI;
   uint64_t mPictureIdRPSI;
@@ -156,12 +191,13 @@ union GMPCodecSpecificInfoUnion
 };
 
 // Note: if any pointers are added to this struct or its sub-structs, it
 // must be fitted with a copy-constructor. This is because it is copied
 // in the copy-constructor of VCMEncodedFrame.
 struct GMPCodecSpecificInfo
 {
   GMPVideoCodecType mCodecType;
+  GMPBufferType mBufferType;
   GMPCodecSpecificInfoUnion mCodecSpecific;
 };
 
 #endif // GMP_VIDEO_CODEC_h_
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -373,17 +373,17 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_mediarecorder_record_session.html]
 [test_mediarecorder_record_startstopstart.html]
 [test_mediarecorder_record_stopms.html]
 [test_mediarecorder_record_timeslice.html]
 [test_mediarecorder_reload_crash.html]
 [test_mediarecorder_unsupported_src.html]
 [test_metadata.html]
 [test_mixed_principals.html]
-skip-if = (os == 'linux'  && debug) || (toolkit == 'android' && debug) || toolkit == 'gonk' # bug 567954 and intermittent leaks
+skip-if = true # bug 567954 and intermittent leaks
 [test_mozHasAudio.html]
 [test_networkState.html]
 [test_new_audio.html]
 [test_no_load_event.html]
 [test_paused.html]
 [test_paused_after_ended.html]
 [test_play_events.html]
 [test_play_events_2.html]
--- a/content/media/test/test_playback_rate_playpause.html
+++ b/content/media/test/test_playback_rate_playpause.html
@@ -14,17 +14,17 @@
 // ASSERTION: Clock should go forwards if the playback rate is > 0.:
 //            'mCurrentFrameTime <= clock_time || mPlaybackRate <= 0',
 //            file MediaDecoderStateMachine.cpp, line 2379
 if (navigator.platform.startsWith("Win")) {
   SimpleTest.expectAssertions(1, 6);
 } else if (navigator.platform.startsWith("Mac")) {
   SimpleTest.expectAssertions(3, 7);
 } else if (navigator.platform.startsWith("Linux arm")) {
-  SimpleTest.expectAssertions(4, 11);
+  SimpleTest.expectAssertions(3, 11);
 }
 
 let manager = new MediaTestManager;
 
 function ontimeupdate(e) {
   var t = e.target;
   if (t.currentTime != 0.0) {
     dump(t.token + " t.currentTime != 0.0.\n");
--- a/content/media/webrtc/LoadManager.cpp
+++ b/content/media/webrtc/LoadManager.cpp
@@ -53,16 +53,17 @@ LoadManager::LoadManager(int aLoadMeasur
   MOZ_ASSERT(mHighLoadThreshold > mLowLoadThreshold);
   mLoadMonitor = new LoadMonitor(mLoadMeasurementInterval);
   mLoadMonitor->Init(mLoadMonitor);
   mLoadMonitor->SetLoadChangeCallback(this);
 }
 
 LoadManager::~LoadManager()
 {
+  LOG(("LoadManager: shutting down LoadMonitor"));
   mLoadMonitor->Shutdown();
 }
 
 void
 LoadManager::LoadChanged(float aSystemLoad, float aProcesLoad)
 {
   // Update total load, and total amount of measured seconds.
   mLoadSum += aSystemLoad;
--- a/content/media/webrtc/LoadMonitor.cpp
+++ b/content/media/webrtc/LoadMonitor.cpp
@@ -144,21 +144,27 @@ public:
   }
 
 private:
   nsRefPtr<LoadMonitor> mLoadMonitor;
 };
 
 void LoadMonitor::Shutdown()
 {
-  MutexAutoLock lock(mLock);
   if (mLoadInfoThread) {
-    mShutdownPending = true;
-    mCondVar.Notify();
+    {
+      MutexAutoLock lock(mLock);
+      LOG(("LoadMonitor: shutting down"));
+      mShutdownPending = true;
+      mCondVar.Notify();
+    }
 
+    // Note: can't just call ->Shutdown() from here; that spins the event
+    // loop here, causing re-entrancy issues if we're invoked from cycle
+    // collection.  Argh.
     mLoadInfoThread = nullptr;
 
     nsRefPtr<LoadMonitorRemoveObserver> remObsRunner = new LoadMonitorRemoveObserver(this);
     if (!NS_IsMainThread()) {
       NS_DispatchToMainThread(remObsRunner);
     } else {
       remObsRunner->Run();
     }
@@ -511,26 +517,41 @@ nsresult LoadInfo::UpdateProcessLoad() {
 #endif
   return NS_OK;
 }
 
 class LoadInfoCollectRunner : public nsRunnable
 {
 public:
   LoadInfoCollectRunner(nsRefPtr<LoadMonitor> loadMonitor,
-                        RefPtr<LoadInfo> loadInfo)
-    : mLoadUpdateInterval(loadMonitor->mLoadUpdateInterval),
+                        RefPtr<LoadInfo> loadInfo,
+                        nsIThread *loadInfoThread)
+    : mThread(loadInfoThread),
+      mLoadUpdateInterval(loadMonitor->mLoadUpdateInterval),
       mLoadNoiseCounter(0)
   {
     mLoadMonitor = loadMonitor;
     mLoadInfo = loadInfo;
   }
 
   NS_IMETHOD Run()
   {
+    if (NS_IsMainThread()) {
+      if (mThread) {
+        // Don't leak threads!
+        mThread->Shutdown(); // can't Shutdown from the thread itself, darn
+        // Don't null out mThread!
+        // See bug 999104.  We must hold a ref to the thread across Dispatch()
+        // since the internal mThread ref could be released while processing
+        // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
+        // assumes the caller does.
+      }
+      return NS_OK;
+    }
+
     MutexAutoLock lock(mLoadMonitor->mLock);
     while (!mLoadMonitor->mShutdownPending) {
       mLoadInfo->UpdateSystemLoad();
       mLoadInfo->UpdateProcessLoad();
       float sysLoad = mLoadInfo->GetSystemLoad();
       float procLoad = mLoadInfo->GetProcessLoad();
 
       if ((++mLoadNoiseCounter % (LOG_MANY_ENABLED() ? 1 : 10)) == 0) {
@@ -538,20 +559,23 @@ public:
         mLoadNoiseCounter = 0;
       }
       mLoadMonitor->SetSystemLoad(sysLoad);
       mLoadMonitor->SetProcessLoad(procLoad);
       mLoadMonitor->FireCallbacks();
 
       mLoadMonitor->mCondVar.Wait(PR_MillisecondsToInterval(mLoadUpdateInterval));
     }
+    // ok, we need to exit safely and can't shut ourselves down (DARN)
+    NS_DispatchToMainThread(this);
     return NS_OK;
   }
 
 private:
+  nsCOMPtr<nsIThread> mThread;
   RefPtr<LoadInfo> mLoadInfo;
   nsRefPtr<LoadMonitor> mLoadMonitor;
   int mLoadUpdateInterval;
   int mLoadNoiseCounter;
 };
 
 void
 LoadMonitor::SetProcessLoad(float load) {
@@ -600,17 +624,17 @@ LoadMonitor::Init(nsRefPtr<LoadMonitor> 
   }
 
   nsRefPtr<LoadMonitorAddObserver> addObsRunner = new LoadMonitorAddObserver(self);
   NS_DispatchToMainThread(addObsRunner);
 
   NS_NewNamedThread("Sys Load Info", getter_AddRefs(mLoadInfoThread));
 
   nsRefPtr<LoadInfoCollectRunner> runner =
-    new LoadInfoCollectRunner(self, load_info);
+    new LoadInfoCollectRunner(self, load_info, mLoadInfoThread);
   mLoadInfoThread->Dispatch(runner, NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
 
 void
 LoadMonitor::SetLoadChangeCallback(LoadNotificationCallback* aCallback)
 {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -8185,19 +8185,26 @@ class CGUnionStruct(CGThing):
                                        const=True,
                                        bodyInHeader=True,
                                        body=body))
 
             unionValues.append(
                 fill("UnionMember<${structType} > m${name}", **vars))
             enumValues.append("e" + vars["name"])
 
-            toJSValCases.append(
-                CGCase("e" + vars["name"],
-                       self.getConversionToJS(vars, t)))
+            skipToJSVal = False
+            try:
+                toJSValCases.append(
+                    CGCase("e" + vars["name"],
+                           self.getConversionToJS(vars, t)))
+            except MethodNotNewObjectError:
+                # If we can't have a ToJSVal() because one of our members can
+                # only be returned from [NewObject] methods, then just skip
+                # generating ToJSVal.
+                skipToJSVal = True
             destructorCases.append(
                 CGCase("e" + vars["name"],
                        CGGeneric("Destroy%s();\n" % vars["name"])))
             assignmentCases.append(
                 CGCase("e" + vars["name"],
                        CGGeneric("SetAs%s() = aOther.GetAs%s();\n" %
                                  (vars["name"], vars["name"]))))
             if self.ownsMembers and typeNeedsRooting(t):
@@ -8221,28 +8228,29 @@ class CGUnionStruct(CGThing):
 
         dtor = CGSwitch("mType", destructorCases).define()
 
         methods.append(ClassMethod("Uninit", "void", [],
                                    visibility="private", body=dtor,
                                    bodyInHeader=not self.ownsMembers,
                                    inline=not self.ownsMembers))
 
-        methods.append(
-            ClassMethod(
-                "ToJSVal",
-                "bool",
-                [
-                    Argument("JSContext*", "cx"),
-                    Argument("JS::Handle<JSObject*>", "scopeObj"),
-                    Argument("JS::MutableHandle<JS::Value>", "rval")
-                ],
-                body=CGSwitch("mType", toJSValCases,
-                              default=CGGeneric("return false;\n")).define(),
-                const=True))
+        if not skipToJSVal:
+            methods.append(
+                ClassMethod(
+                    "ToJSVal",
+                    "bool",
+                    [
+                        Argument("JSContext*", "cx"),
+                        Argument("JS::Handle<JSObject*>", "scopeObj"),
+                        Argument("JS::MutableHandle<JS::Value>", "rval")
+                    ],
+                    body=CGSwitch("mType", toJSValCases,
+                                  default=CGGeneric("return false;\n")).define(),
+                    const=True))
 
         constructors = [ctor]
         selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
         if self.ownsMembers:
             if traceCases:
                 traceBody = CGSwitch("mType", traceCases,
                                      default=CGGeneric("")).define()
             else:
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -3195,22 +3195,21 @@ PluginInstanceChild::PaintRectToSurface(
     if (mHelperSurface) {
         // On X11 we can paint to non Xlib surface only with HelperSurface
         renderSurface = mHelperSurface;
     }
 #endif
 
     if (mIsTransparent && !CanPaintOnBackground()) {
         // Clear surface content for transparent rendering
-        ColorPattern color(ToColor(aColor));
-        RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(renderSurface);
-        dt->FillRect(gfx::Rect(plPaintRect.x, plPaintRect.y,
-                               plPaintRect.width, plPaintRect.height),
-                     color,
-                     DrawOptions(1.f, CompositionOp::OP_SOURCE));
+        nsRefPtr<gfxContext> ctx = new gfxContext(renderSurface);
+        ctx->SetDeviceColor(aColor);
+        ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+        ctx->Rectangle(GfxFromNsRect(plPaintRect));
+        ctx->Fill();
     }
 
     PaintRectToPlatformSurface(plPaintRect, renderSurface);
 
     if (renderSurface != aSurface) {
         // Copy helper surface content to target
         RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(aSurface);
         RefPtr<SourceSurface> surface =
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -1,15 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * http://dev.w3.org/2011/webrtc/editor/webrtc.html#rtcstatsreport-object
+ * http://www.w3.org/2011/04/webrtc/wiki/Stats
  */
 
 enum RTCStatsType {
   "inboundrtp",
   "outboundrtp",
   "session",
   "track",
   "transport",
@@ -26,44 +27,64 @@ dictionary RTCStats {
 
 dictionary RTCRTPStreamStats : RTCStats {
   DOMString ssrc;
   DOMString remoteId;
   boolean isRemote = false;
   DOMString mediaTrackId;
   DOMString transportId;
   DOMString codecId;
+
+  // Video encoder/decoder measurements (absent for rtcp)
+  double bitrateMean;
+  double bitrateStdDev;
+  double framerateMean;
+  double framerateStdDev;
 };
 
 dictionary RTCInboundRTPStreamStats : RTCRTPStreamStats {
   unsigned long packetsReceived;
   unsigned long long bytesReceived;
   double jitter;
   unsigned long packetsLost;
   long mozAvSyncDelay;
   long mozJitterBufferDelay;
   long mozRtt;
+
+  // Video decoder measurement (absent in rtcp case)
+  unsigned long discardedPackets;
 };
 
 dictionary RTCOutboundRTPStreamStats : RTCRTPStreamStats {
   unsigned long packetsSent;
   unsigned long long bytesSent;
+  double targetBitrate;  // config encoder bitrate target of this SSRC in bits/s
+
+  // Video encoder measurement (absent in rtcp case)
+  unsigned long droppedFrames;
 };
 
 dictionary RTCMediaStreamTrackStats : RTCStats {
   DOMString trackIdentifier;      // track.id property
   boolean remoteSource;
   sequence<DOMString> ssrcIds;
-  unsigned long audioLevel;       // Only for audio, the rest are only for video
+  // Stuff that makes sense for video
   unsigned long frameWidth;
   unsigned long frameHeight;
-  double framesPerSecond;         // The nominal FPS value
+  double framesPerSecond;        // The nominal FPS value
   unsigned long framesSent;
-  unsigned long framesReceived;   // Only for remoteSource=true
+  unsigned long framesReceived;  // Only for remoteSource=true
   unsigned long framesDecoded;
+  unsigned long framesDropped;   // See VideoPlaybackQuality.droppedVideoFrames
+  unsigned long framesCorrupted; // as above.
+  // Stuff that makes sense for audio
+  double audioLevel;               // linear, 1.0 = 0 dBov (from RFC 6464).
+  // AEC stuff on audio tracks sourced from a microphone where AEC is applied
+  double echoReturnLoss;           // in decibels from G.168 (2012) section 3.14
+  double echoReturnLossEnhancement; // as above, section 3.15
 };
 
 dictionary RTCMediaStreamStats : RTCStats {
   DOMString streamIdentifier;     // stream.id property
   sequence<DOMString> trackIds;   // Note: stats object ids, not track.id
 };
 
 dictionary RTCTransportStats: RTCStats {
--- a/dom/xbl/nsXBLMaybeCompiled.h
+++ b/dom/xbl/nsXBLMaybeCompiled.h
@@ -11,16 +11,19 @@
 /*
  * A union containing either a pointer representing uncompiled source or a
  * JSObject* representing the compiled result.  The class is templated on the
  * source object type.
  *
  * The purpose of abstracting this as a separate class is to allow it to be
  * wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject
  * pointer, when present.
+ *
+ * No implementation of rootKind() is provided, which prevents
+ * Root<nsXBLMaybeCompiled<UncompiledT>> from being used.
  */
 template <class UncompiledT>
 class nsXBLMaybeCompiled
 {
 public:
   nsXBLMaybeCompiled() : mUncompiled(BIT_UNCOMPILED) {}
 
   nsXBLMaybeCompiled(UncompiledT* uncompiled)
@@ -83,21 +86,16 @@ namespace js {
 
 template <class UncompiledT>
 struct GCMethods<nsXBLMaybeCompiled<UncompiledT> >
 {
   typedef struct GCMethods<JSObject *> Base;
 
   static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); }
 
-  /*
-   * No implementation of kind() is provided to prevent
-   * Root<nsXBLMaybeCompiled<UncompiledT>> from being used.
-   */
-
   static bool poisoned(nsXBLMaybeCompiled<UncompiledT> function)
   {
     return function.IsCompiled() && Base::poisoned(function.GetJSFunction());
   }
 
   static bool needsPostBarrier(nsXBLMaybeCompiled<UncompiledT> function)
   {
     return function.IsCompiled() && Base::needsPostBarrier(function.GetJSFunction());
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -328,16 +328,34 @@ GLLibraryEGL::EnsureInitialized()
 
             mSymbols.fCreateImage = nullptr;
             mSymbols.fDestroyImage = nullptr;
         }
     } else {
         MarkExtensionUnsupported(KHR_image_pixmap);
     }
 
+    if (IsExtensionSupported(ANDROID_native_fence_sync)) {
+        GLLibraryLoader::SymLoadStruct nativeFenceSymbols[] = {
+            { (PRFuncPtr*) &mSymbols.fDupNativeFenceFDANDROID, { "eglDupNativeFenceFDANDROID", nullptr } },
+            { nullptr, { nullptr } }
+        };
+
+        bool success = GLLibraryLoader::LoadSymbols(mEGLLibrary,
+                                                    &nativeFenceSymbols[0],
+                                                    lookupFunction);
+        if (!success) {
+            NS_ERROR("EGL supports ANDROID_native_fence_sync without exposing its functions!");
+
+            MarkExtensionUnsupported(ANDROID_native_fence_sync);
+
+            mSymbols.fDupNativeFenceFDANDROID = nullptr;
+        }
+    }
+
     mInitialized = true;
     reporter.SetSuccessful();
     return true;
 }
 
 void
 GLLibraryEGL::InitExtensions()
 {
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -407,16 +407,24 @@ public:
     EGLBoolean fGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLint *value)
     {
         BEFORE_GL_CALL;
         EGLBoolean b = mSymbols.fGetSyncAttrib(dpy, sync, attribute, value);
         AFTER_GL_CALL;
         return b;
     }
 
+    EGLint fDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSync sync)
+    {
+        MOZ_ASSERT(mSymbols.fDupNativeFenceFDANDROID);
+        BEFORE_GL_CALL;
+        EGLint ret = mSymbols.fDupNativeFenceFDANDROID(dpy, sync);
+        AFTER_GL_CALL;
+        return ret;
+    }
 
     EGLDisplay Display() {
         return mEGLDisplay;
     }
 
     bool IsANGLE() const {
         return mIsANGLE;
     }
@@ -517,16 +525,18 @@ public:
         typedef EGLSync (GLAPIENTRY * pfnCreateSync)(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
         pfnCreateSync fCreateSync;
         typedef EGLBoolean (GLAPIENTRY * pfnDestroySync)(EGLDisplay dpy, EGLSync sync);
         pfnDestroySync fDestroySync;
         typedef EGLint (GLAPIENTRY * pfnClientWaitSync)(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
         pfnClientWaitSync fClientWaitSync;
         typedef EGLBoolean (GLAPIENTRY * pfnGetSyncAttrib)(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLint *value);
         pfnGetSyncAttrib fGetSyncAttrib;
+        typedef EGLint (GLAPIENTRY * pfnDupNativeFenceFDANDROID)(EGLDisplay dpy, EGLSync sync);
+        pfnDupNativeFenceFDANDROID fDupNativeFenceFDANDROID;
     } mSymbols;
 
 #ifdef DEBUG
     static void BeforeGLCall(const char* glFunction);
     static void AfterGLCall(const char* glFunction);
 #endif
 
 #ifdef MOZ_B2G
--- a/gfx/gl/SharedSurfaceGralloc.cpp
+++ b/gfx/gl/SharedSurfaceGralloc.cpp
@@ -148,21 +148,34 @@ SharedSurface_Gralloc::Fence()
         mSync = 0;
     }
 
     // When Android native fences are available, try
     // them first since they're more likely to work.
     // Android native fences are also likely to perform better.
     if (mEGL->IsExtensionSupported(GLLibraryEGL::ANDROID_native_fence_sync)) {
         mGL->MakeCurrent();
-        mSync = mEGL->fCreateSync(mEGL->Display(),
-                                  LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID,
-                                  nullptr);
-        if (mSync) {
+        EGLSync sync = mEGL->fCreateSync(mEGL->Display(),
+                                         LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID,
+                                         nullptr);
+        if (sync) {
             mGL->fFlush();
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+            int fenceFd = mEGL->fDupNativeFenceFDANDROID(mEGL->Display(), sync);
+            if (fenceFd != -1) {
+                mEGL->fDestroySync(mEGL->Display(), sync);
+                android::sp<android::Fence> fence(new android::Fence(fenceFd));
+                FenceHandle handle = FenceHandle(fence);
+                mTextureClient->SetAcquireFenceHandle(handle);
+            } else {
+                mSync = sync;
+            }
+#else
+            mSync = sync;
+#endif
             return;
         }
     }
 
     if (mEGL->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)) {
         mGL->MakeCurrent();
         mSync = mEGL->fCreateSync(mEGL->Display(),
                                   LOCAL_EGL_SYNC_FENCE,
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -733,16 +733,17 @@ ContainerLayer::ContainerLayer(LayerMana
   : Layer(aManager, aImplData),
     mFirstChild(nullptr),
     mLastChild(nullptr),
     mScrollHandoffParentId(FrameMetrics::NULL_SCROLL_ID),
     mPreXScale(1.0f),
     mPreYScale(1.0f),
     mInheritedXScale(1.0f),
     mInheritedYScale(1.0f),
+    mBackgroundColor(0, 0, 0, 0),
     mUseIntermediateSurface(false),
     mSupportsComponentAlphaChildren(false),
     mMayHaveReadbackChild(false)
 {
   mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
 }
 
 ContainerLayer::~ContainerLayer() {}
@@ -891,17 +892,18 @@ ContainerLayer::RepositionChild(Layer* a
   return true;
 }
 
 void
 ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
 {
   aAttrs = ContainerLayerAttributes(GetFrameMetrics(), mScrollHandoffParentId,
                                     mPreXScale, mPreYScale,
-                                    mInheritedXScale, mInheritedYScale);
+                                    mInheritedXScale, mInheritedYScale,
+                                    mBackgroundColor);
 }
 
 bool
 ContainerLayer::HasMultipleChildren()
 {
   uint32_t count = 0;
   for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
     const nsIntRect *clipRect = child->GetEffectiveClipRect();
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1678,16 +1678,27 @@ public:
     }
 
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) InheritedScale", this));
     mInheritedXScale = aXScale;
     mInheritedYScale = aYScale;
     Mutated();
   }
 
+  void SetBackgroundColor(const gfxRGBA& aColor)
+  {
+    if (mBackgroundColor == aColor) {
+      return;
+    }
+
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) BackgroundColor", this));
+    mBackgroundColor = aColor;
+    Mutated();
+  }
+
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs);
 
   void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
 
   // These getters can be used anytime.
 
   virtual ContainerLayer* AsContainerLayer() { return this; }
   virtual const ContainerLayer* AsContainerLayer() const { return this; }
@@ -1695,16 +1706,18 @@ public:
   virtual Layer* GetFirstChild() const { return mFirstChild; }
   virtual Layer* GetLastChild() const { return mLastChild; }
   const FrameMetrics& GetFrameMetrics() const { return mFrameMetrics; }
   FrameMetrics::ViewID GetScrollHandoffParentId() const { return mScrollHandoffParentId; }
   float GetPreXScale() const { return mPreXScale; }
   float GetPreYScale() const { return mPreYScale; }
   float GetInheritedXScale() const { return mInheritedXScale; }
   float GetInheritedYScale() const { return mInheritedYScale; }
+  
+  gfxRGBA GetBackgroundColor() const { return mBackgroundColor; }
 
   MOZ_LAYER_DECL_NAME("ContainerLayer", TYPE_CONTAINER)
 
   /**
    * ContainerLayer backends need to override ComputeEffectiveTransforms
    * since the decision about whether to use a temporary surface for the
    * container is backend-specific. ComputeEffectiveTransforms must also set
    * mUseIntermediateSurface.
@@ -1781,16 +1794,20 @@ protected:
   nsRefPtr<AsyncPanZoomController> mAPZC;
   FrameMetrics::ViewID mScrollHandoffParentId;
   float mPreXScale;
   float mPreYScale;
   // The resolution scale inherited from the parent layer. This will already
   // be part of mTransform.
   float mInheritedXScale;
   float mInheritedYScale;
+  // This is currently set and used only for scrollable container layers.
+  // When multi-layer-apz (bug 967844) is implemented, this is likely to move
+  // elsewhere (e.g. to Layer).
+  gfxRGBA mBackgroundColor;
   bool mUseIntermediateSurface;
   bool mSupportsComponentAlphaChildren;
   bool mMayHaveReadbackChild;
 };
 
 /**
  * A Layer which just renders a solid color in its visible region. It actually
  * can fill any area that contains the visible region, so if you need to
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -500,16 +500,26 @@ public:
                       const TimeDuration& aDelta) MOZ_OVERRIDE
   {
     // Can't inline these variables due to short-circuit evaluation.
     bool continueX = mApzc.mX.SampleSnapBack(aDelta);
     bool continueY = mApzc.mY.SampleSnapBack(aDelta);
     return continueX || continueY;
   }
 
+  virtual void Cancel() MOZ_OVERRIDE
+  {
+    // If the snap-back animation is cancelled for some reason, we need to
+    // clear the overscroll, otherwise the user would be stuck in the
+    // overscrolled state (since touch blocks beginning in an overscrolled
+    // state are ignored).
+    mApzc.mX.ClearOverscroll();
+    mApzc.mY.ClearOverscroll();
+  }
+
 private:
   AsyncPanZoomController& mApzc;
 };
 
 void
 AsyncPanZoomController::SetFrameTime(const TimeStamp& aTime) {
   sFrameTime = aTime;
 }
@@ -607,16 +617,18 @@ AsyncPanZoomController::GetGestureEventL
   MonitorAutoLock lock(mRefPtrMonitor);
   nsRefPtr<GestureEventListener> listener = mGestureEventListener;
   return listener.forget();
 }
 
 void
 AsyncPanZoomController::Destroy()
 {
+  CancelAnimation();
+
   { // scope the lock
     MonitorAutoLock lock(mRefPtrMonitor);
     mGeckoContentController = nullptr;
     mGestureEventListener = nullptr;
   }
   mPrevSibling = nullptr;
   mLastChild = nullptr;
   mParent = nullptr;
@@ -1679,17 +1691,22 @@ void AsyncPanZoomController::StartAnimat
   mAnimation = aAnimation;
   mLastSampleTime = GetFrameTime();
   ScheduleComposite();
 }
 
 void AsyncPanZoomController::CancelAnimation() {
   ReentrantMonitorAutoEnter lock(mMonitor);
   SetState(NOTHING);
-  mAnimation = nullptr;
+  if (mAnimation) {
+    mAnimation->Cancel();
+    mAnimation = nullptr;
+    // mAnimation->Cancel() may have done something that requires a repaint.
+    RequestContentRepaint();
+  }
 }
 
 void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorParent) {
   mCompositorParent = aCompositorParent;
 }
 
 void AsyncPanZoomController::ShareFrameMetricsAcrossProcesses() {
   mSharingFrameMetricsAcrossProcesses = true;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -1027,16 +1027,19 @@ public:
   AsyncPanZoomAnimation(const TimeDuration& aRepaintInterval =
                         TimeDuration::Forever())
     : mRepaintInterval(aRepaintInterval)
   { }
 
   virtual bool Sample(FrameMetrics& aFrameMetrics,
                       const TimeDuration& aDelta) = 0;
 
+  // Called if the animation is cancelled before it ends.
+  virtual void Cancel() {}
+
   /**
    * Get the deferred tasks in |mDeferredTasks|. See |mDeferredTasks|
    * for more information.
    * Clears |mDeferredTasks|.
    */
   Vector<Task*> TakeDeferredTasks() {
     Vector<Task*> result;
     mDeferredTasks.swap(result);
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -172,16 +172,20 @@ bool Axis::SampleSnapBack(const TimeDura
   // No overscroll on this axis, do not continue animation.
   return false;
 }
 
 bool Axis::IsOverscrolled() const {
   return mOverscroll != 0;
 }
 
+void Axis::ClearOverscroll() {
+  mOverscroll = 0;
+}
+
 float Axis::PanDistance() {
   return fabsf(mPos - mStartPos);
 }
 
 float Axis::PanDistance(float aPos) {
   return fabsf(aPos - mStartPos);
 }
 
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -105,16 +105,21 @@ public:
   bool SampleSnapBack(const TimeDuration& aDelta);
 
   /**
    * Return whether this axis is overscrolled in either direction.
    */
   bool IsOverscrolled() const;
 
   /**
+   * Clear any overscroll amount on this axis.
+   */
+  void ClearOverscroll();
+
+  /**
    * Gets the distance between the starting position of the touch supplied in
    * startTouch() and the current touch from the last
    * updateWithTouchAtDevicePoint().
    */
   float PanDistance();
 
   /**
    * Gets the distance between the starting position of the touch supplied in
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -166,17 +166,17 @@ CanvasClientSurfaceStream::Update(gfx::I
 
     // If IPDLActor is null means this TextureClient didn't AddTextureClient yet
     if (!grallocTextureClient->GetIPDLActor()) {
       grallocTextureClient->SetTextureFlags(mTextureInfo.mTextureFlags);
       AddTextureClient(grallocTextureClient);
     }
 
     if (grallocTextureClient->GetIPDLActor()) {
-      GetForwarder()->UseTexture(this, grallocTextureClient);
+      UseTexture(grallocTextureClient);
     }
 
     if (mBuffer && CompositorChild::ChildProcessHasCompositor()) {
       // remove old buffer from CompositableHost
       RefPtr<AsyncTransactionTracker> tracker = new RemoveTextureFromCompositableTracker();
       // Hold TextureClient until transaction complete.
       tracker->SetTextureClient(mBuffer);
       mBuffer->SetRemoveFromCompositableTracker(tracker);
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -198,15 +198,33 @@ CompositableClient::AddTextureClient(Tex
 }
 
 void
 CompositableClient::OnTransaction()
 {
 }
 
 void
+CompositableClient::UseTexture(TextureClient* aTexture)
+{
+  MOZ_ASSERT(aTexture);
+  if (!aTexture) {
+    return;
+  }
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+  FenceHandle handle = aTexture->GetAcquireFenceHandle();
+  if (handle.IsValid()) {
+    RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(handle);
+    mForwarder->SendFenceHandle(tracker, aTexture->GetIPDLActor(), handle);
+  }
+#endif
+  mForwarder->UseTexture(this, aTexture);
+}
+
+void
 CompositableClient::RemoveTexture(TextureClient* aTexture)
 {
   mForwarder->RemoveTextureFromCompositable(this, aTexture);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -184,16 +184,18 @@ public:
   virtual void OnDetach() {}
 
   /**
    * Clear any resources that are not immediately necessary. This may be called
    * in low-memory conditions.
    */
   virtual void ClearCachedResources() {}
 
+  virtual void UseTexture(TextureClient* aTexture);
+
   /**
    * Should be called when deataching a TextureClient from a Compositable, because
    * some platforms need to do some extra book keeping when this happens (for
    * example to properly keep track of fences on Gonk).
    *
    * See AutoRemoveTexture to automatically invoke this at the end of a scope.
    */
   virtual void RemoveTexture(TextureClient* aTexture);
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -286,23 +286,36 @@ public:
    * Triggers the destruction of the shared data and the corresponding TextureHost.
    *
    * If the texture flags contain TextureFlags::DEALLOCATE_CLIENT, the destruction
    * will be synchronously coordinated with the compositor side, otherwise it
    * will be done asynchronously.
    */
   void ForceRemove();
 
-  virtual void SetReleaseFenceHandle(FenceHandle aReleaseFenceHandle) {}
+  virtual void SetReleaseFenceHandle(FenceHandle aReleaseFenceHandle)
+  {
+    mReleaseFenceHandle = aReleaseFenceHandle;
+  }
 
   const FenceHandle& GetReleaseFenceHandle() const
   {
     return mReleaseFenceHandle;
   }
 
+  virtual void SetAcquireFenceHandle(FenceHandle aAcquireFenceHandle)
+  {
+    mAcquireFenceHandle = aAcquireFenceHandle;
+  }
+
+  const FenceHandle& GetAcquireFenceHandle() const
+  {
+    return mAcquireFenceHandle;
+  }
+
   /**
    * Set AsyncTransactionTracker of RemoveTextureFromCompositableAsync() transaction.
    */
   virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) {}
 
   /**
    * This function waits until the buffer is no longer being used.
    */
@@ -342,16 +355,17 @@ protected:
     mFlags |= aFlags;
   }
 
   RefPtr<TextureChild> mActor;
   TextureFlags mFlags;
   bool mShared;
   bool mValid;
   FenceHandle mReleaseFenceHandle;
+  FenceHandle mAcquireFenceHandle;
 
   friend class TextureChild;
   friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
   friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
 };
 
 /**
  * TextureClient that wraps a random access buffer such as a Shmem or raw memory.
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point, IntPoint
 #include "mozilla/gfx/Rect.h"           // for IntRect, Rect
 #include "mozilla/layers/Compositor.h"  // for Compositor, etc
 #include "mozilla/layers/CompositorTypes.h"  // for DiagnosticFlags::CONTAINER
 #include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
 #include "mozilla/layers/TextureHost.h"  // for CompositingRenderTarget
+#include "mozilla/layers/AsyncPanZoomController.h"  // for AsyncPanZoomController
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsISupportsUtils.h"           // for NS_ADDREF, NS_RELEASE
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion
@@ -311,16 +312,38 @@ ContainerRender(ContainerT* aContainer,
     compositor->SetRenderTarget(surface);
   } else {
     surface = previousTarget;
   }
 
   nsAutoTArray<Layer*, 12> children;
   aContainer->SortChildrenBy3DZOrder(children);
 
+  // If this is a scrollable container layer, and it's overscrolled, the layer's
+  // contents are transformed in a way that would leave blank regions in the
+  // composited area. If the layer has a background color, fill these areas
+  // with the background color by drawing a rectangle of the background color
+  // over the entire composited area before drawing the container contents.
+  if (AsyncPanZoomController* apzc = aContainer->GetAsyncPanZoomController()) {
+    if (apzc->IsOverscrolled()) {
+      gfxRGBA color = aContainer->GetBackgroundColor();
+      // If the background is completely transparent, there's no point in
+      // drawing anything for it. Hopefully the layers behind, if any, will
+      // provide suitable content for the overscroll effect.
+      if (color.a != 0.0) {
+        EffectChain effectChain(aContainer);
+        effectChain.mPrimaryEffect = new EffectSolidColor(ToColor(color));
+        gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
+        Compositor* compositor = aManager->GetCompositor();
+        compositor->DrawQuad(compositor->ClipRectInLayersCoordinates(clipRect),
+            clipRect, effectChain, opacity, Matrix4x4());
+      }
+    }
+  }
+
   /**
    * Render this container's contents.
    */
   for (uint32_t i = 0; i < children.Length(); i++) {
     LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
 
     if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty() &&
         !layerToRender->GetLayer()->AsContainerLayer()) {
old mode 100644
new mode 100755
--- a/gfx/layers/ipc/AsyncTransactionTracker.cpp
+++ b/gfx/layers/ipc/AsyncTransactionTracker.cpp
@@ -84,19 +84,23 @@ AsyncTransactionTrackersHolder::AsyncTra
 
 AsyncTransactionTrackersHolder::~AsyncTransactionTrackersHolder()
 {
   if (!mIsTrackersHolderDestroyed) {
     DestroyAsyncTransactionTrackersHolder();
   }
 
   {
-    MOZ_ASSERT(sHolderLock);
-    MutexAutoLock lock(*sHolderLock);
+    if (sHolderLock) {
+      sHolderLock->Lock();
+    }
     sTrackersHolders.erase(mSerial);
+    if (sHolderLock) {
+      sHolderLock->Unlock();
+    }
   }
   MOZ_COUNT_DTOR(AsyncTransactionTrackersHolder);
 }
 
 void
 AsyncTransactionTrackersHolder::HoldUntilComplete(AsyncTransactionTracker* aTransactionTracker)
 {
   if (!aTransactionTracker) {
@@ -165,23 +169,28 @@ AsyncTransactionTrackersHolder::SetRelea
     return;
   }
   holder->SetReleaseFenceHandle(aReleaseFenceHandle, aTransactionId);
 }
 
 void
 AsyncTransactionTrackersHolder::ClearAllAsyncTransactionTrackers()
 {
-  MutexAutoLock lock(*sHolderLock);
+  if (sHolderLock) {
+    sHolderLock->Lock();
+  }
   std::map<uint64_t, RefPtr<AsyncTransactionTracker> >::iterator it;
   for (it = mAsyncTransactionTrackeres.begin();
        it != mAsyncTransactionTrackeres.end(); it++) {
     it->second->NotifyCancel();
   }
   mAsyncTransactionTrackeres.clear();
+  if (sHolderLock) {
+    sHolderLock->Unlock();
+  }
 }
 
 void
 AsyncTransactionTrackersHolder::DestroyAsyncTransactionTrackersHolder() {
   mIsTrackersHolderDestroyed = true;
   ClearAllAsyncTransactionTrackers();
 }
 
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -179,16 +179,21 @@ public:
   /**
    * Tell the compositor side that the shared data has been modified so that
    * it can react accordingly (upload textures, etc.).
    */
   virtual void UpdatedTexture(CompositableClient* aCompositable,
                               TextureClient* aTexture,
                               nsIntRegion* aRegion) = 0;
 
+
+  virtual void SendFenceHandle(AsyncTransactionTracker* aTracker,
+                               PTextureChild* aTexture,
+                               const FenceHandle& aFence) = 0;
+
   void IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier);
 
   virtual int32_t GetMaxTextureSize() const MOZ_OVERRIDE
   {
     return mTextureFactoryIdentifier.mMaxTextureSize;
   }
 
   bool IsOnCompositorSide() const MOZ_OVERRIDE { return false; }
--- a/gfx/layers/ipc/FenceUtils.h
+++ b/gfx/layers/ipc/FenceUtils.h
@@ -13,31 +13,52 @@
 /**
  * FenceHandle is used for delivering Fence object via ipc.
  */
 #if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 # include "mozilla/layers/FenceUtilsGonk.h"
 #else
 namespace mozilla {
 namespace layers {
+
+struct FenceHandleFromChild;
+
 struct FenceHandle {
+  FenceHandle() {}
+  FenceHandle(const FenceHandleFromChild& aFenceHandle) {}
   bool operator==(const FenceHandle&) const { return false; }
   bool IsValid() const { return false; }
 };
+
+struct FenceHandleFromChild {
+  FenceHandleFromChild() {}
+  FenceHandleFromChild(const FenceHandle& aFence) {}
+  bool operator==(const FenceHandle&) const { return false; }
+  bool operator==(const FenceHandleFromChild&) const { return false; }
+  bool IsValid() const { return false; }
+};
+
 } // namespace layers
 } // namespace mozilla
 #endif // MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 
 namespace IPC {
 
 #if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 #else
 template <>
 struct ParamTraits<mozilla::layers::FenceHandle> {
   typedef mozilla::layers::FenceHandle paramType;
   static void Write(Message*, const paramType&) {}
   static bool Read(const Message*, void**, paramType*) { return false; }
 };
+
+template <>
+struct ParamTraits<mozilla::layers::FenceHandleFromChild> {
+  typedef mozilla::layers::FenceHandleFromChild paramType;
+  static void Write(Message*, const paramType&) {}
+  static bool Read(const Message*, void**, paramType*) { return false; }
+};
 #endif // MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 
 } // namespace IPC
 
 #endif // IPC_FencerUtils_h
--- a/gfx/layers/ipc/FenceUtilsGonk.cpp
+++ b/gfx/layers/ipc/FenceUtilsGonk.cpp
@@ -104,20 +104,123 @@ ParamTraits<FenceHandle>::Read(const Mes
   if (NO_ERROR == flattenable->unflatten(data, nbytes, fds, nfds)) {
 #endif
     aResult->mFence = buffer;
     return true;
   }
   return false;
 }
 
+void
+ParamTraits<FenceHandleFromChild>::Write(Message* aMsg,
+                                         const paramType& aParam)
+{
+#if ANDROID_VERSION >= 19
+  sp<Fence> flattenable = aParam.mFence;
+#else
+  Flattenable *flattenable = aParam.mFence.get();
+#endif
+  size_t nbytes = flattenable->getFlattenedSize();
+  size_t nfds = flattenable->getFdCount();
+
+  char data[nbytes];
+  int fds[nfds];
+
+#if ANDROID_VERSION >= 19
+  // Make a copy of "data" and "fds" for flatten() to avoid casting problem
+  void *pdata = (void *)data;
+  int *pfds = fds;
+
+  flattenable->flatten(pdata, nbytes, pfds, nfds);
+
+  // In Kitkat, flatten() will change the value of nbytes and nfds, which dues
+  // to multiple parcelable object consumption. The actual size and fd count
+  // which returned by getFlattenedSize() and getFdCount() are not changed.
+  // So we change nbytes and nfds back by call corresponding calls.
+  nbytes = flattenable->getFlattenedSize();
+  nfds = flattenable->getFdCount();
+#else
+  flattenable->flatten(data, nbytes, fds, nfds);
+#endif
+  aMsg->WriteSize(nbytes);
+  aMsg->WriteSize(nfds);
+
+  aMsg->WriteBytes(data, nbytes);
+  for (size_t n = 0; n < nfds; ++n) {
+    // If the Fence was shared cross-process, SCM_RIGHTS does
+    // the right thing and dup's the fd.  If it's shared cross-thread,
+    // SCM_RIGHTS doesn't dup the fd.  That's surprising, but we just
+    // deal with it here.  NB: only the "default" (master) process can
+    // alloc gralloc buffers.
+    bool sameProcess = (XRE_GetProcessType() == GeckoProcessType_Default);
+    int dupFd = sameProcess ? dup(fds[n]) : fds[n];
+    //int dupFd = fds[n];
+
+    // These buffers can't die in transit because they're created
+    // synchonously and the parent-side buffer can only be dropped if
+    // there's a crash.
+    aMsg->WriteFileDescriptor(FileDescriptor(dupFd, false));
+  }
+}
+
+bool
+ParamTraits<FenceHandleFromChild>::Read(const Message* aMsg,
+                                        void** aIter, paramType* aResult)
+{
+  size_t nbytes;
+  size_t nfds;
+  const char* data;
+
+  if (!aMsg->ReadSize(aIter, &nbytes) ||
+      !aMsg->ReadSize(aIter, &nfds) ||
+      !aMsg->ReadBytes(aIter, &data, nbytes)) {
+    return false;
+  }
+
+  int fds[nfds];
+
+  for (size_t n = 0; n < nfds; ++n) {
+    FileDescriptor fd;
+    if (!aMsg->ReadFileDescriptor(aIter, &fd)) {
+      return false;
+    }
+    fds[n] = fd.fd;
+  }
+
+  sp<Fence> buffer(new Fence());
+#if ANDROID_VERSION >= 19
+  // Make a copy of "data" and "fds" for unflatten() to avoid casting problem
+  void const *pdata = (void const *)data;
+  int const *pfds = fds;
+
+  if (NO_ERROR == buffer->unflatten(pdata, nbytes, pfds, nfds)) {
+#else
+  Flattenable *flattenable = buffer.get();
+
+  if (NO_ERROR == flattenable->unflatten(data, nbytes, fds, nfds)) {
+#endif
+    aResult->mFence = buffer;
+    return true;
+  }
+  return false;
+}
+
 } // namespace IPC
 
 namespace mozilla {
 namespace layers {
 
 FenceHandle::FenceHandle(const sp<Fence>& aFence)
   : mFence(aFence)
 {
 }
 
+FenceHandle::FenceHandle(const FenceHandleFromChild& aFenceHandle) {
+  mFence = aFenceHandle.mFence;
+}
+
+FenceHandleFromChild::FenceHandleFromChild(const sp<Fence>& aFence)
+  : mFence(aFence)
+{
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/FenceUtilsGonk.h
+++ b/gfx/layers/ipc/FenceUtilsGonk.h
@@ -11,43 +11,82 @@
 #include <unistd.h>
 #include <ui/Fence.h>
 
 #include "ipc/IPCMessageUtils.h"
 
 namespace mozilla {
 namespace layers {
 
+struct FenceHandleFromChild;
+
 struct FenceHandle {
   typedef android::Fence Fence;
 
   FenceHandle()
   { }
   FenceHandle(const android::sp<Fence>& aFence);
 
+  FenceHandle(const FenceHandleFromChild& aFenceHandle);
+
   bool operator==(const FenceHandle& aOther) const {
     return mFence.get() == aOther.mFence.get();
   }
 
   bool IsValid() const
   {
     return mFence.get() && mFence->isValid();
   }
 
   android::sp<Fence> mFence;
 };
 
+struct FenceHandleFromChild {
+  typedef android::Fence Fence;
+
+  FenceHandleFromChild()
+  { }
+  FenceHandleFromChild(const android::sp<Fence>& aFence);
+
+  FenceHandleFromChild(const FenceHandle& aFence) {
+    mFence = aFence.mFence;
+  }
+
+  bool operator==(const FenceHandle& aOther) const {
+    return mFence.get() == aOther.mFence.get();
+  }
+
+  bool operator==(const FenceHandleFromChild& aOther) const {
+    return mFence.get() == aOther.mFence.get();
+  }
+
+  bool IsValid() const
+  {
+    return mFence.get() && mFence->isValid();
+  }
+
+  android::sp<Fence> mFence;
+};
+
 } // namespace layers
 } // namespace mozilla
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::layers::FenceHandle> {
   typedef mozilla::layers::FenceHandle paramType;
 
   static void Write(Message* aMsg, const paramType& aParam);
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult);
 };
 
+template <>
+struct ParamTraits<mozilla::layers::FenceHandleFromChild> {
+  typedef mozilla::layers::FenceHandleFromChild paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam);
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult);
+};
+
 } // namespace IPC
 
 #endif  // mozilla_layers_FenceUtilsGonk_h
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -147,16 +147,29 @@ ImageBridgeChild::UpdatedTexture(Composi
   MaybeRegion region = aRegion ? MaybeRegion(*aRegion)
                                : MaybeRegion(null_t());
   mTxn->AddNoSwapEdit(OpUpdateTexture(nullptr, aCompositable->GetIPDLActor(),
                                       nullptr, aTexture->GetIPDLActor(),
                                       region));
 }
 
 void
+ImageBridgeChild::SendFenceHandle(AsyncTransactionTracker* aTracker,
+                                  PTextureChild* aTexture,
+                                  const FenceHandle& aFence)
+{
+  HoldUntilComplete(aTracker);
+  InfallibleTArray<AsyncChildMessageData> messages;
+  messages.AppendElement(OpDeliverFenceFromChild(aTracker->GetId(),
+                                                 nullptr, aTexture,
+                                                 FenceHandleFromChild(aFence)));
+  SendChildAsyncMessages(messages);
+}
+
+void
 ImageBridgeChild::UpdatePictureRect(CompositableClient* aCompositable,
                                     const nsIntRect& aRect)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->GetIPDLActor());
   mTxn->AddNoSwapEdit(OpUpdatePictureRect(nullptr, aCompositable->GetIPDLActor(), aRect));
 }
 
@@ -528,16 +541,18 @@ ImageBridgeChild::EndTransaction()
 
 
 PImageBridgeChild*
 ImageBridgeChild::StartUpInChildProcess(Transport* aTransport,
                                         ProcessId aOtherProcess)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
 
+  gfxPlatform::GetPlatform();
+
   ProcessHandle processHandle;
   if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
     return nullptr;
   }
 
   sImageBridgeChildThread = new Thread("ImageBridgeChild");
   if (!sImageBridgeChildThread->Start()) {
     return nullptr;
@@ -797,17 +812,17 @@ ImageBridgeChild::AllocPTextureChild(con
 
 bool
 ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
 {
   return TextureClient::DestroyIPDLActor(actor);
 }
 
 bool
-ImageBridgeChild::RecvParentAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessages)
+ImageBridgeChild::RecvParentAsyncMessages(const InfallibleTArray<AsyncParentMessageData>& aMessages)
 {
   for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
     const AsyncParentMessageData& message = aMessages[i];
 
     switch (message.type()) {
       case AsyncParentMessageData::TOpDeliverFence: {
         const OpDeliverFence& op = message.get_OpDeliverFence();
         FenceHandle fence = op.fence();
@@ -828,23 +843,28 @@ ImageBridgeChild::RecvParentAsyncMessage
                                                               op.destHolderId(),
                                                               op.destTransactionId());
         // Send back a response.
         InfallibleTArray<AsyncChildMessageData> replies;
         replies.AppendElement(OpReplyDeliverFence(op.transactionId()));
         SendChildAsyncMessages(replies);
         break;
       }
+      case AsyncParentMessageData::TOpReplyDeliverFence: {
+        const OpReplyDeliverFence& op = message.get_OpReplyDeliverFence();
+        TransactionCompleteted(op.transactionId());
+        break;
+      }
       case AsyncParentMessageData::TOpReplyRemoveTexture: {
         const OpReplyRemoveTexture& op = message.get_OpReplyRemoveTexture();
 
         AsyncTransactionTrackersHolder::TransactionCompleteted(op.holderId(),
                                                                op.transactionId());
-      break;
-    }
+        break;
+      }
       default:
         NS_ERROR("unknown AsyncParentMessageData type");
         return false;
     }
   }
   return true;
 }
 
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -6,16 +6,17 @@
 #ifndef MOZILLA_GFX_IMAGEBRIDGECHILD_H
 #define MOZILLA_GFX_IMAGEBRIDGECHILD_H
 
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/RefPtr.h"             // for TemporaryRef
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
+#include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTrackerHolder
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/CompositorTypes.h"  // for TextureIdentifier, etc
 #include "mozilla/layers/PImageBridgeChild.h"
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsRegion.h"                   // for nsIntRegion
 class MessageLoop;
 struct nsIntPoint;
 struct nsIntRect;
@@ -96,16 +97,17 @@ bool InImageBridgeChildThread();
  * until the layer transaction happens. This means this scenario is not harmful.
  *
  * Since sending an image through imageBridge triggers compositing, the main thread is
  * not used at all (except for the very first transaction that provides the
  * CompositableHost with an AsyncID).
  */
 class ImageBridgeChild : public PImageBridgeChild
                        , public CompositableForwarder
+                       , public AsyncTransactionTrackersHolder
 {
   friend class ImageContainer;
   typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
 public:
 
   /**
    * Creates the image bridge with a dedicated thread for ImageBridgeChild.
    *
@@ -181,17 +183,17 @@ public:
 
   virtual PTextureChild*
   AllocPTextureChild(const SurfaceDescriptor& aSharedData, const TextureFlags& aFlags) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPTextureChild(PTextureChild* actor) MOZ_OVERRIDE;
 
   virtual bool
-  RecvParentAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessages) MOZ_OVERRIDE;
+  RecvParentAsyncMessages(const InfallibleTArray<AsyncParentMessageData>& aMessages) MOZ_OVERRIDE;
 
   TemporaryRef<ImageClient> CreateImageClient(CompositableType aType);
   TemporaryRef<ImageClient> CreateImageClientNow(CompositableType aType);
 
   static void DispatchReleaseImageClient(ImageClient* aClient);
   static void DispatchReleaseTextureClient(TextureClient* aClient);
   static void DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer);
 
@@ -217,16 +219,20 @@ public:
    * See CompositableForwarder::UseTexture
    */
   virtual void UseTexture(CompositableClient* aCompositable,
                           TextureClient* aClient) MOZ_OVERRIDE;
   virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                          TextureClient* aClientOnBlack,
                                          TextureClient* aClientOnWhite) MOZ_OVERRIDE;
 
+  virtual void SendFenceHandle(AsyncTransactionTracker* aTracker,
+                               PTextureChild* aTexture,
+                               const FenceHandle& aFence) MOZ_OVERRIDE;
+
   virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                              TextureClient* aTexture) MOZ_OVERRIDE;
 
   virtual void RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
                                                   CompositableClient* aCompositable,
                                                   TextureClient* aTexture) MOZ_OVERRIDE;
 
   virtual void RemoveTexture(TextureClient* aTexture) MOZ_OVERRIDE;
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/CompositableTransactionParent.h"
 #include "mozilla/layers/CompositorParent.h"  // for CompositorParent
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply
 #include "mozilla/layers/LayersSurfaces.h"  // for PGrallocBufferParent
 #include "mozilla/layers/PCompositableParent.h"
 #include "mozilla/layers/PImageBridgeParent.h"
+#include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT, etc
 #include "nsISupportsImpl.h"            // for ImageBridgeParent::Release, etc
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl
 #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
@@ -203,32 +204,53 @@ ImageBridgeParent::SendFenceHandle(Async
                                    PTextureParent* aTexture,
                                    const FenceHandle& aFence)
 {
   HoldUntilComplete(aTracker);
   InfallibleTArray<AsyncParentMessageData> messages;
   messages.AppendElement(OpDeliverFence(aTracker->GetId(),
                                         aTexture, nullptr,
                                         aFence));
-  mozilla::unused << SendParentAsyncMessage(messages);
+  mozilla::unused << SendParentAsyncMessages(messages);
 }
 
 void
 ImageBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
 {
-  mozilla::unused << SendParentAsyncMessage(aMessage);
+  mozilla::unused << SendParentAsyncMessages(aMessage);
 }
 
 bool
 ImageBridgeParent::RecvChildAsyncMessages(const InfallibleTArray<AsyncChildMessageData>& aMessages)
 {
   for (AsyncChildMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
     const AsyncChildMessageData& message = aMessages[i];
 
     switch (message.type()) {
+      case AsyncChildMessageData::TOpDeliverFenceFromChild: {
+        const OpDeliverFenceFromChild& op = message.get_OpDeliverFenceFromChild();
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+        FenceHandle fence = FenceHandle(op.fence());
+        PTextureParent* parent = op.textureParent();
+
+        TextureHostOGL* hostOGL = nullptr;
+        RefPtr<TextureHost> texture = TextureHost::AsTextureHost(parent);
+        if (texture) {
+          hostOGL = texture->AsHostOGL();
+        }
+        if (hostOGL) {
+          hostOGL->SetAcquireFence(fence.mFence);
+        }
+#endif
+        // Send back a response.
+        InfallibleTArray<AsyncParentMessageData> replies;
+        replies.AppendElement(OpReplyDeliverFence(op.transactionId()));
+        mozilla::unused << SendParentAsyncMessages(replies);
+        break;
+      }
       case AsyncChildMessageData::TOpReplyDeliverFence: {
         const OpReplyDeliverFence& op = message.get_OpReplyDeliverFence();
         TransactionCompleteted(op.transactionId());
         break;
       }
       default:
         NS_ERROR("unknown AsyncChildMessageData type");
         return false;
@@ -299,17 +321,17 @@ bool ImageBridgeParent::IsSameProcess() 
   return OtherProcess() == ipc::kInvalidProcessHandle;
 }
 
 void
 ImageBridgeParent::ReplyRemoveTexture(const OpReplyRemoveTexture& aReply)
 {
   InfallibleTArray<AsyncParentMessageData> messages;
   messages.AppendElement(aReply);
-  mozilla::unused << SendParentAsyncMessage(messages);
+  mozilla::unused << SendParentAsyncMessages(messages);
 }
 
 /*static*/ void
 ImageBridgeParent::ReplyRemoveTexture(base::ProcessId aChildProcessId,
                                       const OpReplyRemoveTexture& aReply)
 {
   ImageBridgeParent* imageBridge = ImageBridgeParent::GetInstance(aChildProcessId);
   if (!imageBridge) {
@@ -334,17 +356,17 @@ ImageBridgeParent::SendFenceHandleToTrac
 
   RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(fence);
   HoldUntilComplete(tracker);
   InfallibleTArray<AsyncParentMessageData> messages;
   messages.AppendElement(OpDeliverFenceToTracker(tracker->GetId(),
                                                  aDestHolderId,
                                                  aTransactionId,
                                                  fence));
-  mozilla::unused << SendParentAsyncMessage(messages);
+  mozilla::unused << SendParentAsyncMessages(messages);
 }
 
 /*static*/ void
 ImageBridgeParent::SendFenceHandleToTrackerIfPresent(base::ProcessId aChildProcessId,
                                                      uint64_t aDestHolderId,
                                                      uint64_t aTransactionId,
                                                      PTextureParent* aTexture)
 {
--- a/gfx/layers/ipc/LayerTransactionChild.cpp
+++ b/gfx/layers/ipc/LayerTransactionChild.cpp
@@ -61,17 +61,17 @@ LayerTransactionChild::AllocPCompositabl
 
 bool
 LayerTransactionChild::DeallocPCompositableChild(PCompositableChild* actor)
 {
   return CompositableClient::DestroyIPDLActor(actor);
 }
 
 bool
-LayerTransactionChild::RecvParentAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessages)
+LayerTransactionChild::RecvParentAsyncMessages(const InfallibleTArray<AsyncParentMessageData>& aMessages)
 {
   for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
     const AsyncParentMessageData& message = aMessages[i];
 
     switch (message.type()) {
       case AsyncParentMessageData::TOpDeliverFence: {
         const OpDeliverFence& op = message.get_OpDeliverFence();
         FenceHandle fence = op.fence();
@@ -86,27 +86,46 @@ LayerTransactionChild::RecvParentAsyncMe
         } else {
           // Send back a response.
           InfallibleTArray<AsyncChildMessageData> replies;
           replies.AppendElement(OpReplyDeliverFence(op.transactionId()));
           SendChildAsyncMessages(replies);
         }
         break;
       }
+      case AsyncParentMessageData::TOpReplyDeliverFence: {
+        const OpReplyDeliverFence& op = message.get_OpReplyDeliverFence();
+        TransactionCompleteted(op.transactionId());
+        break;
+      }
       default:
         NS_ERROR("unknown AsyncParentMessageData type");
         return false;
     }
   }
   return true;
 }
 
 void
+LayerTransactionChild::SendFenceHandle(AsyncTransactionTracker* aTracker,
+                                       PTextureChild* aTexture,
+                                       const FenceHandle& aFence)
+{
+  HoldUntilComplete(aTracker);
+  InfallibleTArray<AsyncChildMessageData> messages;
+  messages.AppendElement(OpDeliverFenceFromChild(aTracker->GetId(),
+                                                 nullptr, aTexture,
+                                                 FenceHandleFromChild(aFence)));
+  SendChildAsyncMessages(messages);
+}
+
+void
 LayerTransactionChild::ActorDestroy(ActorDestroyReason why)
 {
+  DestroyAsyncTransactionTrackersHolder();
 #ifdef MOZ_B2G
   // Due to poor lifetime management of gralloc (and possibly shmems) we will
   // crash at some point in the future when we get destroyed due to abnormal
   // shutdown. Its better just to crash here. On desktop though, we have a chance
   // of recovering.
   if (why == AbnormalShutdown) {
     NS_RUNTIMEABORT("ActorDestroy by IPC channel failure at LayerTransactionChild");
   }
--- a/gfx/layers/ipc/LayerTransactionChild.h
+++ b/gfx/layers/ipc/LayerTransactionChild.h
@@ -6,29 +6,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
 #define MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
 
 #include <stdint.h>                     // for uint32_t
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTracker
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 
 namespace layout {
 class RenderFrameChild;
 class ShadowLayerForwarder;
 }
 
 namespace layers {
 
 class LayerTransactionChild : public PLayerTransactionChild
+                            , public AsyncTransactionTrackersHolder
 {
   typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LayerTransactionChild)
   /**
    * Clean this up, finishing with Send__delete__().
    *
    * It is expected (checked with an assert) that all shadow layers
@@ -42,16 +44,20 @@ public:
   void SetHasNoCompositor() { mHasNoCompositor = true; }
   bool HasNoCompositor() { return mHasNoCompositor; }
 
   void SetForwarder(ShadowLayerForwarder* aForwarder)
   {
     mForwarder = aForwarder;
   }
 
+  virtual void SendFenceHandle(AsyncTransactionTracker* aTracker,
+                               PTextureChild* aTexture,
+                               const FenceHandle& aFence);
+
 protected:
   LayerTransactionChild()
     : mForwarder(nullptr)
     , mIPCOpen(false)
     , mDestroyed(false)
     , mHasNoCompositor(false)
   {}
   ~LayerTransactionChild() { }
@@ -62,17 +68,17 @@ protected:
   virtual PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo) MOZ_OVERRIDE;
   virtual bool DeallocPCompositableChild(PCompositableChild* actor) MOZ_OVERRIDE;
 
   virtual PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
                                             const TextureFlags& aFlags) MOZ_OVERRIDE;
   virtual bool DeallocPTextureChild(PTextureChild* actor) MOZ_OVERRIDE;
 
   virtual bool
-  RecvParentAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessages) MOZ_OVERRIDE;
+  RecvParentAsyncMessages(const InfallibleTArray<AsyncParentMessageData>& aMessages) MOZ_OVERRIDE;
 
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   void AddIPDLReference() {
     MOZ_ASSERT(mIPCOpen == false);
     mIPCOpen = true;
     AddRef();
   }
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/layers/ContainerLayerComposite.h"
 #include "mozilla/layers/ImageLayerComposite.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for PGrallocBufferParent
 #include "mozilla/layers/LayersTypes.h"  // for MOZ_LAYERS_LOG
 #include "mozilla/layers/PCompositableParent.h"
 #include "mozilla/layers/PLayerParent.h"  // for PLayerParent
+#include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
 #include "mozilla/layers/ThebesLayerComposite.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "mozilla/unused.h"
 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsDeviceContext.h"            // for AppUnitsPerCSSPixel
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
 #include "nsLayoutUtils.h"              // for nsLayoutUtils
@@ -345,16 +346,17 @@ LayerTransactionParent::RecvUpdate(const
           return false;
         }
         const ContainerLayerAttributes& attrs =
           specific.get_ContainerLayerAttributes();
         containerLayer->SetFrameMetrics(attrs.metrics());
         containerLayer->SetScrollHandoffParentId(attrs.scrollParentId());
         containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
         containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
+        containerLayer->SetBackgroundColor(attrs.backgroundColor().value());
         break;
       }
       case Specific::TColorLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   color layer"));
 
         ColorLayerComposite* colorLayer = layerParent->AsColorLayerComposite();
         if (!colorLayer) {
           return false;
@@ -797,16 +799,37 @@ LayerTransactionParent::DeallocPTextureP
 
 bool
 LayerTransactionParent::RecvChildAsyncMessages(const InfallibleTArray<AsyncChildMessageData>& aMessages)
 {
   for (AsyncChildMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
     const AsyncChildMessageData& message = aMessages[i];
 
     switch (message.type()) {
+      case AsyncChildMessageData::TOpDeliverFenceFromChild: {
+        const OpDeliverFenceFromChild& op = message.get_OpDeliverFenceFromChild();
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+        FenceHandle fence = FenceHandle(op.fence());
+        PTextureParent* parent = op.textureParent();
+
+        TextureHostOGL* hostOGL = nullptr;
+        RefPtr<TextureHost> texture = TextureHost::AsTextureHost(parent);
+        if (texture) {
+          hostOGL = texture->AsHostOGL();
+        }
+        if (hostOGL) {
+          hostOGL->SetAcquireFence(fence.mFence);
+        }
+#endif
+        // Send back a response.
+        InfallibleTArray<AsyncParentMessageData> replies;
+        replies.AppendElement(OpReplyDeliverFence(op.transactionId()));
+        mozilla::unused << SendParentAsyncMessages(replies);
+        break;
+      }
       case AsyncChildMessageData::TOpReplyDeliverFence: {
         const OpReplyDeliverFence& op = message.get_OpReplyDeliverFence();
         TransactionCompleteted(op.transactionId());
         break;
       }
       default:
         NS_ERROR("unknown AsyncChildMessageData type");
         return false;
@@ -831,19 +854,19 @@ LayerTransactionParent::SendFenceHandle(
                                         PTextureParent* aTexture,
                                         const FenceHandle& aFence)
 {
   HoldUntilComplete(aTracker);
   InfallibleTArray<AsyncParentMessageData> messages;
   messages.AppendElement(OpDeliverFence(aTracker->GetId(),
                                         aTexture, nullptr,
                                         aFence));
-  mozilla::unused << SendParentAsyncMessage(messages);
+  mozilla::unused << SendParentAsyncMessages(messages);
 }
 
 void
 LayerTransactionParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
 {
-  mozilla::unused << SendParentAsyncMessage(aMessage);
+  mozilla::unused << SendParentAsyncMessages(aMessage);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -35,16 +35,17 @@ using mozilla::LayerMargin from "Units.h
 using mozilla::LayerPoint from "Units.h";
 using mozilla::LayerRect from "Units.h";
 using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using struct mozilla::layers::FenceHandle from "mozilla/layers/FenceUtils.h";
+using struct mozilla::layers::FenceHandleFromChild from "mozilla/layers/FenceUtils.h";
 using mozilla::layers::TextureIdentifier from "mozilla/layers/CompositorTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 struct TargetConfig {
   nsIntRect naturalBounds;
   ScreenRotation rotation;
@@ -221,16 +222,17 @@ struct ThebesLayerAttributes {
 };
 struct ContainerLayerAttributes {
   FrameMetrics metrics;
   ViewID scrollParentId;
   float preXScale;
   float preYScale;
   float inheritedXScale;
   float inheritedYScale;
+  LayerColor backgroundColor;
 };
 struct ColorLayerAttributes     { LayerColor color; nsIntRect bounds; };
 struct CanvasLayerAttributes    { GraphicsFilterType filter; nsIntRect bounds; };
 struct RefLayerAttributes       { int64_t id; };
 struct ImageLayerAttributes     { GraphicsFilterType filter; IntSize scaleToSize; ScaleMode scaleMode; };
 
 union SpecificLayerAttributes {
   null_t;
@@ -382,16 +384,22 @@ struct OpDeliverFence {
 
 struct OpDeliverFenceToTracker {
   uint64_t transactionId;
   uint64_t destHolderId;
   uint64_t destTransactionId;
   FenceHandle fence;
 };
 
+struct OpDeliverFenceFromChild {
+  uint64_t transactionId;
+  PTexture texture;
+  FenceHandleFromChild fence;
+};
+
 struct OpReplyDeliverFence {
   uint64_t transactionId;
 };
 
 union CompositableOperation {
   OpUpdatePictureRect;
 
   OpCreatedIncrementalTexture;
@@ -461,17 +469,19 @@ union EditReply {
   OpTextureSwap;
 
   ReturnReleaseFence;
 };
 
 union AsyncParentMessageData {
   OpDeliverFence;
   OpDeliverFenceToTracker;
+  OpReplyDeliverFence;
   OpReplyRemoveTexture;
 };
 
 union AsyncChildMessageData {
+  OpDeliverFenceFromChild;
   OpReplyDeliverFence;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -24,17 +24,17 @@ namespace layers {
  * which might be too busy dealing with content script.
  */
 intr protocol PImageBridge
 {
   manages PCompositable;
   manages PTexture;
 
 child:
-  async ParentAsyncMessage(AsyncParentMessageData[] aMessages);
+  async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
 parent:
 
   sync Update(CompositableOperation[] ops) returns (EditReply[] reply);
   async UpdateNoSwap(CompositableOperation[] ops);
 
   // First step of the destruction sequence. This puts ImageBridge
   // in a state in which it can't send asynchronous messages
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -38,17 +38,17 @@ union MaybeTransform {
 
 sync protocol PLayerTransaction {
   manager PRenderFrame or PCompositor;
   manages PLayer;
   manages PCompositable;
   manages PTexture;
 
 child:
-  async ParentAsyncMessage(AsyncParentMessageData[] aMessages);
+  async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
 parent:
   async PLayer();
   async PCompositable(TextureInfo aTextureInfo);
   async PTexture(SurfaceDescriptor aSharedData, TextureFlags aTextureFlags);
 
   // The isFirstPaint flag can be used to indicate that this is the first update
   // for a particular document.
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -406,16 +406,27 @@ ShadowLayerForwarder::UseComponentAlphaT
   MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
   MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
   mTxn->AddEdit(OpUseComponentAlphaTextures(nullptr, aCompositable->GetIPDLActor(),
                                             nullptr, aTextureOnBlack->GetIPDLActor(),
                                             nullptr, aTextureOnWhite->GetIPDLActor()));
 }
 
 void
+ShadowLayerForwarder::SendFenceHandle(AsyncTransactionTracker* aTracker,
+                                        PTextureChild* aTexture,
+                                        const FenceHandle& aFence)
+{
+  if (!HasShadowManager() || !mShadowManager->IPCOpen()) {
+    return;
+  }
+  mShadowManager->SendFenceHandle(aTracker, aTexture, aFence);
+}
+
+void
 ShadowLayerForwarder::RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                                     TextureClient* aTexture)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aCompositable->GetIPDLActor());
   MOZ_ASSERT(aTexture->GetIPDLActor());
   mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -283,16 +283,20 @@ public:
    * See CompositableForwarder::UseTexture
    */
   virtual void UseTexture(CompositableClient* aCompositable,
                           TextureClient* aClient) MOZ_OVERRIDE;
   virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                          TextureClient* aClientOnBlack,
                                          TextureClient* aClientOnWhite) MOZ_OVERRIDE;
 
+  virtual void SendFenceHandle(AsyncTransactionTracker* aTracker,
+                               PTextureChild* aTexture,
+                               const FenceHandle& aFence) MOZ_OVERRIDE;
+
   /**
    * End the current transaction and forward it to LayerManagerComposite.
    * |aReplies| are directions from the LayerManagerComposite to the
    * caller of EndTransaction().
    */
   bool EndTransaction(InfallibleTArray<EditReply>* aReplies,
                       const nsIntRegion& aRegionToClear,
                       uint64_t aId,
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -71,22 +71,16 @@ GrallocTextureClientOGL::ToSurfaceDescri
     return false;
   }
 
   aOutDescriptor = NewSurfaceDescriptorGralloc(mGrallocHandle, mSize);
   return true;
 }
 
 void
-GrallocTextureClientOGL::SetReleaseFenceHandle(FenceHandle aReleaseFenceHandle)
-{
-  mReleaseFenceHandle = aReleaseFenceHandle;
-}
-
-void
 GrallocTextureClientOGL::SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker)
 {
   mRemoveFromCompositableTracker = aTracker;
 }
 
 void
 GrallocTextureClientOGL::WaitForBufferOwnership()
 {
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -55,18 +55,16 @@ public:
   virtual bool ImplementsLocking() const MOZ_OVERRIDE { return true; }
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; }
 
   virtual bool IsAllocated() const MOZ_OVERRIDE;
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
-  virtual void SetReleaseFenceHandle(FenceHandle aReleaseFenceHandle) MOZ_OVERRIDE;
-
   virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) MOZ_OVERRIDE;
 
   virtual void WaitForBufferOwnership() MOZ_OVERRIDE;
 
   void InitWith(MaybeMagicGrallocBufferHandle aDesc, gfx::IntSize aSize);
 
   void SetTextureFlags(TextureFlags aFlags) { AddFlags(aFlags); }
 
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -85,19 +85,21 @@ TextureTargetForAndroidPixelFormat(andro
       // like undesirable behaviour. We'd rather have a subtle artifact.
       MOZ_ASSERT(false, "Unknown Android pixel format.");
       return LOCAL_GL_TEXTURE_EXTERNAL;
     }
   }
 }
 
 GrallocTextureSourceOGL::GrallocTextureSourceOGL(CompositorOGL* aCompositor,
+                                                 GrallocTextureHostOGL* aTextureHost,
                                                  android::GraphicBuffer* aGraphicBuffer,
                                                  gfx::SurfaceFormat aFormat)
   : mCompositor(aCompositor)
+  , mTextureHost(aTextureHost)
   , mGraphicBuffer(aGraphicBuffer)
   , mEGLImage(0)
   , mFormat(aFormat)
   , mNeedsReset(true)
 {
   MOZ_ASSERT(mGraphicBuffer.get());
 }
 
@@ -295,16 +297,17 @@ GrallocTextureHostOGL::GrallocTextureHos
   if (graphicBuffer) {
     format =
       SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(),
                                          aFlags & TextureFlags::RB_SWAPPED);
   } else {
     NS_WARNING("gralloc buffer is nullptr");
   }
   mTextureSource = new GrallocTextureSourceOGL(nullptr,
+                                               this,
                                                graphicBuffer,
                                                format);
 }
 
 GrallocTextureHostOGL::~GrallocTextureHostOGL()
 {
   mTextureSource = nullptr;
 }
@@ -435,16 +438,22 @@ GrallocTextureSourceOGL::GetGLTexture()
   }
 
   return mTexture;
 }
 
 void
 GrallocTextureSourceOGL::BindEGLImage()
 {
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+  if (mTextureHost) {
+    mTextureHost->WaitAcquireFenceSyncComplete();
+  }
+#endif
+
   if (mCompositableBackendData) {
     CompositableDataGonkOGL* backend = static_cast<CompositableDataGonkOGL*>(mCompositableBackendData.get());
     backend->BindEGLImage(GetTextureTarget(), mEGLImage);
   } else {
     gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage);
   }
 }
 
--- a/gfx/layers/opengl/GrallocTextureHost.h
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -18,16 +18,17 @@ class GrallocTextureHostOGL;
 
 class GrallocTextureSourceOGL : public NewTextureSource
                               , public TextureSourceOGL
 {
 public:
   friend class GrallocTextureHostOGL;
 
   GrallocTextureSourceOGL(CompositorOGL* aCompositor,
+                          GrallocTextureHostOGL* aTextureHost,
                           android::GraphicBuffer* aGraphicBuffer,
                           gfx::SurfaceFormat aFormat);
 
   virtual ~GrallocTextureSourceOGL();
 
   virtual bool IsValid() const MOZ_OVERRIDE;
 
   virtual void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) MOZ_OVERRIDE;
@@ -51,28 +52,30 @@ public:
 
   gl::GLContext* gl() const;
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   void ForgetBuffer()
   {
     mGraphicBuffer = nullptr;
+    mTextureHost = nullptr;
   }
 
   TemporaryRef<gfx::DataSourceSurface> GetAsSurface();
 
   GLuint GetGLTexture();
 
   void BindEGLImage();
 
   void Lock();
 
 protected:
   CompositorOGL* mCompositor;
+  GrallocTextureHostOGL* mTextureHost;
   android::sp<android::GraphicBuffer> mGraphicBuffer;
   EGLImage mEGLImage;
   GLuint mTexture;
   gfx::SurfaceFormat mFormat;
   bool mNeedsReset;
 };
 
 class GrallocTextureHostOGL : public TextureHost
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TextureHostOGL.h"
 #include "GLContext.h"                  // for GLContext, etc
+#include "GLLibraryEGL.h"               // for GLLibraryEGL
 #include "GLSharedHandleHelpers.h"
 #include "GLUploadHelpers.h"
 #include "GLReadTexImageHelper.h"
 #include "SharedSurface.h"              // for SharedSurface
 #include "SharedSurfaceEGL.h"           // for SharedSurface_EGLImage
 #include "SharedSurfaceGL.h"            // for SharedSurface_GLTexture, etc
 #include "SurfaceStream.h"              // for SurfaceStream
 #include "SurfaceTypes.h"               // for SharedSurfaceType, etc
@@ -215,16 +216,66 @@ android::sp<android::Fence>
 TextureHostOGL::GetAndResetReleaseFence()
 {
   // Hold previous ReleaseFence to prevent Fence delivery failure via gecko IPC.
   mPrevReleaseFence = mReleaseFence;
   // Reset current ReleaseFence.
   mReleaseFence = android::Fence::NO_FENCE;
   return mPrevReleaseFence;
 }
+
+void
+TextureHostOGL::SetAcquireFence(const android::sp<android::Fence>& aAcquireFence)
+{
+  mAcquireFence = aAcquireFence;
+}
+
+android::sp<android::Fence>
+TextureHostOGL::GetAcquireFence()
+{
+  return mAcquireFence;
+}
+
+void
+TextureHostOGL::WaitAcquireFenceSyncComplete()
+{
+  if (!mAcquireFence.get() || !mAcquireFence->isValid()) {
+    return;
+  }
+
+  int fenceFd = mAcquireFence->dup();
+  if (fenceFd == -1) {
+    NS_WARNING("failed to dup fence fd");
+    return;
+  }
+
+  EGLint attribs[] = {
+              LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd,
+              LOCAL_EGL_NONE
+          };
+
+  EGLSync sync = sEGLLibrary.fCreateSync(EGL_DISPLAY(),
+                                         LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID,
+                                         attribs);
+  if (!sync) {
+    NS_WARNING("failed to create native fence sync");
+    return;
+  }
+
+  EGLint status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(),
+                                              sync,
+                                              0,
+                                              LOCAL_EGL_FOREVER);
+  if (status != LOCAL_EGL_CONDITION_SATISFIED) {
+    NS_WARNING("failed to wait native fence sync");
+  }
+  MOZ_ALWAYS_TRUE( sEGLLibrary.fDestroySync(EGL_DISPLAY(), sync) );
+  mAcquireFence = nullptr;
+}
+
 #endif
 
 bool
 TextureImageTextureSourceOGL::Update(gfx::DataSourceSurface* aSurface,
                                      nsIntRegion* aDestRegion,
                                      gfx::IntPoint* aSrcOffset)
 {
   MOZ_ASSERT(mGL);
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -167,19 +167,27 @@ public:
    */
   virtual bool SetReleaseFence(const android::sp<android::Fence>& aReleaseFence);
 
   /**
    * Return a releaseFence's Fence and clear a reference to the Fence.
    */
   virtual android::sp<android::Fence> GetAndResetReleaseFence();
 
+  virtual void SetAcquireFence(const android::sp<android::Fence>& aAcquireFence);
+
+  virtual android::sp<android::Fence> GetAcquireFence();
+
+  virtual void WaitAcquireFenceSyncComplete();
+
 protected:
   android::sp<android::Fence> mReleaseFence;
 
+  android::sp<android::Fence> mAcquireFence;
+
   /**
    * Hold previous ReleaseFence to prevent Fence delivery failure via gecko IPC.
    * Fence is a kernel object and its lifetime is managed by a reference count.
    * Until the Fence is delivered to client side, need to hold Fence on host side.
    */
   android::sp<android::Fence> mPrevReleaseFence;
 #endif
 };
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -370,17 +370,17 @@ ShrinkGCBuffers(JSRuntime *rt);
 
 /*
  * Assert if a GC occurs while this class is live. This class does not disable
  * the static rooting hazard analysis.
  */
 class JS_PUBLIC_API(AutoAssertOnGC)
 {
 #ifdef DEBUG
-    JSRuntime *runtime;
+    js::gc::GCRuntime *gc;
     size_t gcNumber;
 
   public:
     AutoAssertOnGC();
     explicit AutoAssertOnGC(JSRuntime *rt);
     ~AutoAssertOnGC();
 
     static void VerifyIsSafeToGC(JSRuntime *rt);
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -166,17 +166,16 @@ IsPoisonedId(jsid iden)
     if (JSID_IS_OBJECT(iden))
         return JS::IsPoisonedPtr(JSID_TO_OBJECT(iden));
     return false;
 }
 
 template <> struct GCMethods<jsid>
 {
     static jsid initial() { return JSID_VOID; }
-    static ThingRootKind kind() { return THING_ROOT_ID; }
     static bool poisoned(jsid id) { return IsPoisonedId(id); }
     static bool needsPostBarrier(jsid id) { return false; }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(jsid *idp) {}
     static void relocate(jsid *idp) {}
 #endif
 };
 
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -649,30 +649,28 @@ struct RootKind<T *>
 {
     static ThingRootKind rootKind() { return T::rootKind(); }
 };
 
 template <typename T>
 struct GCMethods<T *>
 {
     static T *initial() { return nullptr; }
-    static ThingRootKind kind() { return RootKind<T *>::rootKind(); }
     static bool poisoned(T *v) { return JS::IsPoisonedPtr(v); }
     static bool needsPostBarrier(T *v) { return false; }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(T **vp) {}
     static void relocate(T **vp) {}
 #endif
 };
 
 template <>
 struct GCMethods<JSObject *>
 {
     static JSObject *initial() { return nullptr; }
-    static ThingRootKind kind() { return RootKind<JSObject *>::rootKind(); }
     static bool poisoned(JSObject *v) { return JS::IsPoisonedPtr(v); }
     static bool needsPostBarrier(JSObject *v) {
         return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell *>(v));
     }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(JSObject **vp) {
         JS::HeapCellPostBarrier(reinterpret_cast<js::gc::Cell **>(vp));
     }
@@ -702,17 +700,17 @@ namespace JS {
  */
 template <typename T>
 class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
 {
     /* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */
     template <typename CX>
     void init(CX *cx) {
 #ifdef JSGC_TRACK_EXACT_ROOTS
-        js::ThingRootKind kind = js::GCMethods<T>::kind();
+        js::ThingRootKind kind = js::RootKind<T>::rootKind();
         this->stack = &cx->thingGCRooters[kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
 
         MOZ_ASSERT(!js::GCMethods<T>::poisoned(ptr));
 #endif
     }
 
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1545,24 +1545,22 @@ JS_PUBLIC_API(void) HeapValueRelocate(Va
 }
 #endif
 
 namespace js {
 
 template <> struct GCMethods<const JS::Value>
 {
     static JS::Value initial() { return JS::UndefinedValue(); }
-    static ThingRootKind kind() { return THING_ROOT_VALUE; }
     static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
 };
 
 template <> struct GCMethods<JS::Value>
 {
     static JS::Value initial() { return JS::UndefinedValue(); }
-    static ThingRootKind kind() { return THING_ROOT_VALUE; }
     static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
     static bool needsPostBarrier(const JS::Value &v) {
         return v.isObject() && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(&v.toObject()));
     }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(JS::Value *v) { JS::HeapValuePostBarrier(v); }
     static void relocate(JS::Value *v) { JS::HeapValueRelocate(v); }
 #endif
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -515,17 +515,17 @@ SelectForGC(JSContext *cx, unsigned argc
      * start to detect missing pre-barriers. It is invalid for nursery things
      * to be in the set, so evict the nursery before adding items.
      */
     JSRuntime *rt = cx->runtime();
     MinorGC(rt, JS::gcreason::EVICT_NURSERY);
 
     for (unsigned i = 0; i < args.length(); i++) {
         if (args[i].isObject()) {
-            if (!rt->gc.selectedForMarking.append(&args[i].toObject()))
+            if (!rt->gc.selectForMarking(&args[i].toObject()))
                 return false;
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
 
@@ -594,17 +594,17 @@ DeterministicGC(JSContext *cx, unsigned 
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
 
-    gc::SetDeterministicGC(cx, ToBoolean(args[0]));
+    cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
     args.rval().setUndefined();
     return true;
 }
 #endif /* JS_GC_ZEAL */
 
 static bool
 GCSlice(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -636,33 +636,33 @@ ValidateGC(JSContext *cx, unsigned argc,
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
 
-    gc::SetValidateGC(cx, ToBoolean(args[0]));
+    cx->runtime()->gc.setValidate(ToBoolean(args[0]));
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 FullCompartmentChecks(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
 
-    gc::SetFullCompartmentChecks(cx, ToBoolean(args[0]));
+    cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 NondeterministicGetWeakMapKeys(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -417,25 +417,21 @@ case "$target" in
             AC_MSG_ERROR([\$(CXX) test failed.  You must have MS VC++ in your path to build.]) )
         AC_LANG_RESTORE
 
         changequote(,)
         _MSVC_VER_FILTER='s|.*[^!-~]([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?).*|\1|p'
         changequote([,])
 
         # Determine compiler version
-        CC_VERSION=`${CC} -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
-        _CC_MAJOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $1 }'`
-        _CC_MINOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $2 }'`
-        _CC_RELEASE=`echo ${CC_VERSION} | $AWK -F\. '{ print $3 }'`
-        _CC_BUILD=`echo ${CC_VERSION} | $AWK -F\. '{ print $4 }'`
-        _MSC_VER=${_CC_MAJOR_VERSION}${_CC_MINOR_VERSION}
-
-        CXX_VERSION=`${CXX} -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
-        _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | $AWK -F\. '{ print $1 }'`
+        _CC_MAJOR_VERSION=`echo ${CC_VERSION} | cut -c 1-2`
+        _CC_MINOR_VERSION=`echo ${CC_VERSION} | cut -c 3-4`
+        _MSC_VER=${CC_VERSION}
+
+        _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | cut -c 1-2`
 
         if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then
             AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.])
         fi
 
         AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
         AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
 
@@ -1673,17 +1669,21 @@ ia64*-hpux*)
         CFLAGS="$CFLAGS -we4553"
         CXXFLAGS="$CXXFLAGS -we4553"
         LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib psapi.lib"
         MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
         WARNINGS_AS_ERRORS='-WX'
         MOZ_OPTIMIZE_FLAGS="-O2"
         MOZ_FIX_LINK_PATHS=
         MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)'
-        LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
+        # Disable these flags on clang-cl since it doesn't ignore unknown arguments by default, and
+        # autoconf insists on passing $LDFLAGS to the compiler.
+        if test -z "$CLANG_CL"; then
+            LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
+        fi
         if test -z "$DEVELOPER_OPTIONS"; then
             LDFLAGS="$LDFLAGS -RELEASE"
         fi
         dnl For profile-guided optimization
         PROFILE_GEN_CFLAGS="-GL"
         PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT"
         dnl XXX: PGO builds can fail with warnings treated as errors,
         dnl specifically "no profile data available" appears to be
@@ -1753,17 +1753,21 @@ ia64*-hpux*)
         fi
 
         if test -n "$GNU_CC"; then
             CFLAGS="$CFLAGS -mstackrealign -fno-keep-inline-dllexport"
             CXXFLAGS="$CXXFLAGS -mstackrealign -fno-keep-inline-dllexport"
             LDFLAGS="$LDFLAGS -Wl,--large-address-aware"
         else
             DSO_LDOPTS="$DSO_LDOPTS -MACHINE:X86"
-            LDFLAGS="$LDFLAGS -SAFESEH"
+            # Disable this flag on clang-cl since it doesn't ignore unknown arguments by default, and
+            # autoconf insists on passing $LDFLAGS to the compiler.
+            if test -z "$CLANG_CL"; then
+                LDFLAGS="$LDFLAGS -SAFESEH"
+            fi
         fi
 
     	AC_DEFINE(_X86_)
 	;;
     x86_64-*)
         if test -n "$_WIN32_MSVC"; then
             DSO_LDOPTS="$DSO_LDOPTS -MACHINE:X64"
         fi
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -21,33 +21,16 @@
 
 /* Perform validation of incremental marking in debug builds but not on B2G. */
 #if defined(DEBUG) && !defined(MOZ_B2G)
 #define JS_GC_MARKING_VALIDATION
 #endif
 
 namespace js {
 
-struct ScriptAndCounts
-{
-    /* This structure is stored and marked from the JSRuntime. */
-    JSScript *script;
-    ScriptCounts scriptCounts;
-
-    PCCounts &getPCCounts(jsbytecode *pc) const {
-        return scriptCounts.pcCountsVector[script->pcToOffset(pc)];
-    }
-
-    jit::IonScriptCounts *getIonCounts() const {
-        return scriptCounts.ionCounts;
-    }
-};
-
-typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector;
-
 namespace gc {
 
 typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
 
 class MarkingValidator;
 class AutoPrepareForTracing;
 
 struct ConservativeGCData
@@ -108,17 +91,20 @@ class CallbackVector : public Vector<Cal
 
 class GCRuntime
 {
   public:
     explicit GCRuntime(JSRuntime *rt);
     bool init(uint32_t maxbytes);
     void finish();
 
-    void setGCZeal(uint8_t zeal, uint32_t frequency);
+    inline int zeal();
+    inline bool upcomingZealousGC();
+    inline bool needZealousGC();
+
     template <typename T> bool addRoot(T *rp, const char *name, JSGCRootType rootType);
     void removeRoot(void *rp);
     void setMarkStackLimit(size_t limit);
 
     bool isHeapBusy() { return heapState != js::Idle; }
     bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; }
     bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; }
     bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); }
@@ -128,24 +114,31 @@ class GCRuntime
     void maybeGC(Zone *zone);
     void minorGC(JS::gcreason::Reason reason);
     void minorGC(JSContext *cx, JS::gcreason::Reason reason);
     void gcIfNeeded(JSContext *cx);
     void collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                  JS::gcreason::Reason reason);
     void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis);
     void runDebugGC();
+    inline void poke();
 
     void markRuntime(JSTracer *trc, bool useSavedRoots = false);
 
 #ifdef JS_GC_ZEAL
+    const void *addressOfZealMode() { return &zealMode; }
+    void setZeal(uint8_t zeal, uint32_t frequency);
+    void setNextScheduled(uint32_t count);
     void verifyPreBarriers();
     void verifyPostBarriers();
     void maybeVerifyPreBarriers(bool always);
     void maybeVerifyPostBarriers(bool always);
+    bool selectForMarking(JSObject *object);
+    void clearSelectedForMarking();
+    void setDeterministic(bool enable);
 #endif
 
   public:
     // Internal public interface
     void recordNativeStackTop();
 #ifdef JS_THREADSAFE
     void notifyRequestEnd() { conservativeGC.updateForRequestEnd(); }
 #endif
@@ -196,40 +189,65 @@ class GCRuntime
 
 #ifdef DEBUG
     bool isAllocAllowed() { return noGCOrAllocationCheck == 0; }
     void disallowAlloc() { ++noGCOrAllocationCheck; }
     void allowAlloc() {
         JS_ASSERT(!isAllocAllowed());
         --noGCOrAllocationCheck;
     }
+
+    bool isInsideUnsafeRegion() { return inUnsafeRegion != 0; }
+    void enterUnsafeRegion() { ++inUnsafeRegion; }
+    void leaveUnsafeRegion() {
+        JS_ASSERT(inUnsafeRegion > 0);
+        --inUnsafeRegion;
+    }
 #endif
 
     void setAlwaysPreserveCode() { alwaysPreserveCode = true; }
 
     bool isGenerationalGCEnabled() { return generationalDisabled == 0; }
     void disableGenerationalGC();
     void enableGenerationalGC();
 
+    void setGrayRootsTracer(JSTraceDataOp traceOp, void *data);
+    bool addBlackRootsTracer(JSTraceDataOp traceOp, void *data);
+    void removeBlackRootsTracer(JSTraceDataOp traceOp, void *data);
+
+    void setMaxMallocBytes(size_t value);
+    void resetMallocBytes();
+    bool isTooMuchMalloc() const { return mallocBytes <= 0; }
+    void updateMallocCounter(JS::Zone *zone, size_t nbytes);
+    void onTooMuchMalloc();
+
+    void setGCCallback(JSGCCallback callback, void *data);
+    bool addFinalizeCallback(JSFinalizeCallback callback, void *data);
+    void removeFinalizeCallback(JSFinalizeCallback func);
+    JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
+
+    void setValidate(bool enable);
+    void setFullCompartmentChecks(bool enable);
+
 #ifdef JS_GC_ZEAL
     void startVerifyPreBarriers();
     bool endVerifyPreBarriers();
     void startVerifyPostBarriers();
     bool endVerifyPostBarriers();
     void finishVerifier();
 #endif
 
   private:
     // For ArenaLists::allocateFromArenaInline()
     friend class ArenaLists;
     Chunk *pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation);
 
     inline bool wantBackgroundAllocation() const;
 
-    bool initGCZeal();
+    bool initZeal();
     void requestInterrupt(JS::gcreason::Reason reason);
     bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                  JS::gcreason::Reason reason);
     void budgetIncrementalGC(int64_t *budget);
     void resetIncrementalGC(const char *reason);
     void incrementalCollectSlice(int64_t budget, JS::gcreason::Reason reason,
                                  JSGCInvocationKind gckind);
     void pushZealSelectedObjects();
@@ -452,25 +470,26 @@ class GCRuntime
      * zone with no incoming cross-compartment pointers. Typically if
      * this happens it signals that an incremental GC is marking too much
      * stuff. At various times we check this counter and, if it has changed, we
      * run an immediate, non-incremental GC to clean up the dead
      * zones. This should happen very rarely.
      */
     unsigned              objectsMarkedInDeadZones;
 
-    bool                  poke;
+    bool                  poked;
 
     volatile js::HeapState heapState;
 
 #ifdef JSGC_GENERATIONAL
     js::Nursery           nursery;
     js::gc::StoreBuffer   storeBuffer;
 #endif
 
+  private:
     /*
      * These options control the zealousness of the GC. The fundamental values
      * are   nextScheduled and gcDebugCompartmentGC. At every allocation,
      *   nextScheduled is decremented. When it reaches zero, we do either a
      * full or a compartmental GC, based on   debugCompartmentGC.
      *
      * At this point, if   zeal_ is one of the types that trigger periodic
      * collection, then   nextScheduled is reset to the value of
@@ -498,82 +517,97 @@ class GCRuntime
     int                   incrementalLimit;
 
     js::Vector<JSObject *, 0, js::SystemAllocPolicy>   selectedForMarking;
 #endif
 
     bool                  validate;
     bool                  fullCompartmentChecks;
 
-    JSGCCallback          gcCallback;
-    void                  *gcCallbackData;
-
-    JS::GCSliceCallback   sliceCallback;
+    Callback<JSGCCallback>  gcCallback;
     CallbackVector<JSFinalizeCallback> finalizeCallbacks;
 
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
      * from   maxMallocBytes down to zero.
      */
     mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire>   mallocBytes;
 
     /*
-     * Whether a GC has been triggered as a result of   mallocBytes falling
+     * Whether a GC has been triggered as a result of mallocBytes falling
      * below zero.
      */
     mozilla::Atomic<bool, mozilla::ReleaseAcquire>   mallocGCTriggered;
 
     /*
      * The trace operations to trace embedding-specific GC roots. One is for
      * tracing through black roots and the other is for tracing through gray
      * roots. The black/gray distinction is only relevant to the cycle
      * collector.
      */
     CallbackVector<JSTraceDataOp> blackRootTracers;
     Callback<JSTraceDataOp> grayRootTracer;
 
-    /*
-     * The GC can only safely decommit memory when the page size of the
-     * running process matches the compiled arena size.
-     */
-    size_t                systemPageSize;
-
-    /* The OS allocation granularity may not match the page size. */
-    size_t                systemAllocGranularity;
-
-    /* Strong references on scripts held for PCCount profiling API. */
-    js::ScriptAndCountsVector *scriptAndCountsVector;
-
 #ifdef DEBUG
     /*
      * Some regions of code are hard for the static rooting hazard analysis to
      * understand. In those cases, we trade the static analysis for a dynamic
      * analysis. When this is non-zero, we should assert if we trigger, or
      * might trigger, a GC.
      */
     int inUnsafeRegion;
 #endif
 
-  private:
     /* Always preserve JIT code during GCs, for testing. */
     bool                  alwaysPreserveCode;
 
 #ifdef DEBUG
     size_t                noGCOrAllocationCheck;
 #endif
 
     /* Synchronize GC heap access between main thread and GCHelperState. */
     PRLock                *lock;
     mozilla::DebugOnly<PRThread *>   lockOwner;
 
     GCHelperState helperState;
 
     ConservativeGCData conservativeGC;
 
-    //friend class js::gc::Chunk; // todo: remove
     friend class js::GCHelperState;
     friend class js::gc::MarkingValidator;
 };
 
+#ifdef JS_GC_ZEAL
+inline int
+GCRuntime::zeal() {
+    return zealMode;
+}
+
+inline bool
+GCRuntime::upcomingZealousGC() {
+    return nextScheduled == 1;
+}
+
+inline bool
+GCRuntime::needZealousGC() {
+    if (nextScheduled > 0 && --nextScheduled == 0) {
+        if (zealMode == ZealAllocValue ||
+            zealMode == ZealGenerationalGCValue ||
+            (zealMode >= ZealIncrementalRootsThenFinish &&
+             zealMode <= ZealIncrementalMultipleSlices))
+        {
+            nextScheduled = zealFrequency;
+        }
+        return true;
+    }
+    return false;
+}
+#else
+inline int GCRuntime::zeal() { return 0; }
+inline bool GCRuntime::upcomingZealousGC() { return false; }
+inline bool GCRuntime::needZealousGC() { return false; }
+#endif
+
+
 } /* namespace gc */
 } /* namespace js */
 
 #endif
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -102,17 +102,17 @@ js::Nursery::enable()
 {
     JS_ASSERT(isEmpty());
     if (isEnabled())
         return;
     numActiveChunks_ = 1;
     setCurrentChunk(0);
     currentStart_ = position();
 #ifdef JS_GC_ZEAL
-    if (runtime()->gc.zealMode == ZealGenerationalGCValue)
+    if (runtime()->gcZeal() == ZealGenerationalGCValue)
         enterZealMode();
 #endif
 }
 
 void
 js::Nursery::disable()
 {
     JS_ASSERT(isEmpty());
@@ -124,17 +124,17 @@ js::Nursery::disable()
 }
 
 bool
 js::Nursery::isEmpty() const
 {
     JS_ASSERT(runtime_);
     if (!isEnabled())
         return true;
-    JS_ASSERT_IF(runtime_->gc.zealMode != ZealGenerationalGCValue, currentStart_ == start());
+    JS_ASSERT_IF(runtime_->gcZeal() != ZealGenerationalGCValue, currentStart_ == start());
     return position() == currentStart_;
 }
 
 #ifdef JS_GC_ZEAL
 void
 js::Nursery::enterZealMode() {
     if (isEnabled())
         numActiveChunks_ = NumNurseryChunks;
@@ -925,17 +925,17 @@ void
 js::Nursery::sweep()
 {
 #ifdef JS_GC_ZEAL
     /* Poison the nursery contents so touching a freed object will crash. */
     JS_POISON((void *)start(), JS_SWEPT_NURSERY_PATTERN, NurserySize);
     for (int i = 0; i < NumNurseryChunks; ++i)
         initChunk(i);
 
-    if (runtime()->gc.zealMode == ZealGenerationalGCValue) {
+    if (runtime()->gcZeal() == ZealGenerationalGCValue) {
         MOZ_ASSERT(numActiveChunks_ == NumNurseryChunks);
 
         /* Only reset the alloc point when we are close to the end. */
         if (currentChunk_ + 1 == NumNurseryChunks)
             setCurrentChunk(0);
     } else
 #endif
     {
@@ -950,26 +950,26 @@ js::Nursery::sweep()
     /* Set current start position for isEmpty checks. */
     currentStart_ = position();
 }
 
 void
 js::Nursery::growAllocableSpace()
 {
 #ifdef JS_GC_ZEAL
-    MOZ_ASSERT_IF(runtime()->gc.zealMode == ZealGenerationalGCValue,
+    MOZ_ASSERT_IF(runtime()->gcZeal() == ZealGenerationalGCValue,
                   numActiveChunks_ == NumNurseryChunks);
 #endif
     numActiveChunks_ = Min(numActiveChunks_ * 2, NumNurseryChunks);
 }
 
 void
 js::Nursery::shrinkAllocableSpace()
 {
 #ifdef JS_GC_ZEAL
-    if (runtime()->gc.zealMode == ZealGenerationalGCValue)
+    if (runtime()->gcZeal() == ZealGenerationalGCValue)
         return;
 #endif
     numActiveChunks_ = Max(numActiveChunks_ - 1, 1);
     updateDecommittedRegion();
 }
 
 #endif /* JSGC_GENERATIONAL */
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -725,18 +725,18 @@ js::gc::GCRuntime::markRuntime(JSTracer 
                 MarkScriptRoot(trc, reinterpret_cast<JSScript **>(key), name);
             else
                 MOZ_ASSUME_UNREACHABLE("unexpected js::RootInfo::type value");
         }
     }
 
     MarkPersistentRootedChains(trc);
 
-    if (scriptAndCountsVector) {
-        ScriptAndCountsVector &vec = *scriptAndCountsVector;
+    if (rt->scriptAndCountsVector) {
+        ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
         for (size_t i = 0; i < vec.length(); i++)
             MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
     }
 
     if (!rt->isBeingDestroyed() && !trc->runtime()->isHeapMinorCollecting()) {
         if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment()->zone()->isCollecting()) {
             MarkPermanentAtoms(trc);
             MarkAtoms(trc);
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -441,17 +441,18 @@ Statistics::Statistics(JSRuntime *rt)
     fp(nullptr),
     fullFormat(false),
     gcDepth(0),
     collectedCount(0),
     zoneCount(0),
     compartmentCount(0),
     nonincrementalReason(nullptr),
     preBytes(0),
-    phaseNestingDepth(0)
+    phaseNestingDepth(0),
+    sliceCallback(nullptr)
 {
     PodArrayZero(phaseTotals);
     PodArrayZero(counts);
 
     char *env = getenv("MOZ_GCTIMER");
     if (!env || strcmp(env, "none") == 0) {
         fp = nullptr;
         return;
@@ -484,16 +485,23 @@ Statistics::~Statistics()
             }
         }
 
         if (fp != stdout && fp != stderr)
             fclose(fp);
     }
 }
 
+JS::GCSliceCallback
+Statistics::setSliceCallback(JS::GCSliceCallback newCallback) {
+    JS::GCSliceCallback oldCallback = sliceCallback;
+    sliceCallback = newCallback;
+    return oldCallback;
+}
+
 void
 Statistics::printStats()
 {
     if (fullFormat) {
         StatisticsSerializer ss(StatisticsSerializer::AsText);
         formatData(ss, 0);
         char *msg = ss.finishCString();
         if (msg) {
@@ -576,19 +584,19 @@ Statistics::beginSlice(int collectedCoun
     (void) slices.append(data); /* Ignore any OOMs here. */
 
     if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
         (*cb)(JS_TELEMETRY_GC_REASON, reason);
 
     // Slice callbacks should only fire for the outermost level
     if (++gcDepth == 1) {
         bool wasFullGC = collectedCount == zoneCount;
-        if (JS::GCSliceCallback cb = runtime->gc.sliceCallback)
-            (*cb)(runtime, first ? JS::GC_CYCLE_BEGIN : JS::GC_SLICE_BEGIN,
-                  JS::GCDescription(!wasFullGC));
+        if (sliceCallback)
+            (*sliceCallback)(runtime, first ? JS::GC_CYCLE_BEGIN : JS::GC_SLICE_BEGIN,
+                             JS::GCDescription(!wasFullGC));
     }
 }
 
 void
 Statistics::endSlice()
 {
     slices.back().end = PRMJ_Now();
     slices.back().endFaults = SystemPageAllocator::GetPageFaultCount();
@@ -600,19 +608,19 @@ Statistics::endSlice()
 
     bool last = runtime->gc.incrementalState == gc::NO_INCREMENTAL;
     if (last)
         endGC();
 
     // Slice callbacks should only fire for the outermost level
     if (--gcDepth == 0) {
         bool wasFullGC = collectedCount == zoneCount;
-        if (JS::GCSliceCallback cb = runtime->gc.sliceCallback)
-            (*cb)(runtime, last ? JS::GC_CYCLE_END : JS::GC_SLICE_END,
-                  JS::GCDescription(!wasFullGC));
+        if (sliceCallback)
+            (*sliceCallback)(runtime, last ? JS::GC_CYCLE_END : JS::GC_SLICE_END,
+                             JS::GCDescription(!wasFullGC));
     }
 
     /* Do this after the slice callback since it uses these values. */
     if (last)
         PodArrayZero(counts);
 }
 
 void
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -93,16 +93,18 @@ struct Statistics {
     }
 
     int64_t beginSCC();
     void endSCC(unsigned scc, int64_t start);
 
     jschar *formatMessage();
     jschar *formatJSON(uint64_t timestamp);
 
+    JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
+
   private:
     JSRuntime *runtime;
 
     int64_t startupTime;
 
     FILE *fp;
     bool fullFormat;
 
@@ -155,16 +157,18 @@ struct Statistics {
     static const size_t MAX_NESTING = 8;
     Phase phaseNesting[MAX_NESTING];
 #endif
     mozilla::DebugOnly<size_t> phaseNestingDepth;
 
     /* Sweep times for SCCs of compartments. */
     Vector<int64_t, 0, SystemAllocPolicy> sccTimes;
 
+    JS::GCSliceCallback sliceCallback;
+
     void beginGC();
     void endGC();
 
     void gcDuration(int64_t *total, int64_t *maxPause);
     void sccDurations(int64_t *total, int64_t *maxPause);
     void printStats();
     bool formatData(StatisticsSerializer &ss, uint64_t timestamp);
 
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -187,16 +187,74 @@ function rsub_object(i) {
     var o = { valueOf: function () { return t; } };
     var x = o - i; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_sub_object(i) || uceFault_sub_object(i))
         assertEq(x, 0);
     return i;
 }
 
+var uceFault_mul_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_mul_number'));
+function rmul_number(i) {
+    var x = 2 * i;
+    if (uceFault_mul_number(i) || uceFault_mul_number(i))
+        assertEq(x, 198  /* = 1 * 99 */);
+    return i;
+}
+
+var uceFault_mul_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_mul_float'));
+function rmul_float(i) {
+    var t = Math.fround(1/3);
+    var fi = Math.fround(i);
+    var x = Math.fround(Math.fround(Math.fround(Math.fround(t * fi) * t) * fi) * t);
+    if (uceFault_mul_float(i) || uceFault_mul_float(i))
+        assertEq(x, 363); /* != 363.0000324547301 (when computed with double multiplications) */
+    return i;
+}
+
+var uceFault_mul_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_mul_object'));
+function rmul_object(i) {
+    var t = i;
+    var o = { valueOf: function () { return t; } };
+    var x = o * i; /* computed with t == i, not 1000 */
+    t = 1000;
+    if (uceFault_mul_object(i) || uceFault_mul_object(i))
+        assertEq(x, 9801);
+    return i;
+}
+
+var uceFault_div_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_div_number'));
+function rdiv_number(i) {
+    var x = 1 / i;
+    if (uceFault_div_number(i) || uceFault_div_number(i))
+        assertEq(x, 0.010101010101010102  /* = 1 / 99 */);
+    return i;
+}
+
+var uceFault_div_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_div_float'));
+function rdiv_float(i) {
+    var t = Math.fround(1/3);
+    var fi = Math.fround(i);
+    var x = Math.fround(Math.fround(Math.fround(Math.fround(t / fi) / t) / fi) / t);
+    if (uceFault_div_float(i) || uceFault_div_float(i))
+        assertEq(x, 0.0003060912131331861); /* != 0.0003060912060598955 (when computed with double divisions) */
+    return i;
+}
+
+var uceFault_div_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_div_object'));
+function rdiv_object(i) {
+    var t = i;
+    var o = { valueOf: function () { return t; } };
+    var x = o / i; /* computed with t == i, not 1000 */
+    t = 1000;
+    if (uceFault_div_object(i) || uceFault_div_object(i))
+        assertEq(x, 1);
+    return i;
+}
+
 var uceFault_mod_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_mod_number'));
 function rmod_number(i) {
     var x = i % 98;
     if (uceFault_mod_number(i) || uceFault_mod_number(i))
         assertEq(x, 1); /* 99 % 98 = 1 */
     return i;
 }
 
@@ -226,16 +284,22 @@ for (i = 0; i < 100; i++) {
     rursh_object(i);
     radd_number(i);
     radd_float(i);
     radd_string(i);
     radd_object(i);
     rsub_number(i);
     rsub_float(i);
     rsub_object(i);
+    rmul_number(i);
+    rmul_float(i);
+    rmul_object(i);
+    rdiv_number(i);
+    rdiv_float(i);
+    rdiv_object(i);
     rmod_number(i);
     rmod_object(i);
 }
 
 // Test that we can refer multiple time to the same recover instruction, as well
 // as chaining recover instructions.
 
 function alignedAlloc($size, $alignment) {
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -63,17 +63,17 @@ CompileRuntime::addressOfLastCachedNativ
 {
     return &runtime()->nativeIterCache.last;
 }
 
 #ifdef JS_GC_ZEAL
 const void *
 CompileRuntime::addressOfGCZeal()
 {
-    return &runtime()->gc.zealMode;
+    return runtime()->gc.addressOfZealMode();
 }
 #endif
 
 const void *
 CompileRuntime::addressOfInterrupt()
 {
     return &runtime()->interrupt;
 }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4338,17 +4338,17 @@ class MSub : public MBinaryArithInstruct
 
     bool fallible() const;
     void computeRange(TempAllocator &alloc);
     bool truncate(TruncateKind kind);
     TruncateKind operandTruncateKind(size_t index) const;
 
     bool writeRecoverData(CompactBufferWriter &writer) const;
     bool canRecoverOnBailout() const {
-        return specialization_ != MIRType_None;
+        return specialization_ < MIRType_Object;
     }
 };
 
 class MMul : public MBinaryArithInstruction
 {
   public:
     enum Mode {
         Normal,
@@ -4435,16 +4435,21 @@ class MMul : public MBinaryArithInstruct
 
     bool isFloat32Commutative() const { return true; }
 
     void computeRange(TempAllocator &alloc);
     bool truncate(TruncateKind kind);
     TruncateKind operandTruncateKind(size_t index) const;
 
     Mode mode() const { return mode_; }
+
+    bool writeRecoverData(CompactBufferWriter &writer) const;
+    bool canRecoverOnBailout() const {
+        return specialization_ < MIRType_Object;
+    }
 };
 
 class MDiv : public MBinaryArithInstruction
 {
     bool canBeNegativeZero_;
     bool canBeNegativeOverflow_;
     bool canBeDivideByZero_;
     bool canBeNegativeDividend_;
@@ -4531,16 +4536,21 @@ class MDiv : public MBinaryArithInstruct
 
     bool isFloat32Commutative() const { return true; }
 
     void computeRange(TempAllocator &alloc);
     bool fallible() const;
     bool truncate(TruncateKind kind);
     void collectRangeInfoPreTrunc();
     TruncateKind operandTruncateKind(size_t index) const;
+
+    bool writeRecoverData(CompactBufferWriter &writer) const;
+    bool canRecoverOnBailout() const {
+        return specialization_ < MIRType_Object;
+    }
 };
 
 class MMod : public MBinaryArithInstruction
 {
     bool unsigned_;
     bool canBeNegativeDividend_;
 
     MMod(MDefinition *left, MDefinition *right, MIRType type)
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -359,16 +359,81 @@ RSub::recover(JSContext *cx, SnapshotIte
     // rounded to a Float32.
     if (isFloatOperation_ && !RoundFloat32(cx, result, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
 }
 
+bool
+MMul::writeRecoverData(CompactBufferWriter &writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_Mul));
+    writer.writeByte(specialization_ == MIRType_Float32);
+    return true;
+}
+
+RMul::RMul(CompactBufferReader &reader)
+{
+    isFloatOperation_ = reader.readByte();
+}
+
+bool
+RMul::recover(JSContext *cx, SnapshotIterator &iter) const
+{
+    RootedValue lhs(cx, iter.read());
+    RootedValue rhs(cx, iter.read());
+    RootedValue result(cx);
+
+    if (!js::MulValues(cx, &lhs, &rhs, &result))
+        return false;
+
+    // MIRType_Float32 is a specialization embedding the fact that the result is
+    // rounded to a Float32.
+    if (isFloatOperation_ && !RoundFloat32(cx, result, &result))
+        return false;
+
+    iter.storeInstructionResult(result);
+    return true;
+}
+
+bool
+MDiv::writeRecoverData(CompactBufferWriter &writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_Div));
+    writer.writeByte(specialization_ == MIRType_Float32);
+    return true;
+}
+
+RDiv::RDiv(CompactBufferReader &reader)
+{
+    isFloatOperation_ = reader.readByte();
+}
+
+bool
+RDiv::recover(JSContext *cx, SnapshotIterator &iter) const
+{
+    RootedValue lhs(cx, iter.read());
+    RootedValue rhs(cx, iter.read());
+    RootedValue result(cx);
+
+    if (!js::DivValues(cx, &lhs, &rhs, &result))
+        return false;
+
+    // MIRType_Float32 is a specialization embedding the fact that the result is
+    // rounded to a Float32.
+    if (isFloatOperation_ && !RoundFloat32(cx, result, &result))
+        return false;
+
+    iter.storeInstructionResult(result);
+    return true;
+}
 
 bool
 MMod::writeRecoverData(CompactBufferWriter &writer) const
 {
 	MOZ_ASSERT(canRecoverOnBailout());
 	writer.writeUnsigned(uint32_t(RInstruction::Recover_Mod));
 	return true;
 }
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -21,16 +21,18 @@ namespace jit {
     _(BitNot)                                   \
     _(BitOr)                                    \
     _(BitXor)                                   \
     _(Lsh)                                      \
     _(Rsh)                                      \
     _(Ursh)                                     \
     _(Add)                                      \
     _(Sub)                                      \
+    _(Mul)                                      \
+    _(Div)                                      \
     _(Mod)                                      \
     _(NewObject)                                \
     _(NewDerivedTypedObject)
 
 class RResumePoint;
 class SnapshotIterator;
 
 class RInstruction
@@ -205,16 +207,46 @@ class RMod MOZ_FINAL : public RInstructi
 
     virtual uint32_t numOperands() const {
         return 2;
     }
 
     bool recover(JSContext *cx, SnapshotIterator &iter) const;
 };
 
+class RMul MOZ_FINAL : public RInstruction
+{
+  private:
+    bool isFloatOperation_;
+
+  public:
+    RINSTRUCTION_HEADER_(Mul)
+
+    virtual uint32_t numOperands() const {
+        return 2;
+    }
+
+    bool recover(JSContext *cx, SnapshotIterator &iter) const;
+};
+
+class RDiv MOZ_FINAL : public RInstruction
+{
+  private:
+    bool isFloatOperation_;
+
+  public:
+    RINSTRUCTION_HEADER_(Div)
+
+    virtual uint32_t numOperands() const {
+        return 2;
+    }
+
+    bool recover(JSContext *cx, SnapshotIterator &iter) const;
+};
+
 class RNewObject MOZ_FINAL : public RInstruction
 {
   private:
     bool templateObjectIsClassPrototype_;
 
   public:
     RINSTRUCTION_HEADER_(NewObject)
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1610,30 +1610,23 @@ JS::RemoveScriptRootRT(JSRuntime *rt, JS
 {
     RemoveRoot(rt, (void *)rp);
     *rp = nullptr;
 }
 
 JS_PUBLIC_API(bool)
 JS_AddExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
 {
-    AssertHeapIsIdle(rt);
-    return !!rt->gc.blackRootTracers.append(Callback<JSTraceDataOp>(traceOp, data));
+    return rt->gc.addBlackRootsTracer(traceOp, data);
 }
 
 JS_PUBLIC_API(void)
 JS_RemoveExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
 {
-    for (size_t i = 0; i < rt->gc.blackRootTracers.length(); i++) {
-        Callback<JSTraceDataOp> *e = &rt->gc.blackRootTracers[i];
-        if (e->op == traceOp && e->data == data) {
-            rt->gc.blackRootTracers.erase(e);
-            break;
-        }
-    }
+    return rt->gc.removeBlackRootsTracer(traceOp, data);
 }
 
 #ifdef DEBUG
 
 typedef struct JSHeapDumpNode JSHeapDumpNode;
 
 struct JSHeapDumpNode {
     void            *thing;
@@ -1894,38 +1887,30 @@ JS_MaybeGC(JSContext *cx)
 {
     MaybeGC(cx);
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCCallback(JSRuntime *rt, JSGCCallback cb, void *data)
 {
     AssertHeapIsIdle(rt);
-    rt->gc.gcCallback = cb;
-    rt->gc.gcCallbackData = data;
+    rt->gc.setGCCallback(cb, data);
 }
 
 JS_PUBLIC_API(bool)
 JS_AddFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb, void *data)
 {
     AssertHeapIsIdle(rt);
-    return rt->gc.finalizeCallbacks.append(Callback<JSFinalizeCallback>(cb, data));
+    return rt->gc.addFinalizeCallback(cb, data);
 }
 
 JS_PUBLIC_API(void)
 JS_RemoveFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb)
 {
-    for (Callback<JSFinalizeCallback> *p = rt->gc.finalizeCallbacks.begin();
-         p < rt->gc.finalizeCallbacks.end(); p++)
-    {
-        if (p->op == cb) {
-            rt->gc.finalizeCallbacks.erase(p);
-            break;
-        }
-    }
+    rt->gc.removeFinalizeCallback(cb);
 }
 
 JS_PUBLIC_API(bool)
 JS_IsAboutToBeFinalized(JS::Heap<JSObject *> *objp)
 {
     return IsObjectAboutToBeFinalized(objp->unsafeGet());
 }
 
@@ -1940,17 +1925,17 @@ JS_SetGCParameter(JSRuntime *rt, JSGCPar
 {
     switch (key) {
       case JSGC_MAX_BYTES: {
         JS_ASSERT(value >= rt->gc.bytes);
         rt->gc.maxBytes = value;
         break;
       }
       case JSGC_MAX_MALLOC_BYTES:
-        rt->setGCMaxMallocBytes(value);
+        rt->gc.setMaxMallocBytes(value);
         break;
       case JSGC_SLICE_TIME_BUDGET:
         rt->gc.sliceBudget = SliceBudget::TimeBudget(value);
         break;
       case JSGC_MARK_STACK_LIMIT:
         js::SetMarkStackLimit(rt, value);
         break;
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
@@ -6215,23 +6200,23 @@ JS_AbortIfWrongThread(JSRuntime *rt)
     if (!js::TlsPerThreadData.get()->associatedWith(rt))
         MOZ_CRASH();
 }
 
 #ifdef JS_GC_ZEAL
 JS_PUBLIC_API(void)
 JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency)
 {
-    SetGCZeal(cx->runtime(), zeal, frequency);
+    cx->runtime()->gc.setZeal(zeal, frequency);
 }
 
 JS_PUBLIC_API(void)
 JS_ScheduleGC(JSContext *cx, uint32_t count)
 {
-    cx->runtime()->gc.nextScheduled = count;
+    cx->runtime()->gc.setNextScheduled(count);
 }
 #endif
 
 JS_PUBLIC_API(void)
 JS_SetParallelParsingEnabled(JSRuntime *rt, bool enabled)
 {
 #ifdef JS_ION
     rt->setParallelParsingEnabled(enabled);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3004,17 +3004,16 @@ class MutablePropertyDescriptorOperation
 
 } /* namespace JS */
 
 namespace js {
 
 template <>
 struct GCMethods<JSPropertyDescriptor> {
     static JSPropertyDescriptor initial() { return JSPropertyDescriptor(); }
-    static ThingRootKind kind() { return THING_ROOT_PROPERTY_DESCRIPTOR; }
     static bool poisoned(const JSPropertyDescriptor &desc) {
         return (desc.obj && JS::IsPoisonedPtr(desc.obj)) ||
                (desc.attrs & JSPROP_GETTER && desc.getter && JS::IsPoisonedPtr(desc.getter)) ||
                (desc.attrs & JSPROP_SETTER && desc.setter && JS::IsPoisonedPtr(desc.setter)) ||
                (desc.value.isGCThing() && JS::IsPoisonedPtr(desc.value.toGCThing()));
     }
 };
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -58,18 +58,17 @@ JS_FRIEND_API(SourceHook *)
 js::ForgetSourceHook(JSRuntime *rt)
 {
     return rt->sourceHook.forget();
 }
 
 JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
 {
-    rt->gc.grayRootTracer.op = traceOp;
-    rt->gc.grayRootTracer.data = data;
+    rt->gc.setGrayRootsTracer(traceOp, data);
 }
 
 JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt)
 {
     JS_ASSERT(rt->hasContexts());
     return rt->commonNames->anonymous;
 }
@@ -859,19 +858,17 @@ JS_FRIEND_API(bool)
 js::IsContextRunningJS(JSContext *cx)
 {
     return cx->currentlyRunning();
 }
 
 JS_FRIEND_API(JS::GCSliceCallback)
 JS::SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback)
 {
-    JS::GCSliceCallback old = rt->gc.sliceCallback;
-    rt->gc.sliceCallback = callback;
-    return old;
+    return rt->gc.setSliceCallback(callback);
 }
 
 JS_FRIEND_API(bool)
 JS::WasIncrementalGC(JSRuntime *rt)
 {
     return rt->gc.isIncremental;
 }
 
@@ -1021,17 +1018,17 @@ JS_FRIEND_API(void)
 JS::IncrementalValueBarrier(const Value &v)
 {
     js::HeapValue::writeBarrierPre(v);
 }
 
 JS_FRIEND_API(void)
 JS::PokeGC(JSRuntime *rt)
 {
-    rt->gc.poke = true;
+    rt->gc.poke();
 }
 
 JS_FRIEND_API(JSCompartment *)
 js::GetAnyCompartmentInZone(JS::Zone *zone)
 {
     CompartmentsInZoneIter comp(zone);
     JS_ASSERT(!comp.done());
     return comp.get();
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1090,59 +1090,50 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     markingValidator(nullptr),
 #endif
     interFrameGC(0),
     sliceBudget(SliceBudget::Unlimited),
     incrementalEnabled(true),
     generationalDisabled(0),
     manipulatingDeadZones(false),
     objectsMarkedInDeadZones(0),
-    poke(false),
+    poked(false),
     heapState(Idle),
 #ifdef JSGC_GENERATIONAL
     nursery(rt),
     storeBuffer(rt, nursery),
 #endif
 #ifdef JS_GC_ZEAL
     zealMode(0),
     zealFrequency(0),
     nextScheduled(0),
     deterministicOnly(false),
     incrementalLimit(0),
 #endif
     validate(true),
     fullCompartmentChecks(false),
-    gcCallback(nullptr),
-    sliceCallback(nullptr),
     mallocBytes(0),
     mallocGCTriggered(false),
-    scriptAndCountsVector(nullptr),
 #ifdef DEBUG
     inUnsafeRegion(0),
 #endif
     alwaysPreserveCode(false),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     lock(nullptr),
     lockOwner(nullptr),
     helperState(rt)
 {
 }
 
 #ifdef JS_GC_ZEAL
 
-extern void
-js::SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency)
-{
-    rt->gc.setGCZeal(zeal, frequency);
-}
-
 void
-GCRuntime::setGCZeal(uint8_t zeal, uint32_t frequency)
+GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
 {
     if (verifyPreData)
         VerifyBarriers(rt, PreBarrierVerifier);
     if (verifyPostData)
         VerifyBarriers(rt, PostBarrierVerifier);
 
 #ifdef JSGC_GENERATIONAL
     if (zealMode == ZealGenerationalGCValue) {
@@ -1155,18 +1146,24 @@ GCRuntime::setGCZeal(uint8_t zeal, uint3
 #endif
 
     bool schedule = zeal >= js::gc::ZealAllocValue;
     zealMode = zeal;
     zealFrequency = frequency;
     nextScheduled = schedule ? frequency : 0;
 }
 
+void
+GCRuntime::setNextScheduled(uint32_t count)
+{
+    nextScheduled = count;
+}
+
 bool
-GCRuntime::initGCZeal()
+GCRuntime::initZeal()
 {
     const char *env = getenv("JS_GC_ZEAL");
     if (!env)
         return true;
 
     int zeal = -1;
     int frequency = JS_DEFAULT_ZEAL_FREQ;
     if (strcmp(env, "help") != 0) {
@@ -1192,17 +1189,17 @@ GCRuntime::initGCZeal()
                 "  9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
                 " 10: Incremental GC in multiple slices\n"
                 " 11: Verify post write barriers between instructions\n"
                 " 12: Verify post write barriers between paints\n"
                 " 13: Purge analysis state every F allocations (default: 100)\n");
         return false;
     }
 
-    setGCZeal(zeal, frequency);
+    setZeal(zeal, frequency);
     return true;
 }
 
 #endif
 
 /* Lifetime for type sets attached to scripts containing observed types. */
 static const int64_t JIT_SCRIPT_RELEASE_TYPES_INTERVAL = 60 * 1000 * 1000;
 
@@ -1224,32 +1221,32 @@ GCRuntime::init(uint32_t maxbytes)
     if (!helperState.init())
         return false;
 
     /*
      * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
      * for default backward API compatibility.
      */
     maxBytes = maxbytes;
-    rt->setGCMaxMallocBytes(maxbytes);
+    setMaxMallocBytes(maxbytes);
 
 #ifndef JS_MORE_DETERMINISTIC
     jitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
 #endif
 
 #ifdef JSGC_GENERATIONAL
     if (!nursery.init())
         return false;
 
     if (!storeBuffer.enable())
         return false;
 #endif
 
 #ifdef JS_GC_ZEAL
-    if (!initGCZeal())
+    if (!initZeal())
         return false;
 #endif
 
     if (!marker.init(mode))
         return false;
 
     return true;
 }
@@ -1324,16 +1321,73 @@ js::gc::FinishPersistentRootedChains(JSR
     rt->stringPersistentRooteds.clear();
     rt->valuePersistentRooteds.clear();
 }
 
 template <typename T> struct BarrierOwner {};
 template <typename T> struct BarrierOwner<T *> { typedef T result; };
 template <> struct BarrierOwner<Value> { typedef HeapValue result; };
 
+bool
+GCRuntime::addBlackRootsTracer(JSTraceDataOp traceOp, void *data)
+{
+    AssertHeapIsIdle(rt);
+    return !!blackRootTracers.append(Callback<JSTraceDataOp>(traceOp, data));
+}
+
+void
+GCRuntime::removeBlackRootsTracer(JSTraceDataOp traceOp, void *data)
+{
+    // Can be called from finalizers
+    for (size_t i = 0; i < blackRootTracers.length(); i++) {
+        Callback<JSTraceDataOp> *e = &blackRootTracers[i];
+        if (e->op == traceOp && e->data == data) {
+            blackRootTracers.erase(e);
+        }
+    }
+}
+
+void
+GCRuntime::setGrayRootsTracer(JSTraceDataOp traceOp, void *data)
+{
+    AssertHeapIsIdle(rt);
+    grayRootTracer.op = traceOp;
+    grayRootTracer.data = data;
+}
+
+void
+GCRuntime::setGCCallback(JSGCCallback callback, void *data)
+{
+    gcCallback.op = callback;
+    gcCallback.data = data;
+}
+
+bool
+GCRuntime::addFinalizeCallback(JSFinalizeCallback callback, void *data)
+{
+    return finalizeCallbacks.append(Callback<JSFinalizeCallback>(callback, data));
+}
+
+void
+GCRuntime::removeFinalizeCallback(JSFinalizeCallback callback)
+{
+    for (Callback<JSFinalizeCallback> *p = finalizeCallbacks.begin();
+         p < finalizeCallbacks.end(); p++) {
+        if (p->op == callback) {
+            finalizeCallbacks.erase(p);
+            break;
+        }
+    }
+}
+
+JS::GCSliceCallback
+GCRuntime::setSliceCallback(JS::GCSliceCallback callback) {
+    return stats.setSliceCallback(callback);
+}
+
 template <typename T>
 bool
 GCRuntime::addRoot(T *rp, const char *name, JSGCRootType rootType)
 {
     /*
      * Sometimes Firefox will hold weak references to objects and then convert
      * them to strong references by calling AddRoot (e.g., via PreserveWrapper,
      * or ModifyBusyCount in workers). We need a read barrier to cover these
@@ -1344,17 +1398,17 @@ GCRuntime::addRoot(T *rp, const char *na
 
     return rt->gc.rootsHash.put((void *)rp, RootInfo(name, rootType));
 }
 
 void
 GCRuntime::removeRoot(void *rp)
 {
     rootsHash.remove(rp);
-    poke = true;
+    poke();
 }
 
 template <typename T>
 static bool
 AddRoot(JSRuntime *rt, T *rp, const char *name, JSGCRootType rootType)
 {
     return rt->gc.addRoot(rp, name, rootType);
 }
@@ -1418,19 +1472,52 @@ js::RemoveRawValueRoot(JSContext *cx, Va
 }
 
 void
 js::RemoveRoot(JSRuntime *rt, void *rp)
 {
     rt->gc.removeRoot(rp);
 }
 
-typedef RootedValueMap::Range RootRange;
-typedef RootedValueMap::Entry RootEntry;
-typedef RootedValueMap::Enum RootEnum;
+void
+GCRuntime::setMaxMallocBytes(size_t value)
+{
+    /*
+     * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
+     * mean that value.
+     */
+    maxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
+    resetMallocBytes();
+    for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
+        zone->setGCMaxMallocBytes(value);
+}
+
+void
+GCRuntime::resetMallocBytes()
+{
+    mallocBytes = ptrdiff_t(maxMallocBytes);
+    mallocGCTriggered = false;
+}
+
+void
+GCRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
+{
+    mallocBytes -= ptrdiff_t(nbytes);
+    if (MOZ_UNLIKELY(isTooMuchMalloc()))
+        onTooMuchMalloc();
+    else if (zone)
+        zone->updateMallocCounter(nbytes);
+}
+
+void
+GCRuntime::onTooMuchMalloc()
+{
+    if (!mallocGCTriggered)
+        mallocGCTriggered = triggerGC(JS::gcreason::TOO_MUCH_MALLOC);
+}
 
 static size_t
 ComputeTriggerBytes(Zone *zone, size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
 {
     size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, zone->runtimeFromMainThread()->gc.allocationThreshold);
     double trigger = double(base) * zone->gcHeapGrowthFactor;
     return size_t(Min(double(maxBytes), trigger));
 }
@@ -4349,26 +4436,26 @@ AutoGCSession::~AutoGCSession()
 #ifndef JS_MORE_DETERMINISTIC
     gc->nextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
 #endif
 
     gc->chunkAllocationSinceLastGC = false;
 
 #ifdef JS_GC_ZEAL
     /* Keeping these around after a GC is dangerous. */
-    gc->selectedForMarking.clearAndFree();
+    gc->clearSelectedForMarking();
 #endif
 
     /* Clear gcMallocBytes for all compartments */
     for (ZonesIter zone(gc->rt, WithAtoms); !zone.done(); zone.next()) {
         zone->resetGCMallocBytes();
         zone->unscheduleGC();
     }
 
-    gc->rt->resetGCMallocBytes();
+    gc->resetMallocBytes();
 }
 
 AutoCopyFreeListToArenas::AutoCopyFreeListToArenas(JSRuntime *rt, ZoneSelector selector)
   : runtime(rt),
     selector(selector)
 {
     for (ZonesIter zone(rt, selector); !zone.done(); zone.next())
         zone->allocator.arenas.copyFreeListsToArenas();
@@ -4691,17 +4778,17 @@ GCRuntime::budgetIncrementalGC(int64_t *
 
     if (mode != JSGC_MODE_INCREMENTAL) {
         resetIncrementalGC("GC mode change");
         *budget = SliceBudget::Unlimited;
         stats.nonincremental("GC mode");
         return;
     }
 
-    if (rt->isTooMuchMalloc()) {
+    if (isTooMuchMalloc()) {
         *budget = SliceBudget::Unlimited;
         stats.nonincremental("malloc bytes trigger");
     }
 
     bool reset = false;
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         if (zone->gcBytes >= zone->gcTriggerBytes) {
             *budget = SliceBudget::Unlimited;
@@ -4890,31 +4977,31 @@ GCRuntime::collect(bool incremental, int
         gcstats::AutoGCSlice agc(stats, collectedCount, zoneCount, compartmentCount, reason);
 
         /*
          * Let the API user decide to defer a GC if it wants to (unless this
          * is the last context). Invoke the callback regardless.
          */
         if (incrementalState == NO_INCREMENTAL) {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_BEGIN);
-            if (gcCallback)
-                gcCallback(rt, JSGC_BEGIN, gcCallbackData);
+            if (gcCallback.op)
+                gcCallback.op(rt, JSGC_BEGIN, gcCallback.data);
         }
 
-        poke = false;
+        poked = false;
         bool wasReset = gcCycle(incremental, budget, gckind, reason);
 
         if (incrementalState == NO_INCREMENTAL) {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END);
-            if (gcCallback)
-                gcCallback(rt, JSGC_END, gcCallbackData);
+            if (gcCallback.op)
+                gcCallback.op(rt, JSGC_END, gcCallback.data);
         }
 
         /* Need to re-schedule all zones for GC. */
-        if (poke && shouldCleanUpEverything)
+        if (poked && shouldCleanUpEverything)
             JS::PrepareForFullGC(rt);
 
         /*
          * This code makes an extra effort to collect compartments that we
          * thought were dead at the start of the GC. See the large comment in
          * beginMarkPhase.
          */
         bool repeatForDeadZone = false;
@@ -4930,17 +5017,17 @@ GCRuntime::collect(bool incremental, int
         }
 
         /*
          * If we reset an existing GC, we need to start a new one. Also, we
          * repeat GCs that happen during shutdown (the gcShouldCleanUpEverything
          * case) until we can be sure that no additional garbage is created
          * (which typically happens if roots are dropped during finalizers).
          */
-        repeat = (poke && shouldCleanUpEverything) || wasReset || repeatForDeadZone;
+        repeat = (poked && shouldCleanUpEverything) || wasReset || repeatForDeadZone;
     } while (repeat);
 
     if (incrementalState == NO_INCREMENTAL) {
 #ifdef JS_THREADSAFE
         EnqueuePendingParseTasksAfterGC(rt);
 #endif
     }
 }
@@ -5290,37 +5377,50 @@ GCRuntime::runDebugGC()
     } else {
         collect(false, SliceBudget::Unlimited, GC_NORMAL, JS::gcreason::DEBUG_GC);
     }
 
 #endif
 }
 
 void
-gc::SetDeterministicGC(JSContext *cx, bool enabled)
-{
-#ifdef JS_GC_ZEAL
-    JSRuntime *rt = cx->runtime();
-    rt->gc.deterministicOnly = enabled;
-#endif
+GCRuntime::setValidate(bool enabled)
+{
+    JS_ASSERT(!isHeapMajorCollecting());
+    validate = enabled;
 }
 
 void
-gc::SetValidateGC(JSContext *cx, bool enabled)
-{
-    JSRuntime *rt = cx->runtime();
-    rt->gc.validate = enabled;
+GCRuntime::setFullCompartmentChecks(bool enabled)
+{
+    JS_ASSERT(!isHeapMajorCollecting());
+    fullCompartmentChecks = enabled;
+}
+
+#ifdef JS_GC_ZEAL
+bool
+GCRuntime::selectForMarking(JSObject *object)
+{
+    JS_ASSERT(!isHeapMajorCollecting());
+    return selectedForMarking.append(object);
 }
 
 void
-gc::SetFullCompartmentChecks(JSContext *cx, bool enabled)
-{
-    JSRuntime *rt = cx->runtime();
-    rt->gc.fullCompartmentChecks = enabled;
-}
+GCRuntime::clearSelectedForMarking()
+{
+    selectedForMarking.clearAndFree();
+}
+
+void
+GCRuntime::setDeterministic(bool enabled)
+{
+    JS_ASSERT(!isHeapMajorCollecting());
+    deterministicOnly = enabled;
+}
+#endif
 
 #ifdef DEBUG
 
 /* Should only be called manually under gdb */
 void PreventGCDuringInteractiveDebug()
 {
     TlsPerThreadData.get()->suppressGC++;
 }
@@ -5369,115 +5469,16 @@ js::ReleaseAllJITCode(FreeOp *fop)
             jit::FinishDiscardBaselineScript(fop, script);
         }
 
         zone->jitZone()->optimizedStubSpace()->free();
     }
 #endif
 }
 
-/*
- * There are three possible PCCount profiling states:
- *
- * 1. None: Neither scripts nor the runtime have count information.
- * 2. Profile: Active scripts have count information, the runtime does not.
- * 3. Query: Scripts do not have count information, the runtime does.
- *
- * When starting to profile scripts, counting begins immediately, with all JIT
- * code discarded and recompiled with counts as necessary. Active interpreter
- * frames will not begin profiling until they begin executing another script
- * (via a call or return).
- *
- * The below API functions manage transitions to new states, according
- * to the table below.
- *
- *                                  Old State
- *                          -------------------------
- * Function                 None      Profile   Query
- * --------
- * StartPCCountProfiling    Profile   Profile   Profile
- * StopPCCountProfiling     None      Query     Query
- * PurgePCCounts            None      None      None
- */
-
-static void
-ReleaseScriptCounts(FreeOp *fop)
-{
-    JSRuntime *rt = fop->runtime();
-    JS_ASSERT(rt->gc.scriptAndCountsVector);
-
-    ScriptAndCountsVector &vec = *rt->gc.scriptAndCountsVector;
-
-    for (size_t i = 0; i < vec.length(); i++)
-        vec[i].scriptCounts.destroy(fop);
-
-    fop->delete_(rt->gc.scriptAndCountsVector);
-    rt->gc.scriptAndCountsVector = nullptr;
-}
-
-JS_FRIEND_API(void)
-js::StartPCCountProfiling(JSContext *cx)
-{
-    JSRuntime *rt = cx->runtime();
-
-    if (rt->profilingScripts)
-        return;
-
-    if (rt->gc.scriptAndCountsVector)
-        ReleaseScriptCounts(rt->defaultFreeOp());
-
-    ReleaseAllJITCode(rt->defaultFreeOp());
-
-    rt->profilingScripts = true;
-}
-
-JS_FRIEND_API(void)
-js::StopPCCountProfiling(JSContext *cx)
-{
-    JSRuntime *rt = cx->runtime();
-
-    if (!rt->profilingScripts)
-        return;
-    JS_ASSERT(!rt->gc.scriptAndCountsVector);
-
-    ReleaseAllJITCode(rt->defaultFreeOp());
-
-    ScriptAndCountsVector *vec = cx->new_<ScriptAndCountsVector>(SystemAllocPolicy());
-    if (!vec)
-        return;
-
-    for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-        for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
-            JSScript *script = i.get<JSScript>();
-            if (script->hasScriptCounts() && script->types) {
-                ScriptAndCounts sac;
-                sac.script = script;
-                sac.scriptCounts.set(script->releaseScriptCounts());
-                if (!vec->append(sac))
-                    sac.scriptCounts.destroy(rt->defaultFreeOp());
-            }
-        }
-    }
-
-    rt->profilingScripts = false;
-    rt->gc.scriptAndCountsVector = vec;
-}
-
-JS_FRIEND_API(void)
-js::PurgePCCounts(JSContext *cx)
-{
-    JSRuntime *rt = cx->runtime();
-
-    if (!rt->gc.scriptAndCountsVector)
-        return;
-    JS_ASSERT(!rt->profilingScripts);
-
-    ReleaseScriptCounts(rt->defaultFreeOp());
-}
-
 void
 js::PurgeJITCaches(Zone *zone)
 {
 #ifdef JS_ION
     for (ZoneCellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
 
         /* Discard Ion caches. */
@@ -5596,74 +5597,72 @@ JS::AssertGCThingMustBeTenured(JSObject 
 {
     JS_ASSERT((!IsNurseryAllocable(obj->tenuredGetAllocKind()) || obj->getClass()->finalize) &&
               obj->isTenured());
 }
 
 JS_FRIEND_API(void)
 js::gc::AssertGCThingHasType(js::gc::Cell *cell, JSGCTraceKind kind)
 {
-#ifdef DEBUG
     JS_ASSERT(cell);
     if (IsInsideNursery(cell))
         JS_ASSERT(kind == JSTRACE_OBJECT);
     else
         JS_ASSERT(MapAllocToTraceKind(cell->tenuredGetAllocKind()) == kind);
-#endif
 }
 
 JS_FRIEND_API(size_t)
 JS::GetGCNumber()
 {
     JSRuntime *rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
     if (!rt)
         return 0;
     return rt->gc.number;
 }
 #endif
 
 #ifdef DEBUG
 JS::AutoAssertOnGC::AutoAssertOnGC()
-  : runtime(nullptr), gcNumber(0)
+  : gc(nullptr), gcNumber(0)
 {
     js::PerThreadData *data = js::TlsPerThreadData.get();
     if (data) {
         /*
          * GC's from off-thread will always assert, so off-thread is implicitly
          * AutoAssertOnGC. We still need to allow AutoAssertOnGC to be used in
          * code that works from both threads, however. We also use this to
          * annotate the off thread run loops.
          */
-        runtime = data->runtimeIfOnOwnerThread();
+        JSRuntime *runtime = data->runtimeIfOnOwnerThread();
         if (runtime) {
-            gcNumber = runtime->gc.number;
-            ++runtime->gc.inUnsafeRegion;
+            gc = &runtime->gc;
+            gcNumber = gc->number;
+            gc->enterUnsafeRegion();
         }
     }
 }
 
 JS::AutoAssertOnGC::AutoAssertOnGC(JSRuntime *rt)
-  : runtime(rt), gcNumber(rt->gc.number)
-{
-    ++rt->gc.inUnsafeRegion;
+  : gc(&rt->gc), gcNumber(rt->gc.number)
+{
+    gc->enterUnsafeRegion();
 }
 
 JS::AutoAssertOnGC::~AutoAssertOnGC()
 {
-    if (runtime) {
-        --runtime->gc.inUnsafeRegion;
-        MOZ_ASSERT(runtime->gc.inUnsafeRegion >= 0);
+    if (gc) {
+        gc->leaveUnsafeRegion();
 
         /*
          * The following backstop assertion should never fire: if we bumped the
          * gcNumber, we should have asserted because inUnsafeRegion was true.
          */
-        MOZ_ASSERT(gcNumber == runtime->gc.number, "GC ran inside an AutoAssertOnGC scope.");
+        MOZ_ASSERT(gcNumber == gc->number, "GC ran inside an AutoAssertOnGC scope.");
     }
 }
 
 /* static */ void
 JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
 {
-    if (rt->gc.inUnsafeRegion > 0)
+    if (rt->gc.isInsideUnsafeRegion())
         MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
 }
 #endif
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -895,21 +895,16 @@ extern void
 PrepareForDebugGC(JSRuntime *rt);
 
 extern void
 MinorGC(JSRuntime *rt, JS::gcreason::Reason reason);
 
 extern void
 MinorGC(JSContext *cx, JS::gcreason::Reason reason);
 
-#ifdef JS_GC_ZEAL
-extern void
-SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency);
-#endif
-
 /* Functions for managing cross compartment gray pointers. */
 
 extern void
 DelayCrossCompartmentGrayMarking(JSObject *src);
 
 extern void
 NotifyGCNukeWrapper(JSObject *o);
 
@@ -1156,25 +1151,16 @@ namespace gc {
 
 extern void
 GCIfNeeded(JSContext *cx);
 
 /* Tries to run a GC no matter what (used for GC zeal). */
 void
 RunDebugGC(JSContext *cx);
 
-void
-SetDeterministicGC(JSContext *cx, bool enabled);
-
-void
-SetValidateGC(JSContext *cx, bool enabled);
-
-void
-SetFullCompartmentChecks(JSContext *cx, bool enabled);
-
 /* Wait for the background thread to finish sweeping if it is running. */
 void
 FinishBackgroundFinalize(JSRuntime *rt);
 
 /*
  * Merge all contents of source into target. This can only be used if source is
  * the only compartment in its zone.
  */
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -71,25 +71,25 @@ GetGCThingTraceKind(const void *thing)
     const Cell *cell = static_cast<const Cell *>(thing);
 #ifdef JSGC_GENERATIONAL
     if (IsInsideNursery(cell))
         return JSTRACE_OBJECT;
 #endif
     return MapAllocToTraceKind(cell->tenuredGetAllocKind());
 }
 
-static inline void
-GCPoke(JSRuntime *rt)
+inline void
+GCRuntime::poke()
 {
-    rt->gc.poke = true;
+    poked = true;
 
 #ifdef JS_GC_ZEAL
     /* Schedule a GC to happen "soon" after a GC poke. */
-    if (rt->gcZeal() == js::gc::ZealPokeValue)
-        rt->gc.nextScheduled = 1;
+    if (zealMode == ZealPokeValue)
+        nextScheduled = 1;
 #endif
 }
 
 class ArenaIter
 {
     ArenaHeader *aheader;
     ArenaHeader *remainingHeader;
 
@@ -484,17 +484,17 @@ CheckAllocatorState(ThreadSafeContext *c
     // For testing out of memory conditions
     if (!PossiblyFail()) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
     if (allowGC) {
 #ifdef JS_GC_ZEAL
-        if (rt->needZealousGC())
+        if (rt->gc.needZealousGC())
             js::gc::RunDebugGC(ncx);
 #endif
 
         if (rt->interrupt) {
             // Invoking the interrupt callback can fail and we can't usefully
             // handle that here. Just check in case we need to collect instead.
             js::gc::GCIfNeeded(ncx);
         }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -66,24 +66,22 @@ template <>
 struct RootKind<TaggedProto>
 {
     static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
 };
 
 template <> struct GCMethods<const TaggedProto>
 {
     static TaggedProto initial() { return TaggedProto(); }
-    static ThingRootKind kind() { return THING_ROOT_OBJECT; }
     static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); }
 };
 
 template <> struct GCMethods<TaggedProto>
 {
     static TaggedProto initial() { return TaggedProto(); }
-    static ThingRootKind kind() { return THING_ROOT_OBJECT; }
     static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); }
 };
 
 template<class Outer>
 class TaggedProtoOperations
 {
     const TaggedProto *value() const {
         return static_cast<const Outer*>(this)->extract();
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1266,28 +1266,26 @@ JSScript::ensureHasTypes(JSContext *cx)
 }
 
 namespace js {
 
 template <>
 struct GCMethods<const types::Type>
 {
     static types::Type initial() { return types::Type::UnknownType(); }
-    static ThingRootKind kind() { return THING_ROOT_TYPE; }
     static bool poisoned(const types::Type &v) {
         return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
             || (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));
     }
 };
 
 template <>
 struct GCMethods<types::Type>
 {
     static types::Type initial() { return types::Type::UnknownType(); }
-    static ThingRootKind kind() { return THING_ROOT_TYPE; }
     static bool poisoned(const types::Type &v) {
         return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
             || (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));
     }
 };
 
 } // namespace js
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5269,17 +5269,17 @@ baseops::DeleteGeneric(JSContext *cx, Ha
     if (!shape || proto != obj) {
         /*
          * If no property, or the property comes from a prototype, call the
          * class's delProperty hook, passing succeeded as the result parameter.
          */
         return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
     }
 
-    GCPoke(cx->runtime());
+    cx->runtime()->gc.poke();
 
     if (IsImplicitDenseOrTypedArrayElement(shape)) {
         if (obj->is<TypedArrayObject>()) {
             // Don't delete elements from typed arrays.
             *succeeded = false;
             return true;
         }
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1931,25 +1931,124 @@ js::IsValidBytecodeOffset(JSContext *cx,
     for (BytecodeRange r(cx, script); !r.empty(); r.popFront()) {
         size_t here = r.frontOffset();
         if (here >= offset)
             return here == offset;
     }
     return false;
 }
 
+/*
+ * There are three possible PCCount profiling states:
+ *
+ * 1. None: Neither scripts nor the runtime have count information.
+ * 2. Profile: Active scripts have count information, the runtime does not.
+ * 3. Query: Scripts do not have count information, the runtime does.
+ *
+ * When starting to profile scripts, counting begins immediately, with all JIT
+ * code discarded and recompiled with counts as necessary. Active interpreter
+ * frames will not begin profiling until they begin executing another script
+ * (via a call or return).
+ *
+ * The below API functions manage transitions to new states, according
+ * to the table below.
+ *
+ *                                  Old State
+ *                          -------------------------
+ * Function                 None      Profile   Query
+ * --------
+ * StartPCCountProfiling    Profile   Profile   Profile
+ * StopPCCountProfiling     None      Query     Query
+ * PurgePCCounts            None      None      None
+ */
+
+static void
+ReleaseScriptCounts(FreeOp *fop)
+{
+    JSRuntime *rt = fop->runtime();
+    JS_ASSERT(rt->scriptAndCountsVector);
+
+    ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
+
+    for (size_t i = 0; i < vec.length(); i++)
+        vec[i].scriptCounts.destroy(fop);
+
+    fop->delete_(rt->scriptAndCountsVector);
+    rt->scriptAndCountsVector = nullptr;
+}
+
+JS_FRIEND_API(void)
+js::StartPCCountProfiling(JSContext *cx)
+{
+    JSRuntime *rt = cx->runtime();
+
+    if (rt->profilingScripts)
+        return;
+
+    if (rt->scriptAndCountsVector)
+        ReleaseScriptCounts(rt->defaultFreeOp());
+
+    ReleaseAllJITCode(rt->defaultFreeOp());
+
+    rt->profilingScripts = true;
+}
+
+JS_FRIEND_API(void)
+js::StopPCCountProfiling(JSContext *cx)
+{
+    JSRuntime *rt = cx->runtime();
+
+    if (!rt->profilingScripts)
+        return;
+    JS_ASSERT(!rt->scriptAndCountsVector);
+
+    ReleaseAllJITCode(rt->defaultFreeOp());
+
+    ScriptAndCountsVector *vec = cx->new_<ScriptAndCountsVector>(SystemAllocPolicy());
+    if (!vec)
+        return;
+
+    for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
+        for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
+            if (script->hasScriptCounts() && script->types) {
+                ScriptAndCounts sac;
+                sac.script = script;
+                sac.scriptCounts.set(script->releaseScriptCounts());
+                if (!vec->append(sac))
+                    sac.scriptCounts.destroy(rt->defaultFreeOp());
+            }
+        }
+    }
+
+    rt->profilingScripts = false;
+    rt->scriptAndCountsVector = vec;
+}
+
+JS_FRIEND_API(void)
+js::PurgePCCounts(JSContext *cx)
+{
+    JSRuntime *rt = cx->runtime();
+
+    if (!rt->scriptAndCountsVector)
+        return;
+    JS_ASSERT(!rt->profilingScripts);
+
+    ReleaseScriptCounts(rt->defaultFreeOp());
+}
+
 JS_FRIEND_API(size_t)
 js::GetPCCountScriptCount(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime();
 
-    if (!rt->gc.scriptAndCountsVector)
+    if (!rt->scriptAndCountsVector)
         return 0;
 
-    return rt->gc.scriptAndCountsVector->length();
+    return rt->scriptAndCountsVector->length();
 }
 
 enum MaybeComma {NO_COMMA, COMMA};
 
 static void
 AppendJSONProperty(StringBuffer &buf, const char *name, MaybeComma comma = COMMA)
 {
     if (comma)
@@ -1974,22 +2073,22 @@ AppendArrayJSONProperties(JSContext *cx,
     }
 }
 
 JS_FRIEND_API(JSString *)
 js::GetPCCountScriptSummary(JSContext *cx, size_t index)
 {
     JSRuntime *rt = cx->runtime();
 
-    if (!rt->gc.scriptAndCountsVector || index >= rt->gc.scriptAndCountsVector->length()) {
+    if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
         return nullptr;
     }
 
-    const ScriptAndCounts &sac = (*rt->gc.scriptAndCountsVector)[index];
+    const ScriptAndCounts &sac = (*rt->scriptAndCountsVector)[index];
     RootedScript script(cx, sac.script);
 
     /*
      * OOM on buffer appends here will not be caught immediately, but since
      * StringBuffer uses a ContextAllocPolicy will trigger an exception on the
      * context if they occur, which we'll catch before returning.
      */
     StringBuffer buf(cx);
@@ -2234,22 +2333,22 @@ GetPCCountJSON(JSContext *cx, const Scri
     return !cx->isExceptionPending();
 }
 
 JS_FRIEND_API(JSString *)
 js::GetPCCountScriptContents(JSContext *cx, size_t index)
 {
     JSRuntime *rt = cx->runtime();
 
-    if (!rt->gc.scriptAndCountsVector || index >= rt->gc.scriptAndCountsVector->length()) {
+    if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
         return nullptr;
     }
 
-    const ScriptAndCounts &sac = (*rt->gc.scriptAndCountsVector)[index];
+    const ScriptAndCounts &sac = (*rt->scriptAndCountsVector)[index];
     JSScript *script = sac.script;
 
     StringBuffer buf(cx);
 
     if (!script->functionNonDelazifying() && !script->compileAndGo())
         return buf.finishString();
 
     {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -268,17 +268,16 @@ class Bindings
 
     static js::ThingRootKind rootKind() { return js::THING_ROOT_BINDINGS; }
     void trace(JSTracer *trc);
 };
 
 template <>
 struct GCMethods<Bindings> {
     static Bindings initial();
-    static ThingRootKind kind() { return THING_ROOT_BINDINGS; }
     static bool poisoned(const Bindings &bindings) {
         return IsPoisonedPtr(static_cast<Shape *>(bindings.callObjShape()));
     }
 };
 
 class ScriptCounts
 {
     friend class ::JSScript;
@@ -1923,16 +1922,31 @@ extern void
 UnmarkScriptData(JSRuntime *rt);
 
 extern void
 SweepScriptData(JSRuntime *rt);
 
 extern void
 FreeScriptData(JSRuntime *rt);
 
+struct ScriptAndCounts
+{
+    /* This structure is stored and marked from the JSRuntime. */
+    JSScript *script;
+    ScriptCounts scriptCounts;
+
+    PCCounts &getPCCounts(jsbytecode *pc) const {
+        return scriptCounts.pcCountsVector[script->pcToOffset(pc)];
+    }
+
+    jit::IonScriptCounts *getIonCounts() const {
+        return scriptCounts.ionCounts;
+    }
+};
+
 struct GSNCache;
 
 jssrcnote *
 GetSrcNote(GSNCache &cache, JSScript *script, jsbytecode *pc);
 
 } /* namespace js */
 
 extern jssrcnote *
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TypeTraits.h"
 
 #include <ctype.h>
 #include <string.h>
+#include <wchar.h>
 
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsnum.h"
@@ -1092,63 +1093,143 @@ struct ManualCmp {
         for (; p != extent; ++p, ++t) {
             if (*p != *t)
                 return false;
         }
         return true;
     }
 };
 
+template <typename TextChar, typename PatChar>
+static const TextChar*
+FirstCharMatcherUnrolled(const TextChar *text, uint32_t n, const PatChar pat)
+{
+    const TextChar *textend = text + n;
+    const TextChar *t = text;
+
+    switch ((textend - t) & 7) {
+        case 0: if (*t++ == pat) return t - 1;
+        case 7: if (*t++ == pat) return t - 1;
+        case 6: if (*t++ == pat) return t - 1;
+        case 5: if (*t++ == pat) return t - 1;
+        case 4: if (*t++ == pat) return t - 1;
+        case 3: if (*t++ == pat) return t - 1;
+        case 2: if (*t++ == pat) return t - 1;
+        case 1: if (*t++ == pat) return t - 1;
+    }
+    while (textend != t) {
+        if (t[0] == pat) return t;
+        if (t[1] == pat) return t + 1;
+        if (t[2] == pat) return t + 2;
+        if (t[3] == pat) return t + 3;
+        if (t[4] == pat) return t + 4;
+        if (t[5] == pat) return t + 5;
+        if (t[6] == pat) return t + 6;
+        if (t[7] == pat) return t + 7;
+        t += 8;
+    }
+    return nullptr;
+}
+
+static const char*
+FirstCharMatcher8bit(const char *text, uint32_t n, const char pat)
+{
+#if  defined(__clang__)
+    return FirstCharMatcherUnrolled<char, char>(text, n, pat);
+#else
+    return reinterpret_cast<const char *>(memchr(text, pat, n));
+#endif
+}
+
+static const jschar *
+FirstCharMatcher16bit (const jschar *text, uint32_t n, const jschar pat)
+{
+    /* Some platforms define wchar_t as signed and others not. */
+#if (WCHAR_MIN == 0 && WCHAR_MAX == UINT16_MAX) || (WCHAR_MIN == INT16_MIN && WCHAR_MAX == INT16_MAX)
+    /*
+     * Wmemchr works the best.
+     * But only possible to use this when,
+     * size of jschar = size of wchar_t.
+     */
+    const wchar_t *wtext = (const wchar_t *) text;
+    const wchar_t wpat = (const wchar_t) pat;
+    return (jschar *) (wmemchr(wtext, wpat, n));
+#elif defined(__clang__)
+    /*
+     * Performance under memchr is horrible in clang.
+     * Hence it is best to use UnrolledMatcher in this case
+     */
+    return FirstCharMatcherUnrolled<jschar, jschar>(text, n, pat);
+#else
+    /*
+     * For linux the best performance is obtained by slightly hacking memchr.
+     * memchr works only on 8bit char but jschar is 16bit. So we treat jschar
+     * in blocks of 8bit and use memchr.
+     */
+
+    const char *text8 = (const char *) text;
+    const char *pat8 = reinterpret_cast<const char *>(&pat);
+
+    JS_ASSERT(n < UINT32_MAX/2);
+    n *= 2;
+
+    uint32_t i = 0;
+    while (i < n) {
+        /* Find the first 8 bits of 16bit character in text. */
+        const char *pos8 = FirstCharMatcher8bit(text8 + i, n - i, pat8[0]);
+        if (pos8 == nullptr)
+            return nullptr;
+        i = static_cast<uint32_t>(pos8 - text8);
+
+        /* Incorrect match if it matches the last 8 bits of 16bit char. */
+        if (i % 2 != 0) {
+            i++;
+            continue;
+        }
+
+        /* Test if last 8 bits match last 8 bits of 16bit char. */
+        if (pat8[1] == text8[i + 1])
+            return (text + (i/2));
+
+        i += 2;
+    }
+    return nullptr;
+#endif
+}
+
 template <class InnerMatch, typename TextChar, typename PatChar>
 static int
-UnrolledMatch(const TextChar *text, uint32_t textLen, const PatChar *pat, uint32_t patLen)
+Matcher(const TextChar *text, uint32_t textlen, const PatChar *pat, uint32_t patlen)
 {
-    JS_ASSERT(patLen > 0);
-    JS_ASSERT(textLen > 0);
-
-    const TextChar *textend = text + textLen - (patLen - 1);
-    const PatChar p0 = *pat;
-    const PatChar *const patNext = pat + 1;
-    const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patLen);
-    uint8_t fixup;
-
-    const TextChar *t = text;
-    switch ((textend - t) & 7) {
-      case 0: if (*t++ == p0) { fixup = 8; goto match; }
-      case 7: if (*t++ == p0) { fixup = 7; goto match; }
-      case 6: if (*t++ == p0) { fixup = 6; goto match; }
-      case 5: if (*t++ == p0) { fixup = 5; goto match; }
-      case 4: if (*t++ == p0) { fixup = 4; goto match; }
-      case 3: if (*t++ == p0) { fixup = 3; goto match; }
-      case 2: if (*t++ == p0) { fixup = 2; goto match; }
-      case 1: if (*t++ == p0) { fixup = 1; goto match; }
-    }
-    while (t != textend) {
-      if (t[0] == p0) { t += 1; fixup = 8; goto match; }
-      if (t[1] == p0) { t += 2; fixup = 7; goto match; }
-      if (t[2] == p0) { t += 3; fixup = 6; goto match; }
-      if (t[3] == p0) { t += 4; fixup = 5; goto match; }
-      if (t[4] == p0) { t += 5; fixup = 4; goto match; }
-      if (t[5] == p0) { t += 6; fixup = 3; goto match; }
-      if (t[6] == p0) { t += 7; fixup = 2; goto match; }
-      if (t[7] == p0) { t += 8; fixup = 1; goto match; }
-        t += 8;
-        continue;
-        do {
-            if (*t++ == p0) {
-              match:
-                if (!InnerMatch::match(patNext, t, extent))
-                    goto failed_match;
-                return t - text - 1;
-            }
-          failed_match:;
-        } while (--fixup > 0);
-    }
-    return -1;
-}
+    const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
+
+    uint32_t i = 0;
+    uint32_t n = textlen - patlen + 1;
+    while (i < n) {
+        const TextChar *pos;
+
+        if (sizeof(TextChar) == 2 && sizeof(PatChar) == 2)
+            pos = (TextChar *) FirstCharMatcher16bit((jschar *)text + i, n - i, pat[0]);
+        else if (sizeof(TextChar) == 1 && sizeof(PatChar) == 1)
+            pos = (TextChar *) FirstCharMatcher8bit((char *) text + i, n - i, pat[0]);
+        else
+            pos = (TextChar *) FirstCharMatcherUnrolled<TextChar, PatChar>(text + i, n - i, pat[0]);
+
+        if (pos == nullptr)
+            return -1;
+
+        i = static_cast<uint32_t>(pos - text);
+        if (InnerMatch::match(pat + 1, text + i + 1, extent))
+            return i;
+
+        i += 1;
+     }
+     return -1;
+ }
+
 
 template <typename TextChar, typename PatChar>
 static MOZ_ALWAYS_INLINE int
 StringMatch(const TextChar *text, uint32_t textLen, const PatChar *pat, uint32_t patLen)
 {
     if (patLen == 0)
         return 0;
     if (textLen < patLen)
@@ -1193,20 +1274,20 @@ StringMatch(const TextChar *text, uint32
      * speed of memcmp. For small patterns, a simple loop is faster. We also can't
      * use memcmp if one of the strings is TwoByte and the other is Latin1.
      *
      * FIXME: Linux memcmp performance is sad and the manual loop is faster.
      */
     return
 #if !defined(__linux__)
         (patLen > 128 && IsSame<TextChar, PatChar>::value)
-            ? UnrolledMatch<MemCmp<TextChar, PatChar>>(text, textLen, pat, patLen)
+            ? Matcher<MemCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen)
             :
 #endif
-              UnrolledMatch<ManualCmp<TextChar, PatChar>>(text, textLen, pat, patLen);
+              Matcher<ManualCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen);
 }
 
 static int32_t
 StringMatch(JSLinearString *text, JSLinearString *pat, uint32_t start = 0)
 {
     MOZ_ASSERT(start <= text->length());
     uint32_t textLen = text->length() - start;
     uint32_t patLen = pat->length();
--- a/js/src/vm/PropDesc.h
+++ b/js/src/vm/PropDesc.h
@@ -331,17 +331,16 @@ class MutablePropDescOperations : public
 
 } /* namespace JS */
 
 namespace js {
 
 template <>
 struct GCMethods<PropDesc> {
     static PropDesc initial() { return PropDesc(); }
-    static ThingRootKind kind() { return THING_ROOT_PROP_DESC; }
     static bool poisoned(const PropDesc &desc) {
         return JS::IsPoisonedPtr(desc.descObj_) ||
                (desc.value_.isGCThing() &&
                 JS::IsPoisonedPtr(desc.value_.toGCThing())) ||
                (desc.get_.isGCThing() &&
                 JS::IsPoisonedPtr(desc.get_.toGCThing())) ||
                (desc.set_.isGCThing() &&
                 JS::IsPoisonedPtr(desc.set_.toGCThing()));
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -51,17 +51,17 @@ NewObjectCache::newObjectFromHit(JSConte
 
     // Do an end run around JSObject::type() to avoid doing AutoUnprotectCell
     // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread.
     types::TypeObject *type = templateObj->type_;
 
     if (type->shouldPreTenure())
         heap = gc::TenuredHeap;
 
-    if (cx->runtime()->upcomingZealousGC())
+    if (cx->runtime()->gc.upcomingZealousGC())
         return nullptr;
 
     // Trigger an identical allocation to the one that notified us of OOM
     // so that we trigger the right kind of GC automatically.
     if (allowGC) {
         mozilla::DebugOnly<JSObject *> obj =
             js::gc::AllocateObjectForCacheHit<allowGC>(cx, entry->kind, heap);
         JS_ASSERT(!obj);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -176,16 +176,17 @@ JSRuntime::JSRuntime(JSRuntime *parentRu
 #ifdef DEBUG
     activeContext(nullptr),
 #endif
     gc(thisFromCtor()),
     gcInitialized(false),
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     simulatorRuntime_(nullptr),
 #endif
+    scriptAndCountsVector(nullptr),
     NaNValue(DoubleNaNValue()),
     negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
     positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
     emptyString(nullptr),
     debugMode(false),
     spsProfiler(thisFromCtor()),
     profilingScripts(false),
     hadOutOfMemory(false),
@@ -690,53 +691,33 @@ JSRuntime::triggerActivityCallback(bool 
      * property and ensures that it remains true in the future.
      */
     AutoSuppressGC suppress(this);
 
     activityCallback(activityCallbackArg, active);
 }
 
 void
-JSRuntime::setGCMaxMallocBytes(size_t value)
-{
-    /*
-     * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
-     * mean that value.
-     */
-    gc.maxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
-    resetGCMallocBytes();
-    for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next())
-        zone->setGCMaxMallocBytes(value);
-}
-
-void
 JSRuntime::updateMallocCounter(size_t nbytes)
 {
     updateMallocCounter(nullptr, nbytes);
 }
 
 void
 JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
 {
-    /* We tolerate any thread races when updating gcMallocBytes. */
-    gc.mallocBytes -= ptrdiff_t(nbytes);
-    if (MOZ_UNLIKELY(gc.mallocBytes <= 0))
-        onTooMuchMalloc();
-    else if (zone)
-        zone->updateMallocCounter(nbytes);
+    gc.updateMallocCounter(zone, nbytes);
 }
 
 JS_FRIEND_API(void)
 JSRuntime::onTooMuchMalloc()
 {
     if (!CurrentThreadCanAccessRuntime(this))
         return;
-
-    if (!gc.mallocGCTriggered)
-        gc.mallocGCTriggered = TriggerGC(this, JS::gcreason::TOO_MUCH_MALLOC);
+    gc.onTooMuchMalloc();
 }
 
 JS_FRIEND_API(void *)
 JSRuntime::onOutOfMemory(void *p, size_t nbytes)
 {
     return onOutOfMemory(p, nbytes, nullptr);
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -129,16 +129,18 @@ struct ScopeCoordinateNameCache {
 
     Shape *shape;
     Map map;
 
     ScopeCoordinateNameCache() : shape(nullptr) {}
     void purge();
 };
 
+typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector;
+
 struct EvalCacheEntry
 {
     JSScript *script;
     JSScript *callerScript;
     jsbytecode *pc;
 };
 
 struct EvalCacheLookup
@@ -948,41 +950,17 @@ struct JSRuntime : public JS::shadow::Ru
         gc.marker.setGCMode(mode);
     }
 
     bool isHeapBusy() { return gc.isHeapBusy(); }
     bool isHeapMajorCollecting() { return gc.isHeapMajorCollecting(); }
     bool isHeapMinorCollecting() { return gc.isHeapMinorCollecting(); }
     bool isHeapCollecting() { return gc.isHeapCollecting(); }
 
-#ifdef JS_GC_ZEAL
-    int gcZeal() { return gc.zealMode; }
-
-    bool upcomingZealousGC() {
-        return gc.nextScheduled == 1;
-    }
-
-    bool needZealousGC() {
-        if (gc.nextScheduled > 0 && --gc.nextScheduled == 0) {
-            if (gcZeal() == js::gc::ZealAllocValue ||
-                gcZeal() == js::gc::ZealGenerationalGCValue ||
-                (gcZeal() >= js::gc::ZealIncrementalRootsThenFinish &&
-                 gcZeal() <= js::gc::ZealIncrementalMultipleSlices))
-            {
-                gc.nextScheduled = gc.zealFrequency;
-            }
-            return true;
-        }
-        return false;
-    }
-#else
-    int gcZeal() { return 0; }
-    bool upcomingZealousGC() { return false; }
-    bool needZealousGC() { return false; }
-#endif
+    int gcZeal() { return gc.zeal(); }
 
     void lockGC() {
         assertCanLock(js::GCLock);
         gc.lockGC();
     }
 
     void unlockGC() {
         gc.unlockGC();
@@ -997,16 +975,19 @@ struct JSRuntime : public JS::shadow::Ru
         needsBarrier_ = needs;
     }
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     js::jit::SimulatorRuntime *simulatorRuntime() const;
     void setSimulatorRuntime(js::jit::SimulatorRuntime *srt);
 #endif
 
+    /* Strong references on scripts held for PCCount profiling API. */
+    js::ScriptAndCountsVector *scriptAndCountsVector;
+
     /* Well-known numbers held for use by this runtime's contexts. */
     const js::Value     NaNValue;
     const js::Value     negativeInfinityValue;
     const js::Value     positiveInfinityValue;
 
     js::PropertyName    *emptyString;
 
     /* List of active contexts sharing this runtime. */
@@ -1271,40 +1252,29 @@ struct JSRuntime : public JS::shadow::Ru
 
     JSRuntime(JSRuntime *parentRuntime);
     ~JSRuntime();
 
     bool init(uint32_t maxbytes);
 
     JSRuntime *thisFromCtor() { return this; }
 
-    void setGCMaxMallocBytes(size_t value);
-
-    void resetGCMallocBytes() {
-        gc.mallocBytes = ptrdiff_t(gc.maxMallocBytes);
-        gc.mallocGCTriggered = false;
-    }
-
     /*
      * Call this after allocating memory held by GC things, to update memory
      * pressure counters or report the OOM error if necessary. If oomError and
      * cx is not null the function also reports OOM error.
      *
      * The function must be called outside the GC lock and in case of OOM error
      * the caller must ensure that no deadlock possible during OOM reporting.
      */
     void updateMallocCounter(size_t nbytes);
     void updateMallocCounter(JS::Zone *zone, size_t nbytes);
 
     void reportAllocationOverflow() { js_ReportAllocationOverflow(nullptr); }
 
-    bool isTooMuchMalloc() const {
-        return gc.mallocBytes <= 0;
-    }
-
     /*
      * The function must be called outside the GC lock.
      */
     JS_FRIEND_API(void) onTooMuchMalloc();
 
     /*
      * This should be called after system malloc/realloc returns nullptr to try
      * to recove some memory or to report an error. Failures in malloc and
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -734,27 +734,27 @@ static void RecordFrameMetrics(nsIFrame*
   // If aScrollFrame is null, we are in a document without a root scroll frame,
   // so it's a xul document. In this case, use the size of the viewport frame.
   nsIFrame* frameForCompositionBoundsCalculation = aScrollFrame ? aScrollFrame : aForFrame;
   nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame),
                            frameForCompositionBoundsCalculation->GetSize());
   metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel)
                                             * layoutToParentLayerScale);
 
-
   // For the root scroll frame of the root content document, the above calculation
   // will yield the size of the viewport frame as the composition bounds, which
   // doesn't actually correspond to what is visible when
   // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of
   // the prescontext that the viewport frame is reflowed into. In that case if our
   // document has a widget then the widget's bounds will correspond to what is
   // visible. If we don't have a widget the root view's bounds correspond to what
   // would be visible because they don't get modified by setCSSViewport.
-  bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument()
-                                      && aScrollFrame == presShell->GetRootScrollFrame();
+  bool isRootScrollFrame = aScrollFrame == presShell->GetRootScrollFrame();
+  bool isRootContentDocRootScrollFrame = isRootScrollFrame
+                                      && presContext->IsRootContentDocument();
   if (isRootContentDocRootScrollFrame) {
     if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
       if (nsView* view = rootFrame->GetView()) {
         nsRect viewBoundsAppUnits = view->GetBounds() + rootFrame->GetOffsetToCrossDoc(aReferenceFrame);
         ParentLayerIntRect viewBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(viewBoundsAppUnits, auPerDevPixel)
                                                      * layoutToParentLayerScale);
 
         // On Android, we need to do things a bit differently to get things
@@ -813,16 +813,28 @@ static void RecordFrameMetrics(nsIFrame*
   // If the scroll frame's content is marked 'scrollgrab', record this
   // in the FrameMetrics so APZ knows to provide the scroll grabbing
   // behaviour.
   if (aScrollFrame && nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())) {
     metrics.mHasScrollgrab = true;
   }
 
   aRoot->SetFrameMetrics(metrics);
+
+  // Also compute and set the background color on the container.
+  // This is needed for APZ overscrolling support.
+  if (aScrollFrame) {
+    // FindBackground() does not work for a root scroll frame, need to use the
+    // root frame instead.
+    nsIFrame* backgroundFrame = isRootScrollFrame ? presShell->GetRootFrame() : aScrollFrame;
+    nsStyleContext* backgroundStyle;
+    if (nsCSSRendering::FindBackground(backgroundFrame, &backgroundStyle)) {
+      aRoot->SetBackgroundColor(backgroundStyle->StyleBackground()->mBackgroundColor);
+    }
+  }
 }
 
 nsDisplayListBuilder::~nsDisplayListBuilder() {
   NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
                "All frames should have been unmarked");
   NS_ASSERTION(mPresShellStates.Length() == 0,
                "All presshells should have been exited");
   NS_ASSERTION(!mCurrentTableItem, "No table item should be active");
--- a/layout/generic/nsVideoFrame.h
+++ b/layout/generic/nsVideoFrame.h
@@ -21,18 +21,16 @@ class Layer;
 class LayerManager;
 }
 }
 
 class nsAString;
 class nsPresContext;
 class nsDisplayItem;
 
-nsIFrame* NS_NewVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
-
 class nsVideoFrame : public nsContainerFrame, public nsIAnonymousContentCreator
 {
 public:
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
 
   nsVideoFrame(nsStyleContext* aContext);
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -213,33 +213,33 @@ fails-if(B2G&&browserIsRemote) == menclo
 fails-if(B2G&&browserIsRemote) == menclose-5-radical.html menclose-5-radical-ref.html
 fails-if(B2G&&browserIsRemote) == menclose-5-right.html menclose-5-right-ref.html
 fails-if(B2G&&browserIsRemote) == menclose-5-roundedbox.html menclose-5-roundedbox-ref.html
 fails-if(B2G&&browserIsRemote) == menclose-5-top.html menclose-5-top-ref.html
 fails-if(B2G&&browserIsRemote) == menclose-5-updiagonalarrow.html menclose-5-updiagonalarrow-ref.html
 fails-if(B2G&&browserIsRemote) == menclose-5-updiagonalstrike.html menclose-5-updiagonalstrike-ref.html
 fails-if(B2G&&browserIsRemote) == menclose-5-verticalstrike.html menclose-5-verticalstrike-ref.html
 fails-if(B2G&&browserIsRemote) == menclose-5-phasorangle.html menclose-5-phasorangle-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-actuarial.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-bottom.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-box.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-circle.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-downdiagonalstrike.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-horizontalstrike.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-left.html menclose-6-ref.html
-== menclose-6-longdiv.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-madruwb.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-radical.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-right.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-roundedbox.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-top.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-updiagonalarrow.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-updiagonalstrike.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-verticalstrike.html menclose-6-ref.html
-fails-if(B2G&&browserIsRemote) == menclose-6-phasorangle.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-actuarial.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-bottom.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-box.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-circle.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-downdiagonalstrike.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-horizontalstrike.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-left.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-longdiv.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-madruwb.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-radical.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-right.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-roundedbox.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-top.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-updiagonalarrow.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-updiagonalstrike.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-verticalstrike.html menclose-6-ref.html
+random-if(B2G&&browserIsRemote) == menclose-6-phasorangle.html menclose-6-ref.html
 == mmultiscript-align.html mmultiscript-align-ref.html
 fails-if(winWidget&&!/^Windows\x20NT\x205\.1/.test(http.oscpu)) == subscript-italic-correction.html subscript-italic-correction-ref.html # Windows versions with Cambria Math, bug 961482
 fails-if(B2G||Android||/^Windows\x20NT\x205\.1/.test(http.oscpu)||OSX==10.6) == mathvariant-1a.html mathvariant-1a-ref.html # Bug 1010679
 fails-if(B2G||Android||/^Windows\x20NT\x205\.1/.test(http.oscpu)||OSX==10.6) == mathvariant-1b.html mathvariant-1b-ref.html # Bug 1010679
 fails-if(B2G||Android||/^Windows\x20NT\x205\.1/.test(http.oscpu)||OSX==10.6) == mathvariant-1c.html mathvariant-1c-ref.html # Bug 1010679
 == mathvariant-1d.html mathvariant-1d-ref.html
 fails-if(B2G||Android||/^Windows\x20NT\x205\.1/.test(http.oscpu)||OSX) == mathvariant-2.html mathvariant-2-ref.html # Bugs 1010678, 1010679
 == mathvariant-3.html mathvariant-3-ref.html
--- a/media/webrtc/moz.build
+++ b/media/webrtc/moz.build
@@ -57,16 +57,17 @@ if CONFIG['MOZ_WEBRTC_SIGNALING']:
         'signaling/src/sipcc/core/gsm/lsm.c',        # Because of name clash in the logTag variable
         'signaling/src/sipcc/core/sdp/sdp_base64.c', # Because of name clash with the macro PADDING
     ]
     # These files cannot be built in unified mode because they force NSPR logging.
     signaling_non_unified_sources_2 = [
         'signaling/src/callcontrol/CallControlManagerImpl.cpp',
         'signaling/src/common/browser_logging/CSFLog.cpp',
         'signaling/src/media-conduit/AudioConduit.cpp',
+        'signaling/src/media-conduit/CodecStatistics.cpp',
         'signaling/src/media-conduit/VideoConduit.cpp',
         'signaling/src/media/CSFAudioControlWrapper.cpp',
         'signaling/src/media/CSFVideoControlWrapper.cpp',
         'signaling/src/media/VcmSIPCCBinding.cpp',
         'signaling/src/mediapipeline/MediaPipeline.cpp',
         'signaling/src/mediapipeline/MediaPipelineFilter.cpp',
         'signaling/src/mediapipeline/SrtpFlow.cpp',
         'signaling/src/peerconnection/MediaStreamList.cpp',
--- a/media/webrtc/signaling/signaling.gyp
+++ b/media/webrtc/signaling/signaling.gyp
@@ -79,16 +79,21 @@
       # SOURCES
       #
       'sources': [
         # Media Conduit
         './src/media-conduit/AudioConduit.h',
         './src/media-conduit/AudioConduit.cpp',
         './src/media-conduit/VideoConduit.h',
         './src/media-conduit/VideoConduit.cpp',
+        './src/media-conduit/CodecStatistics.h',
+        './src/media-conduit/CodecStatistics.cpp',
+        './src/media-conduit/RunningStat.h',
+        './src/media-conduit/GmpVideoCodec.cpp',
+        './src/media-conduit/WebrtcGmpVideoCodec.cpp',
         # Common
         './src/common/CommonTypes.h',
         './src/common/csf_common.h',
         './src/common/NullDeleter.h',
         './src/common/Wrapper.h',
         './src/common/NullTransport.h',
         './src/common/YuvStamper.cpp',
         # Browser Logging
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
@@ -173,16 +173,32 @@ public:
   virtual ~WebrtcAudioConduit();
 
   MediaConduitErrorCode Init(WebrtcAudioConduit *other);
 
   int GetChannel() { return mChannel; }
   webrtc::VoiceEngine* GetVoiceEngine() { return mVoiceEngine; }
   bool GetLocalSSRC(unsigned int* ssrc);
   bool GetRemoteSSRC(unsigned int* ssrc);
+  bool GetVideoEncoderStats(double* framerateMean,
+                            double* framerateStdDev,
+                            double* bitrateMean,
+                            double* bitrateStdDev,
+                            uint32_t* droppedFrames)
+  {
+    return false;
+  }
+  bool GetVideoDecoderStats(double* framerateMean,
+                            double* framerateStdDev,
+                            double* bitrateMean,
+                            double* bitrateStdDev,
+                            uint32_t* discardedPackets)
+  {
+    return false;
+  }
   bool GetAVStats(int32_t* jitterBufferDelayMs,
                   int32_t* playoutBufferDelayMs,
                   int32_t* avSyncOffsetMs);
   bool GetRTPStats(unsigned int* jitterMs, unsigned int* cumulativeLost);
   bool GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp,
                              uint32_t* jitterMs,
                              uint32_t* packetsReceived,
                              uint64_t* bytesReceived,
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/CodecStatistics.cpp
@@ -0,0 +1,174 @@
+/* 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 "CodecStatistics.h"
+
+#include "CSFLog.h"
+#include "mozilla/Telemetry.h"
+
+using namespace mozilla;
+using namespace webrtc;
+
+// use the same tag as VideoConduit
+static const char* logTag ="WebrtcVideoSessionConduit";
+
+VideoCodecStatistics::VideoCodecStatistics(int channel,
+                                           ViECodec* codec,
+                                           bool encoder) :
+  mChannel(channel),
+  mSentRawFrames(0),
+  mPtrViECodec(codec),
+  mEncoderDroppedFrames(0),
+  mDecoderDiscardedPackets(0),
+  mEncoderMode(encoder),
+  mReceiveState(kReceiveStateInitial),
+  mRecoveredBeforeLoss(0),
+  mRecoveredLosses(0)
+{
+  MOZ_ASSERT(mPtrViECodec);
+  if (mEncoderMode) {
+    mPtrViECodec->RegisterEncoderObserver(mChannel, *this);
+  } else {
+    mPtrViECodec->RegisterDecoderObserver(mChannel, *this);
+  }
+}
+
+VideoCodecStatistics::~VideoCodecStatistics()
+{
+  if (mEncoderMode) {
+    mPtrViECodec->DeregisterEncoderObserver(mChannel);
+  } else {
+    mPtrViECodec->DeregisterDecoderObserver(mChannel);
+  }
+}
+
+void VideoCodecStatistics::OutgoingRate(const int video_channel,
+                                        const uint32_t framerate,
+                                        const uint32_t bitrate)
+{
+  unsigned int keyFrames, deltaFrames;
+  mPtrViECodec->GetSendCodecStatistics(video_channel, keyFrames, deltaFrames);
+  uint32_t dropped = mSentRawFrames - (keyFrames + deltaFrames);
+  CSFLogDebug(logTag,
+              "encoder statistics - framerate: %u, bitrate: %u, dropped frames: %u",
+              framerate, bitrate, dropped);
+  mEncoderBitRate.Push(bitrate);
+  mEncoderFps.Push(framerate);
+  mEncoderDroppedFrames += dropped;
+}
+
+void VideoCodecStatistics::IncomingCodecChanged(const int video_channel,
+                                                const VideoCodec& video_codec)
+{
+  CSFLogDebug(logTag,
+              "channel %d change codec to \"%s\" ",
+              video_channel, video_codec.plName);
+}
+
+void VideoCodecStatistics::IncomingRate(const int video_channel,
+                                        const unsigned int framerate,
+                                        const unsigned int bitrate)
+{
+  unsigned int discarded = mPtrViECodec->GetDiscardedPackets(video_channel);
+  CSFLogDebug(logTag,
+      "decoder statistics - framerate: %u, bitrate: %u, discarded packets %u",
+      framerate, bitrate, discarded);
+  mDecoderBitRate.Push(bitrate);
+  mDecoderFps.Push(framerate);
+  mDecoderDiscardedPackets += discarded;
+}
+
+void VideoCodecStatistics::ReceiveStateChange(const int aChannel,
+                                              VideoReceiveState aState)
+{
+  CSFLogDebug(logTag,"New state for %d: %d (was %d)", aChannel, aState, mReceiveState);
+#ifdef MOZILLA_INTERNAL_API
+  if (mFirstDecodeTime.IsNull()) {
+    mFirstDecodeTime = TimeStamp::Now();
+  }
+  /*
+   * Invalid transitions:
+   * WaitingKey -> PreemptiveNACK
+   * DecodingWithErrors -> PreemptiveNACK
+   */
+
+  switch (mReceiveState) {
+    case kReceiveStateNormal:
+    case kReceiveStateInitial:
+      // in a normal state
+      if (aState != kReceiveStateNormal && aState != kReceiveStateInitial) {
+        // no longer in a normal state
+        if (aState != kReceiveStatePreemptiveNACK) {
+          mReceiveFailureTime = TimeStamp::Now();
+        }
+      } // else Normal<->Initial transition
+      break;
+    default:
+      // not in a normal state
+      if (aState == kReceiveStateNormal || aState == kReceiveStateInitial) {
+
+        if (mReceiveState == kReceiveStatePreemptiveNACK) {
+          mRecoveredBeforeLoss++;
+          CSFLogError(logTag, "Video error avoided by NACK recovery");
+        } else if (!mReceiveFailureTime.IsNull()) { // safety
+          TimeDuration timeDelta = TimeStamp::Now() - mReceiveFailureTime;
+          CSFLogError(logTag, "Video error duration: %u ms",
+                      static_cast<uint32_t>(timeDelta.ToMilliseconds()));
+          Telemetry::Accumulate(Telemetry::WEBRTC_VIDEO_ERROR_RECOVERY_MS,
+                                static_cast<uint32_t>(timeDelta.ToMilliseconds()));
+
+          mRecoveredLosses++; // to calculate losses per minute
+          mTotalLossTime += timeDelta;  // To calculate % time in recovery
+        }
+      } // else non-Normal to different non-normal transition
+      break;
+  }
+
+#endif
+
+  mReceiveState = aState;
+}
+
+void VideoCodecStatistics::EndOfCallStats()
+{
+#ifdef MOZILLA_INTERNAL_API
+  if (!mFirstDecodeTime.IsNull()) {
+    TimeDuration callDelta = TimeStamp::Now() - mFirstDecodeTime;
+    if (callDelta.ToSeconds() != 0) {
+      uint32_t recovered_per_min = mRecoveredBeforeLoss/(callDelta.ToSeconds()/60);
+      CSFLogError(logTag, "Video recovery before error per min %f", recovered_per_min);
+      Telemetry::Accumulate(Telemetry::WEBRTC_VIDEO_RECOVERY_BEFORE_ERROR_PER_MIN,
+                            recovered_per_min);
+      uint32_t err_per_min = mRecoveredLosses/(callDelta.ToSeconds()/60);
+      CSFLogError(logTag, "Video recovery after error per min %u", err_per_min);
+      Telemetry::Accumulate(Telemetry::WEBRTC_VIDEO_RECOVERY_AFTER_ERROR_PER_MIN,
+                            err_per_min);
+      float percent = (mTotalLossTime.ToSeconds()*100)/callDelta.ToSeconds();
+      CSFLogError(logTag, "Video error time percentage %f%%", percent);
+      Telemetry::Accumulate(Telemetry::WEBRTC_VIDEO_DECODE_ERROR_TIME_PERMILLE,
+                            static_cast<uint32_t>(percent*10));
+    }
+  }
+#endif
+}
+
+void VideoCodecStatistics::SentFrame()
+{
+  mSentRawFrames++;
+}
+
+void VideoCodecStatistics::Dump()
+{
+  Dump(mEncoderBitRate, "encoder bitrate");
+  Dump(mEncoderFps, "encoder fps");
+  Dump(mDecoderBitRate, "decoder bitrate");
+  Dump(mDecoderFps, "decoder fps");
+}
+
+void VideoCodecStatistics::Dump(RunningStat& s, const char *name)
+{
+  CSFLogDebug(logTag,
+              "%s, mean: %f, variance: %f, standard deviation: %f",
+              name, s.Mean(), s.Variance(), s.StandardDeviation());
+}
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/CodecStatistics.h
@@ -0,0 +1,108 @@
+/* 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 CODEC_STATISTICS_H_
+#define CODEC_STATISTICS_H_
+#include <math.h>
+
+#include "nsTArray.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/TimeStamp.h"
+#include "webrtc/common_types.h"
+#include "webrtc/video_engine/include/vie_codec.h"
+#include "MediaEngineWrapper.h"
+#include "RunningStat.h"
+
+namespace mozilla {
+
+// Statistics-gathering observer for Video Encoder and Decoder
+
+class VideoCodecStatistics : public webrtc::ViEEncoderObserver
+                           , public webrtc::ViEDecoderObserver
+{
+public:
+  VideoCodecStatistics(int channel, webrtc::ViECodec* vieCodec, bool encoder);
+  ~VideoCodecStatistics();
+
+  void SentFrame();
+  virtual void OutgoingRate(const int video_channel,
+    const unsigned int framerate, const unsigned int bitrate) MOZ_OVERRIDE;
+
+  virtual void IncomingCodecChanged(const int video_channel,
+    const webrtc::VideoCodec& video_codec) MOZ_OVERRIDE;
+
+  virtual void IncomingRate(const int video_channel,
+                            const unsigned int framerate,
+                            const unsigned int bitrate) MOZ_OVERRIDE;
+
+  void ReceiveStateChange(const int video_channel, webrtc::VideoReceiveState state) MOZ_OVERRIDE;
+
+  void EndOfCallStats();
+
+  virtual void RequestNewKeyFrame(const int video_channel) MOZ_OVERRIDE {};
+
+  virtual void SuspendChange(int video_channel, bool is_suspended) MOZ_OVERRIDE {};
+  virtual void DecoderTiming(int decode_ms,
+                             int max_decode_ms,
+                             int current_delay_ms,
+                             int target_delay_ms,
+                             int jitter_buffer_ms,
+                             int min_playout_delay_ms,
+                             int render_delay_ms) MOZ_OVERRIDE {}
+
+  bool GetEncoderStats(double* framerateMean,
+                       double* framerateStdDev,
+                       double* bitrateMean,
+                       double* bitrateStdDev,
+                       uint32_t* droppedFrames)
+  {
+    *framerateMean   = mEncoderFps.Mean();
+    *framerateStdDev = mEncoderFps.StandardDeviation();
+    *bitrateMean     = mEncoderBitRate.Mean();
+    *bitrateStdDev   = mEncoderBitRate.StandardDeviation();
+    *droppedFrames   = mEncoderDroppedFrames;
+    return true;
+  }
+
+  bool GetDecoderStats(double* framerateMean,
+                       double* framerateStdDev,
+                       double* bitrateMean,
+                       double* bitrateStdDev,
+                       uint32_t* discardedPackets)
+  {
+    *framerateMean    = mDecoderFps.Mean();
+    *framerateStdDev  = mDecoderFps.StandardDeviation();
+    *bitrateMean      = mDecoderBitRate.Mean();
+    *bitrateStdDev    = mDecoderBitRate.StandardDeviation();
+    *discardedPackets = mDecoderDiscardedPackets;
+    return true;
+  }
+
+  void Dump();
+private:
+  void Dump(RunningStat& s, const char *name);
+
+  int mChannel;
+  uint32_t mSentRawFrames;
+  ScopedCustomReleasePtr<webrtc::ViECodec> mPtrViECodec; // back-pointer
+
+  RunningStat mEncoderBitRate;
+  RunningStat mEncoderFps;
+  uint32_t mEncoderDroppedFrames;
+  RunningStat mDecoderBitRate;
+  RunningStat mDecoderFps;
+  uint32_t mDecoderDiscardedPackets;
+  const bool mEncoderMode;
+
+  webrtc::VideoReceiveState mReceiveState;
+#ifdef MOZILLA_INTERNAL_API
+  TimeStamp mFirstDecodeTime;
+  TimeStamp mReceiveFailureTime;
+  TimeDuration mTotalLossTime;
+#endif
+  uint32_t mRecoveredBeforeLoss;
+  uint32_t mRecoveredLosses;
+};
+}
+
+#endif //CODEC_STATISTICS_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebrtcGmpVideoCodec.h"
+#include "GmpVideoCodec.h"
+
+namespace mozilla {
+
+VideoEncoder* GmpVideoCodec::CreateEncoder() {
+  return static_cast<VideoEncoder*>(new WebrtcGmpVideoEncoder());
+}
+
+VideoDecoder* GmpVideoCodec::CreateDecoder() {
+  return static_cast<VideoDecoder*>(new WebrtcGmpVideoDecoder());
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h
@@ -0,0 +1,19 @@
+/* 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 GMPVIDEOCODEC_H_
+#define GMPVIDEOCODEC_H_
+
+#include "MediaConduitInterface.h"
+
+namespace mozilla {
+class GmpVideoCodec {
+ public:
+  static VideoEncoder* CreateEncoder();
+  static VideoDecoder* CreateDecoder();
+};
+
+}
+
+#endif
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -159,16 +159,26 @@ public:
   virtual MediaConduitErrorCode AttachTransport(RefPtr<TransportInterface> aTransport) = 0;
 
   virtual bool GetLocalSSRC(unsigned int* ssrc) = 0;
   virtual bool GetRemoteSSRC(unsigned int* ssrc) = 0;
 
   /**
    * Functions returning stats needed by w3c stats model.
    */
+  virtual bool GetVideoEncoderStats(double* framerateMean,
+                                    double* framerateStdDev,
+                                    double* bitrateMean,
+                                    double* bitrateStdDev,
+                                    uint32_t* droppedFrames) = 0;
+  virtual bool GetVideoDecoderStats(double* framerateMean,
+                                    double* framerateStdDev,
+                                    double* bitrateMean,
+                                    double* bitrateStdDev,
+                                    uint32_t* discardedPackets) = 0;
   virtual bool GetAVStats(int32_t* jitterBufferDelayMs,
                           int32_t* playoutBufferDelayMs,
                           int32_t* avSyncOffsetMs) = 0;
   virtual bool GetRTPStats(unsigned int* jitterMs,
                            unsigned int* cumulativeLost) = 0;
   virtual bool GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp,
                                      uint32_t* jitterMs,
                                      uint32_t* packetsReceived,
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/RunningStat.h
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* Adapted from "Accurately computing running variance - John D. Cook"
+   http://www.johndcook.com/standard_deviation.html */
+
+#ifndef RUNNING_STAT_H_
+#define RUNNING_STAT_H_
+#include <math.h>
+
+namespace mozilla {
+
+class RunningStat
+{
+public:
+  RunningStat() : mN(0) {}
+
+  void Clear()
+  {
+    mN = 0;
+  }
+
+  void Push(double x)
+  {
+    mN++;
+
+    // See Knuth TAOCP vol 2, 3rd edition, page 232
+    if (mN == 1)
+    {
+      mOldM = mNewM = x;
+      mOldS = 0.0;
+    } else {
+      mNewM = mOldM + (x - mOldM) / mN;
+      mNewS = mOldS + (x - mOldM) * (x - mNewM);
+
+      // set up for next iteration
+      mOldM = mNewM;
+      mOldS = mNewS;
+    }
+  }
+
+  int NumDataValues() const
+  {
+    return mN;
+  }
+
+  double Mean() const
+  {
+    return (mN > 0) ? mNewM : 0.0;
+  }
+
+  double Variance() const
+  {
+    return (mN > 1) ? mNewS / (mN - 1) : 0.0;
+  }
+
+  double StandardDeviation() const
+  {
+    return sqrt(Variance());
+  }
+
+private:
+  int mN;
+  double mOldM, mNewM, mOldS, mNewS;
+};
+}
+#endif //RUNNING_STAT_H_
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -75,16 +75,21 @@ WebrtcVideoConduit::~WebrtcVideoConduit(
       mPtrViECapture->DisconnectCaptureDevice(mCapId);
       mPtrViECapture->ReleaseCaptureDevice(mCapId);
       mPtrExtCapture = nullptr;
       if (mOtherDirection)
         mOtherDirection->mPtrExtCapture = nullptr;
     }
   }
 
+   if (mPtrExtCodec) {
+     mPtrExtCodec->Release();
+     mPtrExtCodec = NULL;
+   }
+
   //Deal with External Renderer
   if(mPtrViERender)
   {
     if (!mShutDown) {
       if(mRenderer) {
         mPtrViERender->StopRender(mChannel);
       }
       mPtrViERender->RemoveRenderer(mChannel);
@@ -112,16 +117,21 @@ WebrtcVideoConduit::~WebrtcVideoConduit(
   if (mOtherDirection)
   {
     // mOtherDirection owns these now!
     mOtherDirection->mOtherDirection = nullptr;
     // let other side we terminated the channel
     mOtherDirection->mShutDown = true;
     mVideoEngine = nullptr;
   } else {
+    // mVideoCodecStat has a back-ptr to mPtrViECodec that must be released first
+    if (mVideoCodecStat) {
+      mVideoCodecStat->EndOfCallStats();
+    }
+    mVideoCodecStat = nullptr;
     // We can't delete the VideoEngine until all these are released!
     // And we can't use a Scoped ptr, since the order is arbitrary
     mPtrViEBase = nullptr;
     mPtrViECapture = nullptr;
     mPtrViECodec = nullptr;
     mPtrViENetwork = nullptr;
     mPtrViERender = nullptr;
     mPtrRTP = nullptr;
@@ -130,24 +140,58 @@ WebrtcVideoConduit::~WebrtcVideoConduit(
     // only one opener can call Delete.  Have it be the last to close.
     if(mVideoEngine)
     {
       webrtc::VideoEngine::Delete(mVideoEngine);
     }
   }
 }
 
-bool WebrtcVideoConduit::GetLocalSSRC(unsigned int* ssrc) {
+bool WebrtcVideoConduit::GetLocalSSRC(unsigned int* ssrc)
+{
   return !mPtrRTP->GetLocalSSRC(mChannel, *ssrc);
 }
 
-bool WebrtcVideoConduit::GetRemoteSSRC(unsigned int* ssrc) {
+bool WebrtcVideoConduit::GetRemoteSSRC(unsigned int* ssrc)
+{
   return !mPtrRTP->GetRemoteSSRC(mChannel, *ssrc);
 }
 
+bool WebrtcVideoConduit::GetVideoEncoderStats(double* framerateMean,
+                                              double* framerateStdDev,
+                                              double* bitrateMean,
+                                              double* bitrateStdDev,
+                                              uint32_t* droppedFrames)
+{
+  if (!mEngineTransmitting) {
+    return false;
+  }
+  MOZ_ASSERT(mVideoCodecStat);
+  mVideoCodecStat->GetEncoderStats(framerateMean, framerateStdDev,
+                                   bitrateMean, bitrateStdDev,
+                                   droppedFrames);
+  return true;
+}
+
+bool WebrtcVideoConduit::GetVideoDecoderStats(double* framerateMean,
+                                              double* framerateStdDev,
+                                              double* bitrateMean,
+                                              double* bitrateStdDev,
+                                              uint32_t* discardedPackets)
+{
+  if (!mEngineReceiving) {
+    return false;
+  }
+  MOZ_ASSERT(mVideoCodecStat);
+  mVideoCodecStat->GetDecoderStats(framerateMean, framerateStdDev,
+                                   bitrateMean, bitrateStdDev,
+                                   discardedPackets);
+  return true;
+}
+
 bool WebrtcVideoConduit::GetAVStats(int32_t* jitterBufferDelayMs,
                                     int32_t* playoutBufferDelayMs,
                                     int32_t* avSyncOffsetMs) {
   return false;
 }
 
 bool WebrtcVideoConduit::GetRTPStats(unsigned int* jitterMs,
                                      unsigned int* cumulativeLost) {
@@ -302,16 +346,23 @@ MediaConduitErrorCode WebrtcVideoConduit
   }
 
   if( !(mPtrViERender = ViERender::GetInterface(mVideoEngine)))
   {
     CSFLogError(logTag, "%s Unable to get video render interface ", __FUNCTION__);
     return kMediaConduitSessionNotInited;
   }
 
+  mPtrExtCodec = webrtc::ViEExternalCodec::GetInterface(mVideoEngine);
+  if (!mPtrExtCodec) {
+    CSFLogError(logTag, "%s Unable to get external codec interface: %d ",
+                __FUNCTION__,mPtrViEBase->LastError());
+    return kMediaConduitSessionNotInited;
+  }
+
   if( !(mPtrRTP = webrtc::ViERTP_RTCP::GetInterface(mVideoEngine)))
   {
     CSFLogError(logTag, "%s Unable to get video RTCP interface ", __FUNCTION__);
     return kMediaConduitSessionNotInited;
   }
 
   if ( !(mPtrExtCodec = webrtc::ViEExternalCodec::GetInterface(mVideoEngine)))
   {
@@ -474,17 +525,17 @@ WebrtcVideoConduit::AttachTransport(mozi
 
 /**
  * Note: Setting the send-codec on the Video Engine will restart the encoder,
  * sets up new SSRC and reset RTP_RTCP module with the new codec setting.
  */
 MediaConduitErrorCode
 WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig)
 {
-  CSFLogDebug(logTag,  "%s for %s", __FUNCTION__, codecConfig->mName.c_str());
+  CSFLogDebug(logTag,  "%s for %s", __FUNCTION__, codecConfig ? codecConfig->mName.c_str() : "<null>");
   bool codecFound = false;
   MediaConduitErrorCode condError = kMediaConduitNoError;
   int error = 0; //webrtc engine errors
   webrtc::VideoCodec  video_codec;
   std::string payloadName;
 
   //validate basic params
   if((condError = ValidateCodecConfig(codecConfig,true)) != kMediaConduitNoError)
@@ -563,16 +614,21 @@ WebrtcVideoConduit::ConfigureSendMediaCo
     {
       CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__);
       return kMediaConduitInvalidSendCodec;
     }
     CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__,
                 mPtrViEBase->LastError());
     return kMediaConduitUnknownError;
   }
+
+  if (!mVideoCodecStat) {
+    mVideoCodecStat = new VideoCodecStatistics(mChannel, mPtrViECodec, true);
+  }
+
   mSendingWidth = 0;
   mSendingHeight = 0;
 
   if(codecConfig->RtcpFbIsSet(SDP_RTCP_FB_NACK_BASIC)) {
     CSFLogDebug(logTag, "Enabling NACK (send) for video stream\n");
     if (mPtrRTP->SetNACKStatus(mChannel, true) != 0)
     {
       CSFLogError(logTag,  "%s NACKStatus Failed %d ", __FUNCTION__,
@@ -731,16 +787,20 @@ WebrtcVideoConduit::ConfigureRecvMediaCo
   }//end for
 
   if(!success)
   {
     CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__);
     return kMediaConduitInvalidReceiveCodec;
   }
 
+  if (!mVideoCodecStat) {
+    mVideoCodecStat = new VideoCodecStatistics(mChannel, mPtrViECodec, false);
+  }
+
   // XXX Currently, we gather up all of the feedback types that the remote
   // party indicated it supports for all video codecs and configure the entire
   // conduit based on those capabilities. This is technically out of spec,
   // as these values should be configured on a per-codec basis. However,
   // the video engine only provides this API on a per-conduit basis, so that's
   // how we have to do it. The approach of considering the remote capablities
   // for the entire conduit to be a union of all remote codec capabilities
   // (rather than the more conservative approach of using an intersection)
@@ -1005,16 +1065,17 @@ WebrtcVideoConduit::SendVideoFrame(unsig
                                    type,
                                    (unsigned long long)capture_time) == -1)
   {
     CSFLogError(logTag,  "%s IncomingFrame Failed %d ", __FUNCTION__,
                                             mPtrViEBase->LastError());
     return kMediaConduitCaptureError;
   }
 
+  mVideoCodecStat->SentFrame();
   CSFLogDebug(logTag, "%s Inserted a frame", __FUNCTION__);
   return kMediaConduitNoError;
 }
 
 // Transport Layer Callbacks
 MediaConduitErrorCode
 WebrtcVideoConduit::ReceivedRTPPacket(const void *data, int len)
 {
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -5,16 +5,17 @@
 #ifndef VIDEO_SESSION_H_
 #define VIDEO_SESSION_H_
 
 #include "nsAutoPtr.h"
 #include "mozilla/Attributes.h"
 
 #include "MediaConduitInterface.h"
 #include "MediaEngineWrapper.h"
+#include "CodecStatistics.h"
 
 // conflicts with #include of scoped_ptr.h
 #undef FF
 // Video Engine Includes
 #include "webrtc/common_types.h"
 #ifdef FF
 #undef FF // Avoid name collision between scoped_ptr.h and nsCRTGlue.h.
 #endif
@@ -245,16 +246,26 @@ public:
   virtual ~WebrtcVideoConduit() ;
 
   MediaConduitErrorCode Init(WebrtcVideoConduit *other);
 
   int GetChannel() { return mChannel; }
   webrtc::VideoEngine* GetVideoEngine() { return mVideoEngine; }
   bool GetLocalSSRC(unsigned int* ssrc);
   bool GetRemoteSSRC(unsigned int* ssrc);
+  bool GetVideoEncoderStats(double* framerateMean,
+                            double* framerateStdDev,
+                            double* bitrateMean,
+                            double* bitrateStdDev,
+                            uint32_t* droppedFrames);
+  bool GetVideoDecoderStats(double* framerateMean,
+                            double* framerateStdDev,
+                            double* bitrateMean,
+                            double* bitrateStdDev,
+                            uint32_t* discardedPackets);
   bool GetAVStats(int32_t* jitterBufferDelayMs,
                   int32_t* playoutBufferDelayMs,
                   int32_t* avSyncOffsetMs);
   bool GetRTPStats(unsigned int* jitterMs, unsigned int* cumulativeLost);
   bool GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp,
                              uint32_t* jitterMs,
                              uint32_t* packetsReceived,
                              uint64_t* bytesReceived,
@@ -339,13 +350,16 @@ private:
   static const unsigned int sAlphaNum = 7;
   static const unsigned int sAlphaDen = 8;
   static const unsigned int sRoundingPadding = 1024;
 
   mozilla::RefPtr<WebrtcAudioConduit> mSyncedTo;
 
   nsAutoPtr<VideoCodecConfig> mExternalSendCodec;
   nsAutoPtr<VideoCodecConfig> mExternalRecvCodec;
+
+  // statistics object for video codec;
+  nsAutoPtr<VideoCodecStatistics> mVideoCodecStat;
 };
 
 } // end namespace
 
 #endif
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
@@ -0,0 +1,601 @@
+/* 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 "WebrtcGmpVideoCodec.h"
+
+#include <iostream>
+#include <vector>
+
+#include "mozilla/Scoped.h"
+#include "VideoConduit.h"
+#include "AudioConduit.h"
+#include "runnable_utils.h"
+
+#include "mozIGeckoMediaPluginService.h"
+#include "nsServiceManagerUtils.h"
+
+#include "gmp-video-host.h"
+#include "gmp-video-frame-i420.h"
+#include "gmp-video-frame-encoded.h"
+
+#include "webrtc/video_engine/include/vie_external_codec.h"
+
+namespace mozilla {
+
+// Encoder.
+WebrtcGmpVideoEncoder::WebrtcGmpVideoEncoder()
+  : mGMPThread(nullptr)
+  , mGMP(nullptr)
+  , mHost(nullptr)
+  , mCallback(nullptr)
+{}
+
+static int
+WebrtcFrameTypeToGmpFrameType(webrtc::VideoFrameType aIn,
+                              GMPVideoFrameType *aOut)
+{
+  MOZ_ASSERT(aOut);
+  switch(aIn) {
+    case webrtc::kKeyFrame:
+      *aOut = kGMPKeyFrame;
+      break;
+    case webrtc::kDeltaFrame:
+      *aOut = kGMPDeltaFrame;
+      break;
+    case webrtc::kGoldenFrame:
+      *aOut = kGMPGoldenFrame;
+      break;
+    case webrtc::kAltRefFrame:
+      *aOut = kGMPAltRefFrame;
+      break;
+    case webrtc::kSkipFrame:
+      *aOut = kGMPSkipFrame;
+      break;
+    default:
+      MOZ_CRASH();
+      return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+static int
+GmpFrameTypeToWebrtcFrameType(GMPVideoFrameType aIn,
+                              webrtc::VideoFrameType *aOut)
+{
+  MOZ_ASSERT(aOut);
+  switch(aIn) {
+    case kGMPKeyFrame:
+      *aOut = webrtc::kKeyFrame;
+      break;
+    case kGMPDeltaFrame:
+      *aOut = webrtc::kDeltaFrame;
+      break;
+    case kGMPGoldenFrame:
+      *aOut = webrtc::kGoldenFrame;
+      break;
+    case kGMPAltRefFrame:
+      *aOut = webrtc::kAltRefFrame;
+      break;
+    case kGMPSkipFrame:
+      *aOut = webrtc::kSkipFrame;
+      break;
+    default:
+      MOZ_CRASH();
+      return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+
+int32_t
+WebrtcGmpVideoEncoder::InitEncode(const webrtc::VideoCodec* aCodecSettings,
+                                  int32_t aNumberOfCores,
+                                  uint32_t aMaxPayloadSize)
+{
+  mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  MOZ_ASSERT(mMPS);
+
+  nsresult rv = mMPS->GetThread(&mGMPThread);
+  if (NS_FAILED(rv)) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  int32_t ret;
+  RUN_ON_THREAD(mGMPThread,
+                WrapRunnableRet(this,
+                                &WebrtcGmpVideoEncoder::InitEncode_g,
+                                aCodecSettings,
+                                aNumberOfCores,
+                                aMaxPayloadSize,
+                                &ret));
+
+  return ret;
+}
+
+int32_t
+WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
+                                    int32_t aNumberOfCores,
+                                    uint32_t aMaxPayloadSize)
+{
+  GMPVideoHost* host = nullptr;
+  GMPVideoEncoder* gmp = nullptr;
+
+  nsresult rv = mMPS->GetGMPVideoEncoderVP8(&host, &gmp);
+  if (NS_FAILED(rv)) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  mGMP = gmp;
+  mHost = host;
+
+  if (!gmp || !host) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  // Bug XXXXXX: transfer settings from codecSettings to codec.
+  GMPVideoCodec codec;
+  memset(&codec, 0, sizeof(codec));
+
+  codec.mWidth = aCodecSettings->width;
+  codec.mHeight = aCodecSettings->height;
+  codec.mStartBitrate = aCodecSettings->startBitrate;
+  codec.mMinBitrate = aCodecSettings->minBitrate;
+  codec.mMaxBitrate = aCodecSettings->maxBitrate;
+  codec.mMaxFramerate = aCodecSettings->maxFramerate;
+
+  GMPVideoErr err = mGMP->InitEncode(codec, this, 1, aMaxPayloadSize);
+  if (err != GMPVideoNoErr) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+
+int32_t
+WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
+                              const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                              const std::vector<webrtc::VideoFrameType>* aFrameTypes)
+{
+  int32_t ret;
+  MOZ_ASSERT(mGMPThread);
+  RUN_ON_THREAD(mGMPThread,
+                WrapRunnableRet(this,
+                                &WebrtcGmpVideoEncoder::Encode_g,
+                                &aInputImage,
+                                aCodecSpecificInfo,
+                                aFrameTypes,
+                                &ret));
+
+  return ret;
+}
+
+
+int32_t
+WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
+                                const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                                const std::vector<webrtc::VideoFrameType>* aFrameTypes)
+{
+  MOZ_ASSERT(mHost);
+  MOZ_ASSERT(mGMP);
+
+  GMPVideoFrame* ftmp = nullptr;
+  GMPVideoErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp);
+  if (err != GMPVideoNoErr) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+  GMPVideoi420Frame* frame = static_cast<GMPVideoi420Frame*>(ftmp);
+
+  err = frame->CreateFrame(aInputImage->allocated_size(webrtc::kYPlane),
+                           aInputImage->buffer(webrtc::kYPlane),
+                           aInputImage->allocated_size(webrtc::kUPlane),
+                           aInputImage->buffer(webrtc::kUPlane),
+                           aInputImage->allocated_size(webrtc::kVPlane),
+                           aInputImage->buffer(webrtc::kVPlane),
+                           aInputImage->width(),
+                           aInputImage->height(),
+                           aInputImage->stride(webrtc::kYPlane),
+                           aInputImage->stride(webrtc::kUPlane),
+                           aInputImage->stride(webrtc::kVPlane));
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+  frame->SetTimestamp(aInputImage->timestamp());
+  frame->SetRenderTime_ms(aInputImage->render_time_ms());
+
+  // Bug XXXXXX: Set codecSpecific info
+  GMPCodecSpecificInfo info;
+  memset(&info, 0, sizeof(info));
+
+  std::vector<GMPVideoFrameType> gmp_frame_types;
+  for (auto it = aFrameTypes->begin(); it != aFrameTypes->end(); ++it) {
+    GMPVideoFrameType ft;
+
+    int32_t ret = WebrtcFrameTypeToGmpFrameType(*it, &ft);
+    if (ret != WEBRTC_VIDEO_CODEC_OK) {
+      return ret;
+    }
+
+    gmp_frame_types.push_back(ft);
+  }
+
+  err = mGMP->Encode(frame, info, gmp_frame_types);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+
+
+int32_t
+WebrtcGmpVideoEncoder::RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback* aCallback)
+{
+  mCallback = aCallback;
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcGmpVideoEncoder::Release()
+{
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcGmpVideoEncoder::SetChannelParameters(uint32_t aPacketLoss, int aRTT)
+{
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcGmpVideoEncoder::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate)
+{
+  int32_t ret;
+  MOZ_ASSERT(mGMPThread);
+  RUN_ON_THREAD(mGMPThread,
+                WrapRunnableRet(this,
+                                &WebrtcGmpVideoEncoder::SetRates_g,
+                                aNewBitRate, aFrameRate,
+                                &ret));
+
+  return ret;
+}
+
+int32_t
+WebrtcGmpVideoEncoder::SetRates_g(uint32_t aNewBitRate, uint32_t aFrameRate)
+{
+  MOZ_ASSERT(mGMP);
+  GMPVideoErr err = mGMP->SetRates(aNewBitRate, aFrameRate);
+  if (err != GMPVideoNoErr) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+#define GMP_ENCODE_HAS_START_CODES 1
+#ifdef GMP_ENCODE_HAS_START_CODES
+// Temporary until inside-sandbox-code switches from start codes to the API here
+static int GetNextNALUnit(const uint8_t **aData,
+                          const uint8_t *aEnd, // at first byte past end
+                          size_t *aNalSize)
+{
+  const uint8_t *data = *aData;
+  uint8_t zeros = 0;
+
+  MOZ_ASSERT(data);
+  // Don't assume we start with a start code (paranoia)
+  while (data < aEnd) {
+    if (*data == 0) {
+      zeros++;
+      if (zeros > 3) {
+        // internal format error; keep going anyways
+        zeros = 3;
+      }
+    } else {
+      if (*data == 0x01) {
+        if (zeros >= 2) {
+          // Found start code 0x000001 or 0x00000001
+          MOZ_ASSERT(zeros == 3); // current temp code only handles 4-byte codes
+          // now find the length of the NAL
+          *aData = ++data; // start of actual data
+
+          while (data < aEnd) {
+            if (*data == 0) {
+              zeros++;
+              if (zeros > 3) {
+                // internal format error; keep going anyways
+                zeros = 3;
+              }
+            } else {
+              if (*data == 0x01) {
+                if (zeros >= 2) {
+                  // Found start code 0x000001 or 0x00000001
+                  *aNalSize = (data - *aData) - zeros;
+                  return 0;
+                }
+              }
+              zeros = 0;
+            }
+            data++;
+          }
+          // NAL ends at the end of the buffer
+          *aNalSize = (data - *aData);
+          return 0;
+        }
+      }
+      zeros = 0;
+    }
+    data++;
+  }
+  return -1; // no nals
+}
+
+#endif
+
+// GMPEncoderCallback virtual functions.
+void
+WebrtcGmpVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
+                               const GMPCodecSpecificInfo& aCodecSpecificInfo)
+{
+  if (mCallback) { // paranoia
+    webrtc::VideoFrameType ft;
+    GmpFrameTypeToWebrtcFrameType(aEncodedFrame->FrameType(), &ft);
+    GMPBufferType type = aCodecSpecificInfo.mBufferType;
+
+#ifdef GMP_ENCODE_HAS_START_CODES
+    {
+      // This code will be removed when the code inside the plugin is updated
+      // Break input encoded data into NALUs and convert to length+data format
+      const uint8_t* data = aEncodedFrame->Buffer();
+      const uint8_t* end  = data + aEncodedFrame->Size(); // at first byte past end
+      size_t nalSize = 0;
+      while (GetNextNALUnit(&data, end, &nalSize) == 0) {
+        // Assumes 4-byte start codes (0x00000001)
+        MOZ_ASSERT(data >= aEncodedFrame->Buffer() + 4);
+        uint8_t *start_code = const_cast<uint8_t*>(data-sizeof(uint32_t));
+        if (*start_code == 0x00 && *(start_code+1) == 0x00 &&
+            *(start_code+2) == 0x00 && *(start_code+3) == 0x01) {
+          *(reinterpret_cast<uint32_t*>(start_code)) = nalSize;
+        }
+        data += nalSize;
+      }
+      type = GMP_BufferLength32;
+    }
+#endif
+
+    // Right now makes one Encoded() callback per unit
+    // XXX convert to FragmentationHeader format (array of offsets and sizes plus a buffer) in
+    // combination with H264 packetization changes in webrtc/trunk code
+    uint8_t *buffer = aEncodedFrame->Buffer();
+    uint8_t *end = aEncodedFrame->Buffer() + aEncodedFrame->Size();
+    uint32_t size;
+    while (buffer < end) {
+      switch (type) {
+        case GMP_BufferSingle:
+          size = aEncodedFrame->Size();
+          break;
+        case GMP_BufferLength8:
+          size = *buffer++;
+          break;
+        case GMP_BufferLength16:
+          // presumes we can do unaligned loads
+          size = *(reinterpret_cast<uint16_t*>(buffer));
+          buffer += 2;
+          break;
+        case GMP_BufferLength24:
+          // 24-bits is a pain, since byte-order issues make things painful
+          // I'm going to define 24-bit as little-endian always; big-endian must convert
+          size = ((uint32_t) *buffer) |
+                 (((uint32_t) *(buffer+1)) << 8) |
+                 (((uint32_t) *(buffer+2)) << 16);
+          buffer += 3;
+          break;
+        case GMP_BufferLength32:
+          // presumes we can do unaligned loads
+          size = *(reinterpret_cast<uint32_t*>(buffer));
+          buffer += 4;
+          break;
+        default:
+          // really that it's not in the enum; gives more readable error
+          MOZ_ASSERT(aCodecSpecificInfo.mBufferType != GMP_BufferSingle);
+          aEncodedFrame->Destroy();
+          return;
+      }
+      webrtc::EncodedImage unit(buffer, size, size);
+      unit._frameType = ft;
+      unit._timeStamp = aEncodedFrame->TimeStamp();
+      unit._completeFrame = true;
+
+      mCallback->Encoded(unit, nullptr, nullptr);
+
+      buffer += size;
+    }
+  }
+  aEncodedFrame->Destroy();
+}
+
+
+// Decoder.
+WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder() :
+  mGMPThread(nullptr),
+  mGMP(nullptr),
+  mHost(nullptr),
+  mCallback(nullptr) {}
+
+int32_t
+WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings,
+                                  int32_t aNumberOfCores)
+{
+  mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  MOZ_ASSERT(mMPS);
+
+  if (NS_WARN_IF(NS_FAILED(mMPS->GetThread(&mGMPThread))))
+  {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  int32_t ret;
+  RUN_ON_THREAD(mGMPThread,
+                WrapRunnableRet(this,
+                                &WebrtcGmpVideoDecoder::InitDecode_g,
+                                aCodecSettings,
+                                aNumberOfCores,
+                                &ret));
+
+  return ret;
+}
+
+int32_t
+WebrtcGmpVideoDecoder::InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
+                                    int32_t aNumberOfCores)
+{
+  GMPVideoHost* host = nullptr;
+  GMPVideoDecoder* gmp = nullptr;
+
+  if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoDecoderVP8(&host, &gmp))))
+  {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  mGMP = gmp;
+  mHost = host;
+  if (!gmp || !host) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  // Bug XXXXXX: transfer settings from codecSettings to codec.
+  GMPVideoCodec codec;
+  memset(&codec, 0, sizeof(codec));
+
+  GMPVideoErr err = mGMP->InitDecode(codec, this, 1);
+  if (err != GMPVideoNoErr) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcGmpVideoDecoder::Decode(const webrtc::EncodedImage& aInputImage,
+                              bool aMissingFrames,
+                              const webrtc::RTPFragmentationHeader* aFragmentation,
+                              const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                              int64_t aRenderTimeMs)
+{
+  int32_t ret;
+  MOZ_ASSERT(mGMPThread);
+  RUN_ON_THREAD(mGMPThread,
+                WrapRunnableRet(this,
+                                &WebrtcGmpVideoDecoder::Decode_g,
+                                aInputImage,
+                                aMissingFrames,
+                                aFragmentation,
+                                aCodecSpecificInfo,
+                                aRenderTimeMs,
+                                &ret));
+
+  return ret;
+}
+
+int32_t
+WebrtcGmpVideoDecoder::Decode_g(const webrtc::EncodedImage& aInputImage,
+                                bool aMissingFrames,
+                                const webrtc::RTPFragmentationHeader* aFragmentation,
+                                const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                                int64_t aRenderTimeMs)
+{
+  MOZ_ASSERT(mHost);
+  MOZ_ASSERT(mGMP);
+
+  GMPVideoFrame* ftmp = nullptr;
+  GMPVideoErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
+  if (err != GMPVideoNoErr) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  GMPVideoEncodedFrame* frame = static_cast<GMPVideoEncodedFrame*>(ftmp);
+  err = frame->CreateEmptyFrame(aInputImage._length);
+  if (err != GMPVideoNoErr) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+  // XXX It'd be wonderful not to have to memcpy the encoded data!
+  memcpy(frame->Buffer(), aInputImage._buffer, frame->Size());
+
+  frame->SetEncodedWidth(aInputImage._encodedWidth);
+  frame->SetEncodedHeight(aInputImage._encodedHeight);
+  frame->SetTimeStamp(aInputImage._timeStamp);
+  frame->SetCompleteFrame(aInputImage._completeFrame);
+
+  GMPVideoFrameType ft;
+  int32_t ret = WebrtcFrameTypeToGmpFrameType(aInputImage._frameType, &ft);
+  if (ret != WEBRTC_VIDEO_CODEC_OK) {
+    return ret;
+  }
+
+  // Bug XXXXXX: Set codecSpecific info
+  GMPCodecSpecificInfo info;
+  memset(&info, 0, sizeof(info));
+
+  err = mGMP->Decode(frame, aMissingFrames, info, aRenderTimeMs);
+  if (err != GMPVideoNoErr) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcGmpVideoDecoder::RegisterDecodeCompleteCallback( webrtc::DecodedImageCallback* aCallback)
+{
+  mCallback = aCallback;
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+
+int32_t
+WebrtcGmpVideoDecoder::Release()
+{
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcGmpVideoDecoder::Reset()
+{
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+void
+WebrtcGmpVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
+{
+  if (mCallback) { // paranioa
+    webrtc::I420VideoFrame image;
+    int ret = image.CreateFrame(aDecodedFrame->AllocatedSize(kGMPYPlane),
+                                aDecodedFrame->Buffer(kGMPYPlane),
+                                aDecodedFrame->AllocatedSize(kGMPUPlane),
+                                aDecodedFrame->Buffer(kGMPUPlane),
+                                aDecodedFrame->AllocatedSize(kGMPVPlane),
+                                aDecodedFrame->Buffer(kGMPVPlane),
+                                aDecodedFrame->Width(),
+                                aDecodedFrame->Height(),
+                                aDecodedFrame->Stride(kGMPYPlane),
+                                aDecodedFrame->Stride(kGMPUPlane),
+                                aDecodedFrame->Stride(kGMPVPlane));
+    if (ret != 0) {
+      return;
+    }
+    image.set_timestamp(aDecodedFrame->Timestamp());
+    image.set_render_time_ms(0);
+
+    mCallback->Decoded(image);
+  }
+  aDecodedFrame->Destroy();
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
@@ -0,0 +1,148 @@
+/* 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/. */
+
+// Class templates copied from WebRTC:
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+
+#ifndef WEBRTCGMPVIDEOCODEC_H_
+#define WEBRTCGMPVIDEOCODEC_H_
+
+#include <iostream>
+#include <queue>
+
+#include "nsThreadUtils.h"
+#include "mozilla/Mutex.h"
+
+#include "mozIGeckoMediaPluginService.h"
+#include "MediaConduitInterface.h"
+#include "AudioConduit.h"
+#include "VideoConduit.h"
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+
+#include "gmp-video-host.h"
+#include "gmp-video-encode.h"
+#include "gmp-video-decode.h"
+#include "gmp-video-frame-i420.h"
+#include "gmp-video-frame-encoded.h"
+
+#include "WebrtcGmpVideoCodec.h"
+
+namespace mozilla {
+
+class WebrtcGmpVideoEncoder : public WebrtcVideoEncoder,
+                              public GMPEncoderCallback
+{
+public:
+  WebrtcGmpVideoEncoder();
+  virtual ~WebrtcGmpVideoEncoder() {}
+
+  // Implement VideoEncoder interface.
+  virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
+                             int32_t aNumberOfCores,
+                             uint32_t aMaxPayloadSize);
+
+  virtual int32_t Encode(const webrtc::I420VideoFrame& aInputImage,
+                         const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                         const std::vector<webrtc::VideoFrameType>* aFrameTypes);
+
+  virtual int32_t RegisterEncodeCompleteCallback(
+    webrtc::EncodedImageCallback* aCallback) MOZ_OVERRIDE;
+
+  virtual int32_t Release();
+
+  virtual int32_t SetChannelParameters(uint32_t aPacketLoss,
+                                       int aRTT) MOZ_OVERRIDE;
+
+  virtual int32_t SetRates(uint32_t aNewBitRate,
+                           uint32_t aFrameRate) MOZ_OVERRIDE;
+
+  // GMPEncoderCallback virtual functions.
+  virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
+                       const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE;
+
+
+private:
+  virtual int32_t InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
+                               int32_t aNumberOfCores,
+                               uint32_t aMaxPayloadSize);
+
+  virtual int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
+                           const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                           const std::vector<webrtc::VideoFrameType>* aFrameTypes);
+
+  virtual int32_t SetRates_g(uint32_t aNewBitRate,
+                             uint32_t aFrameRate);
+
+  nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
+  nsIThread* mGMPThread;
+  GMPVideoEncoder* mGMP;
+  GMPVideoHost* mHost;
+  webrtc::EncodedImageCallback* mCallback;
+};
+
+
+class WebrtcGmpVideoDecoder : public WebrtcVideoDecoder,
+                              public GMPDecoderCallback
+{
+public:
+  WebrtcGmpVideoDecoder();
+  virtual ~WebrtcGmpVideoDecoder() {}
+
+  // Implement VideoDecoder interface.
+  virtual int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
+                             int32_t aNumberOfCores);
+  virtual int32_t Decode(const webrtc::EncodedImage& aInputImage,
+                         bool aMissingFrames,
+                         const webrtc::RTPFragmentationHeader* aFragmentation,
+                         const webrtc::CodecSpecificInfo* aCodecSpecificInfo = nullptr,
+                         int64_t aRenderTimeMs = -1) MOZ_OVERRIDE;
+  virtual int32_t RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* aCallback) MOZ_OVERRIDE;
+
+  virtual int32_t Release();
+
+  virtual int32_t Reset() MOZ_OVERRIDE;
+
+  virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) MOZ_OVERRIDE;
+
+  virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) MOZ_OVERRIDE {
+    MOZ_CRASH();
+  }
+
+  virtual void ReceivedDecodedFrame(const uint64_t aPictureId) MOZ_OVERRIDE {
+    MOZ_CRASH();
+  }
+
+  virtual void InputDataExhausted() MOZ_OVERRIDE {
+    MOZ_CRASH();
+  }
+
+private:
+  virtual int32_t InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
+                               int32_t aNumberOfCores);
+
+  virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage,
+                           bool aMissingFrames,
+                           const webrtc::RTPFragmentationHeader* aFragmentation,
+                           const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                           int64_t aRenderTimeMs);
+
+  nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
+  nsIThread* mGMPThread;
+  GMPVideoDecoder* mGMP;
+  GMPVideoHost* mHost;
+  webrtc::DecodedImageCallback* mCallback;
+};
+
+}
+
+#endif
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -6,16 +6,17 @@
 
 #include "CC_Common.h"
 
 #include "CSFMediaProvider.h"
 #include "CSFAudioTermination.h"
 #include "CSFVideoTermination.h"
 #include "MediaConduitErrors.h"
 #include "MediaConduitInterface.h"
+#include "GmpVideoCodec.h"
 #include "MediaPipeline.h"
 #include "MediaPipelineFilter.h"
 #include "VcmSIPCCBinding.h"
 #include "csf_common.h"
 #include "PeerConnectionImpl.h"
 #include "PeerConnectionMedia.h"
 #include "nsThreadUtils.h"
 #include "transportflow.h"
@@ -2167,44 +2168,50 @@ short vcmTxOpen(cc_mcapid_t mcap_id,
 static int vcmEnsureExternalCodec(
     const mozilla::RefPtr<mozilla::VideoSessionConduit>& conduit,
     mozilla::VideoCodecConfig* config,
     bool send)
 {
   if (config->mName == "VP8") {
     // whitelist internal codecs; I420 will be here once we resolve bug 995884
     return 0;
-#ifdef MOZ_WEBRTC_OMX
   } else if (config->mName == "H264_P0" || config->mName == "H264_P1") {
     // Here we use "I420" to register H.264 because WebRTC.org code has a
     // whitelist of supported video codec in |webrtc::ViECodecImpl::CodecValid()|
     // and will reject registration of those not in it.
     // TODO: bug 995884 to support H.264 in WebRTC.org code.
 
     // Register H.264 codec.
     if (send) {
-      VideoEncoder* encoder = OMXVideoCodec::CreateEncoder(OMXVideoCodec::CodecType::CODEC_H264);
+	VideoEncoder* encoder = nullptr;
+#ifdef MOZ_WEBRTC_OMX
+	encoder = OMXVideoCodec::CreateEncoder(
+	    OMXVideoCodec::CodecType::CODEC_H264);
+#else
+	encoder = mozilla::GmpVideoCodec::CreateEncoder();
+#endif
       if (encoder) {
         return conduit->SetExternalSendCodec(config, encoder);
       } else {
         return kMediaConduitInvalidSendCodec;
       }
     } else {
-      VideoDecoder* decoder = OMXVideoCodec::CreateDecoder(OMXVideoCodec::CodecType::CODEC_H264);
+      VideoDecoder* decoder;
+#ifdef MOZ_WEBRTC_OMX
+      decoder = OMXVideoCodec::CreateDecoder(OMXVideoCodec::CodecType::CODEC_H264);
+#else
+      decoder = mozilla::GmpVideoCodec::CreateDecoder();
+#endif
       if (decoder) {
         return conduit->SetExternalRecvCodec(config, decoder);
       } else {
         return kMediaConduitInvalidReceiveCodec;
       }
     }
     NS_NOTREACHED("Shouldn't get here!");
-#else
-  } else if (config->mName == "I420") {
-    return 0;
-#endif
   } else {
     CSFLogError( logTag, "%s: Invalid video codec configured: %s", __FUNCTION__, config->mName.c_str());
     return send ? kMediaConduitInvalidSendCodec : kMediaConduitInvalidReceiveCodec;
   }
 
   return 0;
 }
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -2242,16 +2242,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
 
   for (size_t p = 0; p < query->pipelines.Length(); ++p) {
     const MediaPipeline& mp = *query->pipelines[p];
     bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO);
     nsString idstr = isAudio ?
         NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_");
     idstr.AppendInt(mp.trackid());
 
+    // Gather pipeline stats.
     switch (mp.direction()) {
       case MediaPipeline::TRANSMIT: {
         nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr;
         nsString remoteId;
         nsString ssrc;
         unsigned int ssrcval;
         if (mp.Conduit()->GetLocalSSRC(&ssrcval)) {
           ssrc.AppendInt(ssrcval);
@@ -2297,16 +2298,36 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
           s.mType.Construct(RTCStatsType::Outboundrtp);
           if (ssrc.Length()) {
             s.mSsrc.Construct(ssrc);
           }
           s.mRemoteId.Construct(remoteId);
           s.mIsRemote = false;
           s.mPacketsSent.Construct(mp.rtp_packets_sent());
           s.mBytesSent.Construct(mp.rtp_bytes_sent());
+
+          // Lastly, fill in video encoder stats if this is video
+          if (!isAudio) {
+            double framerateMean;
+            double framerateStdDev;
+            double bitrateMean;
+            double bitrateStdDev;
+            uint32_t droppedFrames;
+            if (mp.Conduit()->GetVideoEncoderStats(&framerateMean,
+                                                   &framerateStdDev,
+                                                   &bitrateMean,
+                                                   &bitrateStdDev,
+                                                   &droppedFrames)) {
+              s.mFramerateMean.Construct(framerateMean);
+              s.mFramerateStdDev.Construct(framerateStdDev);
+              s.mBitrateMean.Construct(bitrateMean);
+              s.mBitrateStdDev.Construct(bitrateStdDev);
+              s.mDroppedFrames.Construct(droppedFrames);
+            }
+          }
           query->report->mOutboundRTPStreamStats.Value().AppendElement(s);
         }
         break;
       }
       case MediaPipeline::RECEIVE: {
         nsString localId = NS_LITERAL_STRING("inbound_rtp_") + idstr;
         nsString remoteId;
         nsString ssrc;
@@ -2362,16 +2383,35 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
           int32_t avSyncDelta;
           if (mp.Conduit()->GetAVStats(&jitterBufferDelay,
                                        &playoutBufferDelay,
                                        &avSyncDelta)) {
             s.mMozJitterBufferDelay.Construct(jitterBufferDelay);
             s.mMozAvSyncDelay.Construct(avSyncDelta);
           }
         }
+        // Lastly, fill in video decoder stats if this is video
+        if (!isAudio) {
+          double framerateMean;
+          double framerateStdDev;
+          double bitrateMean;
+          double bitrateStdDev;
+          uint32_t discardedPackets;
+          if (mp.Conduit()->GetVideoDecoderStats(&framerateMean,
+                                                 &framerateStdDev,
+                                                 &bitrateMean,
+                                                 &bitrateStdDev,
+                                                 &discardedPackets)) {
+            s.mFramerateMean.Construct(framerateMean);
+            s.mFramerateStdDev.Construct(framerateStdDev);
+            s.mBitrateMean.Construct(bitrateMean);
+            s.mBitrateStdDev.Construct(bitrateStdDev);
+            s.mDiscardedPackets.Construct(discardedPackets);
+          }
+        }
         query->report->mInboundRTPStreamStats.Value().AppendElement(s);
         break;
       }
     }
   }
 
   // Gather stats from ICE
   for (size_t s = 0; s != query->streams.Length(); ++s) {
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
@@ -237,16 +237,18 @@ struct StreamResult {
   uint8_t candidateTypeBitpattern;
   bool streamSucceeded;
 };
 
 static void StoreLongTermICEStatisticsImpl_m(
     nsresult result,
     nsAutoPtr<RTCStatsQuery> query) {
 
+  using namespace Telemetry;
+
   if (NS_FAILED(result) ||
       !query->error.empty() ||
       !query->report->mIceCandidateStats.WasPassed()) {
     return;
   }
 
   query->report->mClosed.Construct(true);
 
@@ -341,16 +343,88 @@ static void StoreLongTermICEStatisticsIm
       Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS,
                             i->second.candidateTypeBitpattern);
     } else {
       Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE,
                             i->second.candidateTypeBitpattern);
     }
   }
 
+  // Beyond ICE, accumulate telemetry for various PER_CALL settings here.
+
+  if (query->report->mOutboundRTPStreamStats.WasPassed()) {
+    auto& array = query->report->mOutboundRTPStreamStats.Value();
+    for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
+      auto& s = array[i];
+      bool isVideo = (s.mId.Value().Find("video") != -1);
+      if (!isVideo || s.mIsRemote) {
+        continue;
+      }
+      if (s.mBitrateMean.WasPassed()) {
+        Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS,
+                   uint32_t(s.mBitrateMean.Value() / 1000));
+      }
+      if (s.mBitrateStdDev.WasPassed()) {
+        Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS,
+                   uint32_t(s.mBitrateStdDev.Value() / 1000));
+      }
+      if (s.mFramerateMean.WasPassed()) {
+        Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL,
+                   uint32_t(s.mFramerateMean.Value()));
+      }
+      if (s.mFramerateStdDev.WasPassed()) {
+        Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL,
+                   uint32_t(s.mFramerateStdDev.Value() * 10));
+      }
+      if (s.mDroppedFrames.WasPassed()) {
+        double mins = (TimeStamp::Now() - query->iceStartTime).ToSeconds() / 60;
+        if (mins > 0) {
+          Accumulate(WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM,
+                     uint32_t(double(s.mDroppedFrames.Value()) / mins));
+        }
+      }
+    }
+  }
+
+  if (query->report->mInboundRTPStreamStats.WasPassed()) {
+    auto& array = query->report->mInboundRTPStreamStats.Value();
+    for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
+      auto& s = array[i];
+      bool isVideo = (s.mId.Value().Find("video") != -1);
+      if (!isVideo || s.mIsRemote) {
+        continue;
+      }
+      if (s.mBitrateMean.WasPassed()) {
+        Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS,
+                   uint32_t(s.mBitrateMean.Value() / 1000));
+      }
+      if (s.mBitrateStdDev.WasPassed()) {
+        Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS,
+                   uint32_t(s.mBitrateStdDev.Value() / 1000));
+      }
+      if (s.mFramerateMean.WasPassed()) {
+        Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL,
+                   uint32_t(s.mFramerateMean.Value()));
+      }
+      if (s.mFramerateStdDev.WasPassed()) {
+        Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL,
+                   uint32_t(s.mFramerateStdDev.Value() * 10));
+      }
+      if (s.mDiscardedPackets.WasPassed()) {
+        double mins = (TimeStamp::Now() - query->iceStartTime).ToSeconds() / 60;
+        if (mins > 0) {
+          Accumulate(WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM,
+                     uint32_t(double(s.mDiscardedPackets.Value()) / mins));
+        }
+      }
+    }
+  }
+
+  // Finally, store the stats
+
   PeerConnectionCtx *ctx = GetPeerConnectionCtx();
   if (ctx) {
     ctx->mStatsForClosedPeerConnections.AppendElement(*query->report);
   }
 }
 
 static void GetStatsForLongTermStorage_s(
     nsAutoPtr<RTCStatsQuery> query) {
--- a/media/webrtc/signaling/test/mediaconduit_unittests.cpp
+++ b/media/webrtc/signaling/test/mediaconduit_unittests.cpp
@@ -7,24 +7,32 @@
 #include <fstream>
 #include <unistd.h>
 #include <vector>
 #include <math.h>
 
 using namespace std;
 
 #include "mozilla/Scoped.h"
+#include "mozilla/SyncRunnable.h"
 #include <MediaConduitInterface.h>
 #include "nsIEventTarget.h"
 #include "FakeMediaStreamsImpl.h"
+#include "GmpVideoCodec.h"
+#include "nsThreadUtils.h"
+#include "runnable_utils.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
 
+nsCOMPtr<nsIThread> gMainThread;
+nsCOMPtr<nsIThread> gGtestThread;
+bool gTestsComplete = false;
+
 #include "mtransport_test_utils.h"
 MtransportTestUtils *test_utils;
 
 //Video Frame Color
 const int COLOR = 0x80; //Gray
 
 //MWC RNG of George Marsaglia
 //taken from xiph.org
@@ -43,65 +51,75 @@ struct VideoTestStats
 {
  int numRawFramesInserted;
  int numFramesRenderedSuccessfully;
  int numFramesRenderedWrongly;
 };
 
 VideoTestStats vidStatsGlobal={0,0,0};
 
-
 /**
  * A Dummy Video Conduit Tester.
  * The test-case inserts a 640*480 grey imagerevery 33 milliseconds
  * to the video-conduit for encoding and transporting.
  */
 
 class VideoSendAndReceive
 {
 public:
   VideoSendAndReceive():width(640),
-                        height(480)
+                        height(480),
+			rate(30)
   {
   }
 
   ~VideoSendAndReceive()
   {
   }
 
+  void SetDimensions(int w, int h)
+  {
+    width = w;
+    height = h;
+  }
+  void SetRate(int r) {
+    rate = r;
+  }
   void Init(mozilla::RefPtr<mozilla::VideoSessionConduit> aSession)
   {
         mSession = aSession;
+        mLen = ((width * height) * 3 / 2);
+        mFrame = (uint8_t*) PR_MALLOC(mLen);
+        memset(mFrame, COLOR, mLen);
+        numFrames = 121;
   }
+
   void GenerateAndReadSamples()
   {
-
-    int len = ((width * height) * 3 / 2);
-    uint8_t* frame = (uint8_t*) PR_MALLOC(len);
-    int numFrames = 121;
-    memset(frame, COLOR, len);
-
     do
     {
-      mSession->SendVideoFrame((unsigned char*)frame,
-                                len,
+      mSession->SendVideoFrame((unsigned char*)mFrame,
+                                mLen,
                                 width,
                                 height,
                                 mozilla::kVideoI420,
                                 0);
-      PR_Sleep(PR_MillisecondsToInterval(33));
+      PR_Sleep(PR_MillisecondsToInterval(1000/rate));
       vidStatsGlobal.numRawFramesInserted++;
       numFrames--;
     } while(numFrames >= 0);
-    PR_Free(frame);
   }
 
 private:
 mozilla::RefPtr<mozilla::VideoSessionConduit> mSession;
+mozilla::ScopedDeletePtr<uint8_t> mFrame;
+int mLen;
 int width, height;
+int rate;
+int numFrames;
 };
 
 
 
 /**
  * A Dummy AudioConduit Tester
  * The test reads PCM samples of a standard test file and
  * passws to audio-conduit for encoding, RTPfication and
@@ -262,27 +280,27 @@ void AudioSendAndReceive::GenerateMusic(
     if(i%6==0)j++;
   }
   cerr << "Generating Input Music Done " << endl;
 }
 
 //Hardcoded for 16 bit samples for now
 void AudioSendAndReceive::GenerateAndReadSamples()
 {
-   int16_t audioInput[PLAYOUT_SAMPLE_LENGTH];
-   int16_t audioOutput[PLAYOUT_SAMPLE_LENGTH];
+   mozilla::ScopedDeletePtr<int16_t> audioInput(new int16_t [PLAYOUT_SAMPLE_LENGTH]);
+   mozilla::ScopedDeletePtr<int16_t> audioOutput(new int16_t [PLAYOUT_SAMPLE_LENGTH]);
    short* inbuf;
    int sampleLengthDecoded = 0;
    unsigned int SAMPLES = (PLAYOUT_SAMPLE_FREQUENCY * 10); //10 seconds
    int CHANNELS = 1; //mono audio
-   int sampleLengthInBytes = sizeof(audioInput);
+   int sampleLengthInBytes = sizeof(int16_t) * PLAYOUT_SAMPLE_LENGTH;
    //generated audio buffer
    inbuf = (short *)moz_xmalloc(sizeof(short)*SAMPLES*CHANNELS);
-   memset(audioInput,0,sampleLengthInBytes);
-   memset(audioOutput,0,sampleLengthInBytes);
+   memset(audioInput.get(),0,sampleLengthInBytes);
+   memset(audioOutput.get(),0,sampleLengthInBytes);
    MOZ_ASSERT(SAMPLES <= PLAYOUT_SAMPLE_LENGTH);
 
    FILE* inFile = fopen( iFile.c_str(), "wb+");
    if(!inFile) {
      cerr << "Input File Creation Failed " << endl;
      return;
    }
 
@@ -298,37 +316,37 @@ void AudioSendAndReceive::GenerateAndRea
    fwrite(inbuf,1,SAMPLES*sizeof(inbuf[0])*CHANNELS,inFile);
    FinishWaveHeader(inFile);
    fclose(inFile);
 
    WriteWaveHeader(PLAYOUT_SAMPLE_FREQUENCY, 1, outFile);
    unsigned int numSamplesReadFromInput = 0;
    do
    {
-    if(!memcpy(audioInput, inbuf, sampleLengthInBytes))
+    if(!memcpy(audioInput.get(), inbuf, sampleLengthInBytes))
     {
       return;
     }
 
     numSamplesReadFromInput += PLAYOUT_SAMPLE_LENGTH;
     inbuf += PLAYOUT_SAMPLE_LENGTH;
 
-    mSession->SendAudioFrame(audioInput,
+    mSession->SendAudioFrame(audioInput.get(),
                              PLAYOUT_SAMPLE_LENGTH,
                              PLAYOUT_SAMPLE_FREQUENCY,10);
 
     PR_Sleep(PR_MillisecondsToInterval(10));
-    mOtherSession->GetAudioFrame(audioOutput, PLAYOUT_SAMPLE_FREQUENCY,
+    mOtherSession->GetAudioFrame(audioOutput.get(), PLAYOUT_SAMPLE_FREQUENCY,
                                  10, sampleLengthDecoded);
     if(sampleLengthDecoded == 0)
     {
       cerr << " Zero length Sample " << endl;
     }
 
-    int wrote_  = fwrite (audioOutput, 1 , sampleLengthInBytes, outFile);
+    int wrote_  = fwrite (audioOutput.get(), 1 , sampleLengthInBytes, outFile);
     if(wrote_ != sampleLengthInBytes)
     {
       cerr << "Couldn't Write " << sampleLengthInBytes << "bytes" << endl;
       break;
     }
    }while(numSamplesReadFromInput < SAMPLES);
 
    FinishWaveHeader(outFile);
@@ -391,32 +409,32 @@ public:
       }
     }
    return 0;
  }
 
 };
 
 /**
- *  Fake Audio and Video External Transport Class
+ *  Webrtc Audio and Video External Transport Class
  *  The functions in this class will be invoked by the conduit
  *  when it has RTP/RTCP frame to transmit.
  *  For everty RTP/RTCP frame we receive, we pass it back
  *  to the conduit for eventual decoding and rendering.
  */
-class FakeMediaTransport : public mozilla::TransportInterface
+class WebrtcMediaTransport : public mozilla::TransportInterface
 {
 public:
-  FakeMediaTransport():numPkts(0),
+  WebrtcMediaTransport():numPkts(0),
                        mAudio(false),
                        mVideo(false)
   {
   }
 
-  ~FakeMediaTransport()
+  ~WebrtcMediaTransport()
   {
   }
 
   virtual nsresult SendRtpPacket(const void* data, int len)
   {
     ++numPkts;
     if(mAudio)
     {
@@ -470,41 +488,65 @@ private:
 };
 
 
 namespace {
 
 class TransportConduitTest : public ::testing::Test
 {
  public:
+
   TransportConduitTest()
   {
     //input and output file names
     iAudiofilename = "input.wav";
     oAudiofilename = "recorded.wav";
   }
 
   ~TransportConduitTest()
   {
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+                                            mozilla::WrapRunnable(
+                                                this,
+                                                &TransportConduitTest::SelfDestruct));
+  }
+
+  void SelfDestruct() {
+    mAudioSession = nullptr;
+    mAudioSession2 = nullptr;
+    mAudioTransport = nullptr;
+
+    mVideoSession = nullptr;
+    mVideoSession2 = nullptr;
+    mVideoRenderer = nullptr;
+    mVideoTransport = nullptr;
   }
 
   //1. Dump audio samples to dummy external transport
   void TestDummyAudioAndTransport()
   {
     //get pointer to AudioSessionConduit
     int err=0;
-    mAudioSession = mozilla::AudioSessionConduit::Create(nullptr);
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+                                            WrapRunnableNMRet(
+                                                &mozilla::AudioSessionConduit::Create,
+                                                nullptr,
+                                                &mAudioSession));
     if( !mAudioSession )
       ASSERT_NE(mAudioSession, (void*)nullptr);
 
-    mAudioSession2 = mozilla::AudioSessionConduit::Create(nullptr);
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+                                            WrapRunnableNMRet(
+                                                &mozilla::AudioSessionConduit::Create,
+                                                nullptr,
+                                                &mAudioSession2));
     if( !mAudioSession2 )
       ASSERT_NE(mAudioSession2, (void*)nullptr);
 
-    FakeMediaTransport* xport = new FakeMediaTransport();
+    WebrtcMediaTransport* xport = new WebrtcMediaTransport();
     ASSERT_NE(xport, (void*)nullptr);
     xport->SetAudioSession(mAudioSession, mAudioSession2);
     mAudioTransport = xport;
 
     // attach the transport to audio-conduit
     err = mAudioSession->AttachTransport(mAudioTransport);
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
     err = mAudioSession2->AttachTransport(mAudioTransport);
@@ -540,33 +582,45 @@ class TransportConduitTest : public ::te
     PR_Sleep(PR_SecondsToInterval(2));
     cerr << "   ******************************************************** " << endl;
     cerr << "    Input Audio  File                " << iAudiofilename << endl;
     cerr << "    Output Audio File                " << oAudiofilename << endl;
     cerr << "   ******************************************************** " << endl;
   }
 
   //2. Dump audio samples to dummy external transport
-  void TestDummyVideoAndTransport()
+  void TestDummyVideoAndTransport(bool send_vp8 = true, const char *source_file = nullptr)
   {
     int err = 0;
     //get pointer to VideoSessionConduit
-    mVideoSession = mozilla::VideoSessionConduit::Create(nullptr);
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+                                            WrapRunnableNMRet(
+                                                &mozilla::VideoSessionConduit::Create,
+                                                nullptr,
+                                                &mVideoSession));
     if( !mVideoSession )
       ASSERT_NE(mVideoSession, (void*)nullptr);
 
    // This session is for other one
-    mVideoSession2 = mozilla::VideoSessionConduit::Create(nullptr);
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+                                            WrapRunnableNMRet(
+                                                &mozilla::VideoSessionConduit::Create,
+                                                nullptr,
+                                                &mVideoSession2));
     if( !mVideoSession2 )
       ASSERT_NE(mVideoSession2,(void*)nullptr);
 
+    if (!send_vp8) {
+      SetGmpCodecs();
+    }
+
     mVideoRenderer = new DummyVideoTarget();
     ASSERT_NE(mVideoRenderer, (void*)nullptr);
 
-    FakeMediaTransport* xport = new FakeMediaTransport();
+    WebrtcMediaTransport* xport = new WebrtcMediaTransport();
     ASSERT_NE(xport, (void*)nullptr);
     xport->SetVideoSession(mVideoSession,mVideoSession2);
     mVideoTransport = xport;
 
     // attach the transport and renderer to video-conduit
     err = mVideoSession2->AttachRenderer(mVideoRenderer);
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
     err = mVideoSession->AttachTransport(mVideoTransport);
@@ -578,56 +632,77 @@ class TransportConduitTest : public ::te
     mozilla::VideoCodecConfig cinst1(120, "VP8", 0);
     mozilla::VideoCodecConfig cinst2(124, "I420", 0);
 
 
     std::vector<mozilla::VideoCodecConfig* > rcvCodecList;
     rcvCodecList.push_back(&cinst1);
     rcvCodecList.push_back(&cinst2);
 
-    err = mVideoSession->ConfigureSendMediaCodec(&cinst1);
+    err = mVideoSession->ConfigureSendMediaCodec(
+        send_vp8 ? &cinst1 : &cinst2);
+
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
 
+    err = mVideoSession2->ConfigureSendMediaCodec(
+        send_vp8 ? &cinst1 : &cinst2);
+
+    ASSERT_EQ(mozilla::kMediaConduitNoError, err);
     err = mVideoSession2->ConfigureRecvMediaCodecs(rcvCodecList);
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
 
     //start generating samples
     cerr << "   *************************************************" << endl;
     cerr << "    Starting the Video Sample Generation " << endl;
     cerr << "   *************************************************" << endl;
     PR_Sleep(PR_SecondsToInterval(2));
     videoTester.Init(mVideoSession);
     videoTester.GenerateAndReadSamples();
     PR_Sleep(PR_SecondsToInterval(2));
+
     cerr << "   **************************************************" << endl;
     cerr << "    Done With The Testing  " << endl;
     cerr << "    VIDEO TEST STATS  "  << endl;
     cerr << "    Num Raw Frames Inserted: "<<
                                         vidStatsGlobal.numRawFramesInserted << endl;
     cerr << "    Num Frames Successfully Rendered: "<<
                                         vidStatsGlobal.numFramesRenderedSuccessfully << endl;
     cerr << "    Num Frames Wrongly Rendered: "<<
                                         vidStatsGlobal.numFramesRenderedWrongly << endl;
 
     cerr << "    Done With The Testing  " << endl;
 
     cerr << "   **************************************************" << endl;
     ASSERT_EQ(0, vidStatsGlobal.numFramesRenderedWrongly);
-    ASSERT_EQ(vidStatsGlobal.numRawFramesInserted,
-        vidStatsGlobal.numFramesRenderedSuccessfully);
+    if (send_vp8) {
+	ASSERT_EQ(vidStatsGlobal.numRawFramesInserted,
+		  vidStatsGlobal.numFramesRenderedSuccessfully);
+    }
+    else {
+	// Allow some fudge because there seems to be some buffering.
+	// TODO(ekr@rtfm.com): Fix this.
+	ASSERT_GE(vidStatsGlobal.numRawFramesInserted,
+		  vidStatsGlobal.numFramesRenderedSuccessfully);
+	ASSERT_LE(vidStatsGlobal.numRawFramesInserted,
+		  vidStatsGlobal.numFramesRenderedSuccessfully + 2);
+    }
   }
 
  void TestVideoConduitCodecAPI()
   {
     int err = 0;
-    mozilla::RefPtr<mozilla::VideoSessionConduit> mVideoSession;
+    mozilla::RefPtr<mozilla::VideoSessionConduit> videoSession;
     //get pointer to VideoSessionConduit
-    mVideoSession = mozilla::VideoSessionConduit::Create(nullptr);
-    if( !mVideoSession )
-      ASSERT_NE(mVideoSession, (void*)nullptr);
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+                                            WrapRunnableNMRet(
+                                                &mozilla::VideoSessionConduit::Create,
+                                                nullptr,
+                                                &videoSession));
+    if( !videoSession )
+      ASSERT_NE(videoSession, (void*)nullptr);
 
     //Test Configure Recv Codec APIS
     cerr << "   *************************************************" << endl;
     cerr << "    Test Receive Codec Configuration API Now " << endl;
     cerr << "   *************************************************" << endl;
 
     std::vector<mozilla::VideoCodecConfig* > rcvCodecList;
 
@@ -635,17 +710,17 @@ class TransportConduitTest : public ::te
     cerr << "   *************************************************" << endl;
     cerr << "    1. Same Codec (VP8) Repeated Twice " << endl;
     cerr << "   *************************************************" << endl;
 
     mozilla::VideoCodecConfig cinst1(120, "VP8", 0);
     mozilla::VideoCodecConfig cinst2(120, "VP8", 0);
     rcvCodecList.push_back(&cinst1);
     rcvCodecList.push_back(&cinst2);
-    err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList);
+    err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList);
     EXPECT_NE(err,mozilla::kMediaConduitNoError);
     rcvCodecList.pop_back();
     rcvCodecList.pop_back();
 
 
     PR_Sleep(PR_SecondsToInterval(2));
     cerr << "   *************************************************" << endl;
     cerr << "    2. Codec With Invalid Payload Names " << endl;
@@ -654,63 +729,67 @@ class TransportConduitTest : public ::te
     cerr << "   Setting payload 2 with name of zero length" << endl;
 
     mozilla::VideoCodecConfig cinst3(124, "I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676", 0);
     mozilla::VideoCodecConfig cinst4(124, "", 0);
 
     rcvCodecList.push_back(&cinst3);
     rcvCodecList.push_back(&cinst4);
 
-    err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList);
+    err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList);
     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
     rcvCodecList.pop_back();
     rcvCodecList.pop_back();
 
 
     PR_Sleep(PR_SecondsToInterval(2));
     cerr << "   *************************************************" << endl;
     cerr << "    3. Null Codec Parameter  " << endl;
     cerr << "   *************************************************" << endl;
 
     rcvCodecList.push_back(0);
 
-    err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList);
+    err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList);
     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
     rcvCodecList.pop_back();
 
     cerr << "   *************************************************" << endl;
     cerr << "    Test Send Codec Configuration API Now " << endl;
     cerr << "   *************************************************" << endl;
 
     cerr << "   *************************************************" << endl;
     cerr << "    1. Same Codec (VP8) Repeated Twice " << endl;
     cerr << "   *************************************************" << endl;
 
 
-    err = mVideoSession->ConfigureSendMediaCodec(&cinst1);
+    err = videoSession->ConfigureSendMediaCodec(&cinst1);
     EXPECT_EQ(mozilla::kMediaConduitNoError, err);
-    err = mVideoSession->ConfigureSendMediaCodec(&cinst1);
+    err = videoSession->ConfigureSendMediaCodec(&cinst1);
     EXPECT_EQ(mozilla::kMediaConduitCodecInUse, err);
 
 
     cerr << "   *************************************************" << endl;
     cerr << "    2. Codec With Invalid Payload Names " << endl;
     cerr << "   *************************************************" << endl;
     cerr << "   Setting payload with name: I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676" << endl;
 
-    err = mVideoSession->ConfigureSendMediaCodec(&cinst3);
+    err = videoSession->ConfigureSendMediaCodec(&cinst3);
     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
 
     cerr << "   *************************************************" << endl;
     cerr << "    3. Null Codec Parameter  " << endl;
     cerr << "   *************************************************" << endl;
 
-    err = mVideoSession->ConfigureSendMediaCodec(nullptr);
+    err = videoSession->ConfigureSendMediaCodec(nullptr);
     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
 
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+                                            WrapRunnable(
+                                                videoSession.forget().drop(),
+                                                &mozilla::VideoSessionConduit::Release));
   }
 
   void DumpMaxFs(int orig_width, int orig_height, int max_fs,
                  int new_width, int new_height)
   {
     cerr << "Applying max_fs=" << max_fs << " to input resolution " <<
                  orig_width << "x" << orig_height << endl;
     cerr << "New resolution: " << new_width << "x" << new_height << endl;
@@ -719,17 +798,21 @@ class TransportConduitTest : public ::te
 
   // Calculate new resolution for sending video by applying max-fs constraint.
   void GetVideoResolutionWithMaxFs(int orig_width, int orig_height, int max_fs,
                                    int *new_width, int *new_height)
   {
     int err = 0;
 
     // Get pointer to VideoSessionConduit.
-    mVideoSession = mozilla::VideoSessionConduit::Create(nullptr);
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+                                            WrapRunnableNMRet(
+                                                &mozilla::VideoSessionConduit::Create,
+                                                nullptr,
+                                                &mVideoSession));
     if( !mVideoSession )
       ASSERT_NE(mVideoSession, (void*)nullptr);
 
     // Configure send codecs on the conduit.
     mozilla::VideoCodecConfig cinst1(120, "VP8", 0, max_fs, 0);
 
     err = mVideoSession->ConfigureSendMediaCodec(&cinst1);
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
@@ -856,30 +939,41 @@ class TransportConduitTest : public ::te
       if ((width & 1) || (height & 1)) {
         DumpMaxFs(orig_width, orig_height, max_fs, width, height);
         ADD_FAILURE();
       }
     }
     cerr << endl;
  }
 
-private:
+  void SetGmpCodecs() {
+    mExternalEncoder = mozilla::GmpVideoCodec::CreateEncoder();
+    mExternalDecoder = mozilla::GmpVideoCodec::CreateDecoder();
+    mozilla::VideoCodecConfig config(124, "H264", 0);
+    mVideoSession->SetExternalSendCodec(&config, mExternalEncoder);
+    mVideoSession2->SetExternalRecvCodec(&config, mExternalDecoder);
+  }
+
+ private:
   //Audio Conduit Test Objects
   mozilla::RefPtr<mozilla::AudioSessionConduit> mAudioSession;
   mozilla::RefPtr<mozilla::AudioSessionConduit> mAudioSession2;
   mozilla::RefPtr<mozilla::TransportInterface> mAudioTransport;
   AudioSendAndReceive audioTester;
 
   //Video Conduit Test Objects
   mozilla::RefPtr<mozilla::VideoSessionConduit> mVideoSession;
   mozilla::RefPtr<mozilla::VideoSessionConduit> mVideoSession2;
   mozilla::RefPtr<mozilla::VideoRenderer> mVideoRenderer;
   mozilla::RefPtr<mozilla::TransportInterface> mVideoTransport;
   VideoSendAndReceive videoTester;
 
+  mozilla::VideoEncoder* mExternalEncoder;
+  mozilla::VideoDecoder* mExternalDecoder;
+
   std::string fileToPlay;
   std::string fileToRecord;
   std::string iAudiofilename;
   std::string oAudiofilename;
 };
 
 
 // Test 1: Test Dummy External Xport
@@ -887,31 +981,83 @@ TEST_F(TransportConduitTest, TestDummyAu
   TestDummyAudioAndTransport();
 }
 
 // Test 2: Test Dummy External Xport
 TEST_F(TransportConduitTest, TestDummyVideoWithTransport) {
   TestDummyVideoAndTransport();
  }
 
+TEST_F(TransportConduitTest, TestVideoConduitExternalCodec) {
+  TestDummyVideoAndTransport(false);
+}
+
 TEST_F(TransportConduitTest, TestVideoConduitCodecAPI) {
   TestVideoConduitCodecAPI();
  }
 
 TEST_F(TransportConduitTest, TestVideoConduitMaxFs) {
   TestVideoConduitMaxFs();
  }
 
 }  // end namespace
 
+static int test_result;
+bool test_finished = false;
+
+
+
+// This exists to send as an event to trigger shutdown.
+static void tests_complete() {
+  gTestsComplete = true;
+}
+
+// The GTest thread runs this instead of the main thread so it can
+// do things like ASSERT_TRUE_WAIT which you could not do on the main thread.
+static int gtest_main(int argc, char **argv) {
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  ::testing::InitGoogleTest(&argc, argv);
+
+  int result = RUN_ALL_TESTS();
+
+  // Set the global shutdown flag and tickle the main thread
+  // The main thread did not go through Init() so calling Shutdown()
+  // on it will not work.
+  gMainThread->Dispatch(mozilla::WrapRunnableNM(tests_complete), NS_DISPATCH_SYNC);
+
+  return result;
+}
+
 int main(int argc, char **argv)
 {
   // This test can cause intermittent oranges on the builders
   CHECK_ENVIRONMENT_FLAG("MOZ_WEBRTC_MEDIACONDUIT_TESTS")
 
   test_utils = new MtransportTestUtils();
-  ::testing::InitGoogleTest(&argc, argv);
-  int rv = RUN_ALL_TESTS();
+
+  // Set the main thread global which is this thread.
+  nsIThread *thread;
+  NS_GetMainThread(&thread);
+  gMainThread = thread;
+
+  // Now create the GTest thread and run all of the tests on it
+  // When it is complete it will set gTestsComplete
+  NS_NewNamedThread("gtest_thread", &thread);
+  gGtestThread = thread;
+
+  int result;
+  gGtestThread->Dispatch(
+    mozilla::WrapRunnableNMRet(gtest_main, argc, argv, &result), NS_DISPATCH_NORMAL);
+
+  // Here we handle the event queue for dispatches to the main thread
+  // When the GTest thread is complete it will send one more dispatch
+  // with gTestsComplete == true.
+  while (!gTestsComplete && NS_ProcessNextEvent());
+
+  gGtestThread->Shutdown();
+
   delete test_utils;
-  return rv;
+  return test_result;
 }
 
 
+
--- a/media/webrtc/trunk/webrtc/common_types.h
+++ b/media/webrtc/trunk/webrtc/common_types.h
@@ -548,16 +548,26 @@ enum RawVideoType
     kVideoARGB1555 = 9,
     kVideoMJPEG    = 10,
     kVideoNV12     = 11,
     kVideoNV21     = 12,
     kVideoBGRA     = 13,
     kVideoUnknown  = 99
 };
 
+enum VideoReceiveState
+{
+  kReceiveStateInitial,            // No video decoded yet
+  kReceiveStateNormal,
+  kReceiveStatePreemptiveNACK,     // NACK sent for missing packet, no decode stall/fail yet
+  kReceiveStateWaitingKey,         // Decoding stalled, waiting for keyframe or NACK
+  kReceiveStateDecodingWithErrors, // Decoding with errors, waiting for keyframe or NACK
+  kReceiveStateNoIncoming,         // No errors, but no incoming video since last decode
+};
+
 // Video codec
 enum { kConfigParameterSize = 128};
 enum { kPayloadNameSize = 32};
 enum { kMaxSimulcastStreams = 4};
 enum { kMaxTemporalStreams = 4};
 
 enum VideoCodecComplexity
 {
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding.h
@@ -408,16 +408,29 @@ public:
     // Input:
     //              - callback      : The callback to be registered in the VCM.
     //
     // Return value     : VCM_OK,     on success.
     //                    <0,              on error.
     virtual int32_t RegisterPacketRequestCallback(
                                         VCMPacketRequestCallback* callback) = 0;
 
+    // Register a receive state change callback. This callback will be called when the
+    // module state has changed
+    //
+    // Input:
+    //      - callback      : The callback object to be used by the module when
+    //                        the receiver decode state changes.
+    //                        De-register with a NULL pointer.
+    //
+    // Return value      : VCM_OK, on success.
+    //                     < 0,         on error.
+    virtual int32_t RegisterReceiveStateCallback(
+                                  VCMReceiveStateCallback* callback) = 0;
+
     // Waits for the next frame in the jitter buffer to become complete
     // (waits no longer than maxWaitTimeMs), then passes it to the decoder for decoding.
     // Should be called as often as possible to get the most out of the decoder.
     //
     // Return value      : VCM_OK, on success.
     //                     < 0,         on error.
     virtual int32_t Decode(uint16_t maxWaitTimeMs = 200) = 0;
 
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding_defines.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/interface/video_coding_defines.h
@@ -170,16 +170,27 @@ class VCMPacketRequestCallback {
   virtual int32_t ResendPackets(const uint16_t* sequenceNumbers,
                                       uint16_t length) = 0;
 
  protected:
   virtual ~VCMPacketRequestCallback() {
   }
 };
 
+// Callback class used for telling the user about the state of the decoder & jitter buffer.
+//
+class VCMReceiveStateCallback {
+ public:
+  virtual void ReceiveStateChange(VideoReceiveState state) = 0;
+
+ protected:
+  virtual ~VCMReceiveStateCallback() {
+  }
+};
+
 // Callback used to inform the user of the the desired resolution
 // as subscribed by Media Optimization (Quality Modes)
 class VCMQMSettingsCallback {
  public:
   virtual int32_t SetVideoQMSettings(const uint32_t frameRate,
                                            const uint32_t width,
                                            const uint32_t height) = 0;
 
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc
@@ -33,16 +33,17 @@ VCMReceiver::VCMReceiver(VCMTiming* timi
       vcm_id_(vcm_id),
       clock_(clock),
       receiver_id_(receiver_id),
       master_(master),
       jitter_buffer_(clock_, event_factory, vcm_id, receiver_id, master),
       timing_(timing),
       render_wait_event_(event_factory->CreateEvent()),
       state_(kPassive),
+      receiveState_(kReceiveStateInitial),
       max_video_delay_ms_(kMaxVideoDelayMs) {}
 
 VCMReceiver::~VCMReceiver() {
   render_wait_event_->Set();
   delete crit_sect_;
 }
 
 void VCMReceiver::Reset() {
@@ -50,18 +51,20 @@ void VCMReceiver::Reset() {
   if (!jitter_buffer_.Running()) {
     jitter_buffer_.Start();
   } else {
     jitter_buffer_.Flush();
   }
   render_wait_event_->Reset();
   if (master_) {
     state_ = kReceiving;
+    receiveState_ = kReceiveStateInitial;
   } else {
     state_ = kPassive;
+    receiveState_ = kReceiveStateInitial;
   }
 }
 
 int32_t VCMReceiver::Initialize() {
   Reset();
   CriticalSectionScoped cs(crit_sect_);
   if (!master_) {
     SetNackMode(kNoNack, -1, -1);
@@ -203,18 +206,19 @@ VCMEncodedFrame* VCMReceiver::FrameForDe
   VCMEncodedFrame* frame = jitter_buffer_.ExtractAndSetDecode(frame_timestamp);
   if (frame == NULL) {
     return NULL;
   }
   frame->SetRenderTime(next_render_time_ms);
   TRACE_EVENT_ASYNC_STEP1("webrtc", "Video", frame->TimeStamp(),
                           "SetRenderTS", "render_time", next_render_time_ms);
   if (dual_receiver != NULL) {
-    dual_receiver->UpdateState(*frame);
+    dual_receiver->UpdateDualState(*frame);
   }
+  UpdateReceiveState(*frame);
   if (!frame->Complete()) {
     // Update stats for incomplete frames.
     bool retransmitted = false;
     const int64_t last_packet_time_ms =
         jitter_buffer_.LastPacketTime(frame, &retransmitted);
     if (last_packet_time_ms >= 0 && !retransmitted) {
       // We don't want to include timestamps which have suffered from
       // retransmission here, since we compensate with extra retransmission
@@ -251,16 +255,17 @@ void VCMReceiver::SetNackMode(VCMNackMod
                               int low_rtt_nack_threshold_ms,
                               int high_rtt_nack_threshold_ms) {
   CriticalSectionScoped cs(crit_sect_);
   // Default to always having NACK enabled in hybrid mode.
   jitter_buffer_.SetNackMode(nackMode, low_rtt_nack_threshold_ms,
                              high_rtt_nack_threshold_ms);
   if (!master_) {
     state_ = kPassive;  // The dual decoder defaults to passive.
+    receiveState_ = kReceiveStateWaitingKey; // XXX Initial
   }
 }
 
 void VCMReceiver::SetNackSettings(size_t max_nack_list_size,
                                   int max_packet_age_to_nack,
                                   int max_incomplete_time_ms) {
   jitter_buffer_.SetNackSettings(max_nack_list_size,
                                  max_packet_age_to_nack,
@@ -310,16 +315,21 @@ void VCMReceiver::CopyJitterBufferStateF
   jitter_buffer_.CopyFrom(receiver.jitter_buffer_);
 }
 
 VCMReceiverState VCMReceiver::State() const {
   CriticalSectionScoped cs(crit_sect_);
   return state_;
 }
 
+VideoReceiveState VCMReceiver::ReceiveState() const {
+  CriticalSectionScoped cs(crit_sect_);
+  return receiveState_;
+}
+
 void VCMReceiver::SetDecodeErrorMode(VCMDecodeErrorMode decode_error_mode) {
   jitter_buffer_.SetDecodeErrorMode(decode_error_mode);
 }
 
 VCMDecodeErrorMode VCMReceiver::DecodeErrorMode() const {
   return jitter_buffer_.decode_error_mode();
 }
 
@@ -347,23 +357,39 @@ int VCMReceiver::RenderBufferSizeMs() {
   const int64_t now_ms = clock_->TimeInMilliseconds();
   timing_->SetJitterDelay(jitter_buffer_.EstimatedJitterMs());
   // Get render timestamps.
   uint32_t render_start = timing_->RenderTimeMs(timestamp_start, now_ms);
   uint32_t render_end = timing_->RenderTimeMs(timestamp_end, now_ms);
   return render_end - render_start;
 }
 
+void VCMReceiver::UpdateReceiveState(const VCMEncodedFrame& frame) {
+  if (frame.Complete() && frame.FrameType() == kVideoFrameKey) {
+    receiveState_ = kReceiveStateNormal;
+    return;
+  }
+  if (frame.MissingFrame() || !frame.Complete()) {
+    // State is corrupted
+    receiveState_ = kReceiveStateWaitingKey;
+  }
+  // state continues
+}
+
 void VCMReceiver::UpdateState(VCMReceiverState new_state) {
   CriticalSectionScoped cs(crit_sect_);
   assert(!(state_ == kPassive && new_state == kWaitForPrimaryDecode));
+  WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCoding,
+               VCMId(vcm_id_, receiver_id_),
+               "Receiver changing state: %d to %d",
+               state_, new_state);
   state_ = new_state;
 }
 
-void VCMReceiver::UpdateState(const VCMEncodedFrame& frame) {
+void VCMReceiver::UpdateDualState(const VCMEncodedFrame& frame) {
   if (jitter_buffer_.nack_mode() == kNoNack) {
     // Dual decoder mode has not been enabled.
     return;
   }
   // Update the dual receiver state.
   if (frame.Complete() && frame.FrameType() == kVideoFrameKey) {
     UpdateState(kPassive);
   }
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.h
@@ -70,44 +70,47 @@ class VCMReceiver {
   VCMNackMode NackMode() const;
   VCMNackStatus NackList(uint16_t* nackList, uint16_t size,
                          uint16_t* nack_list_length);
 
   // Dual decoder.
   bool DualDecoderCaughtUp(VCMEncodedFrame* dual_frame,
                            VCMReceiver& dual_receiver) const;
   VCMReceiverState State() const;
+  VideoReceiveState ReceiveState() const;
 
   // Receiver video delay.
   int SetMinReceiverDelay(int desired_delay_ms);
 
   // Decoding with errors.
   void SetDecodeErrorMode(VCMDecodeErrorMode decode_error_mode);
   VCMDecodeErrorMode DecodeErrorMode() const;
 
   // Returns size in time (milliseconds) of complete continuous frames in the
   // jitter buffer. The render time is estimated based on the render delay at
   // the time this function is called.
   int RenderBufferSizeMs();
 
  private:
   void CopyJitterBufferStateFromReceiver(const VCMReceiver& receiver);
   void UpdateState(VCMReceiverState new_state);
-  void UpdateState(const VCMEncodedFrame& frame);
+  void UpdateReceiveState(const VCMEncodedFrame& frame);
+  void UpdateDualState(const VCMEncodedFrame& frame);
   static int32_t GenerateReceiverId();
 
   CriticalSectionWrapper* crit_sect_;
   int32_t vcm_id_;
   Clock* clock_;
   int32_t receiver_id_;
   bool master_;
   VCMJitterBuffer jitter_buffer_;
   VCMTiming* timing_;
   scoped_ptr<EventWrapper> render_wait_event_;
   VCMReceiverState state_;
+  VideoReceiveState receiveState_;
   int max_video_delay_ms_;
 
   static int32_t receiver_id_counter_;
 };
 
 }  // namespace webrtc
 
 #endif  // WEBRTC_MODULES_VIDEO_CODING_MAIN_SOURCE_RECEIVER_H_
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.cc
@@ -274,16 +274,21 @@ class VideoCodingModuleImpl : public Vid
     return receiver_->RegisterFrameTypeCallback(frameTypeCallback);
   }
 
   virtual int32_t RegisterPacketRequestCallback(
       VCMPacketRequestCallback* callback) OVERRIDE {
     return receiver_->RegisterPacketRequestCallback(callback);
   }
 
+  virtual int32_t RegisterReceiveStateCallback(
+      VCMReceiveStateCallback* callback) OVERRIDE {
+    return receiver_->RegisterReceiveStateCallback(callback);
+  }
+
   virtual int RegisterRenderBufferSizeCallback(
       VCMRenderBufferSizeCallback* callback) OVERRIDE {
     return receiver_->RegisterRenderBufferSizeCallback(callback);
   }
 
   virtual int32_t Decode(uint16_t maxWaitTimeMs) OVERRIDE {
     return receiver_->Decode(maxWaitTimeMs);
   }
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_coding_impl.h
@@ -135,30 +135,32 @@ class VideoSender {
 class VideoReceiver {
  public:
   typedef VideoCodingModule::ReceiverRobustness ReceiverRobustness;
 
   VideoReceiver(const int32_t id, Clock* clock, EventFactory* event_factory);
   ~VideoReceiver();
 
   int32_t InitializeReceiver();
+  void SetReceiveState(VideoReceiveState state);
   int32_t RegisterReceiveCodec(const VideoCodec* receiveCodec,
                                int32_t numberOfCores,
                                bool requireKeyFrame);
 
   int32_t RegisterExternalDecoder(VideoDecoder* externalDecoder,
                                   uint8_t payloadType,
                                   bool internalRenderTiming);
   int32_t RegisterReceiveCallback(VCMReceiveCallback* receiveCallback);
   int32_t RegisterReceiveStatisticsCallback(
       VCMReceiveStatisticsCallback* receiveStats);
   int32_t RegisterDecoderTimingCallback(
       VCMDecoderTimingCallback* decoderTiming);
   int32_t RegisterFrameTypeCallback(VCMFrameTypeCallback* frameTypeCallback);
   int32_t RegisterPacketRequestCallback(VCMPacketRequestCallback* callback);
+  int32_t RegisterReceiveStateCallback(VCMReceiveStateCallback* callback);
   int RegisterRenderBufferSizeCallback(VCMRenderBufferSizeCallback* callback);
 
   int32_t Decode(uint16_t maxWaitTimeMs);
   int32_t DecodeDualFrame(uint16_t maxWaitTimeMs);
   int32_t ResetDecoder();
 
   int32_t ReceiveCodec(VideoCodec* currentReceiveCodec) const;
   VideoCodecType ReceiveCodec() const;
@@ -204,26 +206,28 @@ class VideoReceiver {
                     // in any frame
   };
 
   int32_t _id;
   Clock* clock_;
   scoped_ptr<CriticalSectionWrapper> process_crit_sect_;
   CriticalSectionWrapper* _receiveCritSect;
   bool _receiverInited;
+  VideoReceiveState _receiveState;
   VCMTiming _timing;
   VCMTiming _dualTiming;
   VCMReceiver _receiver;
   VCMReceiver _dualReceiver;
   VCMDecodedFrameCallback _decodedFrameCallback;
   VCMDecodedFrameCallback _dualDecodedFrameCallback;
   VCMFrameTypeCallback* _frameTypeCallback;
   VCMReceiveStatisticsCallback* _receiveStatsCallback;
   VCMDecoderTimingCallback* _decoderTimingCallback;
   VCMPacketRequestCallback* _packetRequestCallback;
+  VCMReceiveStateCallback* _receiveStateCallback;
   VCMRenderBufferSizeCallback* render_buffer_callback_;
   VCMGenericDecoder* _decoder;
   VCMGenericDecoder* _dualDecoder;
 #ifdef DEBUG_DECODER_BIT_STREAM
   FILE* _bitStreamBeforeDecoder;
 #endif
   VCMFrameBuffer _frameFromFile;
   VCMKeyRequestMode _keyRequestMode;
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_receiver.cc
@@ -27,26 +27,28 @@ namespace vcm {
 VideoReceiver::VideoReceiver(const int32_t id,
                              Clock* clock,
                              EventFactory* event_factory)
     : _id(id),
       clock_(clock),
       process_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
       _receiveCritSect(CriticalSectionWrapper::CreateCriticalSection()),
       _receiverInited(false),
+      _receiveState(kReceiveStateInitial),
       _timing(clock_, id, 1),
       _dualTiming(clock_, id, 2, &_timing),
       _receiver(&_timing, clock_, event_factory, id, 1, true),
       _dualReceiver(&_dualTiming, clock_, event_factory, id, 2, false),
       _decodedFrameCallback(_timing, clock_),
       _dualDecodedFrameCallback(_dualTiming, clock_),
       _frameTypeCallback(NULL),
       _receiveStatsCallback(NULL),
       _decoderTimingCallback(NULL),
       _packetRequestCallback(NULL),
+      _receiveStateCallback(NULL),
       render_buffer_callback_(NULL),
       _decoder(NULL),
       _dualDecoder(NULL),
 #ifdef DEBUG_DECODER_BIT_STREAM
       _bitStreamBeforeDecoder(NULL),
 #endif
       _frameFromFile(),
       _keyRequestMode(kKeyOnError),
@@ -159,16 +161,35 @@ int32_t VideoReceiver::Process() {
         }
       }
     }
   }
 
   return returnValue;
 }
 
+void VideoReceiver::SetReceiveState(VideoReceiveState state) {
+  if (state == _receiveState) {
+    return;
+  }
+  if (state == kReceiveStatePreemptiveNACK &&
+      (_receiveState == kReceiveStateWaitingKey ||
+       _receiveState == kReceiveStateDecodingWithErrors)) {
+    // invalid state transition - this lets us try to set it on NACK
+    // without worrying about the current state
+    return;
+  }
+  _receiveState = state;
+
+  CriticalSectionScoped cs(process_crit_sect_.get());
+  if (_receiveStateCallback != NULL) {
+    _receiveStateCallback->ReceiveStateChange(_receiveState);
+  }
+}
+
 int32_t VideoReceiver::TimeUntilNextProcess() {
   uint32_t timeUntilNextProcess = _receiveStatsTimer.TimeUntilProcess();
   if ((_receiver.NackMode() != kNoNack) ||
       (_dualReceiver.State() != kPassive)) {
     // We need a Process call more often if we are relying on
     // retransmissions
     timeUntilNextProcess =
         VCM_MIN(timeUntilNextProcess, _retransmissionTimer.TimeUntilProcess());
@@ -290,16 +311,17 @@ int32_t VideoReceiver::InitializeReceive
 
   _decoder = NULL;
   _decodedFrameCallback.SetUserReceiveCallback(NULL);
   _receiverInited = true;
   _frameTypeCallback = NULL;
   _receiveStatsCallback = NULL;
   _decoderTimingCallback = NULL;
   _packetRequestCallback = NULL;
+  _receiveStateCallback = NULL;
   _keyRequestMode = kKeyOnError;
   _scheduleKeyRequest = false;
 
   return VCM_OK;
 }
 
 // Register a receive callback. Will be called whenever there is a new frame
 // ready for rendering.
@@ -351,16 +373,23 @@ int32_t VideoReceiver::RegisterFrameType
 
 int32_t VideoReceiver::RegisterPacketRequestCallback(
     VCMPacketRequestCallback* callback) {
   CriticalSectionScoped cs(process_crit_sect_.get());
   _packetRequestCallback = callback;
   return VCM_OK;
 }
 
+int32_t VideoReceiver::RegisterReceiveStateCallback(
+    VCMReceiveStateCallback* callback) {
+  CriticalSectionScoped cs(process_crit_sect_.get());
+  _receiveStateCallback = callback;
+  return VCM_OK;
+}
+
 int VideoReceiver::RegisterRenderBufferSizeCallback(
     VCMRenderBufferSizeCallback* callback) {
   CriticalSectionScoped cs(process_crit_sect_.get());
   render_buffer_callback_ = callback;
   return VCM_OK;
 }
 
 // Decode next frame, blocking.
@@ -394,19 +423,24 @@ int32_t VideoReceiver::Decode(uint16_t m
     // start decoding retransmitted frames and recover.
     CriticalSectionScoped cs(_receiveCritSect);
     if (_dualDecoder != NULL) {
       _codecDataBase.ReleaseDecoder(_dualDecoder);
     }
     _dualDecoder = _codecDataBase.CreateDecoderCopy();
     if (_dualDecoder != NULL) {
       _dualDecoder->RegisterDecodeCompleteCallback(&_dualDecodedFrameCallback);
+      SetReceiveState(kReceiveStateDecodingWithErrors);
     } else {
       _dualReceiver.Reset();
+      // presume we have an error, though we might not yet
+      SetReceiveState(kReceiveStateWaitingKey);
     }
+  } else {
+    SetReceiveState(_receiver.ReceiveState());
   }
 
   if (frame == NULL) {
     return VCM_FRAME_NOT_READY;
   } else {
     CriticalSectionScoped cs(_receiveCritSect);
 
     // If this frame was too late, we should adjust the delay accordingly
@@ -676,27 +710,29 @@ int32_t VideoReceiver::IncomingPacket(co
   const VCMPacket packet(incomingPayload, payloadLength, rtpInfo);
   int32_t ret;
   if (_dualReceiver.State() != kPassive) {
     ret = _dualReceiver.InsertPacket(
         packet, rtpInfo.type.Video.width, rtpInfo.type.Video.height);
     if (ret == VCM_FLUSH_INDICATOR) {
       RequestKeyFrame();
       ResetDecoder();
+      SetReceiveState(kReceiveStateWaitingKey);
     } else if (ret < 0) {
       return ret;
     }
   }
   ret = _receiver.InsertPacket(
       packet, rtpInfo.type.Video.width, rtpInfo.type.Video.height);
   // TODO(holmer): Investigate if this somehow should use the key frame
   // request scheduling to throttle the requests.
   if (ret == VCM_FLUSH_INDICATOR) {
     RequestKeyFrame();
     ResetDecoder();
+    SetReceiveState(kReceiveStateWaitingKey);
   } else if (ret < 0) {
     return ret;
   }
   return VCM_OK;
 }
 
 // Minimum playout delay (used for lip-sync). This is the minimum delay required
 // to sync with audio. Not included in  VideoCodingModule::Delay()
@@ -739,21 +775,27 @@ int32_t VideoReceiver::NackList(uint16_t
                    "Out of memory");
       return VCM_MEMORY;
     }
     case kNackKeyFrameRequest: {
       WEBRTC_TRACE(webrtc::kTraceWarning,
                    webrtc::kTraceVideoCoding,
                    VCMId(_id),
                    "Failed to get NACK list, requesting key frame");
+      SetReceiveState(kReceiveStateWaitingKey);
       return RequestKeyFrame();
     }
     default:
       break;
   }
+  if (*size != 0) {
+    // Note: not a valid transition from WaitingKey or DecodingWithErrors;
+    // will be ignored in that case
+    SetReceiveState(kReceiveStatePreemptiveNACK);
+  }
   return VCM_OK;
 }
 
 int32_t VideoReceiver::ReceivedFrameCount(VCMFrameCount* frameCount) const {
   _receiver.ReceivedFrameCount(frameCount);
   return VCM_OK;
 }
 
--- a/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.cc
+++ b/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.cc
@@ -22,33 +22,34 @@ ReceiveStatisticsProxy::ReceiveStatistic
                                                ViECodec* codec,
                                                int channel)
     : channel_(channel),
       lock_(CriticalSectionWrapper::CreateCriticalSection()),
       clock_(clock),
       // 1000ms window, scale 1000 for ms to s.
       decode_fps_estimator_(1000, 1000),
       renders_fps_estimator_(1000, 1000),
+      receive_state_(kReceiveStateInitial),
       codec_(codec),
       rtp_rtcp_(rtp_rtcp) {
   stats_.ssrc = ssrc;
 }
 
 ReceiveStatisticsProxy::~ReceiveStatisticsProxy() {}
 
 VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const {
   VideoReceiveStream::Stats stats;
   {
     CriticalSectionScoped cs(lock_.get());
     stats = stats_;
   }
   stats.c_name = GetCName();
   codec_->GetReceiveSideDelay(channel_, &stats.avg_delay_ms);
   stats.discarded_packets = codec_->GetDiscardedPackets(channel_);
-  codec_->GetReceiveCodecStastistics(
+  codec_->GetReceiveCodecStatistics(
       channel_, stats.key_frames, stats.delta_frames);
 
   return stats;
 }
 
 std::string ReceiveStatisticsProxy::GetCName() const {
   char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength];
   if (rtp_rtcp_->GetRemoteRTCPCName(channel_, rtcp_cname) != 0)
@@ -59,16 +60,22 @@ std::string ReceiveStatisticsProxy::GetC
 void ReceiveStatisticsProxy::IncomingRate(const int video_channel,
                                           const unsigned int framerate,
                                           const unsigned int bitrate) {
   CriticalSectionScoped cs(lock_.get());
   stats_.network_frame_rate = framerate;
   stats_.bitrate_bps = bitrate;
 }
 
+void ReceiveStatisticsProxy::ReceiveStateChange(const int video_channel,
+                                                VideoReceiveState state) {
+  CriticalSectionScoped cs(lock_.get());
+  receive_state_ = state;
+}
+
 void ReceiveStatisticsProxy::StatisticsUpdated(
     const webrtc::RtcpStatistics& statistics,
     uint32_t ssrc) {
   CriticalSectionScoped cs(lock_.get());
 
   stats_.rtcp_stats = statistics;
 }
 
--- a/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.h
+++ b/media/webrtc/trunk/webrtc/video/receive_statistics_proxy.h
@@ -55,16 +55,17 @@ class ReceiveStatisticsProxy : public Vi
   virtual void DecoderTiming(int decode_ms,
                              int max_decode_ms,
                              int current_delay_ms,
                              int target_delay_ms,
                              int jitter_buffer_ms,
                              int min_playout_delay_ms,
                              int render_delay_ms) OVERRIDE {}
   virtual void RequestNewKeyFrame(const int video_channel) OVERRIDE {}
+  virtual void ReceiveStateChange(const int video_channel, VideoReceiveState state) OVERRIDE;
 
   // Overrides RtcpStatisticsBallback.
   virtual void StatisticsUpdated(const webrtc::RtcpStatistics& statistics,
                                  uint32_t ssrc) OVERRIDE;
 
   // Overrides StreamDataCountersCallback.
   virtual void DataCountersUpdated(const webrtc::StreamDataCounters& counters,
                                    uint32_t ssrc) OVERRIDE;
@@ -73,15 +74,16 @@ class ReceiveStatisticsProxy : public Vi
   std::string GetCName() const;
 
   const int channel_;
   scoped_ptr<CriticalSectionWrapper> lock_;
   Clock* clock_;
   VideoReceiveStream::Stats stats_;
   RateStatistics decode_fps_estimator_;
   RateStatistics renders_fps_estimator_;
+  VideoReceiveState receive_state_;
   ViECodec* codec_;
   ViERTP_RTCP* rtp_rtcp_;
 };
 
 }  // namespace internal
 }  // namespace webrtc
 #endif  // WEBRTC_VIDEO_RECEIVE_STATISTICS_PROXY_H_
--- a/media/webrtc/trunk/webrtc/video_engine/include/vie_codec.h
+++ b/media/webrtc/trunk/webrtc/video_engine/include/vie_codec.h
@@ -70,16 +70,19 @@ class WEBRTC_DLLEXPORT ViEDecoderObserve
                              int jitter_buffer_ms,
                              int min_playout_delay_ms,
                              int render_delay_ms) = 0;
 
   // This method is called when the decoder needs a new key frame from encoder
   // on the sender.
   virtual void RequestNewKeyFrame(const int video_channel) = 0;
 
+  // This method is called when the decoder changes state
+  virtual void ReceiveStateChange(const int video_channel, VideoReceiveState state) = 0;
+
  protected:
   virtual ~ViEDecoderObserver() {}
 };
 
 class WEBRTC_DLLEXPORT ViECodec {
  public:
   // Factory for the ViECodec subā€API and increases an internal reference
   // counter if successful. Returns NULL if the API is not supported or if
@@ -125,22 +128,22 @@ class WEBRTC_DLLEXPORT ViECodec {
       unsigned char& config_parameters_size) const = 0;
 
   // Enables advanced scaling of the captured video stream if the stream
   // differs from the send codec settings.
   virtual int SetImageScaleStatus(const int video_channel,
                                   const bool enable) = 0;
 
   // Gets the number of sent key frames and number of sent delta frames.
-  virtual int GetSendCodecStastistics(const int video_channel,
+  virtual int GetSendCodecStatistics(const int video_channel,
                                       unsigned int& key_frames,
                                       unsigned int& delta_frames) const = 0;
 
   // Gets the number of decoded key frames and number of decoded delta frames.
-  virtual int GetReceiveCodecStastistics(const int video_channel,
+  virtual int GetReceiveCodecStatistics(const int video_channel,
                                          unsigned int& key_frames,
                                          unsigned int& delta_frames) const = 0;
 
   // Estimate of the min required buffer time from the expected arrival time
   // until rendering to get smooth playback.
   virtual int GetReceiveSideDelay(const int video_channel,
                                   int* delay_ms) const = 0;
 
--- a/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_custom_call.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/test/auto_test/source/vie_autotest_custom_call.cc
@@ -1620,28 +1620,28 @@ void PrintCodecStatistics(webrtc::ViECod
   int number_of_errors = 0;
   unsigned int key_frames = 0;
   unsigned int delta_frames = 0;
   switch (stat_type) {
     case kReceivedStatistic:
       std::cout << "Codec Receive statistics"
                 << std::endl;
       // Get and print the Receive Codec Statistics
-      error = vie_codec->GetReceiveCodecStastistics(video_channel, key_frames,
-                                                    delta_frames);
+      error = vie_codec->GetReceiveCodecStatistics(video_channel, key_frames,
+                                                   delta_frames);
       number_of_errors += ViETest::TestError(error == 0,
                                              "ERROR: %s at line %d",
                                              __FUNCTION__, __LINE__);
       break;
     case kSendStatistic:
       std::cout << "Codec Send statistics"
                 << std::endl;
       // Get and print the Send Codec Statistics
-      error = vie_codec->GetSendCodecStastistics(video_channel, key_frames,
-                                                 delta_frames);
+      error = vie_codec->GetSendCodecStatistics(video_channel, key_frames,
+                                                delta_frames);
       number_of_errors += ViETest::TestError(error == 0,
                                              "ERROR: %s at line %d",
                                              __FUNCTION__, __LINE__);
       break;
   }
   std::cout << "\tNumber of encoded key frames: "
             << key_frames << std::endl;
   std::cout << "\tNumber of encoded delta frames: "
--- a/media/webrtc/trunk/webrtc/video_engine/vie_channel.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_channel.cc
@@ -181,16 +181,20 @@ int32_t ViEChannel::Init() {
     WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_),
                  "%s: VCM::RegisterReceiveCallback failure", __FUNCTION__);
     return -1;
   }
   if (vcm_.RegisterFrameTypeCallback(this) != 0) {
     WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_),
                  "%s: VCM::RegisterFrameTypeCallback failure", __FUNCTION__);
   }
+  if (vcm_.RegisterReceiveStateCallback(this) != 0) {
+    WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_),
+                 "%s: VCM::RegisterReceiveStateCallback failure", __FUNCTION__);
+  }
   if (vcm_.RegisterReceiveStatisticsCallback(this) != 0) {
     WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_),
                  "%s: VCM::RegisterReceiveStatisticsCallback failure",
                  __FUNCTION__);
   }
   if (vcm_.RegisterDecoderTimingCallback(this) != 0) {
     WEBRTC_TRACE(kTraceWarning, kTraceVideo, ViEId(engine_id_, channel_id_),
                  "%s: VCM::RegisterDecoderTimingCallback failure",
@@ -1913,16 +1917,27 @@ int32_t ViEChannel::SliceLossIndicationR
 
 int32_t ViEChannel::ResendPackets(const uint16_t* sequence_numbers,
                                         uint16_t length) {
   WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, channel_id_),
                "%s(length: %d)", __FUNCTION__, length);
   return rtp_rtcp_->SendNACK(sequence_numbers, length);
 }
 
+void ViEChannel::ReceiveStateChange(VideoReceiveState state) {
+  WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, channel_id_),
+               "%s", __FUNCTION__);
+  {
+    CriticalSectionScoped cs(callback_cs_.get());
+    if (codec_observer_) {
+      codec_observer_->ReceiveStateChange(channel_id_, state);
+    }
+  }
+}
+
 bool ViEChannel::ChannelDecodeThreadFunction(void* obj) {
   return static_cast<ViEChannel*>(obj)->ChannelDecodeProcess();
 }
 
 bool ViEChannel::ChannelDecodeProcess() {
   vcm_.Decode(kMaxDecodeWaitTimeMs);
   return true;
 }
--- a/media/webrtc/trunk/webrtc/video_engine/vie_channel.h
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_channel.h
@@ -52,16 +52,17 @@ class VoEVideoSync;
 struct SenderInfo;
 
 class ViEChannel
     : public VCMFrameTypeCallback,
       public VCMReceiveCallback,
       public VCMReceiveStatisticsCallback,
       public VCMDecoderTimingCallback,
       public VCMPacketRequestCallback,
+      public VCMReceiveStateCallback,
       public RtcpFeedback,
       public RtpFeedback,
       public ViEFrameProviderBase {
  public:
   friend class ChannelStatsObserver;
 
   ViEChannel(int32_t channel_id,
              int32_t engine_id,
@@ -343,16 +344,19 @@ class ViEChannel
   // Implements VideoFrameTypeCallback.
   virtual int32_t SliceLossIndicationRequest(
       const uint64_t picture_id);
 
   // Implements VideoPacketRequestCallback.
   virtual int32_t ResendPackets(const uint16_t* sequence_numbers,
                                 uint16_t length);
 
+  // Implements ReceiveStateCallback.
+  virtual void ReceiveStateChange(VideoReceiveState state);
+
   int32_t SetVoiceChannel(int32_t ve_channel_id,
                           VoEVideoSync* ve_sync_interface);
   int32_t VoiceChannel();
 
   // Implements ViEFrameProviderBase.
   virtual int FrameCallbackChanged() {return -1;}
 
   int32_t RegisterEffectFilter(ViEEffectFilter* effect_filter);
--- a/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc
@@ -407,19 +407,19 @@ int ViECodecImpl::SetImageScaleStatus(co
 
   if (vie_encoder->ScaleInputImage(enable) != 0) {
     shared_data_->SetLastError(kViECodecUnknownError);
     return -1;
   }
   return 0;
 }
 
-int ViECodecImpl::GetSendCodecStastistics(const int video_channel,
-                                          unsigned int& key_frames,
-                                          unsigned int& delta_frames) const {
+int ViECodecImpl::GetSendCodecStatistics(const int video_channel,
+                                         unsigned int& key_frames,
+                                         unsigned int& delta_frames) const {
   WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
                ViEId(shared_data_->instance_id(), video_channel),
                "%s(video_channel %d)", __FUNCTION__, video_channel);
 
   ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
   ViEEncoder* vie_encoder = cs.Encoder(video_channel);
   if (!vie_encoder) {
     WEBRTC_TRACE(kTraceError, kTraceVideo,
@@ -432,19 +432,19 @@ int ViECodecImpl::GetSendCodecStastistic
 
   if (vie_encoder->SendCodecStatistics(&key_frames, &delta_frames) != 0) {
     shared_data_->SetLastError(kViECodecUnknownError);
     return -1;
   }
   return 0;
 }
 
-int ViECodecImpl::GetReceiveCodecStastistics(const int video_channel,
-                                             unsigned int& key_frames,
-                                             unsigned int& delta_frames) const {
+int ViECodecImpl::GetReceiveCodecStatistics(const int video_channel,
+                                            unsigned int& key_frames,
+                                            unsigned int& delta_frames) const {
   WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
                ViEId(shared_data_->instance_id(), video_channel),
                "%s(video_channel: %d)", __FUNCTION__,
                video_channel);
 
   ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
   ViEChannel* vie_channel = cs.Channel(video_channel);
   if (!vie_channel) {
--- a/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.h
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.h
@@ -38,22 +38,22 @@ class ViECodecImpl
                               const VideoCodec& video_codec);
   virtual int GetReceiveCodec(const int video_channel,
                               VideoCodec& video_codec) const;
   virtual int GetCodecConfigParameters(
     const int video_channel,
     unsigned char config_parameters[kConfigParameterSize],
     unsigned char& config_parameters_size) const;
   virtual int SetImageScaleStatus(const int video_channel, const bool enable);
-  virtual int GetSendCodecStastistics(const int video_channel,
-                                      unsigned int& key_frames,
-                                      unsigned int& delta_frames) const;
-  virtual int GetReceiveCodecStastistics(const int video_channel,
-                                         unsigned int& key_frames,
-                                         unsigned int& delta_frames) const;
+  virtual int GetSendCodecStatistics(const int video_channel,
+                                     unsigned int& key_frames,
+                                     unsigned int& delta_frames) const;
+  virtual int GetReceiveCodecStatistics(const int video_channel,
+                                        unsigned int& key_frames,
+                                        unsigned int& delta_frames) const;
   virtual int GetReceiveSideDelay(const int video_channel,
                                   int* delay_ms) const;
   virtual int GetCodecTargetBitrate(const int video_channel,
                                     unsigned int* bitrate) const;
   virtual unsigned int GetDiscardedPackets(const int video_channel) const;
   virtual int SetKeyFrameRequestCallbackStatus(const int video_channel,
                                                const bool enable);
   virtual int SetSignalKeyPacketLossStatus(const int video_channel,
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -245,17 +245,17 @@ pref("media.webm.enabled", true);
 pref("media.gstreamer.enabled", true);
 #endif
 #ifdef MOZ_APPLEMEDIA
 pref("media.apple.mp3.enabled", true);
 #endif
 #ifdef MOZ_WEBRTC
 pref("media.navigator.enabled", true);
 pref("media.navigator.video.enabled", true);
-pref("media.navigator.load_adapt", false);
+pref("media.navigator.load_adapt", true);
 pref("media.navigator.load_adapt.measure_interval",1000);
 pref("media.navigator.load_adapt.avg_seconds",3);
 pref("media.navigator.load_adapt.high_load","0.90");
 pref("media.navigator.load_adapt.low_load","0.40");
 pref("media.navigator.video.default_fps",30);
 pref("media.navigator.video.default_minfps",10);
 #ifdef MOZ_WIDGET_GONK
 pref("media.navigator.video.default_width",320);
--- a/netwerk/cache2/CacheFileMetadata.cpp
+++ b/netwerk/cache2/CacheFileMetadata.cpp
@@ -180,21 +180,27 @@ CacheFileMetadata::ReadMetadata(CacheFil
     LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, creating "
          "empty metadata. [this=%p, filesize=%lld]", this, size));
 
     InitEmptyMetadata();
     aListener->OnMetadataRead(NS_OK);
     return NS_OK;
   }
 
-  // round offset to 4k blocks
-  int64_t offset = (size / kAlignSize) * kAlignSize;
+  // Set offset so that we read at least kMinMetadataRead if the file is big
+  // enough.
+  int64_t offset;
+  if (size < kMinMetadataRead) {
+    offset = 0;
+  } else {
+    offset = size - kMinMetadataRead;
+  }
 
-  if (size - offset < kMinMetadataRead && offset > kAlignSize)
-    offset -= kAlignSize;
+  // round offset to kAlignSize blocks
+  offset = (offset / kAlignSize) * kAlignSize;
 
   mBufSize = size - offset;
   mBuf = static_cast<char *>(moz_xmalloc(mBufSize));
 
   DoMemoryReport(MemoryUsage());
 
   LOG(("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying "
        "offset=%lld, filesize=%lld [this=%p]", offset, size, this));
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -1313,25 +1313,26 @@ DataChannelConnection::HandleDataMessage
     // before we're told about the external negotiation.  We need to buffer
     // data until either a) Open comes in, if the ordering get messed up,
     // or b) the app tells us this channel was externally negotiated.  When
     // these occur, we deliver the data.
 
     // Since this is rare and non-performance, keep a single list of queued
     // data messages to deliver once the channel opens.
     LOG(("Queuing data for stream %u, length %u", stream, length));
+    // Copies data
     mQueuedData.AppendElement(new QueuedDataMessage(stream, ppid, data, length));
     return;
   }
 
   // XXX should this be a simple if, no warnings/debugbreaks?
   NS_ENSURE_TRUE_VOID(channel->mState != CLOSED);
 
   {
-    nsAutoCString recvData(buffer, length);
+    nsAutoCString recvData(buffer, length); // copies (<64) or allocates
     bool is_binary = true;
 
     if (ppid == DATA_CHANNEL_PPID_DOMSTRING ||
         ppid == DATA_CHANNEL_PPID_DOMSTRING_LAST) {
       is_binary = false;
     }
     if (is_binary != channel->mIsRecvBinary && !channel->mRecvBuffer.IsEmpty()) {
       NS_WARNING("DataChannel message aborted by fragment type change!");
@@ -1928,16 +1929,21 @@ DataChannelConnection::ReceiveCallback(s
   } else {
     MutexAutoLock lock(mLock);
     if (flags & MSG_NOTIFICATION) {
       HandleNotification(static_cast<union sctp_notification *>(data), datalen);
     } else {
       HandleMessage(data, datalen, ntohl(rcv.rcv_ppid), rcv.rcv_sid);
     }
   }
+  // sctp allocates 'data' with malloc(), and expects the receiver to free
+  // it (presumably with free).
+  // XXX future optimization: try to deliver messages without an internal
+  // alloc/copy, and if so delay the free until later.
+  free(data);
   // usrsctp defines the callback as returning an int, but doesn't use it
   return 1;
 }
 
 already_AddRefed<DataChannel>
 DataChannelConnection::Open(const nsACString& label, const nsACString& protocol,
                             Type type, bool inOrder,
                             uint32_t prValue, DataChannelListener *aListener,
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -1228,16 +1228,22 @@ function fillInTree(aRoot)
  *        The sum of all explicit HEAP reports for this process.
  * @return A boolean indicating if "heap-allocated" is known for the process.
  */
 function addHeapUnclassifiedNode(aT, aHeapAllocatedNode, aHeapTotal)
 {
   if (aHeapAllocatedNode === undefined)
     return false;
 
+  if (aT.findKid("heap-unclassified")) {
+    // heap-unclassified was already calculated, there's nothing left to do.
+    // This can happen when memory reports are exported from areweslimyet.com.
+    return true;
+  }
+
   assert(aHeapAllocatedNode._isDegenerate, "heap-allocated is not degenerate");
   let heapAllocatedBytes = aHeapAllocatedNode._amount;
   let heapUnclassifiedT = new TreeNode("heap-unclassified", UNITS_BYTES);
   heapUnclassifiedT._amount = heapAllocatedBytes - aHeapTotal;
   heapUnclassifiedT._description =
       "Memory not classified by a more specific report. This includes " +
       "slop bytes due to internal fragmentation in the heap allocator " +
       "(caused when the allocator rounds up request sizes).";
--- a/toolkit/components/downloads/ApplicationReputation.cpp
+++ b/toolkit/components/downloads/ApplicationReputation.cpp
@@ -143,16 +143,20 @@ private:
 
   // Wrapper function for nsIStreamListener.onStopRequest to make it easy to
   // guarantee calling the callback
   nsresult OnStopRequestInternal(nsIRequest *aRequest,
                                  nsISupports *aContext,
                                  nsresult aResult,
                                  bool* aShouldBlock);
 
+  // Strip url parameters, fragments, and user@pass fields from the URI spec
+  // using nsIURL. If aURI is not an nsIURL, returns the original nsIURI.spec.
+  nsresult GetStrippedSpec(nsIURI* aUri, nsACString& spec);
+
   // Escape '/' and '%' in certificate attribute values.
   nsCString EscapeCertificateAttribute(const nsACString& aAttribute);
 
   // Escape ':' in fingerprint values.
   nsCString EscapeFingerprint(const nsACString& aAttribute);
 
   // Generate whitelist strings for the given certificate pair from the same
   // certificate chain.
@@ -586,17 +590,17 @@ PendingLookup::AddRedirects(nsIArray* aR
 
     nsCOMPtr<nsIURI> uri;
     rv = principal->GetURI(getter_AddRefs(uri));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Add the spec to our list of local lookups. The most recent redirect is
     // the last element.
     nsCString spec;
-    rv = uri->GetSpec(spec);
+    rv = GetStrippedSpec(uri, spec);
     NS_ENSURE_SUCCESS(rv, rv);
     mAnylistSpecs.AppendElement(spec);
     LOG(("ApplicationReputation: Appending redirect %s\n", spec.get()));
 
     // Store the redirect information in the remote request.
     ClientDownloadRequest_Resource* resource = mRequest.add_resources();
     resource->set_url(spec.get());
     resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
@@ -614,37 +618,67 @@ PendingLookup::StartLookup()
   nsresult rv = DoLookupInternal();
   if (NS_FAILED(rv)) {
     return OnComplete(false, NS_OK);
   };
   return rv;
 }
 
 nsresult
+PendingLookup::GetStrippedSpec(nsIURI* aUri, nsACString& escaped)
+{
+  // If aURI is not an nsIURL, we do not want to check the lists or send a
+  // remote query.
+  nsresult rv;
+  nsCOMPtr<nsIURL> url = do_QueryInterface(aUri, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = url->GetScheme(escaped);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString temp;
+  rv = url->GetHostPort(temp);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  escaped.Append("://");
+  escaped.Append(temp);
+
+  rv = url->GetFilePath(temp);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // nsIUrl.filePath starts with '/'
+  escaped.Append(temp);
+
+  return NS_OK;
+}
+
+nsresult
 PendingLookup::DoLookupInternal()
 {
   // We want to check the target URI, its referrer, and associated redirects
   // against the local lists.
   nsCOMPtr<nsIURI> uri;
   nsresult rv = mQuery->GetSourceURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCString spec;
-  rv = uri->GetSpec(spec);
+  rv = GetStrippedSpec(uri, spec);
   NS_ENSURE_SUCCESS(rv, rv);
+
   mAnylistSpecs.AppendElement(spec);
+
   ClientDownloadRequest_Resource* resource = mRequest.add_resources();
   resource->set_url(spec.get());
   resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
 
   nsCOMPtr<nsIURI> referrer = nullptr;
   rv = mQuery->GetReferrerURI(getter_AddRefs(referrer));
   if (referrer) {
     nsCString spec;
-    rv = referrer->GetSpec(spec);
+    rv = GetStrippedSpec(referrer, spec);
     NS_ENSURE_SUCCESS(rv, rv);
     mAnylistSpecs.AppendElement(spec);
     resource->set_referrer(spec.get());
   }
   nsCOMPtr<nsIArray> redirects;
   rv = mQuery->GetRedirects(getter_AddRefs(redirects));
   if (redirects) {
     AddRedirects(redirects);
@@ -769,17 +803,17 @@ PendingLookup::SendRemoteQueryInternal()
   LOG(("Sending remote query for application reputation [this = %p]", this));
   // We did not find a local result, so fire off the query to the application
   // reputation service.
   nsCOMPtr<nsIURI> uri;
   nsresult rv;
   rv = mQuery->GetSourceURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
   nsCString spec;
-  rv = uri->GetSpec(spec);
+  rv = GetStrippedSpec(uri, spec);
   NS_ENSURE_SUCCESS(rv, rv);
   mRequest.set_url(spec.get());
 
   uint32_t fileSize;
   rv = mQuery->GetFileSize(&fileSize);
   NS_ENSURE_SUCCESS(rv, rv);
   mRequest.set_length(fileSize);
   // We have no way of knowing whether or not a user initiated the download.
--- a/toolkit/components/downloads/test/unit/test_app_rep.js
+++ b/toolkit/components/downloads/test/unit/test_app_rep.js
@@ -11,19 +11,19 @@ const gAppRep = Cc["@mozilla.org/downloa
                   getService(Ci.nsIApplicationReputationService);
 let gHttpServ = null;
 let gTables = {};
 
 let ALLOW_LIST = 0;
 let BLOCK_LIST = 1;
 let NO_LIST = 2;
 
-let whitelistedURI = createURI("http://whitelisted.com");
-let exampleURI = createURI("http://example.com");
-let blocklistedURI = createURI("http://blocklisted.com");
+let whitelistedURI = createURI("http://foo:bar@whitelisted.com/index.htm#junk");
+let exampleURI = createURI("http://user:password@example.com/i.html?foo=bar");
+let blocklistedURI = createURI("http://baz:qux@blocklisted.com?xyzzy");
 
 function readFileToString(aFilename) {
   let f = do_get_file(aFilename);
   let stream = Cc["@mozilla.org/network/file-input-stream;1"]
                  .createInstance(Ci.nsIFileInputStream);
   stream.init(f, -1, 0, 0);
   let buf = NetUtil.readInputStreamToString(stream, stream.available());
   return buf;
@@ -243,16 +243,35 @@ add_test(function test_unlisted() {
   }, function onComplete(aShouldBlock, aStatus) {
     do_check_eq(Cr.NS_OK, aStatus);
     do_check_false(aShouldBlock);
     check_telemetry(counts.total + 1, counts.shouldBlock, listCounts);
     run_next_test();
   });
 });
 
+add_test(function test_non_uri() {
+  Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
+                             "http://localhost:4444/download");
+  let counts = get_telemetry_counts();
+  let listCounts = counts.listCounts;
+  // No listcount is incremented, since the sourceURI is not an nsIURL
+  let source = NetUtil.newURI("data:application/octet-stream,ABC");
+  do_check_false(source instanceof Ci.nsIURL);
+  gAppRep.queryReputation({
+    sourceURI: source,
+    fileSize: 12,
+  }, function onComplete(aShouldBlock, aStatus) {
+    do_check_eq(Cr.NS_OK, aStatus);
+    do_check_false(aShouldBlock);
+    check_telemetry(counts.total + 1, counts.shouldBlock, listCounts);
+    run_next_test();
+  });
+});
+
 add_test(function test_local_blacklist() {
   Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
                              "http://localhost:4444/download");
   let counts = get_telemetry_counts();
   let listCounts = counts.listCounts;
   listCounts[BLOCK_LIST]++;
   gAppRep.queryReputation({
     sourceURI: blocklistedURI,
--- a/toolkit/components/satchel/test/mochitest.ini
+++ b/toolkit/components/satchel/test/mochitest.ini
@@ -1,21 +1,15 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g' || e10s
+skip-if = toolkit == 'android' || buildapp == 'b2g' || os == 'linux' || e10s # linux - bug 947531
 support-files =
   satchel_common.js
   subtst_form_submission_1.html
   subtst_privbrowsing.html
 
 [test_bug_511615.html]
-skip-if = toolkit == 'android'
 [test_bug_787624.html]
-skip-if = toolkit == 'android'
-# [test_form_autocomplete.html]
-# Test disabled for too many intermittent failures (bug 874429)
+[test_form_autocomplete.html]
+skip-if = true # Test disabled for too many intermittent failures (bug 874429)
 [test_form_autocomplete_with_list.html]
-skip-if = toolkit == 'android'
 [test_form_submission.html]
-skip-if = toolkit == 'android'
 [test_form_submission_cap.html]
-skip-if = toolkit == 'android'
 [test_form_submission_cap2.html]
-skip-if = toolkit == 'android'
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -5319,30 +5319,128 @@
   },
   "WEBRTC_AUDIO_QUALITY_OUTBOUND_JITTER": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 10000,
     "n_buckets": 1000,
     "description": "RTCP-reported jitter by remote recipient of outbound audio (ms). Sampled every second of a call (for easy comparison)."
   },
+  "WEBRTC_VIDEO_ERROR_RECOVERY_MS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 500,
+    "description": "Time to recover from a video error in ms"
+  },
+  "WEBRTC_VIDEO_RECOVERY_BEFORE_ERROR_PER_MIN": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000,
+    "n_buckets": 200,
+    "description": "Number of losses recovered before error per min"
+  },
+  "WEBRTC_VIDEO_RECOVERY_AFTER_ERROR_PER_MIN": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000,
+    "n_buckets": 200,
+    "description": "Number of losses recovered after error per min"
+  },
+  "WEBRTC_VIDEO_DECODE_ERROR_TIME_PERMILLE": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000,
+    "n_buckets": 100,
+    "description": "Percentage*10 (permille) of call decoding with errors or frozen due to errors"
+  },
   "WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 10000,
     "n_buckets": 1000,
     "description": "Roundtrip time of outbound video (ms). Sampled every second of a call."
   },
   "WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 10000,
     "n_buckets": 1000,
     "description": "Roundtrip time of outbound audio (ms). Sampled every second of a call."
   },
+  "WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 100,
+    "description": "Video encoder's average bitrate (in kbits/s) over an entire call"
+  },
+  "WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 5000,
+    "n_buckets": 100,
+    "description": "Standard deviation from video encoder's average bitrate (in kbits/s) over an entire call"
+  },
+  "WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 200,
+    "n_buckets": 50,
+    "description": "Video encoder's average framerate (in fps) over an entire call"
+  },
+  "WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 200,
+    "n_buckets": 50,
+    "description": "Standard deviation from video encoder's average framerate (in 1/10 fps) over an entire call"
+  },
+  "WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 50000,
+    "n_buckets": 100,
+    "description": "Video encoder's number of frames dropped (in frames/min) over an entire call"
+  },
+  "WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 100,
+    "description": "Video decoder's average bitrate (in kbits/s) over an entire call"
+  },
+  "WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 5000,
+    "n_buckets": 100,
+    "description": "Standard deviation from video decoder's average bitrate (in kbits/s) over an entire call"
+  },
+  "WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "200",
+    "n_buckets": "50",
+    "description": "Video decoder's average framerate (in fps) over an entire call"
+  },
+  "WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "200",
+    "n_buckets": "50",
+    "description": "Standard deviation from video decoder's average framerate (in 1/10 fps) over an entire call"
+  },
+  "WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 50000,
+    "n_buckets": 100,
+    "description": "Video decoder's number of discarded packets (in packets/min) over an entire call"
+  },
   "WEBRTC_CALL_DURATION": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "10000",
     "n_buckets": "1000",
     "description": "The length of time (in seconds) that a call lasted."
   },
   "WEBRTC_CALL_COUNT": {
--- a/toolkit/content/aboutWebrtc.xhtml
+++ b/toolkit/content/aboutWebrtc.xhtml
@@ -166,16 +166,46 @@ function dumpRtpStat(stat, label) {
     if (stat.bytesSent !== undefined) {
       statsString += " (" + round00(stat.bytesSent/1024) + " Kb)";
     }
   }
   div.appendChild(document.createTextNode(statsString));
   return div;
 }
 
+function dumpCoderStat(stat) {
+  var div = document.createElement('div');
+  if (stat.bitrateMean !== undefined ||
+      stat.framerateMean !== undefined ||
+      stat.droppedFrames !== undefined ||
+      stat.discardedPackets !== undefined) {
+    var statsString = (stat.packetsReceived !== undefined)? " Decoder:" : " Encoder:";
+    if (stat.bitrateMean !== undefined) {
+      statsString += " Avg. bitrate: " + (stat.bitrateMean/1000000).toFixed(2) + " Mbps";
+      if (stat.bitrateStdDev !== undefined) {
+        statsString += " (" + (stat.bitrateStdDev/1000000).toFixed(2) + " SD)";
+      }
+    }
+    if (stat.framerateMean !== undefined) {
+      statsString += " Avg. framerate: " + (stat.framerateMean).toFixed(2) + " fps";
+      if (stat.framerateStdDev !== undefined) {
+        statsString += " (" + stat.framerateStdDev.toFixed(2) + " SD)";
+      }
+    }
+    if (stat.droppedFrames !== undefined) {
+      statsString += " Dropped frames: " + stat.droppedFrames;
+    }
+    if (stat.discardedPackets !== undefined) {
+      statsString += " Discarded packets: " + stat.discardedPackets;
+    }
+    div.appendChild(document.createTextNode(statsString));
+  }
+  return div;
+}
+
 function buildPcDiv(stats, pcDivHeading) {
   var newPcDiv = document.createElement('div');
 
   var heading = document.createElement('h3');
 
   if (stats.closed) {
     heading.appendChild(document.createTextNode("Closed "));
   }
@@ -295,16 +325,17 @@ function buildPcDiv(stats, pcDivHeading)
   var addRtpStatPairToDocument = function (rtpStat) {
     if (!rtpStat.isRemote) {
       newPcDiv.appendChild(document.createElement('h5'))
               .appendChild(document.createTextNode(rtpStat.id));
       if (rtpStat.mozAvSyncDelay !== undefined ||
           rtpStat.mozJitterBufferDelay !== undefined) {
         newPcDiv.appendChild(dumpAvStat(rtpStat));
       }
+      newPcDiv.appendChild(dumpCoderStat(rtpStat));
       newPcDiv.appendChild(dumpRtpStat(rtpStat, "Local: "));
 
       // Might not be receiving RTCP, so we have no idea what the
       // statistics look like from the perspective of the other end.
       if (rtpStat.remoteId) {
         var remoteRtpStat = remoteRtpStatsMap[rtpStat.remoteId];
         newPcDiv.appendChild(dumpRtpStat(remoteRtpStat, "Remote: "));
       }
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -6017,53 +6017,43 @@ nsWindow::GetSurfaceForGdkDrawable(GdkDr
 
     return result.forget();
 }
 #endif
 
 TemporaryRef<DrawTarget>
 nsWindow::StartRemoteDrawing()
 {
-#if (MOZ_WIDGET_GTK == 2)
   gfxASurface *surf = GetThebesSurface();
-#else
-  // TODO GTK3
-  gfxASurface *surf = nullptr;
-#endif
   if (!surf) {
     return nullptr;
   }
 
   IntSize size(surf->GetSize().width, surf->GetSize().height);
   if (size.width <= 0 || size.height <= 0) {
     return nullptr;
   }
 
   return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
 }
 
 // return the gfxASurface for rendering to this widget
 gfxASurface*
-#if (MOZ_WIDGET_GTK == 2)
 nsWindow::GetThebesSurface()
-#else
+#if (MOZ_WIDGET_GTK == 3)
+{
+    return GetThebesSurface(nullptr);
+}
+gfxASurface*
 nsWindow::GetThebesSurface(cairo_t *cr)
 #endif
 {
     if (!mGdkWindow)
         return nullptr;
 
-#if (MOZ_WIDGET_GTK != 2)
-    cairo_surface_t *surf = cairo_get_target(cr);
-    if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
-      NS_NOTREACHED("Missing cairo target?");
-      return nullptr;
-    }
-#endif // MOZ_WIDGET_GTK2
-
 #ifdef MOZ_X11
     gint width, height;
 
 #if (MOZ_WIDGET_GTK == 2)
     gdk_drawable_get_size(GDK_DRAWABLE(mGdkWindow), &width, &height);
 #else
     width = gdk_window_get_width(mGdkWindow);
     height = gdk_window_get_height(mGdkWindow);
@@ -6087,31 +6077,37 @@ nsWindow::GetThebesSurface(cairo_t *cr)
             nsShmImage::EnsureShmImage(size,
                                        visual, gdk_visual_get_depth(gdkVisual),
                                        mShmImage);
         usingShm = mThebesSurface != nullptr;
     }
     if (!usingShm)
 #  endif  // MOZ_HAVE_SHMIMAGE
 
-#if (MOZ_WIDGET_GTK == 2)
-    mThebesSurface = new gfxXlibSurface
-        (GDK_WINDOW_XDISPLAY(mGdkWindow),
-         gdk_x11_window_get_xid(mGdkWindow),
-         visual,
-         size);
-#else
+#if (MOZ_WIDGET_GTK == 3)
 #if MOZ_TREE_CAIRO
 #error "cairo-gtk3 target must be built with --enable-system-cairo"
-#else
-    mThebesSurface = gfxASurface::Wrap(surf);
+#else    
+    if (cr) {
+        cairo_surface_t *surf = cairo_get_target(cr);
+        if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
+          NS_NOTREACHED("Missing cairo target?");
+          return nullptr;
+        }
+        mThebesSurface = gfxASurface::Wrap(surf);
+    } else
 #endif
-#endif
-
-#endif
+#endif // (MOZ_WIDGET_GTK == 3)
+        mThebesSurface = new gfxXlibSurface
+            (GDK_WINDOW_XDISPLAY(mGdkWindow),
+             gdk_x11_window_get_xid(mGdkWindow),
+             visual,
+             size);
+
+#endif // MOZ_X11
 
     // if the surface creation is reporting an error, then
     // we don't have a surface to give back
     if (mThebesSurface && mThebesSurface->CairoStatus() != 0) {
         mThebesSurface = nullptr;
     }
 
     return mThebesSurface;
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -277,24 +277,21 @@ public:
     void               ApplyTransparencyBitmap();
     void               ClearTransparencyBitmap();
 
    virtual void        SetTransparencyMode(nsTransparencyMode aMode);
    virtual nsTransparencyMode GetTransparencyMode();
    virtual nsresult    ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
    nsresult            UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
                                                             uint8_t* aAlphas, int32_t aStride);
+    virtual gfxASurface *GetThebesSurface() MOZ_OVERRIDE;
 
 #if (MOZ_WIDGET_GTK == 2)
-    gfxASurface       *GetThebesSurface();
-
     static already_AddRefed<gfxASurface> GetSurfaceForGdkDrawable(GdkDrawable* aDrawable,
                                                                   const nsIntSize& aSize);
-#else
-    gfxASurface       *GetThebesSurface(cairo_t *cr);
 #endif
     NS_IMETHOD         ReparentNativeWidget(nsIWidget* aNewParent);
 
     virtual nsresult SynthesizeNativeMouseEvent(nsIntPoint aPoint,
                                                 uint32_t aNativeMessage,
                                                 uint32_t aModifierFlags);
 
     virtual nsresult SynthesizeNativeMouseMove(nsIntPoint aPoint)
@@ -441,16 +438,19 @@ private:
 
     void DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent);
 
     // nsBaseWidget
     virtual LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                                           LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                                           LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                                           bool* aAllowRetaining = nullptr) MOZ_OVERRIDE;
+#if (MOZ_WIDGET_GTK == 3)
+    gfxASurface* GetThebesSurface(cairo_t *cr);
+#endif
 
     void CleanLayerManagerRecursive();
 
     /**
      * |mIMModule| takes all IME related stuff.
      *
      * This is owned by the top-level nsWindow or the topmost child
      * nsWindow embedded in a non-Gecko widget.