Merge m-c to fig
authorLucas Rocha <lucasr@mozilla.com>
Tue, 20 Aug 2013 11:50:04 +0100
changeset 143568 e9c576ab368957705df4aee014863c407cf47f32
parent 143567 9d9eab84d2e7b1a067fa4613ee5c949056c2d44a (current diff)
parent 143202 d8b7195b63c9303cc44cc11686f88dd48d6d73be (diff)
child 143569 37e17d2fe8a7de9b9af6e2b031492fee818dc7b6
push id25130
push userlrocha@mozilla.com
push dateWed, 21 Aug 2013 09:41:27 +0000
treeherdermozilla-central@b2486721572e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fig
browser/modules/AboutHomeUtils.jsm
dom/base/nsIScriptRuntime.h
mobile/android/base/BrowserApp.java
mobile/android/base/BrowserToolbar.java
mobile/android/base/Makefile.in
mobile/android/base/Tab.java
mobile/android/base/Tabs.java
mobile/android/base/resources/values/colors.xml
mobile/android/chrome/content/browser.js
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -324,28 +324,34 @@ function getTabDocAccessible(aAccOrElmOr
  */
 function getApplicationAccessible()
 {
   return gAccRetrieval.getApplicationAccessible().
     QueryInterface(nsIAccessibleApplication);
 }
 
 /**
+ * Flags used for testAccessibleTree
+ */
+const kSkipTreeFullCheck = 1;
+
+/**
  * Compare expected and actual accessibles trees.
  *
  * @param  aAccOrElmOrID  [in] accessible identifier
  * @param  aAccTree       [in] JS object, each field corresponds to property of
  *                         accessible object. Additionally special properties
  *                         are presented:
  *                          children - an array of JS objects representing
  *                                      children of accessible
  *                          states   - an object having states and extraStates
  *                                      fields
+ * @param aFlags          [in, optional] flags, see constants above
  */
-function testAccessibleTree(aAccOrElmOrID, aAccTree)
+function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags)
 {
   var acc = getAccessible(aAccOrElmOrID);
   if (!acc)
     return;
 
   var accTree = aAccTree;
 
   // Support of simplified accessible tree object.
@@ -356,40 +362,111 @@ function testAccessibleTree(aAccOrElmOrI
       role: nsIAccessibleRole[roleName],
       children: accTree[key]
     };
   }
 
   // Test accessible properties.
   for (var prop in accTree) {
     var msg = "Wrong value of property '" + prop + "' for " + prettyName(acc) + ".";
-    if (prop == "role") {
-      is(roleToString(acc[prop]), roleToString(accTree[prop]), msg);
+
+    switch (prop) {
+    case "actions": {
+      var actions = (typeof accTree.actions == "string") ?
+        [ accTree.actions ] : (accTree.actions || []);
+      is(acc.actionCount, actions.length, "Wong number of actions.");
+      for (var i = 0; i < actions.length; i++ )
+        is(acc.getActionName(i), actions[i], "Wrong action name at " + i + " index.");
+      break;
+    }
+
+    case "attributes":
+      testAttrs(acc, accTree[prop], true);
+      break;
+
+    case "absentAttributes":
+      testAbsentAttrs(acc, accTree[prop]);
+      break;
+
+    case "interfaces": {
+      var ifaces = (accTree[prop] instanceof Array) ?
+        accTree[prop] : [ accTree[prop] ];
+      for (var i = 0; i < ifaces.length; i++) {
+        ok((acc instanceof ifaces[i]),
+           "No " + ifaces[i] + " interface on " + prettyName(acc));
+      }
+      break;
+    }
+
+    case "relations": {
+      for (var rel in accTree[prop])
+        testRelation(acc, window[rel], accTree[prop][rel]);
+      break;
+    }
 
-    } else if (prop == "states") {
-      var statesObj = accTree[prop];
-      testStates(acc, statesObj.states, statesObj.extraStates,
-                 statesObj.absentStates, statesObj.absentExtraStates);
+    case "role":
+      isRole(acc, accTree[prop], msg);
+      break;
+
+    case "states":
+    case "extraStates":
+    case "absentStates":
+    case "absentExtraStates": {
+      testStates(acc, accTree["states"], accTree["extraStates"],
+                 accTree["absentStates"], accTree["absentExtraStates"]);
+      break;
+    }
+
+    case "tagName":
+      is(accTree[prop], acc.DOMNode.tagName, msg);
+      break;
 
-    } else if (prop == "tagName") {
-      is(accTree[prop], acc.DOMNode.tagName, msg);
+    case "textAttrs": {
+      var prevOffset = -1;
+      for (var offset in accTree[prop]) {
+        if (prevOffset !=- 1) {
+          var attrs = accTree[prop][prevOffset];
+          testTextAttrs(acc, prevOffset, attrs, { }, prevOffset, offset, true);
+        }
+        prevOffset = offset;
+      }
 
-    } else if (prop != "children") {
-      is(acc[prop], accTree[prop], msg);
+      if (prevOffset != -1) {
+        var charCount = getAccessible(acc, [nsIAccessibleText]).characterCount;
+        var attrs = accTree[prop][prevOffset];
+        testTextAttrs(acc, prevOffset, attrs, { }, prevOffset, charCount, true);
+      }
+
+      break;
+    } 
+
+    default:
+      if (prop.indexOf("todo_") == 0)
+        todo(false, msg);
+      else if (prop != "children")
+        is(acc[prop], accTree[prop], msg);
     }
   }
 
   // Test children.
   if ("children" in accTree && accTree["children"] instanceof Array) {
     var children = acc.children;
-    is(children.length, accTree.children.length,
+    var childCount = children.length;
+
+    is(childCount, accTree.children.length,
        "Different amount of expected children of " + prettyName(acc) + ".");
 
-    if (accTree.children.length == children.length) {
-      var childCount = children.length;
+    if (accTree.children.length == childCount) {
+      if (aFlags & kSkipTreeFullCheck) {
+        for (var i = 0; i < childCount; i++) {
+          var child = children.queryElementAt(i, nsIAccessible);
+          testAccessibleTree(child, accTree.children[i], aFlags);
+        }
+        return;
+      }
 
       // nsIAccessible::firstChild
       var expectedFirstChild = childCount > 0 ?
         children.queryElementAt(0, nsIAccessible) : null;
       var firstChild = null;
       try { firstChild = acc.firstChild; } catch (e) {}
       is(firstChild, expectedFirstChild,
          "Wrong first child of " + prettyName(acc));
@@ -397,17 +474,17 @@ function testAccessibleTree(aAccOrElmOrI
       // nsIAccessible::lastChild
       var expectedLastChild = childCount > 0 ?
         children.queryElementAt(childCount - 1, nsIAccessible) : null;
       var lastChild = null;
       try { lastChild = acc.lastChild; } catch (e) {}
       is(lastChild, expectedLastChild,
          "Wrong last child of " + prettyName(acc));
 
-      for (var i = 0; i < children.length; i++) {
+      for (var i = 0; i < childCount; i++) {
         var child = children.queryElementAt(i, nsIAccessible);
 
         // nsIAccessible::parent
         var parent = null;
         try { parent = child.parent; } catch (e) {}
         is(parent, acc, "Wrong parent of " + prettyName(child));
 
         // nsIAccessible::indexInParent
@@ -428,17 +505,17 @@ function testAccessibleTree(aAccOrElmOrI
         var expectedPrevSibling = (i > 0) ?
           children.queryElementAt(i - 1, nsIAccessible) : null;
         var prevSibling = null;
         try { prevSibling = child.previousSibling; } catch (e) {}
         is(prevSibling, expectedPrevSibling,
            "Wrong previous sibling of " + prettyName(child));
 
         // Go down through subtree
-        testAccessibleTree(child, accTree.children[i]);
+        testAccessibleTree(child, accTree.children[i], aFlags);
       }
     }
   }
 }
 
 /**
  * Return true if accessible for the given node is in cache.
  */
--- a/accessible/tests/mochitest/elm/Makefile.in
+++ b/accessible/tests/mochitest/elm/Makefile.in
@@ -8,14 +8,15 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_A11Y_FILES =\
 		test_figure.html \
+		test_HTMLSpec.html \
 		test_listbox.xul \
 		test_nsApplicationAcc.html \
 		test_plugin.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -0,0 +1,1598 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>HTML a11y spec tests</title>
+  <link id="link" rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../attributes.js"></script>
+  <script type="application/javascript"
+          src="../relations.js"></script>
+  <script type="application/javascript"
+          src="../name.js"></script>
+
+  <script type="application/javascript">
+    function testElm(aID, aTreeObj)
+    {
+      testAccessibleTree(aID, aTreeObj, kSkipTreeFullCheck);
+    }
+
+    function doTest()
+    {
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:a@href
+
+      var obj = {
+        role: ROLE_LINK,
+        states: STATE_LINKED,
+        actions: "jump",
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText, nsIAccessibleHyperLink ],
+        children: [ // all kids inherits linked state and jump action
+          {
+            role: ROLE_TEXT_LEAF,
+            states: STATE_LINKED,
+            actions: "jump"
+          }
+        ]
+      };
+      testElm("a_href", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:a no @href
+
+      obj = {
+        todo_role: ROLE_TEXT_CONTAINER,
+        absentStates: STATE_LINKED,
+        actions: null,
+        children: [
+          {
+            role: ROLE_TEXT_LEAF,
+            absentStates: STATE_LINKED,
+            actions: null
+          }
+        ]
+      };
+      testElm("a_nohref", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:abbr contained by HTML:td
+
+      obj = {
+        role: ROLE_CELL,
+        attributes: { abbr: "WWW" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+        children: [
+          {
+            role: ROLE_TEXT_CONTAINER,
+            children: [ { role: ROLE_TEXT_LEAF } ]
+          }
+        ]
+      };
+      testElm("td_abbr", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:address
+
+      obj = {
+        todo_role: ROLE_PARAGRAPH,
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+      };
+      testElm("address", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:area@href
+
+      obj = {
+        role: ROLE_LINK,
+        states: STATE_LINKED,
+        actions: "jump",
+        interfaces: [ nsIAccessibleHyperLink ],
+        children: []
+      };
+      testElm(getAccessible("imgmap").firstChild, obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:area no @href
+
+      obj = {
+        todo_role: "ROLE_SHAPE",
+        absentStates: STATE_LINKED,
+        children: []
+      };
+      testElm(getAccessible("imgmap").lastChild, obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:article
+      obj = {
+        role: ROLE_DOCUMENT,
+        states: STATE_READONLY,
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+      };
+      testElm("article", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:aside
+      obj = {
+        role: ROLE_NOTE,
+        attributes: { "xml-roles": "complementary" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("aside", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      obj = { // HTML:audio
+        role: ROLE_GROUPING
+      };
+      testElm("audio", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      obj = { // HTML:b contained by paragraph
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "font-weight": kBoldFontWeight }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:b text
+        ]
+      }
+      testElm("b_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      obj = { // HTML:bdi contained by paragraph
+        role: ROLE_PARAGRAPH,
+        todo_textAttrs: {
+          0: { },
+          5: { "writing-mode": "rl" },
+          8: { }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF }, // HTML:bdi text
+          { role: ROLE_TEXT_LEAF } // plain text
+        ]
+      }
+      testElm("bdi_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:bdo contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        todo_textAttrs: {
+          0: { },
+          6: { "writing-mode": "rl" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+        ]
+      }
+      testElm("bdo_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:blockquote
+
+      obj = {
+        role: ROLE_SECTION,
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+        children: [ { role: ROLE_PARAGRAPH } ]
+      };
+      testElm("blockquote", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:br
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        children: [ { role: ROLE_WHITESPACE } ]
+      };
+      testElm("br_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      obj = { // HTML:button
+        role: ROLE_PUSHBUTTON,
+        absentStates: STATE_DEFAULT,
+        actions: "press",
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("button", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:button@type="submit" (default button)
+
+      obj = {
+        role: ROLE_PUSHBUTTON,
+        states: STATE_DEFAULT,
+        actions: "press"
+      };
+      testElm("button_default", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:canvas
+
+      obj = {
+        role: ROLE_CANVAS
+      };
+      testElm("canvas", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:caption under table
+
+      obj = {
+        role: ROLE_TABLE,
+        relations: {
+          RELATION_LABELLED_BY: "caption"
+        },
+        interfaces: nsIAccessibleTable,
+        children: [
+          {
+            role: ROLE_CAPTION,
+            relations: {
+              RELATION_LABEL_FOR: "table"
+            },
+            interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+          },
+          { // td inside thead
+            role: ROLE_ROW,
+            children: [
+              {
+                role: ROLE_COLUMNHEADER,
+                interfaces: [ nsIAccessibleTableCell, nsIAccessibleText, nsIAccessibleHyperText ]
+              },
+              { role: ROLE_COLUMNHEADER }
+            ]
+          },
+          { // td inside tbody
+            role: ROLE_ROW,
+            children: [
+              {
+                role: ROLE_ROWHEADER,
+                interfaces: [ nsIAccessibleTableCell, nsIAccessibleText, nsIAccessibleHyperText ]
+              },
+              {
+                role: ROLE_CELL,
+                interfaces: [ nsIAccessibleTableCell, nsIAccessibleText, nsIAccessibleHyperText ]
+              }
+            ]
+          },
+          { // td inside tfoot
+            role: ROLE_ROW
+          }
+        ]
+      };
+      testElm("table", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:cite contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "font-style": "italic" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:cite text
+        ]
+      };
+      testElm("cite_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:code contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "font-family": kMonospaceFontFamily }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:code text
+        ]
+      };
+      testElm("code_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:col and HTML:colgroup under table
+
+      obj =
+        { TABLE : [
+          { ROW :[
+            { role: ROLE_CELL },
+            { role: ROLE_CELL },
+            { role: ROLE_CELL }
+          ] }
+        ] };
+      testElm("colNcolgroup_table", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:data contained by paragraph
+
+      obj =
+        { PARAGRAPH: [
+          { TEXT_LEAF: [] } // HTML:data text
+        ] };
+      testElm("data_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:datalist associated with input
+
+      todo(false, "datalist and summary tree hierarchy test missed");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:dd, HTML:dl, HTML:dd
+
+      obj = {
+        role: ROLE_DEFINITION_LIST,
+        states: STATE_READONLY,
+        children: [ // dl
+          {
+            role: ROLE_TERM,
+            states: STATE_READONLY,
+            interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+            children: [ // dt
+              { role: ROLE_TEXT_LEAF }
+            ]
+          },
+          {
+            role: ROLE_DEFINITION,
+            interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+            children: [ // dd
+              { role: ROLE_TEXT_LEAF }
+            ]
+          }
+        ]
+      };
+      testElm("dl", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:del contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "text-line-through-style": "solid" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:del text
+        ]
+      };
+      testElm("del_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:details
+
+      todo(isAccessible("details"), "details element is not accessible");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:dfn contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { "font-style": "italic" },
+          12: { }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // HTML:dfn text
+          { role: ROLE_TEXT_LEAF } // plain text
+        ]
+      };
+      testElm("dfn_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:dialog
+
+      todo(isAccessible("dialog"), "dialog element is not accessible");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:div
+
+      obj = {
+        role: ROLE_SECTION,
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+        children: [
+          { role: ROLE_TEXT_LEAF } // plain text
+        ]
+      };
+      testElm("div", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:em in a paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "font-style": "italic" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:em text
+        ]
+      };
+      testElm("em_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:embed (windowless and windowed plugins)
+
+      if (WIN) {
+        obj = {
+          role: ROLE_EMBEDDED_OBJECT,
+          states: STATE_UNAVAILABLE
+        };
+
+        testElm("embed_plugin_windowless", obj);
+
+        obj = {
+          role: ROLE_EMBEDDED_OBJECT,
+          absentStates: STATE_UNAVAILABLE
+        };
+        testElm("embed_plugin_windowed", obj);
+      }
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:fieldset and HTML:legend
+
+      obj = {
+        role: ROLE_GROUPING,
+        relations: {
+          RELATION_LABELLED_BY: "legend"
+        },
+        children: [
+          {
+            role: ROLE_LABEL,
+            relations: {
+              RELATION_LABEL_FOR: "fieldset"
+            },
+            interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+          },
+          {
+            role: ROLE_ENTRY
+          }
+        ]
+      };
+      testElm("fieldset", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:figure and HTML:figcaption
+
+      obj = {
+        role: ROLE_FIGURE,
+        attributes: { "xml-roles": "figure" },
+        relations: {
+          RELATION_LABELLED_BY: "figcaption"
+        },
+        children: [
+          { role: ROLE_GRAPHIC },
+          {
+            role: ROLE_CAPTION,
+            relations: {
+              RELATION_LABEL_FOR: "figure"
+            },
+            interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+          }
+        ]
+      };
+      testElm("figure", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:footer
+
+      obj = {
+        role: ROLE_FOOTER,
+        attributes: { "xml-roles": "contentinfo" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("footer", obj);
+
+      obj = {
+        role: ROLE_FOOTER,
+        absentAttributes: { "xml-roles": "contentinfo" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("footer_in_article", obj);
+      testElm("footer_in_section", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:form
+
+      obj = {
+        role: ROLE_FORM
+      };
+      testElm("form", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // // HTML:frameset, HTML:frame and HTML:iframe
+
+      obj = {
+        INTERNAL_FRAME: [ { // HTML:iframe
+          DOCUMENT: [ {
+            INTERNAL_FRAME: [ { // HTML:frame
+              DOCUMENT: [ { role: ROLE_TEXT_LEAF} ]
+            } ]
+          } ]
+        } ]
+      };
+      testElm("frameset_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:h1, HTML:h2, HTML:h3, HTML:h4, HTML:h5, HTML:h6
+
+      obj = {
+        role: ROLE_HEADING,
+        attributes: { "level": "1" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("h1", obj);
+
+      obj = {
+        role: ROLE_HEADING,
+        attributes: { "level": "2" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("h2", obj);
+
+      obj = {
+        role: ROLE_HEADING,
+        attributes: { "level": "3" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("h3", obj);
+
+      obj = {
+        role: ROLE_HEADING,
+        attributes: { "level": "4" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("h4", obj);
+
+      obj = {
+        role: ROLE_HEADING,
+        attributes: { "level": "5" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("h5", obj);
+
+      obj = {
+        role: ROLE_HEADING,
+        attributes: { "level": "6" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("h6", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:header
+
+      obj = {
+        role: ROLE_HEADER,
+        attributes: { "xml-roles": "banner" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("header", obj);
+
+      obj = {
+        role: ROLE_HEADER,
+        absentAttributes: { "xml-roles": "banner" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("header_in_article", obj);
+      testElm("header_in_section", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:hr
+
+      obj = {
+        role: ROLE_SEPARATOR,
+      };
+      testElm("hr", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      obj = { // HTML:i contained by paragraph
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "font-style": "italic" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:i text
+        ]
+      }
+      testElm("i_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:img
+
+      obj = {
+        role: ROLE_GRAPHIC,
+        interfaces: [ nsIAccessibleImage ]
+      };
+      testElm("img", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="button"
+
+      obj = {
+        role: ROLE_PUSHBUTTON,
+        absentStates: STATE_DEFAULT
+      };
+      testElm("input_button", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="checkbox"
+
+      obj = {
+        role: ROLE_CHECKBUTTON,
+        states: STATE_CHECKABLE,
+        absentStates: STATE_CHECKED,
+        actions: "check"
+      };
+      testElm("input_checkbox", obj);
+
+      obj = {
+        role: ROLE_CHECKBUTTON,
+        states: STATE_CHECKABLE | STATE_CHECKED,
+        actions: "uncheck"
+      };
+      testElm("input_checkbox_true", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="file"
+
+      obj = {
+        TEXT_CONTAINER: [
+          { role: ROLE_PUSHBUTTON },
+          { role: ROLE_LABEL }
+        ]
+      };
+      testElm("input_file", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="image"
+
+      obj = {
+        role: ROLE_PUSHBUTTON,
+        absentStates: STATE_DEFAULT,
+        actions: "press"
+      };
+      testElm("input_image", obj);
+      testElm("input_submit", obj);
+
+      obj = {
+        role: ROLE_PUSHBUTTON,
+        actions: "press",
+        states: STATE_DEFAULT
+      };
+      testElm("input_image_default", obj);
+      testElm("input_submit_default", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="number" and etc
+
+      obj = {
+        role: ROLE_ENTRY,
+        extraStates: EXT_STATE_EDITABLE | EXT_STATE_SINGLE_LINE,
+        actions: "activate",
+        interfaces: [ nsIAccessibleText, nsIAccessibleEditableText ],
+        children: [
+          { role: ROLE_TEXT_LEAF }
+        ]
+      };
+      testElm("input_number", obj);
+      testElm("input_email", obj);
+      testElm("input_search", obj);
+      testElm("input_tel", obj);
+      testElm("input_text", obj);
+      testElm("input_url", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="password"
+
+      obj = {
+        role: ROLE_PASSWORD_TEXT,
+        states: STATE_PROTECTED,
+        extraStates: EXT_STATE_EDITABLE,
+        actions: "activate",
+        children: [ ]
+      };
+      testElm("input_password", obj);
+
+      obj = {
+        role: ROLE_PASSWORD_TEXT,
+        states: STATE_PROTECTED | STATE_READONLY,
+        actions: "activate",
+        children: [ ]
+      };
+      testElm("input_password_readonly", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="radio"
+
+      obj = {
+        role: ROLE_RADIOBUTTON,
+        states: STATE_CHECKABLE,
+        absentStates: STATE_CHECKED,
+        actions: "select"
+      };
+      testElm("input_radio", obj);
+
+      obj = {
+        role: ROLE_RADIOBUTTON,
+        states: STATE_CHECKABLE | STATE_CHECKED,
+        actions: "select"
+      };
+      testElm("input_radio_true", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="range"
+
+      obj = {
+        role: ROLE_SLIDER
+      };
+      testElm("input_range", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:input@type="reset"
+
+      obj = {
+        role: ROLE_PUSHBUTTON,
+        actions: "press",
+        absentStates: STATE_DEFAULT
+      };
+      testElm("input_reset", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:ins contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "text-underline-style": "solid" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:ins text
+        ]
+      };
+      testElm("ins_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:kbd contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "font-family": kMonospaceFontFamily }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:kbd text
+        ]
+      };
+      testElm("kbd_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:keygen
+
+      obj = {
+        role: ROLE_COMBOBOX,
+        states: STATE_COLLAPSED | STATE_HASPOPUP,
+        extraStates: EXT_STATE_EXPANDABLE,
+        actions: "open",
+        children: [ 
+          { COMBOBOX_LIST: [
+            { role: ROLE_COMBOBOX_OPTION }, // high grade
+            { role: ROLE_COMBOBOX_OPTION } // medium grade
+          ] }
+        ]
+      };
+      testElm("keygen", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:label
+
+      obj = {
+        role: ROLE_LABEL,
+        todo_relations: {
+          RELATION_LABEL_FOR: "label_input"
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          {
+            role: ROLE_ENTRY,
+            relations: {
+              RELATION_LABELLED_BY: "label"
+            }
+          }
+        ]
+      };
+      testElm("label", obj);
+
+      obj = {
+        role: ROLE_LABEL,
+        relations: {
+          RELATION_LABEL_FOR: "label_for_input"
+        }
+      };
+      testElm("label_for", obj);
+
+      obj = {
+        role: ROLE_ENTRY,
+        relations: {
+          RELATION_LABELLED_BY: "label_for"
+        }
+      };
+      testElm("label_for_input", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:ul, HTML:ol, HTML:li
+
+      obj = { // ul or ol
+        role: ROLE_LIST,
+        states: STATE_READONLY,
+        children: [
+          { // li
+            role: ROLE_LISTITEM,
+            states: STATE_READONLY,
+            interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+          }
+        ]
+      };
+      testElm("ul", obj);
+      testElm("ol", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:link
+
+      ok(!isAccessible("link"), "link element is not accessible");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:main
+
+      obj = {
+        todo_role: ROLE_GROUPING,
+        attributes: { "xml-roles": "main" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("main", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:map
+
+      ok(!isAccessible("map_imagemap"),
+         "map element is not accessible if used as an image map");
+
+      obj = {
+        role: ROLE_TEXT_CONTAINER
+      };
+      testElm("map", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:mark contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "background-color": "rgb(255, 255, 0)" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:mark text
+        ]
+      };
+      testElm("mark_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:math
+
+      obj = {
+        role: ROLE_EQUATION
+      };
+      testElm("math", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:menu
+
+      obj = {
+        todo_role: ROLE_MENUPOPUP
+      };
+      testElm("menu", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:meter
+
+      todo(isAccessible("meter"), "meter element is not accessible");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:nav
+
+      obj = {
+        role: ROLE_SECTION,
+        attributes: { "xml-roles": "navigation" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("nav", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:object (windowless and windowed plugins) and HTML:param
+
+      if (WIN) {
+        obj = {
+          role: ROLE_EMBEDDED_OBJECT,
+          states: STATE_UNAVAILABLE,
+          children: [ ] // no child for HTML:param
+        };
+        testElm("object_plugin_windowless", obj);
+
+        obj = {
+          role: ROLE_EMBEDDED_OBJECT,
+          absentStates: STATE_UNAVAILABLE
+        };
+        testElm("object_plugin_windowed", obj);
+      }
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:select, HTML:optgroup and HTML:option
+
+      obj = { // HMTL:select@size > 1
+        role: ROLE_LISTBOX,
+        states: STATE_FOCUSABLE,
+        absentStates: STATE_MULTISELECTABLE,
+        interfaces: [ nsIAccessibleSelectable ],
+        children: [
+          { GROUPING: [ // HTML:optgroup
+            { role: ROLE_STATICTEXT },
+            { role: ROLE_OPTION }, // HTML:option
+            { role: ROLE_OPTION }
+          ] },
+          {
+            role: ROLE_OPTION,
+            states: STATE_FOCUSABLE,
+            actions: "select",
+            interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+          }
+        ]
+      };
+      testElm("select_listbox", obj);
+
+      obj = { // HTML:select@multiple
+        role: ROLE_LISTBOX,
+        states: STATE_FOCUSABLE | STATE_MULTISELECTABLE,
+        children: [
+          { role: ROLE_OPTION },
+          { role: ROLE_OPTION },
+          { role: ROLE_OPTION }
+        ]
+      };
+      testElm("select_listbox_multiselectable", obj);
+
+      obj = { // HTML:select
+        role: ROLE_COMBOBOX,
+        states: STATE_FOCUSABLE,
+        children: [
+          {
+            role: ROLE_COMBOBOX_LIST,
+            children: [
+              { role: ROLE_COMBOBOX_OPTION },
+              { role: ROLE_COMBOBOX_OPTION },
+              { role: ROLE_COMBOBOX_OPTION }
+            ]
+          }
+        ]
+      };
+      testElm("select_combobox", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:output
+
+      obj = {
+        role: ROLE_SECTION,
+        attributes: { "live": "polite" },
+        todo_relations: {
+          RELATION_CONTROLLED_BY: "output_input"
+        },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("output", obj);
+
+      obj = {
+        role: ROLE_ENTRY,
+        relations: {
+          RELATION_CONTROLLER_FOR: "output"
+        }
+      };
+      testElm("output_input", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:pre
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("pre", obj);
+
+      ///////////////////////////////////////////////////////////////////////////
+      // HTML:progress
+
+      obj = {
+        role: ROLE_PROGRESSBAR,
+        absentStates: STATE_MIXED,
+        interfaces: [ nsIAccessibleValue ]
+      };
+      testElm("progress", obj);
+
+      obj = {
+        role: ROLE_PROGRESSBAR,
+        states: STATE_MIXED
+      };
+      testElm("progress_indeterminate", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:q
+
+      obj = {
+        role: ROLE_TEXT_CONTAINER,
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+        children: [
+          { role: ROLE_STATICTEXT }, // left quote
+          { role: ROLE_TEXT_LEAF }, // quoted text
+          { role: ROLE_STATICTEXT } // right quote
+        ]
+      };
+      testElm("q", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:ruby
+
+      todo(isAccessible("ruby"), "ruby element is not accessible");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:s contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "text-line-through-style": "solid" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:i text
+        ]
+      };
+      testElm("s_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:samp contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:samp text
+        ]
+      };
+      testElm("samp_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:section
+
+      obj = {
+        role: ROLE_SECTION,
+        attributes: { "xml-roles": "region" },
+        interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+      };
+      testElm("section", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:small contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "font-size": "10pt" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:small text
+        ]
+      };
+      testElm("small_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:source
+
+      ok(!isAccessible("source"), "source element is not accessible");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:span
+
+      ok(!isAccessible("span"), "span element is not accessible");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:strong contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:strong text
+        ]
+      };
+      testElm("strong_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:sub contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "text-position": "sub" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:sub text
+        ]
+      };
+      testElm("sub_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:sup contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "text-position": "super" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:sup text
+        ]
+      };
+      testElm("sup_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:svg
+
+      obj = {
+        todo_role: ROLE_GRAPHIC
+      };
+      testElm("svg", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:textarea
+
+      obj = {
+        role: ROLE_ENTRY,
+        extraStates: EXT_STATE_MULTI_LINE | EXT_STATE_EDITABLE,
+        actions: "activate",
+        interfaces: [ nsIAccessibleText, nsIAccessibleEditableText ]
+      };
+      testElm("textarea", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:time
+
+      ok(!isAccessible("time"), "time element is not accessible");
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:u contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        textAttrs: {
+          0: { },
+          6: { "text-underline-style" : "solid" }
+        },
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:u text
+        ]
+      };
+      testElm("u_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:var contained by paragraph
+
+      obj = {
+        role: ROLE_PARAGRAPH,
+        children: [
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF }, // HTML:var text
+          { role: ROLE_TEXT_LEAF }, // plain text
+          { role: ROLE_TEXT_LEAF } // HTML:var text
+        ]
+      };
+      testElm("var_container", obj);
+
+      //////////////////////////////////////////////////////////////////////////
+      obj = { // HTML:video
+        role: ROLE_GROUPING
+      };
+      testElm("video", obj);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+
+  
+    </script>
+</head>
+<body>
+
+  <a target="_blank"
+    title="Implement figure and figcaption accessibility"
+    href="https://bugzilla.mozilla.org/show_bug.cgi?id=658272">
+     Mozilla Bug 658272
+  </a><br/>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <a id="a_href" href="www.mozilla.com">mozilla site</a>
+  <a id="a_nohref">anchor</a>
+  <table>
+    <tr>
+      <td id="td_abbr"><abbr title="World Wide Web">WWW</abbr></td>
+    </tr>
+  </table>
+  <address id="address">
+    Mozilla Foundation<br>
+    1981 Landings Drive<br>
+    Building K<br>
+    Mountain View, CA 94043-0801<br>
+    USA
+  </address>
+
+  <map name="atoz_map">
+    <area id="area_href"
+          href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+          coords="17,0,30,14" alt="b" shape="rect">
+    <area id="area_nohref"
+          coords="0,0,13,14" alt="a" shape="rect">
+  </map>
+  <img id="imgmap" width="447" height="15"
+       usemap="#atoz_map"
+       src="../letters.gif">
+
+  <article id="article">A document</article>
+  <audio id="audio" controls="true">
+    <source id="source" src="../bug461281.ogg" type="video/ogg">
+  </audio>
+
+  <aside id="aside">
+    <p>Some content related to an &lt;article&gt;</p>
+  </aside>
+
+  <p id="b_container">normal<b>bold</b></p>
+  <p id="bdi_container">User <bdi>إيان</bdi>: 90 points</p>
+  <p id="bdo_container"><bdo dir="rtl">This text will go right to left.</bdo></p>
+
+  <blockquote id="blockquote" cite="http://developer.mozilla.org">
+    <p>This is a quotation taken from the Mozilla Developer Center.</p>
+  </blockquote>
+
+  <p id="br_container"><br></p>
+
+  <button id="button">button</button>
+  <form>
+    <button id="button_default" type="submit">button</button>
+  </form>
+
+  <canvas id="canvas"></canvas>
+
+  <table id="table">
+    <caption id="caption">caption</caption>
+    <thead>
+      <tr>
+        <th>col1</th><th>col2</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <th>col1</th><td>cell2</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td>cell5</td><td>cell6</td>
+      </tr>
+    </tfoot>
+  </table>
+
+  <p id="cite_container">normal<cite>cite</cite></p>
+  <p id="code_container">normal<code>code</code></p>
+
+  <table id="colNcolgroup_table">
+    <colgroup>
+      <col>
+      <col span="2">
+    </colgroup>
+    <tr>
+      <td>Lime</td>
+      <td>Lemon</td>
+      <td>Orange</td>
+    </tr>
+  </table>
+
+  <p id="data_container"><data value="8">Eight</data></p>
+
+  <datalist id="datalist">
+    <summary id="summary">details</summary>
+    <option>Paris</option>
+    <option>San Francisco</option>
+  </datalist>
+  <input id="autocomplete_datalist" list="datalist">
+
+  <dl id="dl">
+    <dt>item1</dt><dd>description</dd>
+  </dl>
+
+  <p id="del_container">normal<del>Removed</del></p>
+
+  <details id="details" open="open">
+    <summary>Information</summary>
+    <p>If your browser supports this element, it should allow you to expand and collapse these details.</p>
+  </details>
+
+  <p id="dfn_container"><dfn id="def-internet">The Internet</dfn> is a global
+    system of interconnected networks that use the Internet Protocol Suite (TCP/IP)
+    to serve billions of users worldwide.</p>
+
+  <dialog id="dialog" open="true">This is a dialog</dialog>
+
+  <div id="div">div</div>
+
+  <p id="em_container">normal<em>emphasis</em></p>
+
+  <embed id="embed_plugin_windowless" type="application/x-test"
+         width="300" height="300"></embed>
+  <embed id="embed_plugin_windowed" type="application/x-test" wmode="window"
+         width="300" height="300"></embed>
+
+  <fieldset id="fieldset">
+    <legend id="legend">legend</legend>
+    <input />
+  </fieldset>
+
+  <figure id="figure">
+    <img src="../moz.png" alt="An awesome picture">
+    <figcaption id="figcaption">Caption for the awesome picture</figcaption>
+  </figure>
+
+  <footer id="footer">Some copyright info</footer>
+  <article>
+    <footer id="footer_in_article">Some copyright info</footer>
+  </article>
+  <section>
+    <footer id="footer_in_section">Some copyright info</footer>
+  </section>
+
+  <form id="form"></form>
+
+  <iframe id="frameset_container"
+          src="data:text/html,<html><frameset><frame src='data:text/html,hi'></frame></frameset></html>">
+  </iframe>
+
+  <h1 id="h1">heading1</h1>
+  <h2 id="h2">heading2</h2>
+  <h3 id="h3">heading3</h3>
+  <h4 id="h4">heading4</h4>
+  <h5 id="h5">heading5</h5>
+  <h6 id="h6">heading6</h6>
+
+  <header id="header">A logo</header>
+  <article>
+    <header id="header_in_article">Not logo</header>
+  </article>
+  <section>
+    <header id="header_in_section">Not logo</header>
+  </section>
+
+  <hr id="hr">
+  <p id="i_container">normal<i>italic</i></p>
+  <img id="img" src="../moz.png">
+
+  <input id="input_button" type="button" value="Button">
+  <input id="input_checkbox" type="checkbox">
+  <input id="input_checkbox_true" type="checkbox" checked>
+  <input id="input_file" type="file">
+  <input id="input_image" type="image">
+  <form>
+    <input id="input_image_default" type="image">
+  </form>
+  <input id="input_submit" type="submit">
+  <form>
+    <input id="input_submit_default" type="submit">
+  </form>
+  <input id="input_number" type="number" value="44">
+  <input id="input_text" type="text" value="hi">
+  <input id="input_search" type="search" value="cats">
+  <input id="input_email" type="email" value="me@mozilla.com">
+  <input id="input_tel" type="tel" value="111.111.1111">
+  <input id="input_url" type="url" value="www.mozilla.com">
+  <input id="input_password" type="password" value="44">
+  <input id="input_password_readonly" type="password" value="44" readonly>
+  <input id="input_radio" type="radio">
+  <input id="input_radio_true" type="radio" checked>
+  <input id="input_range" type="range">
+  <form>
+    <input id="input_reset" type="reset">
+  </form>
+
+  <p id="ins_container">normal<ins>Inserted</ins></p>
+  <p id="kbd_container">normal<kbd>cmd</kbd></p>
+  <keygen id="keygen" name="RSA public key" challenge="123456789" keytype="RSA">
+
+  <label id="label">label<input id="label_input"></label>
+  <label id="label_for" for="label_for_input">label</label>
+  <input id="label_for_input">
+
+  <ul id="ul">
+    <li>item1</li>
+  </ul>
+  <ol id="ol">
+    <li>item1</li>
+  </ol>
+
+  <main id="main">main</main>
+
+  <map id="map_imagemap" name="atoz_map">
+    <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+          coords="17,0,30,14" alt="b" shape="rect">
+    <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
+          coords="0,0,13,14" alt="a" shape="rect">
+  </map>
+  <img id="imgmap" width="447" height="15"
+       usemap="#atoz_map"
+       src="../letters.gif">
+
+  <map id="map" title="Navigation Bar" name="mapgroup">
+    <p>
+      [<a href="#how">Bypass navigation bar</a>]
+      [<a href="home.html">Home</a>]
+    </p>
+  </map>
+
+  <p id="mark_container">normal<mark>highlighted</mark></p>
+
+  <math id="math">
+    <mrow>
+      <mrow>
+        <msup>
+          <mi>a</mi>
+          <mn>2</mn>
+        </msup>
+        <mo>+</mo>
+        <msup>
+          <mi>b</mi>
+          <mn>2</mn>
+        </msup>
+      </mrow>
+      <mo>=</mo>
+      <msup>
+        <mi>c</mi>
+        <mn>2</mn>
+      </msup>
+    </mrow>
+  </math>
+
+  <menu id="menu" type="toolbar">
+    <li>
+      <menu label="File">
+        <button type="button" onclick="new()">New...</button>
+        <button type="button" onclick="save()">Save...</button>
+      </menu>
+    </li>
+    <li>
+      <menu label="Edit">
+        <button type="button" onclick="cut()">Cut...</button>
+        <button type="button" onclick="copy()">Copy...</button>
+        <button type="button" onclick="paste()">Paste...</button>
+      </menu>
+    </li>
+  </menu>
+
+  <meter id="meter" min="0" max="1000" low="300" high="700" value="200">200 Euro</meter>
+
+  <nav id="nav">
+    <ul>
+      <li><a href="#">Home</a></li>
+      <li><a href="#">About</a></li>
+      <li><a href="#">Contact</a></li>
+    </ul>
+  </nav>
+
+  <object id="object_plugin_windowless" type="application/x-test"
+         width="300" height="300">
+    <param name="foo" value="bar">
+  </object>
+  <object id="object_plugin_windowed" type="application/x-test" wmode="window"
+         width="300" height="300"></object>
+
+  <select id="select_listbox" size="4">
+    <optgroup label="Colors">
+      <option>Red</option>
+      <option>Blue</option>
+    </optgroup>
+    <option>Animal</option>
+  </select>
+
+  <select id="select_listbox_multiselectable" multiple>
+    <option>Red</option>
+    <option>Blue</option>
+    <option>Green</option>
+  </select>
+
+  <select id="select_combobox">
+    <option>Red</option>
+    <option>Blue</option>
+    <option>Green</option>
+  </select>
+
+  <input id="output_input">
+  <output id="output" for="output_input"></output>
+
+  <pre id="pre">pre</pre>
+
+  <progress id="progress" min="0" value="21" max="42"></progress>
+  <progress id="progress_indeterminate"></progress>
+
+  <q id="q" cite="http://en.wikipedia.org/wiki/Kenny_McCormick#Cultural_impact">
+    Oh my God, they killed Kenny!
+  </q>
+
+  <ruby id="ruby">
+    漢 <rp>(</rp><rt>Kan</rt><rp>)</rp>
+    字 <rp>(</rp><rt>ji</rt><rp>)</rp>
+  </ruby>
+
+  <p id="s_container">normal<s>striked</s></p>
+  <p id="samp_container">normal<samp>sample</samp></p>
+  <section id="section">section</section>
+  <p id="small_container">normal<small>small</small></p>
+  <span id="span"></span>
+  <p id="strong_container">normal<strong>strong</strong></p>
+  <p id="sub_container">normal<sub>sub</sub></p>
+  <p id="sup_container">normal<sup>sup</sup></p>
+
+  <svg id="svg"></svg>
+  <textarea id="textarea"></textarea>
+
+  <p>The concert took place on <time id="time" datetime="2001-05-15 19:00">May 15</time></p>
+  <p id="u_container">normal<u>underline</u></p>
+  <p id="var_container">An equation: <var>x</var> = <var>y</var></p>
+
+  <video id="video" controls="true">
+    <source id="source" src="../bug461281.ogg" type="video/ogg">
+  </video>
+
+</video>
+</body>
+</html>
--- a/accessible/tests/mochitest/role.js
+++ b/accessible/tests/mochitest/role.js
@@ -19,16 +19,17 @@ const ROLE_COMBOBOX_OPTION = nsIAccessib
 const ROLE_COLUMNHEADER = nsIAccessibleRole.ROLE_COLUMNHEADER;
 const ROLE_DEFINITION = nsIAccessibleRole.ROLE_DEFINITION;
 const ROLE_DEFINITION_LIST = nsIAccessibleRole.ROLE_DEFINITION_LIST;
 const ROLE_DIAGRAM = nsIAccessibleRole.ROLE_DIAGRAM;
 const ROLE_DIALOG = nsIAccessibleRole.ROLE_DIALOG;
 const ROLE_DOCUMENT = nsIAccessibleRole.ROLE_DOCUMENT;
 const ROLE_EMBEDDED_OBJECT = nsIAccessibleRole.ROLE_EMBEDDED_OBJECT;
 const ROLE_ENTRY = nsIAccessibleRole.ROLE_ENTRY;
+const ROLE_EQUATION = nsIAccessibleRole.ROLE_EQUATION;
 const ROLE_FIGURE = nsIAccessibleRole.ROLE_FIGURE;
 const ROLE_FOOTER = nsIAccessibleRole.ROLE_FOOTER;
 const ROLE_FLAT_EQUATION = nsIAccessibleRole.ROLE_FLAT_EQUATION;
 const ROLE_FORM = nsIAccessibleRole.ROLE_FORM;
 const ROLE_GRAPHIC = nsIAccessibleRole.ROLE_GRAPHIC;
 const ROLE_GRID_CELL = nsIAccessibleRole.ROLE_GRID_CELL;
 const ROLE_GROUPING = nsIAccessibleRole.ROLE_GROUPING;
 const ROLE_HEADER = nsIAccessibleRole.ROLE_HEADER;
--- a/accessible/tests/mochitest/states.js
+++ b/accessible/tests/mochitest/states.js
@@ -73,18 +73,20 @@ const kExtraState = 1;
 function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState,
                     aAbsentExtraState, aTestName)
 {
   var [state, extraState] = getStates(aAccOrElmOrID);
   var role = getRole(aAccOrElmOrID);
   var id = prettyName(aAccOrElmOrID) + (aTestName ? " [" + aTestName + "]": "");
 
   // Primary test.
-  isState(state & aState, aState, false,
-          "wrong state bits for " + id + "!");
+  if (aState) {
+    isState(state & aState, aState, false,
+            "wrong state bits for " + id + "!");
+  }
 
   if (aExtraState)
     isState(extraState & aExtraState, aExtraState, true,
             "wrong extra state bits for " + id + "!");
 
   if (aAbsentState)
     isState(state & aAbsentState, 0, false,
             "state bits should not be present in ID " + id + "!");
--- a/accessible/tests/mochitest/treeupdate/test_cssoverflow.html
+++ b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html
@@ -7,16 +7,18 @@
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
 
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
     ////////////////////////////////////////////////////////////////////////////
 
--- a/accessible/tests/mochitest/treeupdate/test_gencontent.html
+++ b/accessible/tests/mochitest/treeupdate/test_gencontent.html
@@ -16,16 +16,18 @@
   </style>
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
 
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
     ////////////////////////////////////////////////////////////////////////////
 
--- a/accessible/tests/mochitest/treeupdate/test_optgroup.html
+++ b/accessible/tests/mochitest/treeupdate/test_optgroup.html
@@ -6,16 +6,18 @@
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
 
     function addOptGroup(aID)
     {
       this.selectNode = getNode(aID);
       this.select = getAccessible(this.selectNode);
--- a/accessible/tests/mochitest/treeupdate/test_select.html
+++ b/accessible/tests/mochitest/treeupdate/test_select.html
@@ -6,16 +6,18 @@
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
 
     function addOptions(aID)
     {
       this.selectNode = getNode(aID);
       this.select = getAccessible(this.selectNode);
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "78decf03b39ded18b914d57f4f3152d6f1b4c56a", 
+    "revision": "f98ec5e292dffd144f0a6520cc18ff5cb7762d09", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -331,17 +331,17 @@ pref("browser.download.manager.closeWhen
 pref("browser.download.manager.focusWhenStarting", false);
 pref("browser.download.manager.flashCount", 2);
 pref("browser.download.manager.addToRecentDocs", true);
 pref("browser.download.manager.quitBehavior", 0);
 pref("browser.download.manager.scanWhenDone", true);
 pref("browser.download.manager.resumeOnWakeDelay", 10000);
 
 // Enables the asynchronous Downloads API in the Downloads Panel.
-pref("browser.download.useJSTransfer", false);
+pref("browser.download.useJSTransfer", true);
 
 // This allows disabling the Downloads Panel in favor of the old interface.
 pref("browser.download.useToolkitUI", false);
 
 // This allows disabling the animated notifications shown by
 // the Downloads Indicator when a download starts or completes.
 pref("browser.download.animateNotifications", true);
 
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -165,16 +165,20 @@ let gObserver = new MutationObserver(fun
 });
 
 window.addEventListener("pageshow", function () {
   // Delay search engine setup, cause browser.js::BrowserOnAboutPageLoad runs
   // later and may use asynchronous getters.
   window.gObserver.observe(document.documentElement, { attributes: true });
   fitToWidth();
   window.addEventListener("resize", fitToWidth);
+
+  // Ask chrome to update snippets.
+  var event = new CustomEvent("AboutHomeLoad", {bubbles:true});
+  document.dispatchEvent(event);
 });
 
 window.addEventListener("pagehide", function() {
   window.gObserver.disconnect();
   window.removeEventListener("resize", fitToWidth);
 });
 
 // This object has the same interface as Map and is used to store and retrieve
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -79,19 +79,16 @@ this.__defineGetter__("PluralForm", func
 this.__defineSetter__("PluralForm", function (val) {
   delete this.PluralForm;
   return this.PluralForm = val;
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
   "resource://gre/modules/TelemetryStopwatch.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
-  "resource:///modules/AboutHomeUtils.jsm");
-
 #ifdef MOZ_SERVICES_SYNC
 XPCOMUtils.defineLazyModuleGetter(this, "Weave",
   "resource://services-sync/main.js");
 #endif
 
 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
   let tmp = {};
   Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
@@ -1279,16 +1276,17 @@ var gBrowserInit = {
     }, this);
 #endif
 #endif
 
     if (gMultiProcessBrowser) {
       // Bug 862519 - Backspace doesn't work in electrolysis builds.
       // We bypass the problem by disabling the backspace-to-go-back command.
       document.getElementById("cmd_handleBackspace").setAttribute("disabled", true);
+      document.getElementById("key_delete").setAttribute("disabled", true);
     }
 
     SessionStore.promiseInitialized.then(() => {
       // Enable the Restore Last Session command if needed
       if (SessionStore.canRestoreLastSession &&
           !PrivateBrowsingUtils.isWindowPrivate(window))
         goSetCommandEnabled("Browser:RestoreLastSession", true);
 
@@ -2310,74 +2308,16 @@ function SetPageProxyState(aState)
 
 function PageProxyClickHandler(aEvent)
 {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
     middleMousePaste(aEvent);
 }
 
 /**
- *  Handle load of some pages (about:*) so that we can make modifications
- *  to the DOM for unprivileged pages.
- */
-function BrowserOnAboutPageLoad(doc) {
-  if (doc.documentURI.toLowerCase() == "about:home") {
-    // XXX bug 738646 - when Marketplace is launched, remove this statement and
-    // the hidden attribute set on the apps button in aboutHome.xhtml
-    if (getBoolPref("browser.aboutHome.apps", false))
-      doc.getElementById("apps").removeAttribute("hidden");
-
-    let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
-             getService(Components.interfaces.nsISessionStore);
-    if (ss.canRestoreLastSession &&
-        !PrivateBrowsingUtils.isWindowPrivate(window))
-      doc.getElementById("launcher").setAttribute("session", "true");
-
-    // Inject search engine and snippets URL.
-    let docElt = doc.documentElement;
-    // set the following attributes BEFORE searchEngineURL, which triggers to
-    // show the snippets when it's set.
-    docElt.setAttribute("snippetsURL", AboutHomeUtils.snippetsURL);
-    if (AboutHomeUtils.showKnowYourRights) {
-      docElt.setAttribute("showKnowYourRights", "true");
-      // Set pref to indicate we've shown the notification.
-      let currentVersion = Services.prefs.getIntPref("browser.rights.version");
-      Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
-    }
-    docElt.setAttribute("snippetsVersion", AboutHomeUtils.snippetsVersion);
-
-    let updateSearchEngine = function() {
-      let engine = AboutHomeUtils.defaultSearchEngine;
-      docElt.setAttribute("searchEngineName", engine.name);
-      docElt.setAttribute("searchEnginePostData", engine.postDataString || "");
-      // Again, keep the searchEngineURL as the last attribute, because the
-      // mutation observer in aboutHome.js is counting on that.
-      docElt.setAttribute("searchEngineURL", engine.searchURL);
-    };
-    updateSearchEngine();
-
-    // Listen for the event that's triggered when the user changes search engine.
-    // At this point we simply reload about:home to reflect the change.
-    Services.obs.addObserver(updateSearchEngine, "browser-search-engine-modified", false);
-
-    // Remove the observer when the page is reloaded or closed.
-    doc.defaultView.addEventListener("pagehide", function removeObserver() {
-      doc.defaultView.removeEventListener("pagehide", removeObserver);
-      Services.obs.removeObserver(updateSearchEngine, "browser-search-engine-modified");
-    }, false);
-
-#ifdef MOZ_SERVICES_HEALTHREPORT
-    doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
-      BrowserSearch.recordSearchInHealthReport(e.detail, "abouthome");
-    }, true, true);
-#endif
-  }
-}
-
-/**
  * Handle command events bubbling up from error page content
  */
 let BrowserOnClick = {
   handleEvent: function BrowserOnClick_handleEvent(aEvent) {
     if (!aEvent.isTrusted || // Don't trust synthetic events
         aEvent.button == 2 || aEvent.target.localName != "button") {
       return;
     }
@@ -2391,19 +2331,16 @@ let BrowserOnClick = {
       this.onAboutCertError(originalTarget, ownerDoc);
     }
     else if (ownerDoc.documentURI.startsWith("about:blocked")) {
       this.onAboutBlocked(originalTarget, ownerDoc);
     }
     else if (ownerDoc.documentURI.startsWith("about:neterror")) {
       this.onAboutNetError(originalTarget, ownerDoc);
     }
-    else if (ownerDoc.documentURI.toLowerCase() == "about:home") {
-      this.onAboutHome(originalTarget, ownerDoc);
-    }
   },
 
   onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
     let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
 
     switch (elmId) {
@@ -2570,59 +2507,16 @@ let BrowserOnClick = {
   },
 
   onAboutNetError: function BrowserOnClick_onAboutNetError(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
     if (elmId != "errorTryAgain" || !/e=netOffline/.test(aOwnerDoc.documentURI))
       return;
     Services.io.offline = false;
   },
-
-  onAboutHome: function BrowserOnClick_onAboutHome(aTargetElm, aOwnerDoc) {
-    let elmId = aTargetElm.getAttribute("id");
-
-    switch (elmId) {
-      case "restorePreviousSession":
-        let ss = Cc["@mozilla.org/browser/sessionstore;1"].
-                 getService(Ci.nsISessionStore);
-        if (ss.canRestoreLastSession) {
-          ss.restoreLastSession();
-        }
-        aOwnerDoc.getElementById("launcher").removeAttribute("session");
-        break;
-
-      case "downloads":
-        BrowserDownloadsUI();
-        break;
-
-      case "bookmarks":
-        PlacesCommandHook.showPlacesOrganizer("AllBookmarks");
-        break;
-
-      case "history":
-        PlacesCommandHook.showPlacesOrganizer("History");
-        break;
-
-      case "apps":
-        openUILinkIn("https://marketplace.mozilla.org/", "tab");
-        break;
-
-      case "addons":
-        BrowserOpenAddonsMgr();
-        break;
-
-      case "sync":
-        openPreferences("paneSync");
-        break;
-
-      case "settings":
-        openPreferences();
-        break;
-    }
-  },
 };
 
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
  * when their own homepage is infected, we can get them somewhere safe.
@@ -4292,32 +4186,30 @@ var TabsProgressListener = {
     // document URI is not yet the about:-uri of the error page.
 
     let doc = gMultiProcessBrowser ? null : aWebProgress.DOMWindow.document;
     if (!gMultiProcessBrowser &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         Components.isSuccessCode(aStatus) &&
         doc.documentURI.startsWith("about:") &&
         !doc.documentURI.toLowerCase().startsWith("about:blank") &&
+        !doc.documentURI.toLowerCase().startsWith("about:home") &&
         !doc.documentElement.hasAttribute("hasBrowserHandlers")) {
       // STATE_STOP may be received twice for documents, thus store an
       // attribute to ensure handling it just once.
       doc.documentElement.setAttribute("hasBrowserHandlers", "true");
       aBrowser.addEventListener("click", BrowserOnClick, true);
       aBrowser.addEventListener("pagehide", function onPageHide(event) {
         if (event.target.defaultView.frameElement)
           return;
         aBrowser.removeEventListener("click", BrowserOnClick, true);
         aBrowser.removeEventListener("pagehide", onPageHide, true);
         if (event.target.documentElement)
           event.target.documentElement.removeAttribute("hasBrowserHandlers");
       }, true);
-
-      // We also want to make changes to page UI for unprivileged about pages.
-      BrowserOnAboutPageLoad(doc);
     }
   },
 
   onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI,
                               aFlags) {
     // Filter out location changes caused by anchor navigation
     // or history.push/pop/replaceState.
     if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -8,16 +8,18 @@ let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this,
   "LoginManagerContent", "resource://gre/modules/LoginManagerContent.jsm");
 XPCOMUtils.defineLazyModuleGetter(this,
   "InsecurePasswordUtils", "resource://gre/modules/InsecurePasswordUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 // Bug 671101 - directly using webNavigation in this context
 // causes docshells to leak
 this.__defineGetter__("webNavigation", function () {
   return docShell.QueryInterface(Ci.nsIWebNavigation);
 });
 
 addMessageListener("WebNavigation:LoadURI", function (message) {
@@ -50,16 +52,145 @@ if (Services.prefs.getBoolPref("browser.
   addEventListener("DOMAutoComplete", function(event) {
     LoginManagerContent.onUsernameInput(event);
   });
   addEventListener("blur", function(event) {
     LoginManagerContent.onUsernameInput(event);
   });
 }
 
+let AboutHomeListener = {
+  init: function(chromeGlobal) {
+    let self = this;
+    chromeGlobal.addEventListener('AboutHomeLoad', function(e) { self.onPageLoad(); }, false, true);
+  },
+
+  handleEvent: function(aEvent) {
+    switch (aEvent.type) {
+      case "AboutHomeLoad":
+        this.onPageLoad();
+        break;
+    }
+  },
+
+  receiveMessage: function(aMessage) {
+    switch (aMessage.name) {
+      case "AboutHome:Update":
+        this.onUpdate(aMessage.data);
+        break;
+    }
+  },
+
+  onUpdate: function(aData) {
+    let doc = content.document;
+    if (doc.documentURI.toLowerCase() != "about:home")
+      return;
+
+    if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isWindowPrivate(content))
+      doc.getElementById("launcher").setAttribute("session", "true");
+
+    // Inject search engine and snippets URL.
+    let docElt = doc.documentElement;
+    // set the following attributes BEFORE searchEngineURL, which triggers to
+    // show the snippets when it's set.
+    docElt.setAttribute("snippetsURL", aData.snippetsURL);
+    if (aData.showKnowYourRights)
+      docElt.setAttribute("showKnowYourRights", "true");
+    docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
+
+    let engine = aData.defaultSearchEngine;
+    docElt.setAttribute("searchEngineName", engine.name);
+    docElt.setAttribute("searchEnginePostData", engine.postDataString || "");
+    // Again, keep the searchEngineURL as the last attribute, because the
+    // mutation observer in aboutHome.js is counting on that.
+    docElt.setAttribute("searchEngineURL", engine.searchURL);
+  },
+
+  onPageLoad: function() {
+    let doc = content.document;
+    if (doc.documentURI.toLowerCase() != "about:home" ||
+        doc.documentElement.hasAttribute("hasBrowserHandlers")) {
+      return;
+    }
+
+    doc.documentElement.setAttribute("hasBrowserHandlers", "true");
+    let updateListener = this;
+    addMessageListener("AboutHome:Update", updateListener);
+    addEventListener("click", this.onClick, true);
+    addEventListener("pagehide", function onPageHide(event) {
+      if (event.target.defaultView.frameElement)
+        return;
+      removeMessageListener("AboutHome:Update", updateListener);
+      removeEventListener("click", this.onClick, true);
+      removeEventListener("pagehide", onPageHide, true);
+      if (event.target.documentElement)
+        event.target.documentElement.removeAttribute("hasBrowserHandlers");
+    }, true);
+
+    // XXX bug 738646 - when Marketplace is launched, remove this statement and
+    // the hidden attribute set on the apps button in aboutHome.xhtml
+    if (Services.prefs.getPrefType("browser.aboutHome.apps") == Services.prefs.PREF_BOOL &&
+        Services.prefs.getBoolPref("browser.aboutHome.apps"))
+      doc.getElementById("apps").removeAttribute("hidden");
+
+    sendAsyncMessage("AboutHome:RequestUpdate");
+
+    doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
+      sendAsyncMessage("AboutHome:Search", { engineName: e.detail });
+    }, true, true);
+  },
+
+  onClick: function(aEvent) {
+    if (!aEvent.isTrusted || // Don't trust synthetic events
+        aEvent.button == 2 || aEvent.target.localName != "button") {
+      return;
+    }
+
+    let originalTarget = aEvent.originalTarget;
+    let ownerDoc = originalTarget.ownerDocument;
+    let elmId = originalTarget.getAttribute("id");
+
+    switch (elmId) {
+      case "restorePreviousSession":
+        sendAsyncMessage("AboutHome:RestorePreviousSession");
+        ownerDoc.getElementById("launcher").removeAttribute("session");
+        break;
+
+      case "downloads":
+        sendAsyncMessage("AboutHome:Downloads");
+        break;
+
+      case "bookmarks":
+        sendAsyncMessage("AboutHome:Bookmarks");
+        break;
+
+      case "history":
+        sendAsyncMessage("AboutHome:History");
+        break;
+
+      case "apps":
+        sendAsyncMessage("AboutHome:Apps");
+        break;
+
+      case "addons":
+        sendAsyncMessage("AboutHome:Addons");
+        break;
+
+      case "sync":
+        sendAsyncMessage("AboutHome:Sync");
+        break;
+
+      case "settings":
+        sendAsyncMessage("AboutHome:Settings");
+        break;
+    }
+  },
+};
+AboutHomeListener.init(this);
+
 
 var global = this;
 
 let ClickEventHandler = {
   init: function init() {
     Cc["@mozilla.org/eventlistenerservice;1"]
       .getService(Ci.nsIEventListenerService)
       .addSystemEventListener(global, "click", this, true);
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -82,17 +82,18 @@ Sanitizer.prototype = {
           // Callers should check returned errors and give user feedback
           // about items that could not be sanitized
           let item = this.items[itemName];
           try {
             if (aCanClear)
               item.clear();
           } catch(er) {
             seenError = true;
-            Cu.reportError("Error sanitizing " + itemName + ": " + er + "\n");
+            Components.utils.reportError("Error sanitizing " + itemName +
+                                         ": " + er + "\n");
           }
           onItemComplete();
         };
         this.canClearItem(itemName, clearCallback);
       } else {
         onItemComplete();
       }
     }
@@ -316,17 +317,17 @@ Sanitizer.prototype = {
                             download.startTime <= this.range[1]) : null;
 
             // Clear all completed/cancelled downloads
             let publicList = yield Downloads.getPublicDownloadList();
             publicList.removeFinished(filterByTime);
 
             let privateList = yield Downloads.getPrivateDownloadList();
             privateList.removeFinished(filterByTime);
-          }.bind(this)).then(null, Cu.reportError);
+          }.bind(this)).then(null, Components.utils.reportError);
         }
         else {
           var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
                                 .getService(Components.interfaces.nsIDownloadManager);
 
           if (this.range) {
             // First, remove the completed/cancelled downloads
             dlMgr.removeDownloadsByTimeframe(this.range[0], this.range[1]);
--- a/browser/base/content/test/browser_aboutHome.js
+++ b/browser/base/content/test/browser_aboutHome.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/commonjs/sdk/core/promise.js");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
-  "resource:///modules/AboutHomeUtils.jsm");
+  "resource:///modules/AboutHome.jsm");
 
 let gRightsVersion = Services.prefs.getIntPref("browser.rights.version");
 
 registerCleanupFunction(function() {
   // Ensure we don't pollute prefs for next tests.
   Services.prefs.clearUserPref("network.cookies.cookieBehavior");
   Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
   Services.prefs.clearUserPref("browser.rights.override");
@@ -101,24 +101,27 @@ let gTests = [
       return Promise.resolve();
     }
 
     let numSearchesBefore = 0;
     let deferred = Promise.defer();
     let doc = gBrowser.contentDocument;
     let engineName = doc.documentElement.getAttribute("searchEngineName");
 
-    // We rely on the listener in browser.js being installed and fired before
-    // this one. If this ever changes, we should add an executeSoon() or similar.
     doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
       is(e.detail, engineName, "Detail is search engine name");
 
-      getNumberOfSearches(engineName).then(num => {
-        is(num, numSearchesBefore + 1, "One more search recorded.");
-        deferred.resolve();
+      // We use executeSoon() to ensure that this code runs after the
+      // count has been updated in browser.js, since it uses the same
+      // event.
+      executeSoon(function () {
+        getNumberOfSearches(engineName).then(num => {
+          is(num, numSearchesBefore + 1, "One more search recorded.");
+          deferred.resolve();
+        });
       });
     }, true, true);
 
     // Get the current number of recorded searches.
     getNumberOfSearches(engineName).then(num => {
       numSearchesBefore = num;
 
       info("Perform a search.");
@@ -270,40 +273,52 @@ let gTests = [
       info("Observer: " + aData + " for " + engine.name);
 
       if (aData != "engine-added")
         return;
 
       if (engine.name != "POST Search")
         return;
 
+      // Ready to execute the tests!
+      let needle = "Search for something awesome.";
+      let document = gBrowser.selectedTab.linkedBrowser.contentDocument;
+      let searchText = document.getElementById("searchText");
+
+      // We're about to change the search engine. Once the change has
+      // propagated to the about:home content, we want to perform a search.
+      let mutationObserver = new MutationObserver(function (mutations) {
+        for (let mutation of mutations) {
+          if (mutation.attributeName == "searchEngineURL") {
+            searchText.value = needle;
+            searchText.focus();
+            EventUtils.synthesizeKey("VK_RETURN", {});
+          }
+        }
+      });
+      mutationObserver.observe(document.documentElement, { attributes: true });
+
+      // Change the search engine, triggering the observer above.
       Services.search.defaultEngine = engine;
 
       registerCleanupFunction(function() {
+        mutationObserver.disconnect();
         Services.search.removeEngine(engine);
         Services.search.defaultEngine = currEngine;
       });
 
 
-      // Ready to execute the tests!
-      let needle = "Search for something awesome.";
-      let document = gBrowser.selectedTab.linkedBrowser.contentDocument;
-      let searchText = document.getElementById("searchText");
-
+      // When the search results load, check them for correctness.
       waitForLoad(function() {
         let loadedText = gBrowser.contentDocument.body.textContent;
         ok(loadedText, "search page loaded");
         is(loadedText, "searchterms=" + escape(needle.replace(/\s/g, "+")),
            "Search text should arrive correctly");
         deferred.resolve();
       });
-
-      searchText.value = needle;
-      searchText.focus();
-      EventUtils.synthesizeKey("VK_RETURN", {});
     };
     Services.obs.addObserver(searchObserver, "browser-search-engine-modified", false);
     registerCleanupFunction(function () {
       Services.obs.removeObserver(searchObserver, "browser-search-engine-modified");
     });
     Services.search.addEngine("http://test:80/browser/browser/base/content/test/POSTSearchEngine.xml",
                               Ci.nsISearchEngine.DATA_XML, null, false);
     return deferred.promise;
--- a/browser/base/content/test/browser_sanitize-timespans.js
+++ b/browser/base/content/test/browser_sanitize-timespans.js
@@ -1,39 +1,53 @@
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 // Bug 453440 - Test the timespan-based logic of the sanitizer code
 var now_uSec = Date.now() * 1000;
 
-const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
-
 const kUsecPerMin = 60 * 1000000;
 
 let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
                                            .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
 let Sanitizer = tempScope.Sanitizer;
 
 let FormHistory = (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
+let Downloads = (Components.utils.import("resource://gre/modules/Downloads.jsm", {})).Downloads;
 
 function promiseFormHistoryRemoved() {
   let deferred = Promise.defer();
   Services.obs.addObserver(function onfh() {
     Services.obs.removeObserver(onfh, "satchel-storage-changed", false);
     deferred.resolve();
   }, "satchel-storage-changed", false);
   return deferred.promise;
 }
 
+function promiseDownloadRemoved(list) {
+  let deferred = Promise.defer();
+
+  let view = {
+    onDownloadRemoved: function(download) {
+      list.removeView(view);
+      deferred.resolve();
+    }
+  };
+
+  list.addView(view);
+  
+  return deferred.promise;
+}
+
 function test() {
   waitForExplicitFinish();
 
   Task.spawn(function() {
-    setupDownloads();
+    yield setupDownloads();
     yield setupFormHistory();
     yield setupHistory();
     yield onHistoryReady();
   }).then(finish);
 }
 
 function countEntries(name, message, check) {
   let deferred = Promise.defer();
@@ -75,22 +89,26 @@ function onHistoryReady() {
   itemPrefs.setBoolPref("cache", false);
   itemPrefs.setBoolPref("cookies", false);
   itemPrefs.setBoolPref("formdata", true);
   itemPrefs.setBoolPref("offlineApps", false);
   itemPrefs.setBoolPref("passwords", false);
   itemPrefs.setBoolPref("sessions", false);
   itemPrefs.setBoolPref("siteSettings", false);
 
+  let publicList = yield Downloads.getPublicDownloadList();
+  let downloadPromise = promiseDownloadRemoved(publicList);
+
   // Clear 10 minutes ago
   s.range = [now_uSec - 10*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   ok(!(yield promiseIsURIVisited(makeURI("http://10minutes.com"))),
      "Pretend visit to 10minutes.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://1hour.com"))),
      "Pretend visit to 1hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
      "Pretend visit to 1hour10minutes.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://2hour.com"))),
@@ -117,33 +135,37 @@ function onHistoryReady() {
   yield countEntries("2hour", "2hour form entry should still exist", checkOne);
   yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
   yield countEntries("4hour", "4hour form entry should still exist", checkOne);
   yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (minutesSinceMidnight > 10)
     yield countEntries("today", "today form entry should still exist", checkOne);
   yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
-  ok(!downloadExists(5555555), "10 minute download should now be deleted");
-  ok(downloadExists(5555551), "<1 hour download should still be present");
-  ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
-  ok(downloadExists(5555550), "Year old download should still be present");
-  ok(downloadExists(5555552), "<2 hour old download should still be present");
-  ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
-  ok(downloadExists(5555553), "<4 hour old download should still be present");
-  ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
+  let downloads = yield publicList.getAll();
+  ok(!(yield downloadExists(publicList, "fakefile-10-minutes")), "10 minute download should now be deleted");
+  ok((yield downloadExists(publicList, "fakefile-1-hour")), "<1 hour download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
 
   if (minutesSinceMidnight > 10)
-    ok(downloadExists(5555554), "'Today' download should still be present");
+    ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
+
+  downloadPromise = promiseDownloadRemoved(publicList);
 
   // Clear 1 hour
   Sanitizer.prefs.setIntPref("timeSpan", 1);
   s.sanitize();
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   ok(!(yield promiseIsURIVisited(makeURI("http://1hour.com"))),
      "Pretend visit to 1hour.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
      "Pretend visit to 1hour10minutes.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://2hour.com"))),
      "Pretend visit to 2hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
@@ -164,33 +186,36 @@ function onHistoryReady() {
   yield countEntries("2hour", "2hour form entry should still exist", checkOne);
   yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
   yield countEntries("4hour", "4hour form entry should still exist", checkOne);
   yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (hoursSinceMidnight > 1)
     yield countEntries("today", "today form entry should still exist", checkOne);
   yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
-  ok(!downloadExists(5555551), "<1 hour download should now be deleted");
-  ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
-  ok(downloadExists(5555550), "Year old download should still be present");
-  ok(downloadExists(5555552), "<2 hour old download should still be present");
-  ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
-  ok(downloadExists(5555553), "<4 hour old download should still be present");
-  ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
+  ok(!(yield downloadExists(publicList, "fakefile-1-hour")), "<1 hour download should now be deleted");
+  ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
 
   if (hoursSinceMidnight > 1)
-    ok(downloadExists(5555554), "'Today' download should still be present");
+    ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
   
+  downloadPromise = promiseDownloadRemoved(publicList);
+
   // Clear 1 hour 10 minutes
   s.range = [now_uSec - 70*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   ok(!(yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
      "Pretend visit to 1hour10minutes.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://2hour.com"))),
      "Pretend visit to 2hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
      "Pretend visit to 2hour10minutes.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://4hour.com"))),
@@ -208,30 +233,33 @@ function onHistoryReady() {
   yield countEntries("2hour", "2hour form entry should still exist", checkOne);
   yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
   yield countEntries("4hour", "4hour form entry should still exist", checkOne);
   yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (minutesSinceMidnight > 70)
     yield countEntries("today", "today form entry should still exist", checkOne);
   yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
-  ok(!downloadExists(5555556), "1 hour 10 minute old download should now be deleted");
-  ok(downloadExists(5555550), "Year old download should still be present");
-  ok(downloadExists(5555552), "<2 hour old download should still be present");
-  ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
-  ok(downloadExists(5555553), "<4 hour old download should still be present");
-  ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
+  ok(!(yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute old download should now be deleted");
+  ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
   if (minutesSinceMidnight > 70)
-    ok(downloadExists(5555554), "'Today' download should still be present");
+    ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
+
+  downloadPromise = promiseDownloadRemoved(publicList);
 
   // Clear 2 hours
   Sanitizer.prefs.setIntPref("timeSpan", 2);
   s.sanitize();
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   ok(!(yield promiseIsURIVisited(makeURI("http://2hour.com"))),
      "Pretend visit to 2hour.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
      "Pretend visit to 2hour10minutes.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://4hour.com"))),
      "Pretend visit to 4hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
@@ -246,30 +274,33 @@ function onHistoryReady() {
   yield countEntries("2hour", "2hour form entry should be deleted", checkZero);
   yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
   yield countEntries("4hour", "4hour form entry should still exist", checkOne);
   yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (hoursSinceMidnight > 2)
     yield countEntries("today", "today form entry should still exist", checkOne);
   yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
-  ok(!downloadExists(5555552), "<2 hour old download should now be deleted");
-  ok(downloadExists(5555550), "Year old download should still be present");
-  ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
-  ok(downloadExists(5555553), "<4 hour old download should still be present");
-  ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
+  ok(!(yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should now be deleted");
+  ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
   if (hoursSinceMidnight > 2)
-    ok(downloadExists(5555554), "'Today' download should still be present");
+    ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
   
+  downloadPromise = promiseDownloadRemoved(publicList);
+
   // Clear 2 hours 10 minutes
   s.range = [now_uSec - 130*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   ok(!(yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
      "Pretend visit to 2hour10minutes.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://4hour.com"))),
      "Pretend visit to 4hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
      "Pretend visit to 4hour10minutes.com should should still exist");
   if (minutesSinceMidnight > 130) {
@@ -281,28 +312,31 @@ function onHistoryReady() {
 
   yield countEntries("2hour10minutes", "2hour10minutes form entry should be deleted", checkZero);
   yield countEntries("4hour", "4hour form entry should still exist", checkOne);
   yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (minutesSinceMidnight > 130)
     yield countEntries("today", "today form entry should still exist", checkOne);
   yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
-  ok(!downloadExists(5555557), "2 hour 10 minute old download should now be deleted");
-  ok(downloadExists(5555553), "<4 hour old download should still be present");
-  ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
-  ok(downloadExists(5555550), "Year old download should still be present");
+  ok(!(yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute old download should now be deleted");
+  ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
   if (minutesSinceMidnight > 130)
-    ok(downloadExists(5555554), "'Today' download should still be present");
+    ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
+
+  downloadPromise = promiseDownloadRemoved(publicList);
 
   // Clear 4 hours
   Sanitizer.prefs.setIntPref("timeSpan", 3);
   s.sanitize();
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   ok(!(yield promiseIsURIVisited(makeURI("http://4hour.com"))),
      "Pretend visit to 4hour.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
      "Pretend visit to 4hour10minutes.com should should still exist");
   if (hoursSinceMidnight > 4) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
@@ -311,85 +345,94 @@ function onHistoryReady() {
     "Pretend visit to before-today.com should still exist");
 
   yield countEntries("4hour", "4hour form entry should be deleted", checkZero);
   yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (hoursSinceMidnight > 4)
     yield countEntries("today", "today form entry should still exist", checkOne);
   yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
-  ok(!downloadExists(5555553), "<4 hour old download should now be deleted");
-  ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
-  ok(downloadExists(5555550), "Year old download should still be present");
+  ok(!(yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should now be deleted");
+  ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
   if (hoursSinceMidnight > 4)
-    ok(downloadExists(5555554), "'Today' download should still be present");
+    ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
+
+  downloadPromise = promiseDownloadRemoved(publicList);
 
   // Clear 4 hours 10 minutes
   s.range = [now_uSec - 250*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   ok(!(yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
      "Pretend visit to 4hour10minutes.com should now be deleted");
   if (minutesSinceMidnight > 250) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
   }
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
     "Pretend visit to before-today.com should still exist");
 
   yield countEntries("4hour10minutes", "4hour10minutes form entry should be deleted", checkZero);
   if (minutesSinceMidnight > 250)
     yield countEntries("today", "today form entry should still exist", checkOne);
   yield countEntries("b4today", "b4today form entry should still exist", checkOne);
   
-  ok(!downloadExists(5555558), "4 hour 10 minute download should now be deleted");
-  ok(downloadExists(5555550), "Year old download should still be present");
+  ok(!(yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should now be deleted");
+  ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
   if (minutesSinceMidnight > 250)
-    ok(downloadExists(5555554), "'Today' download should still be present");
+    ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
+
+  downloadPromise = promiseDownloadRemoved(publicList);
 
   // Clear Today
   Sanitizer.prefs.setIntPref("timeSpan", 4);
   s.sanitize();
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   // Be careful.  If we add our objectss just before midnight, and sanitize
   // runs immediately after, they won't be expired.  This is expected, but
   // we should not test in that case.  We cannot just test for opposite
   // condition because we could cross midnight just one moment after we
   // cache our time, then we would have an even worse random failure.
   var today = isToday(new Date(now_uSec/1000));
   if (today) {
     ok(!(yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should now be deleted");
 
     yield countEntries("today", "today form entry should be deleted", checkZero);
-    ok(!downloadExists(5555554), "'Today' download should now be deleted");
+    ok(!(yield downloadExists(publicList, "fakefile-today")), "'Today' download should now be deleted");
   }
 
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
      "Pretend visit to before-today.com should still exist");
   yield countEntries("b4today", "b4today form entry should still exist", checkOne);
-  ok(downloadExists(5555550), "Year old download should still be present");
+  ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
+
+  downloadPromise = promiseDownloadRemoved(publicList);
 
   // Choose everything
   Sanitizer.prefs.setIntPref("timeSpan", 0);
   s.sanitize();
 
   yield promiseFormHistoryRemoved();
+  yield downloadPromise;
 
   ok(!(yield promiseIsURIVisited(makeURI("http://before-today.com"))),
      "Pretend visit to before-today.com should now be deleted");
 
   yield countEntries("b4today", "b4today form entry should be deleted", checkZero);
 
-  ok(!downloadExists(5555550), "Year old download should now be deleted");
+  ok(!(yield downloadExists(publicList, "fakefile-old")), "Year old download should now be deleted");
 }
 
 function setupHistory() {
   let deferred = Promise.defer();
 
   let places = [];
 
   function addPlace(aURI, aTitle, aVisitDate) {
@@ -557,254 +600,124 @@ function setupFormHistory() {
   yield countEntries("4hour10minutes", "Checking for 4hour10minutes form history entry creation", checkOne);
   yield countEntries("today", "Checking for today form history entry creation", checkOne);
   yield countEntries("b4today", "Checking for b4today form history entry creation", checkOne);
   is(checks, 9, "9 checks made");
 }
 
 function setupDownloads() {
 
-  // Add 10-minutes download to DB
-  let data = {
-    id:   "5555555",
-    name: "fakefile-10-minutes",
-    source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
-    target: "fakefile-10-minutes",
-    startTime: now_uSec - 10 * kUsecPerMin, // 10 minutes ago, in uSec
-    endTime: now_uSec - 11 * kUsecPerMin, // 1 minute later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "a1bcD23eF4g5"
-  };
+  let publicList = yield Downloads.getPublicDownloadList();
 
-  let db = dm.DBConnection;
-  let stmt = db.createStatement(
-    "INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
-      "state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
-    "VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
-      ":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
-  
-  // Add within-1-hour download to DB
-  data = {
-    id:   "5555551",
-    name: "fakefile-1-hour",
+  let download = yield Downloads.createDownload({
+    source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
+    target: "fakefile-10-minutes"
+  });
+  download.startTime = new Date(now_uSec - 10 * kUsecPerMin), // 10 minutes ago, in uSec
+  download.canceled = true;
+  publicList.add(download);
+
+  download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
-    target: "fakefile-1-hour",
-    startTime: now_uSec - 45 * kUsecPerMin, // 45 minutes ago, in uSec
-    endTime: now_uSec - 44 * kUsecPerMin, // 1 minute later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "1bcD23eF4g5a"
-  };
+    target: "fakefile-1-hour"
+  });
+  download.startTime = new Date(now_uSec - 45 * kUsecPerMin), // 45 minutes ago, in uSec
+  download.canceled = true;
+  publicList.add(download);
 
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
-  
-  // Add 1-hour-10-minutes download to DB
-  data = {
-    id:   "5555556",
-    name: "fakefile-1-hour-10-minutes",
+  download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
-    target: "fakefile-1-hour-10-minutes",
-    startTime: now_uSec - 70 * kUsecPerMin, // 70 minutes ago, in uSec
-    endTime: now_uSec - 71 * kUsecPerMin, // 1 minute later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "a1cbD23e4Fg5"
-  };
+    target: "fakefile-1-hour-10-minutes"
+  });
+  download.startTime = new Date(now_uSec - 70 * kUsecPerMin), // 70 minutes ago, in uSec
+  download.canceled = true;
+  publicList.add(download);
 
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
-
-  // Add within-2-hour download  
-  data = {
-    id:   "5555552",
-    name: "fakefile-2-hour",
+  download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
-    target: "fakefile-2-hour",
-    startTime: now_uSec - 90 * kUsecPerMin, // 90 minutes ago, in uSec
-    endTime: now_uSec - 89 * kUsecPerMin, // 1 minute later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "b1aDc23eFg54"
-  };
+    target: "fakefile-2-hour"
+  });
+  download.startTime = new Date(now_uSec - 90 * kUsecPerMin), // 90 minutes ago, in uSec
+  download.canceled = true;
+  publicList.add(download);
 
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
-  
-  // Add 2-hour-10-minutes download  
-  data = {
-    id:   "5555557",
-    name: "fakefile-2-hour-10-minutes",
+  download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
-    target: "fakefile-2-hour-10-minutes",
-    startTime: now_uSec - 130 * kUsecPerMin, // 130 minutes ago, in uSec
-    endTime: now_uSec - 131 * kUsecPerMin, // 1 minute later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "z1bcD23eF4g5"
-  };
-
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
+    target: "fakefile-2-hour-10-minutes"
+  });
+  download.startTime = new Date(now_uSec - 130 * kUsecPerMin), // 130 minutes ago, in uSec
+  download.canceled = true;
+  publicList.add(download);
 
-  // Add within-4-hour download
-  data = {
-    id:   "5555553",
-    name: "fakefile-4-hour",
+  download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
-    target: "fakefile-4-hour",
-    startTime: now_uSec - 180 * kUsecPerMin, // 180 minutes ago, in uSec
-    endTime: now_uSec - 179 * kUsecPerMin, // 1 minute later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "zzzcD23eF4g5"
-  };
-  
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
-  
-  // Add 4-hour-10-minutes download
-  data = {
-    id:   "5555558",
-    name: "fakefile-4-hour-10-minutes",
+    target: "fakefile-4-hour"
+  });
+  download.startTime = new Date(now_uSec - 180 * kUsecPerMin), // 180 minutes ago, in uSec
+  download.canceled = true;
+  publicList.add(download);
+
+  download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
-    target: "fakefile-4-hour-10-minutes",
-    startTime: now_uSec - 250 * kUsecPerMin, // 250 minutes ago, in uSec
-    endTime: now_uSec - 251 * kUsecPerMin, // 1 minute later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "z1bzz23eF4gz"
-  };
-  
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
+    target: "fakefile-4-hour-10-minutes"
+  });
+  download.startTime = new Date(now_uSec - 250 * kUsecPerMin), // 250 minutes ago, in uSec
+  download.canceled = true;
+  publicList.add(download);
 
   // Add "today" download
   let today = new Date();
   today.setHours(0);
   today.setMinutes(0);
   today.setSeconds(1);
-  
-  data = {
-    id:   "5555554",
-    name: "fakefile-today",
+
+  download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
-    target: "fakefile-today",
-    startTime: today.getTime() * 1000,  // 12:00:30am this morning, in uSec
-    endTime: (today.getTime() + 1000) * 1000, // 1 second later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "ffffD23eF4g5"
-  };
-  
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
+    target: "fakefile-today"
+  });
+  download.startTime = new Date(today.getTime() * 1000), // 12:00:30am this morning, in uSec
+  download.canceled = true;
+  publicList.add(download);
   
   // Add "before today" download
   let lastYear = new Date();
   lastYear.setFullYear(lastYear.getFullYear() - 1);
-  data = {
-    id:   "5555550",
-    name: "fakefile-old",
+
+  download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
-    target: "fakefile-old",
-    startTime: lastYear.getTime() * 1000, // 1 year ago, in uSec
-    endTime: (lastYear.getTime() + 1000) * 1000, // 1 second later
-    state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
-    guid: "ggggg23eF4g5"
-  };
-  
-  try {
-    for (let prop in data)
-      stmt.params[prop] = data[prop];
-    stmt.execute();
-  }
-  finally {
-    stmt.finalize();
-  }
+    target: "fakefile-old"
+  });
+  download.startTime = new Date(lastYear.getTime() * 1000), // 1 year ago, in uSec
+  download.canceled = true;
+  publicList.add(download);
   
   // Confirm everything worked
-  ok(downloadExists(5555550), "Pretend download for everything case should exist");
-  ok(downloadExists(5555555), "Pretend download for 10-minutes case should exist");
-  ok(downloadExists(5555551), "Pretend download for 1-hour case should exist");
-  ok(downloadExists(5555556), "Pretend download for 1-hour-10-minutes case should exist");
-  ok(downloadExists(5555552), "Pretend download for 2-hour case should exist");
-  ok(downloadExists(5555557), "Pretend download for 2-hour-10-minutes case should exist");
-  ok(downloadExists(5555553), "Pretend download for 4-hour case should exist");
-  ok(downloadExists(5555558), "Pretend download for 4-hour-10-minutes case should exist");
-  ok(downloadExists(5555554), "Pretend download for Today case should exist");
+  let downloads = yield publicList.getAll();
+  is(downloads.length, 9, "9 Pretend downloads added");
+
+  ok((yield downloadExists(publicList, "fakefile-old")), "Pretend download for everything case should exist");
+  ok((yield downloadExists(publicList, "fakefile-10-minutes")), "Pretend download for 10-minutes case should exist");
+  ok((yield downloadExists(publicList, "fakefile-1-hour")), "Pretend download for 1-hour case should exist");
+  ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "Pretend download for 1-hour-10-minutes case should exist");
+  ok((yield downloadExists(publicList, "fakefile-2-hour")), "Pretend download for 2-hour case should exist");
+  ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "Pretend download for 2-hour-10-minutes case should exist");
+  ok((yield downloadExists(publicList, "fakefile-4-hour")), "Pretend download for 4-hour case should exist");
+  ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "Pretend download for 4-hour-10-minutes case should exist");
+  ok((yield downloadExists(publicList, "fakefile-today")), "Pretend download for Today case should exist");
 }
 
 /**
  * Checks to see if the downloads with the specified id exists.
  *
  * @param aID
  *        The ids of the downloads to check.
  */
-function downloadExists(aID)
+function downloadExists(list, path)
 {
-  let db = dm.DBConnection;
-  let stmt = db.createStatement(
-    "SELECT * " +
-    "FROM moz_downloads " +
-    "WHERE id = :id"
-  );
-  stmt.params.id = aID;
-  var rows = stmt.executeStep();
-  stmt.finalize();
-  return rows;
+  return Task.spawn(function() {
+    let listArray = yield list.getAll();
+    throw new Task.Result(listArray.some(i => i.target.path == path));
+  });
 }
 
 function isToday(aDate) {
   return aDate.getDate() == new Date().getDate();
 }
--- a/browser/base/content/test/browser_sanitizeDialog.js
+++ b/browser/base/content/test/browser_sanitizeDialog.js
@@ -16,28 +16,27 @@
  * Some of this code, especially the history creation parts, was taken from
  * browser/base/content/test/browser_sanitize-timespans.js.
  */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
                                   "resource://gre/modules/FormHistory.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+                                  "resource://gre/modules/Downloads.jsm");
 
 let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
                                            .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
 let Sanitizer = tempScope.Sanitizer;
 
-const dm = Cc["@mozilla.org/download-manager;1"].
-           getService(Ci.nsIDownloadManager);
-
 const kUsecPerMin = 60 * 1000000;
 
-let formEntries;
+let formEntries, downloadIDs, olderDownloadIDs;
 
 // Add tests here.  Each is a function that's called by doNextTest().
 var gAllTests = [
 
   /**
    * Initializes the dialog to its default state.
    */
   function () {
@@ -87,16 +86,33 @@ var gAllTests = [
         yield promiseHistoryClearedState(uris, false);
         yield blankSlate();
         yield promiseHistoryClearedState(uris, true);
       };
       wh.open();
     });
   },
 
+  function () {
+    // Add downloads (within the past hour).
+    Task.spawn(function () {
+      downloadIDs = [];
+      for (let i = 0; i < 5; i++) {
+        yield addDownloadWithMinutesAgo(downloadIDs, i);
+      }
+      // Add downloads (over an hour ago).
+      olderDownloadIDs = [];
+      for (let i = 0; i < 5; i++) {
+        yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i);
+      }
+
+      doNextTest();
+    }).then(null, Components.utils.reportError);
+  },
+
   /**
    * Ensures that the combined history-downloads checkbox clears both history
    * visits and downloads when checked; the dialog respects simple timespan.
    */
   function () {
     // Add history (within the past hour).
     let uris = [];
     let places = [];
@@ -110,26 +126,16 @@ var gAllTests = [
     let olderURIs = [];
     for (let i = 0; i < 5; i++) {
       pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/");
       places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)});
       olderURIs.push(pURI);
     }
 
     addVisits(places, function() {
-      // Add downloads (within the past hour).
-      let downloadIDs = [];
-      for (let i = 0; i < 5; i++) {
-        downloadIDs.push(addDownloadWithMinutesAgo(i));
-      }
-      // Add downloads (over an hour ago).
-      let olderDownloadIDs = [];
-      for (let i = 0; i < 5; i++) {
-        olderDownloadIDs.push(addDownloadWithMinutesAgo(61 + i));
-      }
       let totalHistoryVisits = uris.length + olderURIs.length;
 
       let wh = new WindowHelper();
       wh.onload = function () {
         this.selectDuration(Sanitizer.TIMESPAN_HOUR);
         this.checkPrefCheckbox("history", true);
         this.acceptDialog();
 
@@ -141,26 +147,26 @@ var gAllTests = [
                    "history checkbox checked");
         boolPrefIs("cpd.downloads", true,
                    "downloads pref should be true after accepting dialog with " +
                    "history checkbox checked");
       };
       wh.onunload = function () {
         // History visits and downloads within one hour should be cleared.
         yield promiseHistoryClearedState(uris, true);
-        ensureDownloadsClearedState(downloadIDs, true);
+        yield ensureDownloadsClearedState(downloadIDs, true);
 
         // Visits and downloads > 1 hour should still exist.
         yield promiseHistoryClearedState(olderURIs, false);
-        ensureDownloadsClearedState(olderDownloadIDs, false);
+        yield ensureDownloadsClearedState(olderDownloadIDs, false);
 
         // OK, done, cleanup after ourselves.
         yield blankSlate();
         yield promiseHistoryClearedState(olderURIs, true);
-        ensureDownloadsClearedState(olderDownloadIDs, true);
+        yield ensureDownloadsClearedState(olderDownloadIDs, true);
       };
       wh.open();
     });
   },
 
   /**
    * Add form history entries for the next test.
    */
@@ -173,37 +179,44 @@ var gAllTests = [
         yield undefined;
       }
       doNextTest();
     }();
 
     iter.next();
   },
 
+  function () {
+    // Add downloads (within the past hour).
+    Task.spawn(function () {
+      downloadIDs = [];
+      for (let i = 0; i < 5; i++) {
+        yield addDownloadWithMinutesAgo(downloadIDs, i);
+      }
+
+      doNextTest();
+    }).then(null, Components.utils.reportError);
+  },
+
   /**
    * Ensures that the combined history-downloads checkbox removes neither
    * history visits nor downloads when not checked.
    */
   function () {
     // Add history, downloads, form entries (within the past hour).
     let uris = [];
     let places = [];
     let pURI;
     for (let i = 0; i < 5; i++) {
       pURI = makeURI("http://" + i + "-minutes-ago.com/");
       places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
       uris.push(pURI);
     }
 
     addVisits(places, function() {
-      let downloadIDs = [];
-      for (let i = 0; i < 5; i++) {
-        downloadIDs.push(addDownloadWithMinutesAgo(i));
-      }
-
       let wh = new WindowHelper();
       wh.onload = function () {
         is(this.isWarningPanelVisible(), false,
            "Warning panel should be hidden after previously accepting dialog " +
            "with a predefined timespan");
         this.selectDuration(Sanitizer.TIMESPAN_HOUR);
 
         // Remove only form entries, leave history (including downloads).
@@ -219,27 +232,27 @@ var gAllTests = [
                    "history checkbox unchecked");
         boolPrefIs("cpd.downloads", false,
                    "downloads pref should be false after accepting dialog with " +
                    "history checkbox unchecked");
       };
       wh.onunload = function () {
         // Of the three only form entries should be cleared.
         yield promiseHistoryClearedState(uris, false);
-        ensureDownloadsClearedState(downloadIDs, false);
+        yield ensureDownloadsClearedState(downloadIDs, false);
 
         formEntries.forEach(function (entry) {
           let exists = yield formNameExists(entry);
           is(exists, false, "form entry " + entry + " should no longer exist");
         });
 
         // OK, done, cleanup after ourselves.
         yield blankSlate();
         yield promiseHistoryClearedState(uris, true);
-        ensureDownloadsClearedState(downloadIDs, true);
+        yield ensureDownloadsClearedState(downloadIDs, true);
       };
       wh.open();
     });
   },
 
   /**
    * Ensures that the "Everything" duration option works.
    */
@@ -634,20 +647,16 @@ var gAllTests = [
       var pm = Cc["@mozilla.org/permissionmanager;1"]
                .getService(Ci.nsIPermissionManager);
       is(pm.testPermissionFromPrincipal(principal, "offline-app"), 0, "offline-app permissions removed");
     };
     wh.open();
   }
 ];
 
-// Used as the download database ID for a new download.  Incremented for each
-// new download.  See addDownloadWithMinutesAgo().
-var gDownloadId = 5555551;
-
 // Index in gAllTests of the test currently being run.  Incremented for each
 // test run.  See doNextTest().
 var gCurrTest = 0;
 
 var now_uSec = Date.now() * 1000;
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -842,17 +851,17 @@ WindowHelper.prototype = {
 
         executeSoon(function () {
           // Some exceptions that reach here don't reach the test harness, but
           // ok()/is() do...
           try {
             if (wh.onunload) {
               Task.spawn(wh.onunload).then(function() {
                 waitForAsyncUpdates(doNextTest);
-              });
+              }).then(null, Components.utils.reportError);
             } else {
               waitForAsyncUpdates(doNextTest);
             }
           }
           catch (exc) {
             win.close();
             ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
             finish();
@@ -895,50 +904,33 @@ WindowHelper.prototype = {
 };
 
 /**
  * Adds a download to history.
  *
  * @param aMinutesAgo
  *        The download will be downloaded this many minutes ago
  */
-function addDownloadWithMinutesAgo(aMinutesAgo) {
-  let name = "fakefile-" + aMinutesAgo + "-minutes-ago";
-  let data = {
-    id:        gDownloadId,
-    name:      name,
-    source:   "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
-    target:    name,
-    startTime: now_uSec - (aMinutesAgo * kUsecPerMin),
-    endTime:   now_uSec - ((aMinutesAgo + 1) * kUsecPerMin),
-    state:     Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
-  };
+function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) {
+  let publicList = yield Downloads.getPublicDownloadList();
 
-  let db = dm.DBConnection;
-  let stmt = db.createStatement(
-    "INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
-      "state, currBytes, maxBytes, preferredAction, autoResume) " +
-    "VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
-      ":currBytes, :maxBytes, :preferredAction, :autoResume)");
-  try {
-    for (let prop in data) {
-      stmt.params[prop] = data[prop];
-    }
-    stmt.execute();
-  }
-  finally {
-    stmt.reset();
-  }
+  let name = "fakefile-" + aMinutesAgo + "-minutes-ago";
+  let download = yield Downloads.createDownload({
+    source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
+    target: name
+  });
+  download.startTime = now_uSec - (aMinutesAgo * kUsecPerMin);
+  download.canceled = true;
+  publicList.add(download);
 
-  is(downloadExists(gDownloadId), true,
-     "Sanity check: download " + gDownloadId +
+  ok((yield downloadExists(name)),
+     "Sanity check: download " + name +
      " should exist after creating it");
 
-  return gDownloadId++;
+  aExpectedPathList.push(name);
 }
 
 /**
  * Adds a form entry to history.
  *
  * @param aMinutesAgo
  *        The entry will be added this many minutes ago
  */
@@ -979,25 +971,47 @@ function formNameExists(name)
   return deferred.promise;
 }
 
 /**
  * Removes all history visits, downloads, and form entries.
  */
 function blankSlate() {
   PlacesUtils.bhistory.removeAllPages();
-  dm.cleanUp();
+
+  // The promise is resolved only when removing both downloads and form history are done.
+  let deferred = Promise.defer();
+  let formHistoryDone = false, downloadsDone = false;
 
-  let deferred = Promise.defer();
+  Task.spawn(function deleteAllDownloads() {
+    let publicList = yield Downloads.getPublicDownloadList();
+    let downloads = yield publicList.getAll();
+    for (let download of downloads) {
+      publicList.remove(download);
+      yield download.finalize(true);
+    }
+    downloadsDone = true;
+    if (formHistoryDone) {
+      deferred.resolve();
+    }
+  }).then(null, Components.utils.reportError);
+
   FormHistory.update({ op: "remove" },
                      { handleError: function (error) {
                          do_throw("Error occurred updating form history: " + error);
                          deferred.reject(error);
                        },
-                       handleCompletion: function (reason) { if (!reason) deferred.resolve(); }
+                       handleCompletion: function (reason) {
+                         if (!reason) {
+                           formHistoryDone = true;
+                           if (downloadsDone) {
+                             deferred.resolve();
+                           }
+                         }
+                       }
                      });
   return deferred.promise;
 }
 
 /**
  * Ensures that the given pref is the expected value.
  *
  * @param aPrefName
@@ -1007,34 +1021,29 @@ function blankSlate() {
  * @param aMsg
  *        Passed to is()
  */
 function boolPrefIs(aPrefName, aExpectedVal, aMsg) {
   is(gPrefService.getBoolPref("privacy." + aPrefName), aExpectedVal, aMsg);
 }
 
 /**
- * Checks to see if the download with the specified ID exists.
+ * Checks to see if the download with the specified path exists.
  *
- * @param  aID
- *         The ID of the download to check
+ * @param  aPath
+ *         The path of the download to check
  * @return True if the download exists, false otherwise
  */
-function downloadExists(aID)
+function downloadExists(aPath)
 {
-  let db = dm.DBConnection;
-  let stmt = db.createStatement(
-    "SELECT * " +
-    "FROM moz_downloads " +
-    "WHERE id = :id"
-  );
-  stmt.params.id = aID;
-  let rows = stmt.executeStep();
-  stmt.finalize();
-  return !!rows;
+  return Task.spawn(function() {
+    let publicList = yield Downloads.getPublicDownloadList();
+    let listArray = yield publicList.getAll();
+    throw new Task.Result(listArray.some(i => i.target.path == aPath));
+  });
 }
 
 /**
  * Runs the next test in the gAllTests array.  If all tests have been run,
  * finishes the entire suite.
  */
 function doNextTest() {
   if (gAllTests.length <= gCurrTest) {
@@ -1054,17 +1063,17 @@ function doNextTest() {
  * @param aDownloadIDs
  *        Array of download database IDs
  * @param aShouldBeCleared
  *        True if each download should be cleared, false otherwise
  */
 function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) {
   let niceStr = aShouldBeCleared ? "no longer" : "still";
   aDownloadIDs.forEach(function (id) {
-    is(downloadExists(id), !aShouldBeCleared,
+    is((yield downloadExists(id)), !aShouldBeCleared,
        "download " + id + " should " + niceStr + " exist");
   });
 }
 
 /**
  * Ensures that the given pref is the expected value.
  *
  * @param aPrefName
--- a/browser/components/downloads/test/browser/browser_basic_functionality.js
+++ b/browser/components/downloads/test/browser/browser_basic_functionality.js
@@ -2,61 +2,53 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Make sure the downloads panel can display items in the right order and
  * contains the expected data.
  */
-function gen_test()
+function test_task()
 {
   // Display one of each download state.
   const DownloadData = [
-    { endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED },
-    { endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING },
-    { endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED },
-    { endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING },
-    { endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED },
-    { endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED },
-    { endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED },
-    { endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED },
-    { endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL },
-    { endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY },
-    { endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY },
+    { state: nsIDM.DOWNLOAD_NOTSTARTED },
+    { state: nsIDM.DOWNLOAD_PAUSED },
+    { state: nsIDM.DOWNLOAD_FINISHED },
+    { state: nsIDM.DOWNLOAD_FAILED },
+    { state: nsIDM.DOWNLOAD_CANCELED },
   ];
 
-  // For testing purposes, show all the download items at once.
-  var originalCountLimit = DownloadsView.kItemCountLimit;
-  DownloadsView.kItemCountLimit = DownloadData.length;
-  registerCleanupFunction(function () {
-    DownloadsView.kItemCountLimit = originalCountLimit;
-  });
-
   try {
     // Ensure that state is reset in case previous tests didn't finish.
-    for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
+    yield task_resetState();
+
+    // For testing purposes, show all the download items at once.
+    var originalCountLimit = DownloadsView.kItemCountLimit;
+    DownloadsView.kItemCountLimit = DownloadData.length;
+    registerCleanupFunction(function () {
+      DownloadsView.kItemCountLimit = originalCountLimit;
+    });
 
     // Populate the downloads database with the data required by this test.
-    for (let yy in gen_addDownloadRows(DownloadData)) yield undefined;
+    yield task_addDownloads(DownloadData);
 
     // Open the user interface and wait for data to be fully loaded.
-    for (let yy in gen_openPanel(DownloadsCommon.getData(window))) yield undefined;
+    yield task_openPanel();
 
     // Test item data and count.  This also tests the ordering of the display.
     let richlistbox = document.getElementById("downloadsListBox");
 /* disabled for failing intermittently (bug 767828)
     is(richlistbox.children.length, DownloadData.length,
        "There is the correct number of richlistitems");
 */
-    for (let i = 0; i < richlistbox.children.length; i++) {
-      let element = richlistbox.children[i];
+    let itemCount = richlistbox.children.length;
+    for (let i = 0; i < itemCount; i++) {
+      let element = richlistbox.children[itemCount - i - 1];
       let dataItem = new DownloadsViewItemController(element).dataItem;
-      is(dataItem.target, DownloadData[i].name, "Download names match up");
       is(dataItem.state, DownloadData[i].state, "Download states match up");
-      is(dataItem.file, DownloadData[i].target, "Download targets match up");
-      is(dataItem.uri, DownloadData[i].source, "Download sources match up");
     }
   } finally {
     // Clean up when the test finishes.
-    for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
+    yield task_resetState();
   }
 }
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -3,54 +3,51 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Make sure the downloads panel only opens automatically on the first
  * download it notices. All subsequent downloads, even across sessions, should
  * not open the panel automatically.
  */
-function gen_test()
+function test_task()
 {
   try {
     // Ensure that state is reset in case previous tests didn't finish.
-    for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
+    yield task_resetState();
 
-    // With this set to false, we should automatically open the panel
-    // the first time a download is started.
+    // With this set to false, we should automatically open the panel the first
+    // time a download is started.
     DownloadsCommon.getData(window).panelHasShownBefore = false;
 
-    prepareForPanelOpen();
+    let promise = promisePanelOpened();
     DownloadsCommon.getData(window)._notifyDownloadEvent("start");
-    yield undefined;
+    yield promise;
 
     // If we got here, that means the panel opened.
     DownloadsPanel.hidePanel();
 
     ok(DownloadsCommon.getData(window).panelHasShownBefore,
        "Should have recorded that the panel was opened on a download.")
 
-    // Next, make sure that if we start another download, we don't open
-    // the panel automatically.
-    panelShouldNotOpen();
-    DownloadsCommon.getData(window)._notifyDownloadEvent("start");
-    yield waitFor(2);
-  } catch(e) {
-    ok(false, e);
+    // Next, make sure that if we start another download, we don't open the
+    // panel automatically.
+    let originalOnPopupShown = DownloadsPanel.onPopupShown;
+    DownloadsPanel.onPopupShown = function () {
+      originalOnPopupShown.apply(this, arguments);
+      ok(false, "Should not have opened the downloads panel.");
+    };
+
+    try {
+      DownloadsCommon.getData(window)._notifyDownloadEvent("start");
+
+      // Wait 2 seconds to ensure that the panel does not open.
+      let deferTimeout = Promise.defer();
+      setTimeout(deferTimeout.resolve, 2000);
+      yield deferTimeout.promise;
+    } finally {
+      DownloadsPanel.onPopupShown = originalOnPopupShown;
+    }
   } finally {
     // Clean up when the test finishes.
-    for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
+    yield task_resetState();
   }
 }
-
-/**
- * Call this to record a test failure for the next time the downloads panel
- * opens.
- */
-function panelShouldNotOpen()
-{
-  // Hook to wait until the test data has been loaded.
-  let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
-  DownloadsPanel.onViewLoadCompleted = function () {
-    DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
-    ok(false, "Should not have opened the downloads panel.");
-  };
-}
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -5,270 +5,108 @@
 
 /**
  * Provides infrastructure for automated download components tests.
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+                                  "resource://gre/modules/Downloads.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
+                                  "resource:///modules/DownloadsCommon.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
-                                  "resource:///modules/DownloadsCommon.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
 const nsIDM = Ci.nsIDownloadManager;
 
 let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
 gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
 registerCleanupFunction(function () {
   gTestTargetFile.remove(false);
 });
 
-/**
- * This objects contains a property for each column in the downloads table.
- */
-let gDownloadRowTemplate = {
-  name: "test-download.txt",
-  source: "http://www.example.com/test-download.txt",
-  target: NetUtil.newURI(gTestTargetFile).spec,
-  startTime: 1180493839859230,
-  endTime: 1180493839859234,
-  state: nsIDM.DOWNLOAD_FINISHED,
-  currBytes: 0,
-  maxBytes: -1,
-  preferredAction: 0,
-  autoResume: 0
-};
-
 ////////////////////////////////////////////////////////////////////////////////
 //// Infrastructure
 
-// All test are run through the test runner.
 function test()
 {
-  testRunner.runTest(this.gen_test);
+  waitForExplicitFinish();
+  Task.spawn(test_task).then(null, ex => ok(false, ex)).then(finish);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Asynchronous support subroutines
+
+function promiseFocus()
+{
+  let deferred = Promise.defer();
+  waitForFocus(deferred.resolve);
+  return deferred.promise;
 }
 
-/**
- * Runs a browser-chrome test defined through a generator function.
- *
- * This object is a singleton, initialized automatically when this script is
- * included.  Every browser-chrome test file includes a new copy of this object.
- */
-var testRunner = {
-  _testIterator: null,
-  _lastEventResult: undefined,
-  _testRunning: false,
-  _eventRaised: false,
-
-  // --- Main test runner ---
+function promisePanelOpened()
+{
+  let deferred = Promise.defer();
 
-  /**
-   * Runs the test described by the provided generator function asynchronously.
-   *
-   * Calling yield in the generator will cause it to wait until continueTest is
-   * called.  The parameter provided to continueTest will be the return value of
-   * the yield operator.
-   *
-   * @param aGenerator
-   *        Test generator function.  The function will be called with no
-   *        arguments to retrieve its iterator.
-   */
-  runTest: function TR_runTest(aGenerator) {
-    waitForExplicitFinish();
-    testRunner._testIterator = aGenerator();
-    testRunner.continueTest();
-  },
-
-  /**
-   * Continues the currently running test.
-   *
-   * @param aEventResult
-   *        This will be the return value of the yield operator in the test.
-   */
-  continueTest: function TR_continueTest(aEventResult) {
-    // Store the last event result, or set it to undefined.
-    testRunner._lastEventResult = aEventResult;
-
-    // Never reenter the main loop, but notify that the event has been raised.
-    if (testRunner._testRunning) {
-      testRunner._eventRaised = true;
-      return;
-    }
+  // Hook to wait until the panel is shown.
+  let originalOnPopupShown = DownloadsPanel.onPopupShown;
+  DownloadsPanel.onPopupShown = function () {
+    DownloadsPanel.onPopupShown = originalOnPopupShown;
+    originalOnPopupShown.apply(this, arguments);
 
-    // Enter the main iteration loop.
-    testRunner._testRunning = true;
-    try {
-      do {
-        // Call the iterator, but don't leave the loop if the expected event is
-        // raised during the execution of the generator.
-        testRunner._eventRaised = false;
-        testRunner._testIterator.send(testRunner._lastEventResult);
-      } while (testRunner._eventRaised);
-    }
-    catch (e) {
-      // This block catches exceptions raised by the generator, including the
-      // normal StopIteration exception.  Unexpected exceptions are reported as
-      // test failures.
-      if (!(e instanceof StopIteration))
-        ok(false, e);
-      // In any case, stop the tests in this file.
-      finish();
-    }
+    // Defer to the next tick of the event loop so that we don't continue
+    // processing during the DOM event handler itself.
+    setTimeout(deferred.resolve, 0);
+  };
 
-    // Wait for the next event or finish.
-    testRunner._testRunning = false;
-  }
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//// Asynchronous generator-based support subroutines
+  return deferred.promise;
+}
 
-//
-// The following functions are all generators that can be used inside the main
-// test generator to perform specific tasks asynchronously.  To invoke these
-// subroutines correctly, an iteration syntax should be used:
-//
-//   for (let yy in gen_example("Parameter")) yield undefined;
-//
-
-function gen_resetState(aData)
+function task_resetState()
 {
-  let statement = Services.downloads.DBConnection.createAsyncStatement(
-                  "DELETE FROM moz_downloads");
-  try {
-    statement.executeAsync({
-      handleResult: function(aResultSet) { },
-      handleError: function(aError)
-      {
-        Cu.reportError(aError);
-      },
-      handleCompletion: function(aReason)
-      {
-        testRunner.continueTest();
-      }
-    });
-    yield undefined;
-  } finally {
-    statement.finalize();
+  // Remove all downloads.
+  let publicList = yield Downloads.getPublicDownloadList();
+  let downloads = yield publicList.getAll();
+  for (let download of downloads) {
+    publicList.remove(download);
+    yield download.finalize(true);
   }
 
   // Reset any prefs that might have been changed.
   Services.prefs.clearUserPref("browser.download.panel.shown");
 
-  // Ensure that the panel is closed and data is unloaded.
-  aData.clear();
-  aData._loadState = aData.kLoadNone;
   DownloadsPanel.hidePanel();
 
-  // Wait for focus on the main window.
-  waitForFocus(testRunner.continueTest);
-  yield undefined;
+  yield promiseFocus();
 }
 
-function gen_addDownloadRows(aDataRows)
+function task_addDownloads(aItems)
 {
-  let columnNames = Object.keys(gDownloadRowTemplate).join(", ");
-  let parameterNames = Object.keys(gDownloadRowTemplate)
-                             .map(function(n) ":" + n)
-                             .join(", ");
-  let statement = Services.downloads.DBConnection.createAsyncStatement(
-                  "INSERT INTO moz_downloads (" + columnNames +
-                  ", guid) VALUES(" + parameterNames + ", GENERATE_GUID())");
-  try {
-    // Execute the statement for each of the provided downloads in reverse.
-    for (let i = aDataRows.length - 1; i >= 0; i--) {
-      let dataRow = aDataRows[i];
+  let startTimeMs = Date.now();
 
-      // Populate insert parameters from the provided data.
-      for (let columnName in gDownloadRowTemplate) {
-        if (!(columnName in dataRow)) {
-          // Update the provided row object with data from the global template,
-          // for columns whose value is not provided explicitly.
-          dataRow[columnName] = gDownloadRowTemplate[columnName];
-        }
-        statement.params[columnName] = dataRow[columnName];
-      }
-
-      // Run the statement asynchronously and wait.
-      statement.executeAsync({
-        handleResult: function(aResultSet) { },
-        handleError: function(aError)
-        {
-          Cu.reportError(aError.message + " (Result = " + aError.result + ")");
-        },
-        handleCompletion: function(aReason)
-        {
-          testRunner.continueTest();
-        }
-      });
-      yield undefined;
-
-      // At each iteration, ensure that the start and end time in the global
-      // template is distinct, as these column are used to sort each download
-      // in its category.
-      gDownloadRowTemplate.startTime++;
-      gDownloadRowTemplate.endTime++;
-    }
-  } finally {
-    statement.finalize();
+  let publicList = yield Downloads.getPublicDownloadList();
+  for (let item of aItems) {
+    publicList.add(yield Downloads.createDownload({
+      source: "http://www.example.com/test-download.txt",
+      target: gTestTargetFile,
+      succeeded: item.state == nsIDM.DOWNLOAD_FINISHED,
+      canceled: item.state == nsIDM.DOWNLOAD_CANCELED ||
+                item.state == nsIDM.DOWNLOAD_PAUSED,
+      error: item.state == nsIDM.DOWNLOAD_FAILED ? new Error("Failed.") : null,
+      hasPartialData: item.state == nsIDM.DOWNLOAD_PAUSED,
+      startTime: new Date(startTimeMs++),
+    }));
   }
 }
 
-function gen_openPanel(aData)
+function task_openPanel()
 {
-  // Hook to wait until the test data has been loaded.
-  let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
-  DownloadsPanel.onViewLoadCompleted = function () {
-    DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
-    originalOnViewLoadCompleted.apply(this);
-    testRunner.continueTest();
-  };
-
-  // Start loading all the downloads from the database asynchronously.
-  aData.ensurePersistentDataLoaded(false);
-
-  // Wait for focus on the main window.
-  waitForFocus(testRunner.continueTest);
-  yield undefined;
-
-  // Open the downloads panel, waiting until loading is completed.
-  DownloadsPanel.showPanel();
-  yield undefined;
-}
+  yield promiseFocus();
 
-/**
- * Spin the event loop for aSeconds seconds, and then signal the test to
- * continue.
- *
- * @param aSeconds the number of seconds to wait.
- * @note This helper should _only_ be used when there's no valid event to
- *       listen to and one can't be made.
- */
-function waitFor(aSeconds)
-{
-  setTimeout(function() {
-    testRunner.continueTest();
-  }, aSeconds * 1000);
+  let promise = promisePanelOpened();
+  DownloadsPanel.showPanel();
+  yield promise;
 }
-
-/**
- * Make it so that the next time the downloads panel opens, we signal to
- * continue the test. This function needs to be called each time you want
- * to wait for the panel to open.
- *
- * Example usage:
- *
- * prepareForPanelOpen();
- * // Do something to open the panel
- * yield undefined;
- * // We can assume the panel is open now.
- */
-function prepareForPanelOpen()
-{
-  // Hook to wait until the test data has been loaded.
-  let originalOnPopupShown = DownloadsPanel.onPopupShown;
-  DownloadsPanel.onPopupShown = function (aEvent) {
-    DownloadsPanel.onPopupShown = originalOnPopupShown;
-    DownloadsPanel.onPopupShown.apply(this, [aEvent]);
-    testRunner.continueTest();
-  };
-}
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -9,16 +9,19 @@ const Cr = Components.results;
 const Cu = Components.utils;
 
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/SignInToWebsite.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
+                                  "resource:///modules/AboutHome.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentClick",
                                   "resource:///modules/ContentClick.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
@@ -460,16 +463,17 @@ BrowserGlue.prototype = {
 
     webappsUI.init();
     PageThumbs.init();
     NewTabUtils.init();
     BrowserNewTabPreloader.init();
     SignInToWebsiteUX.init();
     PdfJs.init();
     webrtcUI.init();
+    AboutHome.init();
 
     if (Services.prefs.getBoolPref("browser.tabs.remote"))
       ContentClick.init();
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
   },
 
   _checkForOldBuildUpdates: function () {
--- a/browser/metro/locales/en-US/chrome/browser.properties
+++ b/browser/metro/locales/en-US/chrome/browser.properties
@@ -4,26 +4,19 @@
 
 # LOCALIZATION NOTE : FILE Capitalized phrases like "Top Sites", "Bookmarks",
 # and "Recent History" are typically used as proper noun phrases to refer to
 # the specific visual set (usually displayed as a grid) of top sites,
 # bookmarks, etc. displayed on screen, rather than those concepts in general.
 # Buttons (like with contextAppbar2.pin.topSites) refer to actions against
 # the specific on-screen sets with similarly-named headings.
 
-# Default search engine
-browser.search.defaultenginename=Bing
-
-# Search engine order (order displayed in the search bar dropdown)s
-browser.search.order.1=Bing
-browser.search.order.2=Google
-browser.search.order.3=Yahoo
-
 # LOCALIZATION NOTE (browser.search.contextTextSearchLabel2): search context
 # menu item text will be: |Search (browser.search.defaultenginename) for "string"|
+# browser.search.defaultenginename is defined in region.properties
 browser.search.contextTextSearchLabel2=Search %S for "%S"
 
 # Contextual Appbar - Button Labels
 
 contextAppbar2.pin.topSites=Pin to Top Sites
 contextAppbar2.pin.bookmarks=Pin to Bookmarks
 contextAppbar2.pin.recentHistory=Pin to Recent History
 contextAppbar2.pin.downloads=Pin to Downloads
new file mode 100644
--- /dev/null
+++ b/browser/metro/locales/en-US/chrome/region.properties
@@ -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/.
+
+# LOCALIZATION NOTE: REVIEW_REQUIRED
+# For all locales: please do not commit any changes to this file without a
+# review from the l10n-drivers team. In order to get one, please file a bug,
+# add the "productization" keyword and CC l10n@mozilla.com.
+# en-US: if you're changing this file, please CC l10n@mozilla.com in the
+# associated bug to help us determine if these changes are relevant for other
+# locales and how to localize them.
+
+# Default search engine
+browser.search.defaultenginename=Bing
+
+# Search engine order (order displayed in the search bar dropdown)
+browser.search.order.1=Bing
+browser.search.order.2=Google
+browser.search.order.3=Yahoo
--- a/browser/metro/locales/jar.mn
+++ b/browser/metro/locales/jar.mn
@@ -7,16 +7,17 @@
 # Metro jar resources
 #
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/browser/
   locale/browser/aboutCertError.dtd       (%chrome/aboutCertError.dtd)
   locale/browser/browser.dtd              (%chrome/browser.dtd)
   locale/browser/browser.properties       (%chrome/browser.properties)
+  locale/browser/region.properties        (%chrome/region.properties)
   locale/browser/config.dtd               (%chrome/config.dtd)
   locale/browser/preferences.dtd          (%chrome/preferences.dtd)
   locale/browser/aboutPanel.dtd           (%chrome/aboutPanel.dtd)
   locale/browser/checkbox.dtd             (%chrome/checkbox.dtd)
   locale/browser/sync.dtd                 (%chrome/sync.dtd)
   locale/browser/sync.properties          (%chrome/sync.properties)
   locale/browser/passwordmgr.properties   (%chrome/passwordmgr.properties)
   locale/browser/phishing.dtd             (%chrome/phishing.dtd)
@@ -29,13 +30,12 @@
 
 #
 # Browser jar resources
 #
 
 @AB_CD@.jar:
 relativesrcdir browser/locales:
   locale/browser/syncBrand.dtd            (%chrome/browser/syncBrand.dtd)
-  locale/browser/region.properties        (%chrome/browser-region/region.properties)
   locale/browser/netError.dtd             (%chrome/overrides/netError.dtd)
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
   locale/browser/appstrings.properties    (%chrome/overrides/appstrings.properties)
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -226,29 +226,29 @@ pref("accessibility.typeaheadfind.casese
 pref("accessibility.browsewithcaret_shortcut.enabled", true);
 pref("accessibility.browsewithcaret", false);
 
 // Whether or not we show a dialog box informing the user that the update was
 // successfully applied.
 pref("app.update.showInstalledUI", false);
 
 // pointer to the default engine name
-pref("browser.search.defaultenginename", "chrome://browser/locale/browser.properties");
+pref("browser.search.defaultenginename", "chrome://browser/locale/region.properties");
 
 // SSL error page behaviour
 pref("browser.ssl_override_behavior", 2);
 pref("browser.xul.error_pages.expert_bad_cert", false);
 
 // disable logging for the search service by default
 pref("browser.search.log", false);
 
 // ordering of search engines in the engine list.
-pref("browser.search.order.1", "chrome://browser/locale/browser.properties");
-pref("browser.search.order.2", "chrome://browser/locale/browser.properties");
-pref("browser.search.order.3", "chrome://browser/locale/browser.properties");
+pref("browser.search.order.1", "chrome://browser/locale/region.properties");
+pref("browser.search.order.2", "chrome://browser/locale/region.properties");
+pref("browser.search.order.3", "chrome://browser/locale/region.properties");
 
 // send ping to the server to update
 pref("browser.search.update", true);
 
 // disable logging for the search service update system by default
 pref("browser.search.update.log", false);
 
 // Check whether we need to perform engine updates every 6 hours
rename from browser/modules/AboutHomeUtils.jsm
rename to browser/modules/AboutHome.jsm
--- a/browser/modules/AboutHomeUtils.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -1,36 +1,43 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-this.EXPORTED_SYMBOLS = [ "AboutHomeUtils" ];
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = [ "AboutHomeUtils", "AboutHome" ];
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
 // Url to fetch snippets, in the urlFormatter service format.
 const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
 
 // Should be bumped up if the snippets content format changes.
 const STARTPAGE_VERSION = 4;
 
 this.AboutHomeUtils = {
   get snippetsVersion() STARTPAGE_VERSION,
 
   /**
    * Returns an object containing the name and searchURL of the original default
    * search engine.
    */
   get defaultSearchEngine() {
     let defaultEngine = Services.search.defaultEngine;
     let submission = defaultEngine.getSubmission("_searchTerms_", null, "homepage");
-  
+
     return Object.freeze({
       name: defaultEngine.name,
       searchURL: submission.uri.spec,
       postDataString: submission.postDataString
     });
   },
 
   /*
@@ -78,8 +85,125 @@ this.AboutHomeUtils = {
  * Returns the URL to fetch snippets from, in the urlFormatter service format.
  */
 XPCOMUtils.defineLazyGetter(AboutHomeUtils, "snippetsURL", function() {
   let updateURL = Services.prefs
                           .getCharPref(SNIPPETS_URL_PREF)
                           .replace("%STARTPAGE_VERSION%", STARTPAGE_VERSION);
   return Services.urlFormatter.formatURL(updateURL);
 });
+
+/**
+ * This code provides services to the about:home page. Whenever
+ * about:home needs to do something chrome-privileged, it sends a
+ * message that's handled here.
+ */
+let AboutHome = {
+  MESSAGES: [
+    "AboutHome:RestorePreviousSession",
+    "AboutHome:Downloads",
+    "AboutHome:Bookmarks",
+    "AboutHome:History",
+    "AboutHome:Apps",
+    "AboutHome:Addons",
+    "AboutHome:Sync",
+    "AboutHome:Settings",
+    "AboutHome:RequestUpdate",
+    "AboutHome:Search",
+  ],
+
+  init: function() {
+    let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
+
+    for (let msg of this.MESSAGES) {
+      mm.addMessageListener(msg, this);
+    }
+
+    Services.obs.addObserver(this, "browser-search-engine-modified", false);
+  },
+
+  observe: function(aEngine, aTopic, aVerb) {
+    switch (aTopic) {
+      case "browser-search-engine-modified":
+        this.sendAboutHomeData(null);
+        break;
+    }
+  },
+
+  receiveMessage: function(aMessage) {
+    let window = aMessage.target.ownerDocument.defaultView;
+
+    switch (aMessage.name) {
+      case "AboutHome:RestorePreviousSession":
+        let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+                 getService(Ci.nsISessionStore);
+        if (ss.canRestoreLastSession) {
+          ss.restoreLastSession();
+        }
+        break;
+
+      case "AboutHome:Downloads":
+        window.BrowserDownloadsUI();
+        break;
+
+      case "AboutHome:Bookmarks":
+        window.PlacesCommandHook.showPlacesOrganizer("AllBookmarks");
+        break;
+
+      case "AboutHome:History":
+        window.PlacesCommandHook.showPlacesOrganizer("History");
+        break;
+
+      case "AboutHome:Apps":
+        window.openUILinkIn("https://marketplace.mozilla.org/", "tab");
+        break;
+
+      case "AboutHome:Addons":
+        window.BrowserOpenAddonsMgr();
+        break;
+
+      case "AboutHome:Sync":
+        window.openPreferences("paneSync");
+        break;
+
+      case "AboutHome:Settings":
+        window.openPreferences();
+        break;
+
+      case "AboutHome:RequestUpdate":
+        this.sendAboutHomeData(aMessage.target);
+        break;
+
+      case "AboutHome:Search":
+#ifdef MOZ_SERVICES_HEALTHREPORT
+        window.BrowserSearch.recordSearchInHealthReport(aMessage.data.engineName, "abouthome");
+#endif
+        break;
+    }
+  },
+
+  // Send all the chrome-privileged data needed by about:home. This
+  // gets re-sent when the search engine changes.
+  sendAboutHomeData: function(target) {
+    let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+               getService(Ci.nsISessionStore);
+    let data = {
+      showRestoreLastSession: ss.canRestoreLastSession,
+      snippetsURL: AboutHomeUtils.snippetsURL,
+      showKnowYourRights: AboutHomeUtils.showKnowYourRights,
+      snippetsVersion: AboutHomeUtils.snippetsVersion,
+      defaultSearchEngine: AboutHomeUtils.defaultSearchEngine
+    };
+
+    if (AboutHomeUtils.showKnowYourRights) {
+      // Set pref to indicate we've shown the notification.
+      let currentVersion = Services.prefs.getIntPref("browser.rights.version");
+      Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
+    }
+
+    if (target) {
+      target.messageManager.sendAsyncMessage("AboutHome:Update", data);
+    } else {
+      let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
+      mm.broadcastAsyncMessage("AboutHome:Update", data);
+    }
+  },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -22,12 +22,12 @@ EXTRA_JS_MODULES += [
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'WindowsJumpLists.jsm',
         'WindowsPreviewPerTab.jsm',
     ]
 
 EXTRA_PP_JS_MODULES += [
-    'AboutHomeUtils.jsm',
+    'AboutHome.jsm',
     'RecentWindow.jsm',
 ]
 
--- a/build/cl.py
+++ b/build/cl.py
@@ -14,33 +14,37 @@ def InvokeClWithDependencyGeneration(cmd
     for arg in cmdline:
         if arg.startswith("-Fo"):
             target = arg[3:]
             break
 
     if target == None:
         print >>sys.stderr, "No target set" and sys.exit(1)
 
+    # Assume the source file is the last argument
+    source = cmdline[-1]
+    assert not source.startswith('-')
+
     # The deps target lives here
     depstarget = os.path.basename(target) + ".pp"
 
     cmdline += ['-showIncludes']
     cl = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
 
-    deps = set()
+    deps = set([os.path.normcase(source).replace(os.sep, '/')])
     for line in cl.stdout:
         # cl -showIncludes prefixes every header with "Note: including file:"
         # and an indentation corresponding to the depth (which we don't need)
         if line.startswith(CL_INCLUDES_PREFIX):
             dep = line[len(CL_INCLUDES_PREFIX):].strip()
             # We can't handle pathes with spaces properly in mddepend.pl, but
             # we can assume that anything in a path with spaces is a system
             # header and throw it away.
-            if dep.find(' ') == -1:
-                deps.add(dep)
+            if ' ' not in dep:
+                deps.add(os.path.normcase(dep).replace(os.sep, '/'))
         else:
             sys.stdout.write(line) # Make sure we preserve the relevant output
                                    # from cl
 
     ret = cl.wait()
     if ret != 0 or target == "":
         sys.exit(ret)
 
@@ -49,15 +53,18 @@ def InvokeClWithDependencyGeneration(cmd
     if not os.path.isdir(depsdir):
         try:
             os.makedirs(depsdir)
         except OSError:
             pass # This suppresses the error we get when the dir exists, at the
                  # cost of masking failure to create the directory.  We'll just
                  # die on the next line though, so it's not that much of a loss.
 
-    f = open(depstarget, "w")
-    for dep in sorted(deps):
-        print >>f, "%s: %s" % (target, dep)
-        print >>f, "%s:" % dep
+    with open(depstarget, "w") as f:
+        f.write("%s: %s" % (target, source))
+        for dep in sorted(deps):
+            f.write(" \\\n%s" % dep)
+        f.write('\n')
+        for dep in sorted(deps):
+            f.write("%s:\n" % dep)
 
 if __name__ == "__main__":
     InvokeClWithDependencyGeneration(sys.argv[1:])
--- a/config/baseconfig.mk
+++ b/config/baseconfig.mk
@@ -18,8 +18,14 @@ ifeq ($(HOST_OS_ARCH),WINNT)
 # actually can be found regardless of path-style.
 ifeq (_:,$(.PYMAKE)_$(findstring :,$(srcdir)))
 $(error Windows-style srcdir being used with GNU make. Did you mean to run $(topsrcdir)/build/pymake/make.py instead? [see-also:     https://developer.mozilla.org/en/Gmake_vs._Pymake])
 endif
 ifeq (1_a,$(.PYMAKE)_$(firstword a$(subst /, ,$(srcdir))))
 $(error MSYS-style srcdir being used with Pymake. Did you mean to run GNU Make instead? [see-also: https://developer.mozilla.org/    en/Gmake_vs._Pymake])
 endif
 endif # WINNT
+
+ifdef .PYMAKE
+include_deps = $(eval -includedeps $(1))
+else
+include_deps = $(eval -include $(1))
+endif
--- a/config/makefiles/xpidl/Makefile.in
+++ b/config/makefiles/xpidl/Makefile.in
@@ -71,15 +71,11 @@ depends_files := $(foreach root,$(xpidl_
 
 $(dist_headers): $(dist_include_dir)/%.h: $(idl_headers_dir)/%.h
 	$(INSTALL) $< $(dist_include_dir)
 
 xpidl:: $(linked_xpt_files) $(dist_headers)
 
 $(linked_xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(idl_headers_dir) $(idl_xpt_dir))
 
-ifdef .PYMAKE
--includedeps $(depends_files)
-else
--include $(depends_files)
-endif
+$(call include_deps,$(depends_files))
 
 .PHONY: xpidl
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1616,35 +1616,27 @@ endif
 #   a previous build in the source tree) and thus neglect to create a
 #   dependency directory in the object directory, where we really need
 #   it.
 
 ifneq (,$(filter-out all chrome default export realchrome tools clean clobber clobber_all distclean realclean,$(MAKECMDGOALS)))
 MDDEPEND_FILES		:= $(strip $(wildcard $(foreach file,$(sort $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS) $(TARGETS)),$(MDDEPDIR)/$(notdir $(file)).pp) $(addprefix $(MDDEPDIR)/,$(EXTRA_MDDEPEND_FILES))))
 
 ifneq (,$(MDDEPEND_FILES))
-ifdef .PYMAKE
-includedeps $(MDDEPEND_FILES)
-else
-include $(MDDEPEND_FILES)
-endif
+$(call include_deps,$(MDDEPEND_FILES))
 endif
 
 endif
 
 
 ifneq (,$(filter export,$(MAKECMDGOALS)))
 MDDEPEND_FILES		:= $(strip $(wildcard $(addprefix $(MDDEPDIR)/,$(EXTRA_EXPORT_MDDEPEND_FILES))))
 
 ifneq (,$(MDDEPEND_FILES))
-ifdef .PYMAKE
-includedeps $(MDDEPEND_FILES)
-else
-include $(MDDEPEND_FILES)
-endif
+$(call include_deps,$(MDDEPEND_FILES))
 endif
 
 endif
 
 #############################################################################
 
 -include $(topsrcdir)/$(MOZ_BUILD_APP)/app-rules.mk
 -include $(MY_RULES)
--- a/configure.in
+++ b/configure.in
@@ -211,16 +211,20 @@ if test -n "$gonkdir" ; then
         MOZ_B2G_BT=1
         MOZ_B2G_CAMERA=1
         MOZ_OMX_DECODER=1
         AC_DEFINE(MOZ_OMX_DECODER)
         AC_SUBST(MOZ_OMX_DECODER)
         ;;
     18)
         GONK_INCLUDES="-I$gonkdir/frameworks/native/include"
+        if test -d "$gonkdir/external/bluetooth/bluez"; then
+            GONK_INCLUDES+=" -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib"
+            MOZ_B2G_BT=1
+        fi
         ;;
     *)
         AC_MSG_ERROR([Unsupported platform version: $ANDROID_VERSION])
         ;;
     esac
     CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/hardware/libhardware/include -I$gonkdir/external/valgrind/fxos-include $GONK_INCLUDES $CPPFLAGS"
     CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS"
     CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions -Wno-psabi $CXXFLAGS $STLPORT_CPPFLAGS"
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -29,17 +29,16 @@
 #include "nsUnicharUtils.h"
 #include "nsContentList.h"
 #include "nsIObserver.h"
 #include "nsIBaseWindow.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/css/ImageLoader.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
-#include "nsIScriptRuntime.h"
 #include "nsCOMArray.h"
 #include "nsDOMClassInfo.h"
 #include "nsCxPusher.h"
 
 #include "nsGUIEvent.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsIDOMNodeFilter.h"
 
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -14,17 +14,16 @@
 #include "nsICharsetConverterManager.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIContent.h"
 #include "mozilla/dom/Element.h"
 #include "nsGkAtoms.h"
 #include "nsNetUtil.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
-#include "nsIScriptRuntime.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIScriptElement.h"
 #include "nsIDOMHTMLScriptElement.h"
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -15,17 +15,16 @@
 #include "nsEventListenerManager.h"
 #include "nsCaret.h"
 #include "nsIDOMEventListener.h"
 #include "nsITextControlFrame.h"
 #include "nsGkAtoms.h"
 #include "nsPIDOMWindow.h"
 #include "nsIJSEventListener.h"
 #include "nsIScriptGlobalObject.h"
-#include "nsIScriptRuntime.h"
 #include "nsLayoutUtils.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
 #include "nsIFrame.h"
 #include "nsView.h"
 #include "nsViewManager.h"
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -904,19 +904,16 @@ protected:
   // These events get re-dispatched when the bfcache is exited.
   nsTArray<nsString> mPendingEvents;
 
   // Media loading flags. See:
   //   http://www.whatwg.org/specs/web-apps/current-work/#video)
   nsMediaNetworkState mNetworkState;
   nsMediaReadyState mReadyState;
 
-  // Last value passed from codec or stream source to UpdateReadyStateForData.
-  NextFrameStatus mLastNextFrameStatus;
-
   enum LoadAlgorithmState {
     // No load algorithm instance is waiting for a source to be added to the
     // media in order to continue loading.
     NOT_WAITING,
     // We've run the load algorithm, and we tried all source children of the
     // media element, and failed to load any successfully. We're waiting for
     // another source element to be added to the media element, and will try
     // to load any such element when its added.
@@ -1119,19 +1116,16 @@ protected:
   bool mMediaSecurityVerified;
 
   // The CORS mode when loading the media element
   CORSMode mCORSMode;
 
   // True if the media has an audio track
   bool mHasAudio;
 
-  // True if the media has a video track
-  bool mHasVideo;
-
   // True if the media's channel's download has been suspended.
   bool mDownloadSuspendedByCache;
 
   // Audio Channel Type.
   AudioChannelType mAudioChannelType;
 
   // Is this media element playing?
   bool mPlayingThroughTheAudioChannel;
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -63,18 +63,16 @@
 #include "nsMediaFragmentURIParser.h"
 #include "nsURIHashKey.h"
 #include "nsJSUtils.h"
 #include "MediaStreamGraph.h"
 #include "nsIScriptError.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "mozilla/dom/MediaSource.h"
 #include "MediaMetadataManager.h"
-#include "AudioStreamTrack.h"
-#include "VideoStreamTrack.h"
 
 #include "AudioChannelService.h"
 
 #include "nsCSSParser.h"
 #include "nsIMediaList.h"
 
 #include "ImageContainer.h"
 #include "nsIPowerManagerService.h"
@@ -618,20 +616,17 @@ void HTMLMediaElement::AbortExistingLoad
   mLoadedFirstFrame = false;
   mAutoplaying = true;
   mIsLoadingFromSourceChildren = false;
   mSuspendedAfterFirstFrame = false;
   mAllowSuspendAfterFirstFrame = true;
   mHaveQueuedSelectResource = false;
   mSuspendedForPreloadNone = false;
   mDownloadSuspendedByCache = false;
-  mHasAudio = false;
-  mHasVideo = false;
   mSourcePointer = nullptr;
-  mLastNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
 
   mChannels = 0;
   mRate = 0;
   mTags = nullptr;
 
   if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
     NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
@@ -1906,17 +1901,16 @@ HTMLMediaElement::LookupMediaElementURIT
 }
 
 HTMLMediaElement::HTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mSrcStreamListener(nullptr),
     mCurrentLoadID(0),
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
-    mLastNextFrameStatus(NEXT_FRAME_UNINITIALIZED),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mChannels(0),
     mRate(0),
     mPreloadAction(PRELOAD_UNDEFINED),
     mMediaSize(-1,-1),
     mLastCurrentTime(0.0),
     mFragmentStart(-1.0),
@@ -1947,17 +1941,16 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mAllowSuspendAfterFirstFrame(true),
     mHasPlayedOrSeeked(false),
     mHasSelfReference(false),
     mShuttingDown(false),
     mSuspendedForPreloadNone(false),
     mMediaSecurityVerified(false),
     mCORSMode(CORS_NONE),
     mHasAudio(false),
-    mHasVideo(false),
     mDownloadSuspendedByCache(false),
     mAudioChannelType(AUDIO_CHANNEL_NORMAL),
     mPlayingThroughTheAudioChannel(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaElementLog) {
     gMediaElementLog = PR_NewLogModule("nsMediaElement");
   }
@@ -2699,37 +2692,28 @@ void HTMLMediaElement::SetupSrcMediaStre
   mSrcStreamListener = new StreamListener(this);
   GetSrcMediaStream()->AddListener(mSrcStreamListener);
   if (mPaused) {
     GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
   }
   if (mPausedForInactiveDocumentOrChannel) {
     GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
   }
-
-  nsAutoTArray<nsRefPtr<AudioStreamTrack>,1> audioTracks;
-  aStream->GetAudioTracks(audioTracks);
-  nsAutoTArray<nsRefPtr<VideoStreamTrack>,1> videoTracks;
-  aStream->GetVideoTracks(videoTracks);
-
-  // Clear aChannels, aRate and aTags, but set mHasAudio and mHasVideo
-  MetadataLoaded(0, 0,
-                 !audioTracks.IsEmpty(), !videoTracks.IsEmpty(),
-                 nullptr);
-  DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
-  mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
-
   ChangeDelayLoadStatus(false);
   GetSrcMediaStream()->AddAudioOutput(this);
   GetSrcMediaStream()->SetAudioOutputVolume(this, float(mMuted ? 0.0 : mVolume));
   VideoFrameContainer* container = GetVideoFrameContainer();
   if (container) {
     GetSrcMediaStream()->AddVideoOutput(container);
   }
-
+  ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
+  DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
+  mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
   AddRemoveSelfReference();
   // FirstFrameLoaded(false) will be called when the stream has current data,
   // to complete the setup by entering the HAVE_CURRENT_DATA state.
 }
 
 void HTMLMediaElement::EndSrcMediaStreamPlayback()
 {
   MediaStream* stream = GetSrcMediaStream();
@@ -2776,17 +2760,16 @@ void HTMLMediaElement::MetadataLoaded(in
                                       int aRate,
                                       bool aHasAudio,
                                       bool aHasVideo,
                                       const MetadataTags* aTags)
 {
   mChannels = aChannels;
   mRate = aRate;
   mHasAudio = aHasAudio;
-  mHasVideo = aHasVideo;
   mTags = aTags;
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
   DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
   if (mDecoder && mDecoder->IsTransportSeekable() && mDecoder->IsMediaSeekable()) {
     ProcessMediaFragmentURI();
     mDecoder->SetFragmentEndTime(mFragmentEnd);
   }
@@ -2796,18 +2779,20 @@ void HTMLMediaElement::MetadataLoaded(in
   // audio only file.
   if (!aHasVideo) {
     mVideoFrameContainer = nullptr;
   }
 }
 
 void HTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded)
 {
+  ChangeReadyState(aResourceFullyLoaded ?
+    nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA :
+    nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
   ChangeDelayLoadStatus(false);
-  UpdateReadyStateForData(NEXT_FRAME_UNAVAILABLE);
 
   NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
 
   if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused &&
       !aResourceFullyLoaded &&
       !HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
       mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
     mSuspendedAfterFirstFrame = true;
@@ -2984,18 +2969,16 @@ void HTMLMediaElement::DownloadStalled()
 
 bool HTMLMediaElement::ShouldCheckAllowOrigin()
 {
   return mCORSMode != CORS_NONE;
 }
 
 void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame)
 {
-  mLastNextFrameStatus = aNextFrame;
-
   if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
     // aNextFrame might have a next frame because the decoder can advance
     // on its own thread before ResourceLoaded or MetadataLoaded gets
     // a chance to run.
     // The arrival of more data can't change us out of this readyState.
     return;
   }
 
@@ -3009,24 +2992,16 @@ void HTMLMediaElement::UpdateReadyStateF
     // transition, we will never fire the "canplaythrough" event if the
     // media cache is too small, and scripts are bound to fail. Don't force
     // this transition if the decoder is in ended state; the readyState
     // should remain at HAVE_CURRENT_DATA in this case.
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
     return;
   }
 
-  if (mReadyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA && mHasVideo) {
-    VideoFrameContainer* container = GetVideoFrameContainer();
-    if (container && mMediaSize == nsIntSize(-1,-1)) {
-      // No frame has been set yet. Don't advance.
-      return;
-    }
-  }
-
   if (aNextFrame != MediaDecoderOwner::NEXT_FRAME_AVAILABLE) {
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
     if (!mWaitingFired && aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING) {
       FireTimeUpdate(false);
       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
       mWaitingFired = true;
     }
     return;
@@ -3151,31 +3126,36 @@ void HTMLMediaElement::CheckAutoplayData
       GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
     }
     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
   }
 }
 
 VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
 {
-  if (mVideoFrameContainer) {
+  // If we have loaded the metadata, and the size of the video is still
+  // (-1, -1), the media has no video. Don't go a create a video frame
+  // container.
+  if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
+      mMediaSize == nsIntSize(-1, -1)) {
+    return nullptr;
+  }
+
+  if (mVideoFrameContainer)
     return mVideoFrameContainer;
-  }
 
   // If we have a print surface, this is just a static image so
   // no image container is required
-  if (mPrintSurface) {
+  if (mPrintSurface)
     return nullptr;
-  }
 
   // Only video frames need an image container.
   nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
-  if (!video) {
+  if (!video)
     return nullptr;
-  }
 
   mVideoFrameContainer =
     new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
 
   return mVideoFrameContainer;
 }
 
 nsresult HTMLMediaElement::DispatchAudioAvailableEvent(float* aFrameBuffer,
@@ -3295,17 +3275,16 @@ void HTMLMediaElement::NotifyDecoderPrin
     OutputMediaStream* ms = &mOutputStreams[i];
     ms->mStream->CombineWithPrincipal(principal);
   }
 }
 
 void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
 {
   mMediaSize = size;
-  UpdateReadyStateForData(mLastNextFrameStatus);
 }
 
 void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
 {
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     if (aPauseElement) {
       if (mDecoder) {
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -25,16 +25,17 @@
 #include "BiquadFilterNode.h"
 #include "ScriptProcessorNode.h"
 #include "ChannelMergerNode.h"
 #include "ChannelSplitterNode.h"
 #include "MediaStreamAudioDestinationNode.h"
 #include "WaveShaperNode.h"
 #include "PeriodicWave.h"
 #include "ConvolverNode.h"
+#include "OscillatorNode.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(AudioContext, nsDOMEventTargetHelper,
                                      mDestination, mListener)
 
@@ -59,16 +60,17 @@ AudioContext::AudioContext(nsPIDOMWindow
 {
   // Actually play audio
   mDestination->Stream()->AddAudioOutput(&gWebAudioOutputKey);
   nsDOMEventTargetHelper::BindToOwner(aWindow);
   SetIsDOMBinding();
 
   mPannerNodes.Init();
   mAudioBufferSourceNodes.Init();
+  mOscillatorNodes.Init();
   mScriptProcessorNodes.Init();
 }
 
 AudioContext::~AudioContext()
 {
 }
 
 JSObject*
@@ -364,16 +366,25 @@ AudioContext::CreateDynamicsCompressor()
 already_AddRefed<BiquadFilterNode>
 AudioContext::CreateBiquadFilter()
 {
   nsRefPtr<BiquadFilterNode> filterNode =
     new BiquadFilterNode(this);
   return filterNode.forget();
 }
 
+already_AddRefed<OscillatorNode>
+AudioContext::CreateOscillator()
+{
+  nsRefPtr<OscillatorNode> oscillatorNode =
+    new OscillatorNode(this);
+  mOscillatorNodes.PutEntry(oscillatorNode);
+  return oscillatorNode.forget();
+}
+
 already_AddRefed<PeriodicWave>
 AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
                                  const Float32Array& aImagData,
                                  ErrorResult& aRv)
 {
   if (aRealData.Length() != aImagData.Length() ||
       aRealData.Length() == 0 ||
       aRealData.Length() > 4096) {
@@ -439,16 +450,22 @@ AudioContext::UnregisterPannerNode(Panne
 {
   mPannerNodes.RemoveEntry(aNode);
   if (mListener) {
     mListener->UnregisterPannerNode(aNode);
   }
 }
 
 void
+AudioContext::UnregisterOscillatorNode(OscillatorNode* aNode)
+{
+  mOscillatorNodes.RemoveEntry(aNode);
+}
+
+void
 AudioContext::UnregisterScriptProcessorNode(ScriptProcessorNode* aNode)
 {
   mScriptProcessorNodes.RemoveEntry(aNode);
 }
 
 static PLDHashOperator
 FindConnectedSourcesOn(nsPtrHashKey<PannerNode>* aEntry, void* aData)
 {
@@ -529,16 +546,24 @@ AudioContext::Shutdown()
   // hashtable call to remove the pointer from the hashtable, which is
   // not safe.
   nsTArray<AudioBufferSourceNode*> sourceNodes;
   GetHashtableElements(mAudioBufferSourceNodes, sourceNodes);
   for (uint32_t i = 0; i < sourceNodes.Length(); ++i) {
     ErrorResult rv;
     sourceNodes[i]->Stop(0.0, rv, true);
   }
+  // Stop all Oscillator nodes to make sure they release their
+  // playing reference.
+  nsTArray<OscillatorNode*> oscNodes;
+  GetHashtableElements(mOscillatorNodes, oscNodes);
+  for (uint32_t i = 0; i < oscNodes.Length(); ++i) {
+    ErrorResult rv;
+    oscNodes[i]->Stop(0.0, rv);
+  }
   // Stop all script processor nodes, to make sure that they release
   // their self-references.
   nsTArray<ScriptProcessorNode*> spNodes;
   GetHashtableElements(mScriptProcessorNodes, spNodes);
   for (uint32_t i = 0; i < spNodes.Length(); ++i) {
     spNodes[i]->Stop();
   }
 
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -49,16 +49,17 @@ class ConvolverNode;
 class DelayNode;
 class DynamicsCompressorNode;
 class GainNode;
 class GlobalObject;
 class HTMLMediaElement;
 class MediaElementAudioSourceNode;
 class MediaStreamAudioDestinationNode;
 class MediaStreamAudioSourceNode;
+class OscillatorNode;
 class PannerNode;
 class ScriptProcessorNode;
 class WaveShaperNode;
 class PeriodicWave;
 
 class AudioContext MOZ_FINAL : public nsDOMEventTargetHelper,
                                public EnableWebAudioCheck
 {
@@ -188,16 +189,19 @@ public:
   CreateChannelMerger(uint32_t aNumberOfInputs, ErrorResult& aRv);
 
   already_AddRefed<DynamicsCompressorNode>
   CreateDynamicsCompressor();
 
   already_AddRefed<BiquadFilterNode>
   CreateBiquadFilter();
 
+  already_AddRefed<OscillatorNode>
+  CreateOscillator();
+
   already_AddRefed<PeriodicWave>
   CreatePeriodicWave(const Float32Array& aRealData, const Float32Array& aImagData,
                      ErrorResult& aRv);
 
   void DecodeAudioData(const ArrayBuffer& aBuffer,
                        DecodeSuccessCallback& aSuccessCallback,
                        const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback);
 
@@ -206,16 +210,17 @@ public:
   IMPL_EVENT_HANDLER(complete)
 
   bool IsOffline() const { return mIsOffline; }
 
   MediaStreamGraph* Graph() const;
   MediaStream* DestinationStream() const;
   void UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode);
   void UnregisterPannerNode(PannerNode* aNode);
+  void UnregisterOscillatorNode(OscillatorNode* aNode);
   void UnregisterScriptProcessorNode(ScriptProcessorNode* aNode);
   void UpdatePannerSource();
 
   uint32_t MaxChannelCount() const;
 
   void Mute() const;
   void Unmute() const;
 
@@ -235,16 +240,17 @@ private:
   nsRefPtr<AudioListener> mListener;
   MediaBufferDecoder mDecoder;
   nsTArray<nsRefPtr<WebAudioDecodeJob> > mDecodeJobs;
   // Two hashsets containing all the PannerNodes and AudioBufferSourceNodes,
   // to compute the doppler shift, and also to stop AudioBufferSourceNodes.
   // These are all weak pointers.
   nsTHashtable<nsPtrHashKey<PannerNode> > mPannerNodes;
   nsTHashtable<nsPtrHashKey<AudioBufferSourceNode> > mAudioBufferSourceNodes;
+  nsTHashtable<nsPtrHashKey<OscillatorNode> > mOscillatorNodes;
   // Hashset containing all ScriptProcessorNodes in order to stop them.
   // These are all weak pointers.
   nsTHashtable<nsPtrHashKey<ScriptProcessorNode> > mScriptProcessorNodes;
   // Number of channels passed in the OfflineAudioContext ctor.
   uint32_t mNumberOfChannels;
   bool mIsOffline;
 };
 
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -0,0 +1,435 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "OscillatorNode.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeStream.h"
+#include "AudioDestinationNode.h"
+#include "WebAudioUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(OscillatorNode)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(OscillatorNode)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPeriodicWave)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrequency)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDetune)
+  if (tmp->Context()) {
+    tmp->Context()->UnregisterOscillatorNode(tmp);
+  }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode);
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(OscillatorNode, AudioNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPeriodicWave)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrequency)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDetune)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
+
+class OscillatorNodeEngine : public AudioNodeEngine
+{
+public:
+  OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
+    : AudioNodeEngine(aNode)
+    , mSource(nullptr)
+    , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
+    , mStart(0)
+    , mStop(TRACK_TICKS_MAX)
+    // Keep the default values in sync with OscillatorNode::OscillatorNode.
+    , mFrequency(440.f)
+    , mDetune(0.f)
+    , mType(OscillatorType::Sine)
+    , mPhase(0.)
+  {
+  }
+
+  void SetSourceStream(AudioNodeStream* aSource)
+  {
+    mSource = aSource;
+  }
+
+  enum Parameters {
+    FREQUENCY,
+    DETUNE,
+    TYPE,
+    PERIODICWAVE,
+    START,
+    STOP,
+  };
+  void SetTimelineParameter(uint32_t aIndex,
+                            const AudioParamTimeline& aValue,
+                            TrackRate aSampleRate) MOZ_OVERRIDE
+  {
+    switch (aIndex) {
+    case FREQUENCY:
+      MOZ_ASSERT(mSource && mDestination);
+      mFrequency = aValue;
+      WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination);
+      break;
+    case DETUNE:
+      MOZ_ASSERT(mSource && mDestination);
+      mDetune = aValue;
+      WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination);
+      break;
+    default:
+      NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
+    }
+  }
+  virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
+  {
+    switch (aIndex) {
+    case START: mStart = aParam; break;
+    case STOP: mStop = aParam; break;
+    default:
+      NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
+    }
+  }
+  virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
+  {
+    switch (aIndex) {
+    case TYPE: mType = static_cast<OscillatorType>(aParam); break;
+    default:
+      NS_ERROR("Bad OscillatorNodeEngine Int32Parameter");
+    }
+  }
+
+  double ComputeFrequency(TrackTicks ticks, size_t count)
+  {
+    double frequency, detune;
+    if (mFrequency.HasSimpleValue()) {
+      frequency = mFrequency.GetValue();
+    } else {
+      frequency = mFrequency.GetValueAtTime(ticks, count);
+    }
+    if (mDetune.HasSimpleValue()) {
+      detune = mDetune.GetValue();
+    } else {
+      detune = mDetune.GetValueAtTime(ticks, count);
+    }
+    return frequency * pow(2., detune / 1200.);
+  }
+
+  void FillBounds(float* output, TrackTicks ticks,
+                  uint32_t& start, uint32_t& end)
+  {
+    MOZ_ASSERT(output);
+    static_assert(TrackTicks(WEBAUDIO_BLOCK_SIZE) < UINT_MAX,
+        "WEBAUDIO_BLOCK_SIZE overflows interator bounds.");
+    start = 0;
+    if (ticks < mStart) {
+      start = mStart - ticks;
+      for (uint32_t i = 0; i < start; ++i) {
+        output[i] = 0.0;
+      }
+    }
+    end = WEBAUDIO_BLOCK_SIZE;
+    if (ticks + end > mStop) {
+      end = mStop - ticks;
+      for (uint32_t i = end; i < WEBAUDIO_BLOCK_SIZE; ++i) {
+        output[i] = 0.0;
+      }
+    }
+  }
+
+  void ComputeSine(AudioChunk *aOutput)
+  {
+    AllocateAudioBlock(1, aOutput);
+    float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
+
+    TrackTicks ticks = mSource->GetCurrentPosition();
+    uint32_t start, end;
+    FillBounds(output, ticks, start, end);
+
+    double rate = 2.*M_PI / mSource->SampleRate();
+    double phase = mPhase;
+    for (uint32_t i = start; i < end; ++i) {
+      phase += ComputeFrequency(ticks, i) * rate;
+      output[i] = sin(phase);
+    }
+    mPhase = phase;
+    while (mPhase > 2.0*M_PI) {
+      // Rescale to avoid precision reductions on long runs.
+      mPhase -= 2.0*M_PI;
+    }
+  }
+
+  void ComputeSquare(AudioChunk *aOutput)
+  {
+    AllocateAudioBlock(1, aOutput);
+    float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
+
+    TrackTicks ticks = mSource->GetCurrentPosition();
+    uint32_t start, end;
+    FillBounds(output, ticks, start, end);
+
+    double rate = 1.0 / mSource->SampleRate();
+    double phase = mPhase;
+    for (uint32_t i = start; i < end; ++i) {
+      phase += ComputeFrequency(ticks, i) * rate;
+      if (phase > 1.0) {
+        phase -= 1.0;
+      }
+      output[i] = phase < 0.5 ? 1.0 : -1.0;
+    }
+    mPhase = phase;
+  }
+
+  void ComputeSawtooth(AudioChunk *aOutput)
+  {
+    AllocateAudioBlock(1, aOutput);
+    float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
+
+    TrackTicks ticks = mSource->GetCurrentPosition();
+    uint32_t start, end;
+    FillBounds(output, ticks, start, end);
+
+    double rate = 1.0 / mSource->SampleRate();
+    double phase = mPhase;
+    for (uint32_t i = start; i < end; ++i) {
+      phase += ComputeFrequency(ticks, i) * rate;
+      if (phase > 1.0) {
+        phase -= 1.0;
+      }
+      output[i] = phase < 0.5 ? 2.0*phase : 2.0*(phase - 1.0);
+    }
+    mPhase = phase;
+  }
+
+  void ComputeTriangle(AudioChunk *aOutput)
+  {
+    AllocateAudioBlock(1, aOutput);
+    float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
+
+    TrackTicks ticks = mSource->GetCurrentPosition();
+    uint32_t start, end;
+    FillBounds(output, ticks, start, end);
+
+    double rate = 1.0 / mSource->SampleRate();
+    double phase = mPhase;
+    for (uint32_t i = start; i < end; ++i) {
+      phase += ComputeFrequency(ticks, i) * rate;
+      if (phase > 1.0) {
+        phase -= 1.0;
+      }
+      if (phase < 0.25) {
+        output[i] = 4.0*phase;
+      } else if (phase < 0.75) {
+        output[i] = 1.0 - 4.0*(phase - 0.25);
+      } else {
+        output[i] = 4.0*(phase - 0.75) - 1.0;
+      }
+    }
+    mPhase = phase;
+  }
+
+  void ComputeSilence(AudioChunk *aOutput)
+  {
+    aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+  }
+
+  virtual void ProduceAudioBlock(AudioNodeStream* aStream,
+                                 const AudioChunk& aInput,
+                                 AudioChunk* aOutput,
+                                 bool* aFinished) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(mSource == aStream, "Invalid source stream");
+
+    TrackTicks ticks = aStream->GetCurrentPosition();
+    if (ticks + WEBAUDIO_BLOCK_SIZE < mStart) {
+      // We're not playing yet.
+      ComputeSilence(aOutput);
+      return;
+    }
+    if (ticks >= mStop) {
+      // We've finished playing.
+      ComputeSilence(aOutput);
+      *aFinished = true;
+      return;
+    }
+    // Synthesize the correct waveform.
+    switch (mType) {
+      case OscillatorType::Sine:
+        ComputeSine(aOutput);
+        break;
+      case OscillatorType::Square:
+        ComputeSquare(aOutput);
+        break;
+      case OscillatorType::Sawtooth:
+        ComputeSawtooth(aOutput);
+        break;
+      case OscillatorType::Triangle:
+        ComputeTriangle(aOutput);
+        break;
+      default:
+        ComputeSilence(aOutput);
+    }
+  }
+
+  AudioNodeStream* mSource;
+  AudioNodeStream* mDestination;
+  TrackTicks mStart;
+  TrackTicks mStop;
+  AudioParamTimeline mFrequency;
+  AudioParamTimeline mDetune;
+  OscillatorType mType;
+  double mPhase;
+};
+
+OscillatorNode::OscillatorNode(AudioContext* aContext)
+  : AudioNode(aContext,
+              2,
+              ChannelCountMode::Max,
+              ChannelInterpretation::Speakers)
+  , mType(OscillatorType::Sine)
+  , mFrequency(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
+               SendFrequencyToStream, 440.0f))
+  , mDetune(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
+            SendDetuneToStream, 0.0f))
+  , mStartCalled(false)
+  , mStopped(false)
+{
+  OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
+  mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
+  engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
+}
+
+OscillatorNode::~OscillatorNode()
+{
+  if (Context()) {
+    Context()->UnregisterOscillatorNode(this);
+  }
+}
+
+JSObject*
+OscillatorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return OscillatorNodeBinding::Wrap(aCx, aScope, this);
+}
+
+void
+OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
+{
+  OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
+  SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency);
+}
+
+void
+OscillatorNode::SendDetuneToStream(AudioNode* aNode)
+{
+  OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
+  SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune);
+}
+
+void
+OscillatorNode::SendTypeToStream()
+{
+  SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
+  if (mType == OscillatorType::Custom) {
+    // TODO: Send the custom wave table somehow
+  }
+}
+
+void
+OscillatorNode::Start(double aWhen, ErrorResult& aRv)
+{
+  if (!WebAudioUtils::IsTimeValid(aWhen)) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  if (mStartCalled) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  mStartCalled = true;
+
+  AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
+  if (!ns) {
+    // Nothing to play, or we're already dead for some reason
+    return;
+  }
+
+  // TODO: Perhaps we need to do more here.
+  ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
+                             Context()->DestinationStream(),
+                             aWhen);
+
+  MOZ_ASSERT(!mPlayingRef, "We can only accept a successful start() call once");
+  mPlayingRef.Take(this);
+}
+
+void
+OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
+{
+  if (!WebAudioUtils::IsTimeValid(aWhen)) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  if (!mStartCalled) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  mPlayingRef.Drop(this);
+
+  AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
+  if (!ns || !Context()) {
+    // We've already stopped and had our stream shut down
+    return;
+  }
+
+  // TODO: Perhaps we need to do more here.
+  ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
+                             Context()->DestinationStream(),
+                             std::max(0.0, aWhen));
+}
+
+void
+OscillatorNode::NotifyMainThreadStateChanged()
+{
+  if (mStream->IsFinished()) {
+    class EndedEventDispatcher : public nsRunnable
+    {
+    public:
+      explicit EndedEventDispatcher(OscillatorNode* aNode)
+        : mNode(aNode) {}
+      NS_IMETHODIMP Run()
+      {
+        // If it's not safe to run scripts right now, schedule this to run later
+        if (!nsContentUtils::IsSafeToRunScript()) {
+          nsContentUtils::AddScriptRunner(this);
+          return NS_OK;
+        }
+
+        mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
+        return NS_OK;
+      }
+    private:
+      nsRefPtr<OscillatorNode> mNode;
+    };
+    if (!mStopped) {
+      // Only dispatch the ended event once
+      NS_DispatchToMainThread(new EndedEventDispatcher(this));
+      mStopped = true;
+    }
+
+    // Drop the playing reference
+    // Warning: The below line might delete this.
+    mPlayingRef.Drop(this);
+  }
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/OscillatorNode.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 OscillatorNode_h_
+#define OscillatorNode_h_
+
+#include "AudioNode.h"
+#include "AudioParam.h"
+#include "PeriodicWave.h"
+#include "mozilla/dom/OscillatorNodeBinding.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioContext;
+
+class OscillatorNode : public AudioNode,
+                       public MainThreadMediaStreamListener
+{
+public:
+  explicit OscillatorNode(AudioContext* aContext);
+  virtual ~OscillatorNode();
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OscillatorNode, AudioNode)
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  virtual void DestroyMediaStream() MOZ_OVERRIDE
+  {
+    if (mStream) {
+      mStream->RemoveMainThreadListener(this);
+    }
+    AudioNode::DestroyMediaStream();
+  }
+  virtual uint16_t NumberOfInputs() const MOZ_FINAL MOZ_OVERRIDE
+  {
+    return 0;
+  }
+
+  OscillatorType Type() const
+  {
+    return mType;
+  }
+  void SetType(OscillatorType aType, ErrorResult& aRv)
+  {
+    if (!Preferences::GetBool("media.webaudio.legacy.OscillatorNode")) {
+      // Do not accept the alternate enum values unless the legacy pref
+      // has been turned on.
+      switch (aType) {
+      case OscillatorType::_0:
+      case OscillatorType::_1:
+      case OscillatorType::_2:
+      case OscillatorType::_3:
+      case OscillatorType::_4:
+        // Do nothing in order to emulate setting an invalid enum value.
+        return;
+      default:
+        // Shut up the compiler warning
+        break;
+      }
+    }
+
+    // Handle the alternate enum values
+    switch (aType) {
+    case OscillatorType::_0: aType = OscillatorType::Sine; break;
+    case OscillatorType::_1: aType = OscillatorType::Square; break;
+    case OscillatorType::_2: aType = OscillatorType::Sawtooth; break;
+    case OscillatorType::_3: aType = OscillatorType::Triangle; break;
+    case OscillatorType::_4: aType = OscillatorType::Custom; break;
+    default:
+      // Shut up the compiler warning
+      break;
+    }
+
+    if (aType == OscillatorType::Custom) {
+      aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+      return;
+    }
+    mType = aType;
+    SendTypeToStream();
+  }
+
+  AudioParam* Frequency() const
+  {
+    return mFrequency;
+  }
+  AudioParam* Detune() const
+  {
+    return mDetune;
+  }
+
+  void Start(double aWhen, ErrorResult& aRv);
+  void NoteOn(double aWhen, ErrorResult& aRv)
+  {
+    Start(aWhen, aRv);
+  }
+  void Stop(double aWhen, ErrorResult& aRv);
+  void NoteOff(double aWhen, ErrorResult& aRv)
+  {
+    Stop(aWhen, aRv);
+  }
+  void SetPeriodicWave(PeriodicWave& aPeriodicWave)
+  {
+    mPeriodicWave = &aPeriodicWave;
+    mType = OscillatorType::Custom;
+    SendTypeToStream();
+  }
+
+  IMPL_EVENT_HANDLER(ended)
+
+  virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
+
+private:
+  static void SendFrequencyToStream(AudioNode* aNode);
+  static void SendDetuneToStream(AudioNode* aNode);
+  void SendTypeToStream();
+
+private:
+  OscillatorType mType;
+  nsRefPtr<PeriodicWave> mPeriodicWave;
+  nsRefPtr<AudioParam> mFrequency;
+  nsRefPtr<AudioParam> mDetune;
+  SelfReference<OscillatorNode> mPlayingRef;
+  bool mStartCalled;
+  bool mStopped;
+};
+
+}
+}
+
+#endif
+
--- a/content/media/webaudio/moz.build
+++ b/content/media/webaudio/moz.build
@@ -38,16 +38,17 @@ EXPORTS.mozilla.dom += [
     'DelayNode.h',
     'DynamicsCompressorNode.h',
     'EnableWebAudioCheck.h',
     'GainNode.h',
     'MediaElementAudioSourceNode.h',
     'MediaStreamAudioDestinationNode.h',
     'MediaStreamAudioSourceNode.h',
     'OfflineAudioCompletionEvent.h',
+    'OscillatorNode.h',
     'PannerNode.h',
     'PeriodicWave.h',
     'ScriptProcessorNode.h',
     'WaveShaperNode.h',
 ]
 
 CPP_SOURCES += [
     'AnalyserNode.cpp',
@@ -69,16 +70,17 @@ CPP_SOURCES += [
     'EnableWebAudioCheck.cpp',
     'FFTBlock.cpp',
     'GainNode.cpp',
     'MediaBufferDecoder.cpp',
     'MediaElementAudioSourceNode.cpp',
     'MediaStreamAudioDestinationNode.cpp',
     'MediaStreamAudioSourceNode.cpp',
     'OfflineAudioCompletionEvent.cpp',
+    'OscillatorNode.cpp',
     'PannerNode.cpp',
     'PeriodicWave.cpp',
     'ScriptProcessorNode.cpp',
     'ThreeDPoint.cpp',
     'WaveShaperNode.cpp',
     'WebAudioUtils.cpp',
 ]
 
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -72,16 +72,17 @@ MOCHITEST_FILES := \
   test_mediaStreamAudioSourceNode.html \
   test_mediaStreamAudioSourceNodeCrossOrigin.html \
   test_mediaStreamAudioSourceNodeResampling.html \
   test_mixingRules.html \
   test_nodeToParamConnection.html \
   test_OfflineAudioContext.html \
   test_offlineDestinationChannelCountLess.html \
   test_offlineDestinationChannelCountMore.html \
+  test_oscillatorNode.html \
   test_pannerNode.html \
   test_pannerNode_equalPower.html \
   test_periodicWave.html \
   test_scriptProcessorNode.html \
   test_scriptProcessorNodeChannelCount.html \
   test_scriptProcessorNodeZeroInputOutput.html \
   test_singleSourceDest.html \
   test_waveShaper.html \
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_oscillatorNode.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test the OscillatorNode interface</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+
+  var context = new AudioContext();
+  var osc = context.createOscillator();
+
+  is(osc.channelCount, 2, "Oscillator node has 2 input channels by default");
+  is(osc.channelCountMode, "max", "Correct channelCountMode for the Oscillator node");
+  is(osc.channelInterpretation, "speakers", "Correct channelCountInterpretation for the Oscillator node");
+  is(osc.type, "sine", "Correct default type");
+  expectException(function() {
+    osc.type = "custom";
+  }, DOMException.NOT_SUPPORTED_ERR);
+  expectException(function() {
+    osc.type = osc.CUSTOM;
+  }, DOMException.NOT_SUPPORTED_ERR);
+  is(osc.type, "sine", "Cannot set the type to custom");
+  is(osc.frequency.value, 440, "Correct default frequency value");
+  is(osc.detune.value, 0, "Correct default detine value");
+
+  // Make sure that we can set all of the valid type values
+  var types = [
+    "sine",
+    "square",
+    "sawtooth",
+    "triangle",
+  ];
+  for (var i = 0; i < types.length; ++i) {
+    osc.type = osc[types[i].toUpperCase()];
+    is(osc.type, types[i], "Correct alternname type enum value");
+    osc.type = types[i];
+  }
+
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -8,17 +8,16 @@
 
 #include "nsXBLDocumentInfo.h"
 #include "nsHashtable.h"
 #include "nsIDocument.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
-#include "nsIScriptRuntime.h"
 #include "nsIDOMScriptObjectFactory.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "nsIURI.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIChromeRegistry.h"
 #include "nsIPrincipal.h"
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -32,27 +32,26 @@
 #include "nsIDOMXULElement.h"
 #include "nsIDOMElementCSSInlineStyle.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDocument.h"
 #include "nsEventListenerManager.h"
 #include "nsEventStateManager.h"
 #include "nsFocusManager.h"
 #include "nsHTMLStyleSheet.h"
+#include "nsIJSRuntimeService.h"
 #include "nsINameSpaceManager.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIRDFCompositeDataSource.h"
 #include "nsIRDFNode.h"
 #include "nsIRDFService.h"
 #include "nsIScriptContext.h"
-#include "nsIScriptRuntime.h"
-#include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIServiceManager.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsIStyleSheet.h"
 #include "nsIURL.h"
 #include "nsViewManager.h"
 #include "nsIWidget.h"
 #include "nsIXULDocument.h"
@@ -1986,17 +1985,17 @@ nsXULPrototypeAttribute::~nsXULPrototype
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeElement
 //
 
 nsresult
 nsXULPrototypeElement::Serialize(nsIObjectOutputStream* aStream,
-                                 nsIScriptGlobalObject* aGlobal,
+                                 nsXULPrototypeDocument* aProtoDoc,
                                  const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
     nsresult rv;
 
     // Write basic prototype data
     rv = aStream->Write32(mType);
 
     // Write Node Info
@@ -2048,34 +2047,34 @@ nsXULPrototypeElement::Serialize(nsIObje
       rv = tmp;
     }
     for (i = 0; i < mChildren.Length(); i++) {
         nsXULPrototypeNode* child = mChildren[i].get();
         switch (child->mType) {
         case eType_Element:
         case eType_Text:
         case eType_PI:
-            tmp = child->Serialize(aStream, aGlobal, aNodeInfos);
+            tmp = child->Serialize(aStream, aProtoDoc, aNodeInfos);
             if (NS_FAILED(tmp)) {
               rv = tmp;
             }
             break;
         case eType_Script:
             tmp = aStream->Write32(child->mType);
             if (NS_FAILED(tmp)) {
               rv = tmp;
             }
             nsXULPrototypeScript* script = static_cast<nsXULPrototypeScript*>(child);
 
             tmp = aStream->Write8(script->mOutOfLine);
             if (NS_FAILED(tmp)) {
               rv = tmp;
             }
             if (! script->mOutOfLine) {
-                tmp = script->Serialize(aStream, aGlobal, aNodeInfos);
+                tmp = script->Serialize(aStream, aProtoDoc, aNodeInfos);
                 if (NS_FAILED(tmp)) {
                   rv = tmp;
                 }
             } else {
                 tmp = aStream->WriteCompoundObject(script->mSrcURI,
                                                    NS_GET_IID(nsIURI),
                                                    true);
                 if (NS_FAILED(tmp)) {
@@ -2083,32 +2082,32 @@ nsXULPrototypeElement::Serialize(nsIObje
                 }
 
                 if (script->GetScriptObject()) {
                     // This may return NS_OK without muxing script->mSrcURI's
                     // data into the cache file, in the case where that
                     // muxed document is already there (written by a prior
                     // session, or by an earlier cache episode during this
                     // session).
-                    tmp = script->SerializeOutOfLine(aStream, aGlobal);
+                    tmp = script->SerializeOutOfLine(aStream, aProtoDoc);
                     if (NS_FAILED(tmp)) {
                       rv = tmp;
                     }
                 }
             }
             break;
         }
     }
 
     return rv;
 }
 
 nsresult
 nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
-                                   nsIScriptGlobalObject* aGlobal,
+                                   nsXULPrototypeDocument* aProtoDoc,
                                    nsIURI* aDocumentURI,
                                    const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
     NS_PRECONDITION(aNodeInfos, "missing nodeinfo array");
 
     // Read Node Info
     uint32_t number;
     nsresult rv = aStream->Read32(&number);
@@ -2172,41 +2171,41 @@ nsXULPrototypeElement::Deserialize(nsIOb
 
             switch (childType) {
             case eType_Element:
                 child = new nsXULPrototypeElement();
                 if (! child)
                     return NS_ERROR_OUT_OF_MEMORY;
                 child->mType = childType;
 
-                tmp = child->Deserialize(aStream, aGlobal, aDocumentURI,
+                tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
                                          aNodeInfos);
                 if (NS_FAILED(tmp)) {
                   rv = tmp;
                 }
                 break;
             case eType_Text:
                 child = new nsXULPrototypeText();
                 if (! child)
                     return NS_ERROR_OUT_OF_MEMORY;
                 child->mType = childType;
 
-                tmp = child->Deserialize(aStream, aGlobal, aDocumentURI,
+                tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
                                          aNodeInfos);
                 if (NS_FAILED(tmp)) {
                   rv = tmp;
                 }
                 break;
             case eType_PI:
                 child = new nsXULPrototypePI();
                 if (! child)
                     return NS_ERROR_OUT_OF_MEMORY;
                 child->mType = childType;
 
-                tmp = child->Deserialize(aStream, aGlobal, aDocumentURI,
+                tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
                                          aNodeInfos);
                 if (NS_FAILED(tmp)) {
                   rv = tmp;
                 }
                 break;
             case eType_Script: {
                 // language version/options obtained during deserialization.
                 nsXULPrototypeScript* script = new nsXULPrototypeScript(0, 0);
@@ -2215,28 +2214,28 @@ nsXULPrototypeElement::Deserialize(nsIOb
                 child = script;
                 child->mType = childType;
 
                 tmp = aStream->ReadBoolean(&script->mOutOfLine);
                 if (NS_FAILED(tmp)) {
                   rv = tmp;
                 }
                 if (! script->mOutOfLine) {
-                    tmp = script->Deserialize(aStream, aGlobal, aDocumentURI,
+                    tmp = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
                                               aNodeInfos);
                     if (NS_FAILED(tmp)) {
                       rv = tmp;
                     }
                 } else {
                     tmp = aStream->ReadObject(true, getter_AddRefs(script->mSrcURI));
                     if (NS_FAILED(tmp)) {
                       rv = tmp;
                     }
 
-                    tmp = script->DeserializeOutOfLine(aStream, aGlobal);
+                    tmp = script->DeserializeOutOfLine(aStream, aProtoDoc);
                     if (NS_FAILED(tmp)) {
                       rv = tmp;
                     }
                 }
                 // If we failed to deserialize, consider deleting 'script'?
                 break;
             }
             default:
@@ -2366,49 +2365,51 @@ nsXULPrototypeScript::nsXULPrototypeScri
 
 nsXULPrototypeScript::~nsXULPrototypeScript()
 {
     UnlinkJSObjects();
 }
 
 nsresult
 nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
-                                nsIScriptGlobalObject* aGlobal,
+                                nsXULPrototypeDocument* aProtoDoc,
                                 const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
-    nsIScriptContext *context = aGlobal->GetScriptContext();
+    AutoSafeJSContext cx;
+    JS::Rooted<JSObject*> global(cx, aProtoDoc->GetCompilationGlobal());
+    NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
+    JSAutoCompartment ac(cx, global);
+
     NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr ||
                  !mScriptObject,
                  "script source still loading when serializing?!");
     if (!mScriptObject)
         return NS_ERROR_FAILURE;
 
     // Write basic prototype data
     nsresult rv;
     rv = aStream->Write32(mLineNo);
     if (NS_FAILED(rv)) return rv;
     rv = aStream->Write32(mLangVersion);
     if (NS_FAILED(rv)) return rv;
 
-    // And delegate the writing to the nsIScriptContext.
-    //
     // Calling fromMarkedLocation() is safe because we trace mScriptObject in
     // TraceScriptObject() and because its value is never changed after it has
     // been set.
     JS::Handle<JSScript*> script =
         JS::Handle<JSScript*>::fromMarkedLocation(mScriptObject.address());
-    rv = context->Serialize(aStream, script);
-    if (NS_FAILED(rv)) return rv;
-
-    return NS_OK;
+    MOZ_ASSERT(!strcmp(JS_GetClass(JS::CurrentGlobalOrNull(cx))->name,
+                       "nsXULPrototypeScript compilation scope"));
+    return nsContentUtils::XPConnect()->WriteScript(aStream, cx,
+                                                    xpc_UnmarkGrayScript(script));
 }
 
 nsresult
 nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
-                                         nsIScriptGlobalObject* aGlobal)
+                                         nsXULPrototypeDocument* aProtoDoc)
 {
     nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
 
     bool isChrome = false;
     if (NS_FAILED(mSrcURI->SchemeIs("chrome", &isChrome)) || !isChrome)
        // Don't cache scripts that don't come from chrome uris.
        return rv;
 
@@ -2428,65 +2429,64 @@ nsXULPrototypeScript::SerializeOutOfLine
      */
     if (exists)
         return NS_OK;
 
     nsCOMPtr<nsIObjectOutputStream> oos;
     rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos));
     NS_ENSURE_SUCCESS(rv, rv);
     
-    nsresult tmp = Serialize(oos, aGlobal, nullptr);
+    nsresult tmp = Serialize(oos, aProtoDoc, nullptr);
     if (NS_FAILED(tmp)) {
       rv = tmp;
     }
     tmp = cache->FinishOutputStream(mSrcURI);
     if (NS_FAILED(tmp)) {
       rv = tmp;
     }
 
     if (NS_FAILED(rv))
         cache->AbortCaching();
     return rv;
 }
 
 
 nsresult
 nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
-                                  nsIScriptGlobalObject* aGlobal,
+                                  nsXULPrototypeDocument* aProtoDoc,
                                   nsIURI* aDocumentURI,
                                   const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
-    nsresult rv;
-
     NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr ||
                  !mScriptObject,
                  "prototype script not well-initialized when deserializing?!");
 
     // Read basic prototype data
     aStream->Read32(&mLineNo);
     aStream->Read32(&mLangVersion);
 
-    nsIScriptContext *context = aGlobal->GetScriptContext();
-    NS_ASSERTION(context != nullptr, "Have no context for deserialization");
-    NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
-    JSAutoRequest ar(context->GetNativeContext());
-    JS::Rooted<JSScript*> newScriptObject(context->GetNativeContext());
-    rv = context->Deserialize(aStream, &newScriptObject);
-    if (NS_FAILED(rv)) {
-        NS_WARNING("Language deseralization failed");
-        return rv;
-    }
+    AutoSafeJSContext cx;
+    JS::Rooted<JSObject*> global(cx, aProtoDoc->GetCompilationGlobal());
+    NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
+    JSAutoCompartment ac(cx, global);
+
+    JS::Rooted<JSScript*> newScriptObject(cx);
+    MOZ_ASSERT(!strcmp(JS_GetClass(JS::CurrentGlobalOrNull(cx))->name,
+                       "nsXULPrototypeScript compilation scope"));
+    nsresult rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
+                                                          newScriptObject.address());
+    NS_ENSURE_SUCCESS(rv, rv);
     Set(newScriptObject);
     return NS_OK;
 }
 
 
 nsresult
 nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
-                                           nsIScriptGlobalObject* aGlobal)
+                                           nsXULPrototypeDocument* aProtoDoc)
 {
     // Keep track of failure via rv, so we can
     // AbortCaching if things look bad.
     nsresult rv = NS_OK;
     nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
   
     nsCOMPtr<nsIObjectInputStream> objectInput = aInput;
     if (cache) {
@@ -2520,17 +2520,17 @@ nsXULPrototypeScript::DeserializeOutOfLi
  
             // We do reflect errors into rv, but our caller may want to
             // ignore our return value, because mScriptObject will be null
             // after any error, and that suffices to cause the script to
             // be reloaded (from the src= URI, if any) and recompiled.
             // We're better off slow-loading than bailing out due to a
             // error.
             if (NS_SUCCEEDED(rv))
-                rv = Deserialize(objectInput, aGlobal, nullptr, nullptr);
+                rv = Deserialize(objectInput, aProtoDoc, nullptr, nullptr);
 
             if (NS_SUCCEEDED(rv)) {
                 if (useXULCache && mSrcURI) {
                     bool isChrome = false;
                     mSrcURI->SchemeIs("chrome", &isChrome);
                     if (isChrome)
                         cache->PutScript(mSrcURI, GetScriptObject());
                 }
@@ -2568,17 +2568,22 @@ public:
 NS_IMETHODIMP
 NotifyOffThreadScriptCompletedRunnable::Run()
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     // Note: this unroots mScript so that it is available to be collected by the
     // JS GC. The receiver needs to root the script before performing a call that
     // could GC.
-    JS::FinishOffThreadScript(nsJSRuntime::sRuntime, mScript);
+    nsCOMPtr<nsIJSRuntimeService> svc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
+    NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
+    JSRuntime *rt;
+    svc->GetRuntime(&rt);
+    NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
+    JS::FinishOffThreadScript(rt, mScript);
 
     return mReceiver->OnScriptCompileComplete(mScript, mScript ? NS_OK : NS_ERROR_FAILURE);
 }
 
 static void
 OffThreadScriptReceiverCallback(JSScript *script, void *ptr)
 {
     // Be careful not to adjust the refcount on the receiver, as this callback
@@ -2591,56 +2596,39 @@ OffThreadScriptReceiverCallback(JSScript
 }
 
 nsresult
 nsXULPrototypeScript::Compile(const PRUnichar* aText,
                               int32_t aTextLength,
                               nsIURI* aURI,
                               uint32_t aLineNo,
                               nsIDocument* aDocument,
-                              nsIScriptGlobalObject* aGlobal,
+                              nsXULPrototypeDocument* aProtoDoc,
                               nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
 {
     // We'll compile the script using the prototype document's special
     // script object as the parent. This ensures that we won't end up
     // with an uncollectable reference.
     //
     // Compiling it using (for example) the first document's global
     // object would cause JS to keep a reference via the __proto__ or
     // parent pointer to the first document's global. If that happened,
     // our script object would reference the first document, and the
     // first document would indirectly reference the prototype document
     // because it keeps the prototype cache alive. Circularity!
-    NS_ASSERTION(aGlobal, "prototype doc has no script global");
-    if (!aGlobal) {
-        return NS_ERROR_UNEXPECTED;
-    }
-
-    // Use the prototype document's special context
-    nsIScriptContext *context = aGlobal->GetScriptContext();
-    NS_ASSERTION(context, "no context for script global");
-    if (! context) {
-      return NS_ERROR_UNEXPECTED;
-    }
+    MOZ_ASSERT(aProtoDoc);
+    NS_ENSURE_TRUE(aProtoDoc->GetCompilationGlobal(), NS_ERROR_UNEXPECTED);
+    AutoSafeJSContext cx;
+    JSAutoCompartment ac(cx, aProtoDoc->GetCompilationGlobal());
 
     nsAutoCString urlspec;
     nsContentUtils::GetWrapperSafeScriptFilename(aDocument, aURI, urlspec);
 
     // Ok, compile it to create a prototype script object!
-
-    JSContext* cx = context->GetNativeContext();
-    AutoCxPusher pusher(cx);
-
-    bool ok = false;
-    nsresult rv = nsContentUtils::GetSecurityManager()->
-                    CanExecuteScripts(cx, aDocument->NodePrincipal(), &ok);
-    NS_ENSURE_SUCCESS(rv, rv);
-    NS_ENSURE_TRUE(ok, NS_OK);
     NS_ENSURE_TRUE(JSVersion(mLangVersion) != JSVERSION_UNKNOWN, NS_OK);
-
     JS::CompileOptions options(cx);
     options.setPrincipals(nsJSPrincipals::get(aDocument->NodePrincipal()))
            .setFileAndLine(urlspec.get(), aLineNo)
            .setVersion(JSVersion(mLangVersion));
     // If the script was inline, tell the JS parser to save source for
     // Function.prototype.toSource(). If it's out of line, we retrieve the
     // source from the files on demand.
     options.setSourcePolicy(mOutOfLine ? JS::CompileOptions::LAZY_SOURCE
@@ -2692,17 +2680,17 @@ nsXULPrototypeScript::Set(JSScript* aObj
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeText
 //
 
 nsresult
 nsXULPrototypeText::Serialize(nsIObjectOutputStream* aStream,
-                              nsIScriptGlobalObject* aGlobal,
+                              nsXULPrototypeDocument* aProtoDoc,
                               const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
     nsresult rv;
 
     // Write basic prototype data
     rv = aStream->Write32(mType);
 
     nsresult tmp = aStream->WriteWStringZ(mValue.get());
@@ -2710,17 +2698,17 @@ nsXULPrototypeText::Serialize(nsIObjectO
       rv = tmp;
     }
 
     return rv;
 }
 
 nsresult
 nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream,
-                                nsIScriptGlobalObject* aGlobal,
+                                nsXULPrototypeDocument* aProtoDoc,
                                 nsIURI* aDocumentURI,
                                 const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
     nsresult rv;
 
     rv = aStream->ReadString(mValue);
 
     return rv;
@@ -2728,17 +2716,17 @@ nsXULPrototypeText::Deserialize(nsIObjec
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypePI
 //
 
 nsresult
 nsXULPrototypePI::Serialize(nsIObjectOutputStream* aStream,
-                            nsIScriptGlobalObject* aGlobal,
+                            nsXULPrototypeDocument* aProtoDoc,
                             const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
     nsresult rv;
 
     // Write basic prototype data
     rv = aStream->Write32(mType);
 
     nsresult tmp = aStream->WriteWStringZ(mTarget.get());
@@ -2750,17 +2738,17 @@ nsXULPrototypePI::Serialize(nsIObjectOut
       rv = tmp;
     }
 
     return rv;
 }
 
 nsresult
 nsXULPrototypePI::Deserialize(nsIObjectInputStream* aStream,
-                              nsIScriptGlobalObject* aGlobal,
+                              nsXULPrototypeDocument* aProtoDoc,
                               nsIURI* aDocumentURI,
                               const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
     nsresult rv;
 
     rv = aStream->ReadString(mTarget);
     nsresult tmp = aStream->ReadString(mData);
     if (NS_FAILED(tmp)) {
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -35,20 +35,20 @@
 #include "nsStyledElement.h"
 #include "nsIFrameLoader.h"
 #include "jspubtd.h"
 #include "nsFrameLoader.h"
 
 class nsIDocument;
 class nsString;
 class nsIDocShell;
+class nsXULPrototypeDocument;
 
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
-class nsIScriptGlobalObject;
 class nsXULPrototypeNode;
 typedef nsTArray<nsRefPtr<nsXULPrototypeNode> > nsPrototypeArray;
 
 namespace mozilla {
 namespace css {
 class StyleRule;
 }
 }
@@ -105,20 +105,20 @@ class nsXULPrototypeNode
 {
 public:
     enum Type { eType_Element, eType_Script, eType_Text, eType_PI };
 
     Type                     mType;
 
     virtual ~nsXULPrototypeNode() {}
     virtual nsresult Serialize(nsIObjectOutputStream* aStream,
-                               nsIScriptGlobalObject* aGlobal,
+                               nsXULPrototypeDocument* aProtoDoc,
                                const nsCOMArray<nsINodeInfo> *aNodeInfos) = 0;
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
-                                 nsIScriptGlobalObject* aGlobal,
+                                 nsXULPrototypeDocument* aProtoDoc,
                                  nsIURI* aDocumentURI,
                                  const nsCOMArray<nsINodeInfo> *aNodeInfos) = 0;
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual const char* ClassName() = 0;
     virtual uint32_t ClassSize() = 0;
 #endif
 
@@ -169,20 +169,20 @@ public:
             if (mChildren[i].get())
                 mChildren[i]->ReleaseSubtree();
         }
         mChildren.Clear();
         nsXULPrototypeNode::ReleaseSubtree();
     }
 
     virtual nsresult Serialize(nsIObjectOutputStream* aStream,
-                               nsIScriptGlobalObject* aGlobal,
+                               nsXULPrototypeDocument* aProtoDoc,
                                const nsCOMArray<nsINodeInfo> *aNodeInfos) MOZ_OVERRIDE;
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
-                                 nsIScriptGlobalObject* aGlobal,
+                                 nsXULPrototypeDocument* aProtoDoc,
                                  nsIURI* aDocumentURI,
                                  const nsCOMArray<nsINodeInfo> *aNodeInfos) MOZ_OVERRIDE;
 
     nsresult SetAttrAt(uint32_t aPos, const nsAString& aValue, nsIURI* aDocumentURI);
 
     void Unlink();
 
     // Trace all scripts held by this element and its children.
@@ -212,31 +212,31 @@ public:
     virtual ~nsXULPrototypeScript();
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual const char* ClassName() MOZ_OVERRIDE { return "nsXULPrototypeScript"; }
     virtual uint32_t ClassSize() MOZ_OVERRIDE { return sizeof(*this); }
 #endif
 
     virtual nsresult Serialize(nsIObjectOutputStream* aStream,
-                               nsIScriptGlobalObject* aGlobal,
+                               nsXULPrototypeDocument* aProtoDoc,
                                const nsCOMArray<nsINodeInfo> *aNodeInfos) MOZ_OVERRIDE;
     nsresult SerializeOutOfLine(nsIObjectOutputStream* aStream,
-                                nsIScriptGlobalObject* aGlobal);
+                                nsXULPrototypeDocument* aProtoDoc);
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
-                                 nsIScriptGlobalObject* aGlobal,
+                                 nsXULPrototypeDocument* aProtoDoc,
                                  nsIURI* aDocumentURI,
                                  const nsCOMArray<nsINodeInfo> *aNodeInfos) MOZ_OVERRIDE;
     nsresult DeserializeOutOfLine(nsIObjectInputStream* aInput,
-                                  nsIScriptGlobalObject* aGlobal);
+                                  nsXULPrototypeDocument* aProtoDoc);
 
     nsresult Compile(const PRUnichar* aText, int32_t aTextLength,
                      nsIURI* aURI, uint32_t aLineNo,
                      nsIDocument* aDocument,
-                     nsIScriptGlobalObject* aGlobal,
+                     nsXULPrototypeDocument* aProtoDoc,
                      nsIOffThreadScriptReceiver *aOffThreadReceiver = nullptr);
 
     void UnlinkJSObjects();
 
     void Set(JSScript* aObject);
 
     // It's safe to return a handle because we trace mScriptObject, no one ever
     // uses the handle (or the script object) past the point at which the
@@ -287,20 +287,20 @@ public:
     }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual const char* ClassName() MOZ_OVERRIDE { return "nsXULPrototypeText"; }
     virtual uint32_t ClassSize() MOZ_OVERRIDE { return sizeof(*this); }
 #endif
 
     virtual nsresult Serialize(nsIObjectOutputStream* aStream,
-                               nsIScriptGlobalObject* aGlobal,
+                               nsXULPrototypeDocument* aProtoDoc,
                                const nsCOMArray<nsINodeInfo> *aNodeInfos) MOZ_OVERRIDE;
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
-                                 nsIScriptGlobalObject* aGlobal,
+                                 nsXULPrototypeDocument* aProtoDoc,
                                  nsIURI* aDocumentURI,
                                  const nsCOMArray<nsINodeInfo> *aNodeInfos) MOZ_OVERRIDE;
 
     nsString                 mValue;
 };
 
 class nsXULPrototypePI : public nsXULPrototypeNode
 {
@@ -315,20 +315,20 @@ public:
     }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual const char* ClassName() MOZ_OVERRIDE { return "nsXULPrototypePI"; }
     virtual uint32_t ClassSize() MOZ_OVERRIDE { return sizeof(*this); }
 #endif
 
     virtual nsresult Serialize(nsIObjectOutputStream* aStream,
-                               nsIScriptGlobalObject* aGlobal,
+                               nsXULPrototypeDocument* aProtoDoc,
                                const nsCOMArray<nsINodeInfo> *aNodeInfos) MOZ_OVERRIDE;
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
-                                 nsIScriptGlobalObject* aGlobal,
+                                 nsXULPrototypeDocument* aProtoDoc,
                                  nsIURI* aDocumentURI,
                                  const nsCOMArray<nsINodeInfo> *aNodeInfos) MOZ_OVERRIDE;
 
     nsString                 mTarget;
     nsString                 mData;
 };
 
 ////////////////////////////////////////////////////////////////////////
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -60,18 +60,16 @@
 #include "nsIFrame.h"
 #include "nsXBLService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsMimeTypes.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsContentList.h"
 #include "nsIScriptGlobalObject.h"
-#include "nsIScriptGlobalObjectOwner.h"
-#include "nsIScriptRuntime.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsNodeInfoManager.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
 #include "nsIParser.h"
 #include "nsCharsetSource.h"
 #include "nsIParserService.h"
 #include "nsCSSStyleSheet.h"
@@ -3520,17 +3518,17 @@ XULDocument::OnStreamComplete(nsIStreamL
                    "XULDocument can't load multiple scripts at once");
 
         rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen,
                                             EmptyString(), this, mOffThreadCompileString);
         if (NS_SUCCEEDED(rv)) {
             rv = mCurrentScriptProto->Compile(mOffThreadCompileString.get(),
                                               mOffThreadCompileString.Length(),
                                               uri, 1, this,
-                                              mCurrentPrototype->GetScriptGlobalObject(),
+                                              mCurrentPrototype,
                                               this);
             if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->GetScriptObject()) {
                 // We will be notified via OnOffThreadCompileComplete when the
                 // compile finishes. Keep the contents of the compiled script
                 // alive until the compilation finishes.
                 mOffThreadCompiling = true;
                 BlockOnload();
                 return NS_OK;
@@ -3610,30 +3608,18 @@ XULDocument::OnScriptCompileComplete(JSS
             // written, at the bottom of ResumeWalk.  That way, master
             // out-of-line scripts are serialized in the same order that
             // they'll be read, in the FastLoad file, which reduces the
             // number of seeks that dump the underlying stream's buffer.
             //
             // Ignore the return value, as we don't need to propagate
             // a failure to write to the FastLoad file, because this
             // method aborts that whole process on error.
-            nsIScriptGlobalObject* global =
-                mCurrentPrototype->GetScriptGlobalObject();
-
-            NS_ASSERTION(global != nullptr, "master prototype w/o global?!");
-            if (global) {
-                nsIScriptContext *scriptContext =
-                    global->GetScriptContext();
-                NS_ASSERTION(scriptContext != nullptr,
-                             "Failed to get script context for language");
-                if (scriptContext)
-                    scriptProto->SerializeOutOfLine(nullptr, global);
-            }
+            scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype);
         }
-
         // ignore any evaluation errors
     }
 
     rv = ResumeWalk();
 
     // Load a pointer to the prototype-script's list of XULDocuments who
     // raced to load the same script
     XULDocument** docp = &scriptProto->mSrcLoadWaiters;
--- a/content/xul/document/src/nsXULContentSink.cpp
+++ b/content/xul/document/src/nsXULContentSink.cpp
@@ -25,17 +25,16 @@
 #include "nsIDOMEventListener.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIFormControl.h"
 #include "nsINameSpaceManager.h"
 #include "nsINodeInfo.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptGlobalObject.h"
-#include "nsIScriptRuntime.h"
 #include "nsIServiceManager.h"
 #include "nsIURL.h"
 #include "nsParserBase.h"
 #include "nsViewManager.h"
 #include "nsIXULDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsLayoutCID.h"
 #include "nsNetUtil.h"
@@ -562,18 +561,17 @@ XULContentSinkImpl::HandleEndElement(con
 
         // If given a src= attribute, we must ignore script tag content.
         if (!script->mSrcURI && !script->GetScriptObject()) {
             nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
 
             script->mOutOfLine = false;
             if (doc)
                 script->Compile(mText, mTextLength, mDocumentURL,
-                                script->mLineNo, doc,
-                                mPrototype->GetScriptGlobalObject());
+                                script->mLineNo, doc, mPrototype);
         }
 
         FlushText(false);
     }
     break;
 
     default:
         NS_ERROR("didn't expect that");
@@ -981,18 +979,17 @@ XULContentSinkImpl::OpenScript(const PRU
           if (NS_FAILED(rv)) {
               return rv;
           }
 
           // Attempt to deserialize an out-of-line script from the FastLoad
           // file right away.  Otherwise we'll end up reloading the script and
           // corrupting the FastLoad file trying to serialize it, in the case
           // where it's already there.
-          if (globalObject)
-                script->DeserializeOutOfLine(nullptr, globalObject);
+          script->DeserializeOutOfLine(nullptr, mPrototype);
       }
 
       nsPrototypeArray* children = nullptr;
       rv = mContextStack.GetTopChildren(&children);
       if (NS_FAILED(rv)) {
           return rv;
       }
 
--- a/content/xul/document/src/nsXULPrototypeCache.cpp
+++ b/content/xul/document/src/nsXULPrototypeCache.cpp
@@ -3,17 +3,16 @@
  * 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 "nsXULPrototypeCache.h"
 
 #include "plstr.h"
 #include "nsXULPrototypeDocument.h"
 #include "nsCSSStyleSheet.h"
-#include "nsIScriptRuntime.h"
 #include "nsIServiceManager.h"
 #include "nsIURI.h"
 
 #include "nsIChromeRegistry.h"
 #include "nsIFile.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObserverService.h"
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -10,17 +10,16 @@
 #include "nsAString.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptSecurityManager.h"
-#include "nsIScriptRuntime.h"
 #include "nsIServiceManager.h"
 #include "nsIArray.h"
 #include "nsIURI.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "nsString.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
@@ -31,72 +30,59 @@
 #include "nsCxPusher.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
 #include "xpcpublic.h"
 #include "mozilla/dom/BindingUtils.h"
 
 using mozilla::dom::DestroyProtoAndIfaceCache;
 using mozilla::AutoPushJSContext;
+using mozilla::AutoSafeJSContext;
 using mozilla::dom::XULDocument;
 
 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
                      NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 
-class nsXULPDGlobalObject : public nsIScriptGlobalObject
+class nsXULPDGlobalObject : public nsISupports
 {
 public:
     nsXULPDGlobalObject(nsXULPrototypeDocument* owner);
 
     // nsISupports interface
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-
-    // nsIGlobalJSObjectHolder methods
-    virtual JSObject* GetGlobalJSObject();
-
-    // nsIScriptGlobalObject methods
-    virtual void OnFinalize(JSObject* aObject);
-    virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsXULPDGlobalObject)
 
-    virtual nsresult EnsureScriptEnvironment();
-
-    virtual nsIScriptContext *GetScriptContext();
-
-    // nsIScriptObjectPrincipal methods
-    virtual nsIPrincipal* GetPrincipal();
-
-    NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULPDGlobalObject,
-                                             nsIScriptGlobalObject)
-
+    JSObject* GetCompilationGlobal();
+    void UnmarkCompilationGlobal() { xpc_UnmarkGrayObject(mJSObject); }
+    void Destroy();
+    nsIPrincipal* GetPrincipal();
     void ClearGlobalObjectOwner();
 
 protected:
     virtual ~nsXULPDGlobalObject();
 
+    nsCOMPtr<nsIPrincipal> mCachedPrincipal;
     nsXULPrototypeDocument* mGlobalObjectOwner; // weak reference
-
-    nsCOMPtr<nsIScriptContext> mContext;
-    JSObject* mJSObject;
-
-    nsCOMPtr<nsIPrincipal> mCachedPrincipal;
+    JS::Heap<JSObject*> mJSObject;
+    bool mDestroyed; // Probably not necessary, but let's be safe.
 
     static JSClass gSharedGlobalClass;
 };
 
 nsIPrincipal* nsXULPrototypeDocument::gSystemPrincipal;
 nsXULPDGlobalObject* nsXULPrototypeDocument::gSystemGlobal;
 uint32_t nsXULPrototypeDocument::gRefCnt;
 
 
 void
 nsXULPDGlobalObject_finalize(JSFreeOp *fop, JSObject *obj)
 {
     nsXULPDGlobalObject* nativeThis = static_cast<nsXULPDGlobalObject*>(JS_GetPrivate(obj));
-    nativeThis->OnFinalize(obj);
+    nativeThis->Destroy();
 
     // The addref was part of JSObject construction
     nsContentUtils::DeferredFinalize(nativeThis);
 }
 
 
 bool
 nsXULPDGlobalObject_resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id)
@@ -164,29 +150,27 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULProt
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeDocument)
     tmp->mPrototypeWaiters.Clear();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument)
     if (nsCCUncollectableMarker::InGeneration(cb, tmp->mCCGeneration)) {
         return NS_SUCCESS_INTERRUPTED_TRAVERSE;
     }
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mGlobalObject");
-    cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObject*>(tmp->mGlobalObject));
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObject)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
     for (uint32_t i = 0; i < tmp->mPrototypeWaiters.Length(); ++i) {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mPrototypeWaiters[i]");
         cb.NoteXPCOMChild(static_cast<nsINode*>(tmp->mPrototypeWaiters[i].get()));
     }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument)
-    NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
     NS_INTERFACE_MAP_ENTRY(nsISerializable)
-    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObjectOwner)
+    NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument)
 
 NS_IMETHODIMP
 NS_NewXULPrototypeDocument(nsXULPrototypeDocument** aResult)
 {
@@ -203,18 +187,17 @@ NS_NewXULPrototypeDocument(nsXULPrototyp
     }
 
     NS_ADDREF(*aResult);
     return rv;
 }
 
 // Helper method that shares a system global among all prototype documents
 // that have the system principal as their security principal.   Called by
-// nsXULPrototypeDocument::Read and
-// nsXULPrototypeDocument::GetScriptGlobalObject.
+// nsXULPrototypeDocument::Read and nsXULPrototypeDocument::GetCompilationGlobal.
 // This method greatly reduces the number of nsXULPDGlobalObjects and their
 // nsIScriptContexts in apps that load many XUL documents via chrome: URLs.
 
 nsXULPDGlobalObject *
 nsXULPrototypeDocument::NewXULPDGlobalObject()
 {
     // Now compare DocumentPrincipal() to gSystemPrincipal, in order to create
     // gSystemGlobal if the two pointers are equal.  Thus, gSystemGlobal
@@ -344,26 +327,26 @@ nsXULPrototypeDocument::Read(nsIObjectIn
 
         if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) {
             nsRefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
             if (! pi) {
                rv = NS_ERROR_OUT_OF_MEMORY;
                break;
             }
 
-            tmp = pi->Deserialize(aStream, mGlobalObject, mURI, &nodeInfos);
+            tmp = pi->Deserialize(aStream, this, mURI, &nodeInfos);
             if (NS_FAILED(tmp)) {
               rv = tmp;
             }
             tmp = AddProcessingInstruction(pi);
             if (NS_FAILED(tmp)) {
               rv = tmp;
             }
         } else if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_Element) {
-            tmp = mRoot->Deserialize(aStream, mGlobalObject, mURI, &nodeInfos);
+            tmp = mRoot->Deserialize(aStream, this, mURI, &nodeInfos);
             if (NS_FAILED(tmp)) {
               rv = tmp;
             }
             break;
         } else {
             NS_NOTREACHED("Unexpected prototype node type");
             rv = NS_ERROR_FAILURE;
             break;
@@ -503,30 +486,27 @@ nsXULPrototypeDocument::Write(nsIObjectO
         nodeInfo->GetName(localName);
         tmp = aStream->WriteWStringZ(localName.get());
         if (NS_FAILED(tmp)) {
           rv = tmp;
         }
     }
 
     // Now serialize the document contents
-    nsIScriptGlobalObject* globalObject = GetScriptGlobalObject();
-    NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
-
     count = mProcessingInstructions.Length();
     for (i = 0; i < count; ++i) {
         nsXULPrototypePI* pi = mProcessingInstructions[i];
-        tmp = pi->Serialize(aStream, globalObject, &nodeInfos);
+        tmp = pi->Serialize(aStream, this, &nodeInfos);
         if (NS_FAILED(tmp)) {
           rv = tmp;
         }
     }
 
     if (mRoot) {
-      tmp = mRoot->Serialize(aStream, globalObject, &nodeInfos);
+      tmp = mRoot->Serialize(aStream, this, &nodeInfos);
       if (NS_FAILED(tmp)) {
         rv = tmp;
       }
     }
  
     return rv;
 }
 
@@ -624,16 +604,34 @@ nsXULPrototypeDocument::DocumentPrincipa
 }
 
 void
 nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal* aPrincipal)
 {
     mNodeInfoManager->SetDocumentPrincipal(aPrincipal);
 }
 
+JSObject*
+nsXULPrototypeDocument::GetCompilationGlobal()
+{
+  if (!mGlobalObject) {
+      mGlobalObject = NewXULPDGlobalObject();
+  }
+  return mGlobalObject->GetCompilationGlobal();
+}
+
+void
+nsXULPrototypeDocument::MarkInCCGeneration(uint32_t aCCGeneration)
+{
+    mCCGeneration = aCCGeneration;
+    if (mGlobalObject) {
+        mGlobalObject->UnmarkCompilationGlobal();
+    }
+}
+
 nsNodeInfoManager*
 nsXULPrototypeDocument::GetNodeInfoManager()
 {
     return mNodeInfoManager;
 }
 
 
 nsresult
@@ -687,176 +685,108 @@ nsXULPrototypeDocument::TraceProtos(JSTr
   mGCNumber = aGCNumber;
   if (mRoot) {
     mRoot->TraceAllScripts(aTrc);
   }
 }
 
 //----------------------------------------------------------------------
 //
-// nsIScriptGlobalObjectOwner methods
-//
-
-nsIScriptGlobalObject*
-nsXULPrototypeDocument::GetScriptGlobalObject()
-{
-    if (!mGlobalObject)
-        mGlobalObject = NewXULPDGlobalObject();
-
-    return mGlobalObject;
-}
-
-//----------------------------------------------------------------------
-//
 // nsXULPDGlobalObject
 //
 
 nsXULPDGlobalObject::nsXULPDGlobalObject(nsXULPrototypeDocument* owner)
   : mGlobalObjectOwner(owner)
   , mJSObject(nullptr)
+  , mDestroyed(false)
 {
 }
 
 
 nsXULPDGlobalObject::~nsXULPDGlobalObject()
 {
+  MOZ_ASSERT(!mJSObject);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPDGlobalObject)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsXULPDGlobalObject)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPDGlobalObject)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSObject)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPDGlobalObject)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPDGlobalObject)
+  tmp->Destroy();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPDGlobalObject)
-  NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
-  NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
-  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObject)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPDGlobalObject)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPDGlobalObject)
 
-//----------------------------------------------------------------------
-//
-// nsIScriptGlobalObject methods
-//
-
-nsresult
-nsXULPDGlobalObject::EnsureScriptEnvironment()
+JSObject *
+nsXULPDGlobalObject::GetCompilationGlobal()
 {
-  if (mContext) {
-    return NS_OK;
-  }
-  NS_ASSERTION(!mJSObject, "Have global without context?");
-
-  nsCOMPtr<nsIScriptRuntime> languageRuntime;
-  nsresult rv = NS_GetJSRuntime(getter_AddRefs(languageRuntime));
-  NS_ENSURE_SUCCESS(rv, NS_OK);
-
-  nsCOMPtr<nsIScriptContext> ctxNew = languageRuntime->CreateContext(false, nullptr);
-  MOZ_ASSERT(ctxNew);
-
-  // We have to setup a special global object.  We do this then
-  // attach it as the global for this context.  Then, we
-  // will re-fetch the global and set it up in our language globals array.
-  {
-    AutoPushJSContext cx(ctxNew->GetNativeContext());
-    JS::CompartmentOptions options;
-    options.setZone(JS::SystemZone)
-           .setInvisibleToDebugger(true);
-    JS::Rooted<JSObject*> newGlob(cx,
-      JS_NewGlobalObject(cx, &gSharedGlobalClass,
-                         nsJSPrincipals::get(GetPrincipal()), JS::DontFireOnNewGlobalHook,
-                         options));
-    if (!newGlob)
-        return NS_OK;
-
-    js::SetDefaultObjectForContext(cx, newGlob);
-
-    // Add an owning reference from JS back to us. This'll be
-    // released when the JSObject is finalized.
-    ::JS_SetPrivate(newGlob, this);
-    NS_ADDREF(this);
+  if (mJSObject || mDestroyed) {
+    // We've been initialized before. This is what we get.
+    return xpc_UnmarkGrayObject(mJSObject);
   }
 
-  // should probably assert the context is clean???
-  ctxNew->WillInitializeContext();
-  rv = ctxNew->InitContext();
-  NS_ENSURE_SUCCESS(rv, NS_OK);
+  AutoSafeJSContext cx;
+  JS::CompartmentOptions options;
+  options.setZone(JS::SystemZone)
+         .setInvisibleToDebugger(true);
+  mJSObject = JS_NewGlobalObject(cx, &gSharedGlobalClass,
+                                 nsJSPrincipals::get(GetPrincipal()),
+                                 JS::DontFireOnNewGlobalHook, options);
+  NS_ENSURE_TRUE(mJSObject, nullptr);
 
-  ctxNew->DidInitializeContext();
+  NS_HOLD_JS_OBJECTS(this, nsXULPDGlobalObject);
 
-  JSObject* global = ctxNew->GetNativeGlobal();
-  NS_ASSERTION(global, "GetNativeGlobal returned nullptr!");
-
-  mContext = ctxNew;
-  mJSObject = global;
+  // Add an owning reference from JS back to us. This'll be
+  // released when the JSObject is finalized.
+  JS_SetPrivate(mJSObject, this);
+  NS_ADDREF(this);
 
   // Set the location information for the new global, so that tools like
   // about:memory may use that information
   nsIURI *ownerURI = mGlobalObjectOwner->GetURI();
   xpc::SetLocationForGlobal(mJSObject, ownerURI);
 
-  return NS_OK;
+  return mJSObject;
 }
 
-nsIScriptContext*
-nsXULPDGlobalObject::GetScriptContext()
-{
-  // This global object creates a context on demand - do that now.
-  nsresult rv = EnsureScriptEnvironment();
-  if (NS_FAILED(rv)) {
-    NS_ERROR("Failed to setup script language");
-    return nullptr;
-  }
-
-  return mContext;
-}
-
-JSObject*
-nsXULPDGlobalObject::GetGlobalJSObject()
-{
-  return xpc_UnmarkGrayObject(mJSObject);
-}
-
-
 void
 nsXULPDGlobalObject::ClearGlobalObjectOwner()
 {
   NS_ASSERTION(!mCachedPrincipal, "This shouldn't ever be set until now!");
 
   // Cache mGlobalObjectOwner's principal if possible.
   if (this != nsXULPrototypeDocument::gSystemGlobal)
     mCachedPrincipal = mGlobalObjectOwner->DocumentPrincipal();
 
-  mContext = nullptr;
   mGlobalObjectOwner = nullptr;
 }
 
-
 void
-nsXULPDGlobalObject::OnFinalize(JSObject* aObject)
+nsXULPDGlobalObject::Destroy()
 {
+  mDestroyed = true;
+  if (!mJSObject) {
+    return;
+  }
   mJSObject = nullptr;
+  NS_DROP_JS_OBJECTS(this, nsXULPDGlobalObject);
 }
 
-void
-nsXULPDGlobalObject::SetScriptsEnabled(bool aEnabled, bool aFireTimeouts)
-{
-  // We don't care...
-}
-
-//----------------------------------------------------------------------
-//
-// nsIScriptObjectPrincipal methods
-//
-
 nsIPrincipal*
 nsXULPDGlobalObject::GetPrincipal()
 {
     if (!mGlobalObjectOwner) {
         // See nsXULPrototypeDocument::NewXULPDGlobalObject, the comment
         // about gSystemGlobal implying gSystemPrincipal.
         if (this == nsXULPrototypeDocument::gSystemGlobal) {
             return nsXULPrototypeDocument::gSystemPrincipal;
--- a/content/xul/document/src/nsXULPrototypeDocument.h
+++ b/content/xul/document/src/nsXULPrototypeDocument.h
@@ -6,17 +6,16 @@
 #ifndef nsXULPrototypeDocument_h__
 #define nsXULPrototypeDocument_h__
 
 #include "mozilla/Attributes.h"
 #include "nsAutoPtr.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
-#include "nsIScriptGlobalObjectOwner.h"
 #include "nsISerializable.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsIAtom;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeInfoManager;
 class nsXULPrototypeElement;
@@ -32,18 +31,17 @@ class XULDocument;
 
 /**
  * A "prototype" document that stores shared document information
  * for the XUL cache.
  * Among other things, stores the tree of nsXULPrototype*
  * objects, from which the real DOM tree is built later in
  * XULDocument::ResumeWalk.
  */
-class nsXULPrototypeDocument : public nsIScriptGlobalObjectOwner,
-                               public nsISerializable
+class nsXULPrototypeDocument : public nsISerializable
 {
 public:
     static nsresult
     Create(nsIURI* aURI, nsXULPrototypeDocument** aResult);
 
     // nsISupports interface
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
@@ -109,26 +107,21 @@ public:
      * prototype document that the prototype has finished loading.
      * The notification is performed by calling
      * nsIXULDocument::OnPrototypeLoadDone on the registered documents.
      */
     nsresult NotifyLoadDone();
 
     nsNodeInfoManager *GetNodeInfoManager();
 
-    // nsIScriptGlobalObjectOwner methods
-    virtual nsIScriptGlobalObject* GetScriptGlobalObject() MOZ_OVERRIDE;
+    JSObject* GetCompilationGlobal();
 
-    void MarkInCCGeneration(uint32_t aCCGeneration)
-    {
-        mCCGeneration = aCCGeneration;
-    }
+    void MarkInCCGeneration(uint32_t aCCGeneration);
 
-    NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULPrototypeDocument,
-                                             nsIScriptGlobalObjectOwner)
+    NS_DECL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument)
 
     void TraceProtos(JSTracer* aTrc, uint32_t aGCNumber);
 
 protected:
     nsCOMPtr<nsIURI> mURI;
     nsRefPtr<nsXULPrototypeElement> mRoot;
     nsTArray<nsRefPtr<nsXULPrototypePI> > mProcessingInstructions;
     nsCOMArray<nsIURI> mStyleSheetReferences;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1447,18 +1447,17 @@ bool
 Navigator::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
                         JS::Handle<jsid> aId,
                         JS::MutableHandle<JS::Value> aValue)
 {
   if (!JSID_IS_STRING(aId)) {
     return true;
   }
 
-  nsScriptNameSpaceManager* nameSpaceManager =
-    nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
   if (!nameSpaceManager) {
     return Throw<true>(aCx, NS_ERROR_NOT_INITIALIZED);
   }
 
   nsDependentJSString name(aId);
 
   const nsGlobalNameStruct* name_struct =
     nameSpaceManager->LookupNavigatorName(name);
@@ -1562,18 +1561,17 @@ SaveNavigatorName(const nsAString& aName
   arr->AppendElement(aName);
   return PL_DHASH_NEXT;
 }
 
 void
 Navigator::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
                                ErrorResult& aRv)
 {
-  nsScriptNameSpaceManager *nameSpaceManager =
-    nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   if (!nameSpaceManager) {
     NS_ERROR("Can't get namespace manager.");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   nameSpaceManager->EnumerateNavigatorNames(SaveNavigatorName, &aNames);
 }
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -32,17 +32,16 @@ EXPORTS += [
     'nsIJSEventListener.h',
     'nsIJSNativeInitializer.h',
     'nsIScriptContext.h',
     'nsIScriptExternalNameSet.h',
     'nsIScriptGlobalObject.h',
     'nsIScriptGlobalObjectOwner.h',
     'nsIScriptNameSpaceManager.h',
     'nsIScriptObjectPrincipal.h',
-    'nsIScriptRuntime.h',
     'nsIScriptTimeoutHandler.h',
     'nsJSEnvironment.h',
     'nsJSUtils.h',
     'nsPIDOMWindow.h',
     'nsPIWindowRoot.h',
     'nsPerformance.h',
     'nsStructuredCloneContainer.h',
     'nsWindowMemoryReporter.h',
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -906,18 +906,17 @@ CutPrefix(const char *aName) {
 
   return aName;
 }
 
 // static
 nsresult
 nsDOMClassInfo::RegisterClassProtos(int32_t aClassInfoID)
 {
-  nsScriptNameSpaceManager *nameSpaceManager =
-    nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
   bool found_old;
 
   const nsIID *primary_iid = sClassInfoData[aClassInfoID].mProtoChainInterface;
 
   if (!primary_iid || primary_iid == &NS_GET_IID(nsISupports)) {
     return NS_OK;
   }
@@ -959,18 +958,17 @@ nsDOMClassInfo::RegisterClassProtos(int3
 
   return NS_OK;
 }
 
 // static
 nsresult
 nsDOMClassInfo::RegisterExternalClasses()
 {
-  nsScriptNameSpaceManager *nameSpaceManager =
-    nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   nsCOMPtr<nsIComponentRegistrar> registrar;
   nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsICategoryManager> cm =
     do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
@@ -1092,17 +1090,17 @@ nsDOMClassInfo::Init()
   /* Errors that can trigger early returns are done first,
      otherwise nsDOMClassInfo is left in a half inited state. */
   static_assert(sizeof(uintptr_t) == sizeof(void*),
                 "BAD! You'll need to adjust the size of uintptr_t to the "
                 "size of a pointer on your platform.");
 
   NS_ENSURE_TRUE(!sIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
 
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   nsresult rv = CallGetService(nsIXPConnect::GetCID(), &sXPConnect);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIXPCFunctionThisTranslator> elt = new nsEventListenerThisTranslator();
   sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsIDOMEventListener), elt);
 
@@ -2067,18 +2065,17 @@ nsDOMClassInfo::PostCreatePrototype(JSCo
 
   // Don't overwrite a property set by content.
   bool found;
   if (!::JS_AlreadyHasOwnUCProperty(cx, global, reinterpret_cast<const jschar*>(mData->mNameUTF16),
                                     NS_strlen(mData->mNameUTF16), &found)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsScriptNameSpaceManager *nameSpaceManager =
-    nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_OK);
 
   bool unused;
   return ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16,
                           mData, nullptr, nameSpaceManager, proto, !found,
                           &unused);
 }
 
@@ -2266,18 +2263,17 @@ nsWindowSH::Enumerate(nsIXPConnectWrappe
   JS::Rooted<JSObject*> obj(cx, aObj);
   if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
     *_retval = JS_EnumerateStandardClasses(cx, obj);
     if (!*_retval) {
       return NS_OK;
     }
 
     // Now resolve everything from the namespace manager
-    nsScriptNameSpaceManager *nameSpaceManager =
-      nsJSRuntime::GetNameSpaceManager();
+    nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
     if (!nameSpaceManager) {
       NS_ERROR("Can't get namespace manager.");
       return NS_ERROR_UNEXPECTED;
     }
     ResolveGlobalNameClosure closure = { cx, obj, _retval };
     nameSpaceManager->EnumerateGlobalNames(ResolveGlobalName, &closure);
   }
 
@@ -2551,18 +2547,17 @@ private:
     return nameStruct;
   }
 
   static nsresult GetNameStruct(const nsAString& aName,
                                 const nsGlobalNameStruct **aNameStruct)
   {
     *aNameStruct = nullptr;
 
-    nsScriptNameSpaceManager *nameSpaceManager =
-      nsJSRuntime::GetNameSpaceManager();
+    nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
     if (!nameSpaceManager) {
       NS_ERROR("Can't get namespace manager.");
       return NS_ERROR_UNEXPECTED;
     }
 
     *aNameStruct = nameSpaceManager->LookupName(aName);
 
     // Return NS_OK here, aName just isn't a DOM class but nothing failed.
@@ -2759,18 +2754,17 @@ nsDOMConstructor::HasInstance(nsIXPConne
   NS_ENSURE_TRUE(class_name_struct, NS_ERROR_FAILURE);
 
   if (name_struct == class_name_struct) {
     *bp = true;
 
     return NS_OK;
   }
 
-  nsScriptNameSpaceManager *nameSpaceManager =
-    nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ASSERTION(nameSpaceManager, "Can't get namespace manager?");
 
   const nsIID *class_iid;
   if (class_name_struct->mType == nsGlobalNameStruct::eTypeInterface ||
       class_name_struct->mType == nsGlobalNameStruct::eTypeClassProto) {
     class_iid = &class_name_struct->mIID;
   } else if (class_name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
     class_iid =
@@ -3153,18 +3147,17 @@ OldBindingConstructorEnabled(const nsGlo
 // static
 nsresult
 nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
                           JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                           bool *did_resolve)
 {
   *did_resolve = false;
 
-  nsScriptNameSpaceManager *nameSpaceManager =
-    nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   nsDependentJSString name(id);
 
   const PRUnichar *class_name = nullptr;
   const nsGlobalNameStruct *name_struct =
     nameSpaceManager->LookupName(name, &class_name);
 
--- a/dom/base/nsDOMScriptObjectFactory.cpp
+++ b/dom/base/nsDOMScriptObjectFactory.cpp
@@ -26,17 +26,17 @@
 #include "nsGlobalWindow.h"
 #include "nsDOMException.h"
 #include "nsCRT.h"
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
 #include "nsThreadUtils.h"
 
-static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
+using mozilla::dom::GetNameSpaceManager;
 
 nsIExceptionProvider* gExceptionProvider = nullptr;
 
 nsDOMScriptObjectFactory::nsDOMScriptObjectFactory()
 {
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
   if (observerService) {
@@ -52,19 +52,16 @@ nsDOMScriptObjectFactory::nsDOMScriptObj
     xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_SVG);
     xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_XPATH);
     xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_INDEXEDDB);
     xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_FILEHANDLE);
   }
 
   NS_ASSERTION(!gExceptionProvider, "Registered twice?!");
   provider.swap(gExceptionProvider);
-
-  // And pre-create the javascript language.
-  NS_CreateJSRuntime(getter_AddRefs(mJSRuntime));
 }
 
 NS_INTERFACE_MAP_BEGIN(nsDOMScriptObjectFactory)
   NS_INTERFACE_MAP_ENTRY(nsIDOMScriptObjectFactory)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMScriptObjectFactory)
 NS_INTERFACE_MAP_END
 
@@ -76,17 +73,17 @@ NS_IMETHODIMP_(nsISupports *)
 nsDOMScriptObjectFactory::GetClassInfoInstance(nsDOMClassInfoID aID)
 {
   return NS_GetDOMClassInfoInstance(aID);
 }
 
 NS_IMETHODIMP_(nsISupports *)
 nsDOMScriptObjectFactory::GetExternalClassInfoInstance(const nsAString& aName)
 {
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, nullptr);
 
   const nsGlobalNameStruct *globalStruct = nameSpaceManager->LookupName(aName);
   if (globalStruct) {
     if (globalStruct->mType == nsGlobalNameStruct::eTypeExternalClassInfoCreator) {
       nsresult rv;
       nsCOMPtr<nsIDOMCIExtension> creator(do_CreateInstance(globalStruct->mCID, &rv));
       NS_ENSURE_SUCCESS(rv, nullptr);
@@ -153,64 +150,28 @@ NS_IMETHODIMP
 nsDOMScriptObjectFactory::RegisterDOMClassInfo(const char *aName,
 					       nsDOMClassInfoExternalConstructorFnc aConstructorFptr,
 					       const nsIID *aProtoChainInterface,
 					       const nsIID **aInterfaces,
 					       uint32_t aScriptableFlags,
 					       bool aHasClassInterface,
 					       const nsCID *aConstructorCID)
 {
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   return nameSpaceManager->RegisterDOMCIData(aName,
                                              aConstructorFptr,
                                              aProtoChainInterface,
                                              aInterfaces,
                                              aScriptableFlags,
                                              aHasClassInterface,
                                              aConstructorCID);
 }
 
-
-// Factories
-nsresult
-NS_GetJSRuntime(nsIScriptRuntime** aLanguage)
-{
-  nsCOMPtr<nsIDOMScriptObjectFactory> factory =
-    do_GetService(kDOMScriptObjectFactoryCID);
-  NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
-
-  NS_IF_ADDREF(*aLanguage = factory->GetJSRuntime());
-  return NS_OK;
-}
-
-nsresult NS_GetScriptRuntime(const nsAString &aLanguageName,
-                             nsIScriptRuntime **aLanguage)
-{
-  *aLanguage = NULL;
-
-  NS_ENSURE_TRUE(aLanguageName.EqualsLiteral("application/javascript"),
-                 NS_ERROR_FAILURE);
-
-  return NS_GetJSRuntime(aLanguage);
-}
-
-nsresult NS_GetScriptRuntimeByID(uint32_t aScriptTypeID,
-                                 nsIScriptRuntime **aLanguage)
-{
-  *aLanguage = NULL;
-
-  NS_ENSURE_TRUE(aScriptTypeID == nsIProgrammingLanguage::JAVASCRIPT,
-                 NS_ERROR_FAILURE);
-
-  return NS_GetJSRuntime(aLanguage);
-}
-
-
 NS_IMPL_ISUPPORTS1(nsDOMExceptionProvider, nsIExceptionProvider)
 
 NS_IMETHODIMP
 nsDOMExceptionProvider::GetException(nsresult result,
                                      nsIException *aDefaultException,
                                      nsIException **_retval)
 {
   if (!NS_IsMainThread()) {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -151,17 +151,16 @@
 #include "nsIPrintSettings.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIWebBrowserPrint.h"
 #endif
 
 #include "nsWindowRoot.h"
 #include "nsNetCID.h"
 #include "nsIArray.h"
-#include "nsIScriptRuntime.h"
 
 // XXX An unfortunate dependency exists here (two XUL files).
 #include "nsIDOMXULDocument.h"
 #include "nsIDOMXULCommandDispatcher.h"
 
 #include "nsBindingManager.h"
 #include "nsXBLService.h"
 
@@ -1781,32 +1780,27 @@ nsGlobalWindow::EnsureScriptEnvironment(
 
   if (mJSObject) {
     return NS_OK;
   }
 
   NS_ASSERTION(!GetCurrentInnerWindowInternal(),
                "mJSObject is null, but we have an inner window?");
 
-  nsCOMPtr<nsIScriptRuntime> scriptRuntime;
-  nsresult rv = NS_GetJSRuntime(getter_AddRefs(scriptRuntime));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // If this window is a [i]frame, don't bother GC'ing when the frame's context
   // is destroyed since a GC will happen when the frameset or host document is
   // destroyed anyway.
-  nsCOMPtr<nsIScriptContext> context =
-    scriptRuntime->CreateContext(!IsFrame(), this);
+  nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), this);
 
   NS_ASSERTION(!mContext, "Will overwrite mContext!");
 
   // should probably assert the context is clean???
   context->WillInitializeContext();
 
-  rv = context->InitContext();
+  nsresult rv = context->InitContext();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mContext = context;
   return NS_OK;
 }
 
 nsIScriptContext *
 nsGlobalWindow::GetScriptContext()
--- a/dom/base/nsIDOMScriptObjectFactory.h
+++ b/dom/base/nsIDOMScriptObjectFactory.h
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIDOMScriptObjectFactory_h__
 #define nsIDOMScriptObjectFactory_h__
 
 #include "nsISupports.h"
 #include "nsIDOMClassInfo.h"
 #include "nsStringGlue.h"
-#include "nsIScriptRuntime.h"
 
 #define NS_IDOM_SCRIPT_OBJECT_FACTORY_IID \
 { 0x2a50e17c, 0x46ff, 0x4150, \
   { 0xbb, 0x46, 0xd8, 0x07, 0xb3, 0x36, 0xde, 0xab } }
 
 class nsIScriptContext;
 class nsIScriptGlobalObject;
 class nsIDOMEventListener;
@@ -36,22 +35,14 @@ public:
   // name starts with nsIDOM).
   NS_IMETHOD RegisterDOMClassInfo(const char *aName,
                                   nsDOMClassInfoExternalConstructorFnc aConstructorFptr,
                                   const nsIID *aProtoChainInterface,
                                   const nsIID **aInterfaces,
                                   uint32_t aScriptableFlags,
                                   bool aHasClassInterface,
                                   const nsCID *aConstructorCID) = 0;
-
-  nsIScriptRuntime* GetJSRuntime()
-  {
-    return mJSRuntime;
-  }
-
-protected:
-  nsCOMPtr<nsIScriptRuntime> mJSRuntime;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMScriptObjectFactory,
                               NS_IDOM_SCRIPT_OBJECT_FACTORY_IID)
 
 #endif /* nsIDOMScriptObjectFactory_h__ */
--- a/dom/base/nsIScriptContext.h
+++ b/dom/base/nsIScriptContext.h
@@ -23,18 +23,18 @@ class nsIArray;
 class nsIVariant;
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
 class nsIScriptObjectPrincipal;
 class nsIDOMWindow;
 class nsIURI;
 
 #define NS_ISCRIPTCONTEXT_IID \
-{ 0x6219173f, 0x4a61, 0x4c99, \
-  { 0xb1, 0xfd, 0x8e, 0x7a, 0xf0, 0xdc, 0xe0, 0x56 } }
+{ 0x1d931a17, 0x453a, 0x47fb, \
+  { 0x94, 0x66, 0x2d, 0x3e, 0xd1, 0xef, 0x7a, 0xc5 } }
 
 /* This MUST match JSVERSION_DEFAULT.  This version stuff if we don't
    know what language we have is a little silly... */
 #define SCRIPTVERSION_DEFAULT JSVERSION_DEFAULT
 
 class nsIOffThreadScriptReceiver;
 
 /**
@@ -127,24 +127,16 @@ public:
   /**
    * For garbage collected systems, do a synchronous collection pass.
    * May be a no-op on other systems
    *
    * @return NS_OK if the method is successful
    */
   virtual void GC(JS::gcreason::Reason aReason) = 0;
 
-  virtual nsresult Serialize(nsIObjectOutputStream* aStream,
-                             JS::Handle<JSScript*> aScriptObject) = 0;
-  
-  /* Deserialize a script from a stream.
-   */
-  virtual nsresult Deserialize(nsIObjectInputStream* aStream,
-                               JS::MutableHandle<JSScript*> aResult) = 0;
-
   /**
    * Called to disable/enable script execution in this context.
    */
   virtual bool GetScriptsEnabled() = 0;
   virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts) = 0;
 
   // SetProperty is suspect and jst believes should not be needed.  Currenly
   // used only for "arguments".
deleted file mode 100644
--- a/dom/base/nsIScriptRuntime.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsIScriptRuntime_h__
-#define nsIScriptRuntime_h__
-
-#include "nsIScriptContext.h"
-
-#define NS_ISCRIPTRUNTIME_IID \
-{ 0x86c6b54a, 0xf067, 0x4878, \
-  { 0xb2, 0x77, 0x4e, 0xee, 0x95, 0xda, 0x8f, 0x76 } }
-
-/**
- * A singleton language environment for an application.  Responsible for
- * initializing and cleaning up the global language environment, and a factory
- * for language contexts
- */
-class nsIScriptRuntime : public nsISupports
-{
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTRUNTIME_IID)
-
-  /* Factory for a new context for this language */
-  virtual already_AddRefed<nsIScriptContext>
-  CreateContext(bool aGCOnDestruction,
-                nsIScriptGlobalObject* aGlobalObject) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptRuntime, NS_ISCRIPTRUNTIME_IID)
-
-/* helper functions */
-nsresult NS_GetJSRuntime(nsIScriptRuntime** aLanguage);
-
-nsresult NS_GetScriptRuntime(const nsAString &aLanguageName,
-                             nsIScriptRuntime **aRuntime);
-
-nsresult NS_GetScriptRuntimeByID(uint32_t aLanguageID,
-                                 nsIScriptRuntime **aRuntime);
-
-#endif // nsIScriptRuntime_h__
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -130,17 +130,17 @@ static PRLogModuleInfo* gJSDiagnostics;
 
 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
 
 // Large value used to specify that a script should run essentially forever
 #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
 
 #define NS_MAJOR_FORGET_SKIPPABLE_CALLS 2
 
-// if you add statics here, add them to the list in nsJSRuntime::Startup
+// if you add statics here, add them to the list in StartupJSEnvironment
 
 static nsITimer *sGCTimer;
 static nsITimer *sShrinkGCBuffersTimer;
 static nsITimer *sCCTimer;
 static nsITimer *sFullGCTimer;
 static nsITimer *sInterSliceGCTimer;
 
 static PRTime sLastCCEndTime;
@@ -174,23 +174,24 @@ static uint32_t sForgetSkippableBeforeCC
 static uint32_t sPreviousSuspectedCount = 0;
 static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
 static bool sNeedsFullCC = false;
 static nsJSContext *sContextList = nullptr;
 
 static nsScriptNameSpaceManager *gNameSpaceManager;
 
 static nsIJSRuntimeService *sRuntimeService;
-JSRuntime *nsJSRuntime::sRuntime;
 
 static const char kJSRuntimeServiceContractID[] =
   "@mozilla.org/js/xpc/RuntimeService;1";
 
 static PRTime sFirstCollectionTime;
 
+static JSRuntime *sRuntime;
+
 static bool sIsInitialized;
 static bool sDidShutdown;
 static bool sShuttingDown;
 static int32_t sContextCount;
 
 static nsIScriptSecurityManager *sSecurityManager;
 
 // nsJSEnvironmentObserver observes the memory-pressure notifications
@@ -804,33 +805,35 @@ nsJSContext::JSOptionChangedCallback(con
   int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
   if (zeal >= 0)
     ::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency);
 #endif
 
   return 0;
 }
 
-nsJSContext::nsJSContext(JSRuntime *aRuntime, bool aGCOnDestruction,
+nsJSContext::nsJSContext(bool aGCOnDestruction,
                          nsIScriptGlobalObject* aGlobalObject)
   : mGCOnDestruction(aGCOnDestruction)
   , mGlobalObjectRef(aGlobalObject)
 {
+  EnsureStatics();
+
   mNext = sContextList;
   mPrev = &sContextList;
   if (sContextList) {
     sContextList->mPrev = &mNext;
   }
   sContextList = this;
 
   ++sContextCount;
 
   mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS;
 
-  mContext = ::JS_NewContext(aRuntime, gStackSize);
+  mContext = ::JS_NewContext(sRuntime, gStackSize);
   if (mContext) {
     ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
 
     // Preserve any flags the context callback might have set.
     mDefaultJSOptions |= ::JS_GetOptions(mContext);
 
     // Make sure the new context gets the default context options
     ::JS_SetOptions(mContext, mDefaultJSOptions);
@@ -1060,43 +1063,16 @@ nsJSContext::BindCompiledEventHandler(ns
     funobj = NULL;
   }
 
   aBoundHandler.set(funobj);
 
   return rv;
 }
 
-// serialization
-nsresult
-nsJSContext::Serialize(nsIObjectOutputStream* aStream,
-                       JS::Handle<JSScript*> aScriptObject)
-{
-  if (!aScriptObject)
-    return NS_ERROR_FAILURE;
-
-  AutoPushJSContext cx(mContext);
-  return nsContentUtils::XPConnect()->WriteScript(aStream, cx,
-                                                  xpc_UnmarkGrayScript(aScriptObject));
-}
-
-nsresult
-nsJSContext::Deserialize(nsIObjectInputStream* aStream,
-                         JS::MutableHandle<JSScript*> aResult)
-{
-  AutoPushJSContext cx(mContext);
-  JS::Rooted<JSScript*> script(cx);
-  nsresult rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
-                                                        script.address());
-  if (NS_FAILED(rv)) return rv;
-
-  aResult.set(script);
-  return NS_OK;
-}
-
 nsIScriptGlobalObject *
 nsJSContext::GetGlobalObject()
 {
   AutoJSContext cx;
   JS::Rooted<JSObject*> global(mContext, GetNativeGlobal());
   if (!global) {
     return nullptr;
   }
@@ -1172,17 +1148,17 @@ nsJSContext::InitContext()
   JSOptionChangedCallback(js_options_dot_str, this);
 
   return NS_OK;
 }
 
 nsresult
 nsJSContext::InitializeExternalClasses()
 {
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   return nameSpaceManager->InitForContext(this);
 }
 
 nsresult
 nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs)
 {
@@ -1947,53 +1923,53 @@ nsJSContext::GarbageCollectNow(JS::gcrea
   // timer we scheduled due to a normal GC timer firing while
   // documents were loading. If this happens we're waiting for a
   // document that is taking a long time to load, and we effectively
   // ignore the fact that the currently loading documents are still
   // loading and move on as if they weren't.
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
 
-  if (!nsContentUtils::XPConnect() || !nsJSRuntime::sRuntime) {
+  if (!nsContentUtils::XPConnect() || !sRuntime) {
     return;
   }
 
   if (sCCLockedOut && aIncremental == IncrementalGC) {
     // We're in the middle of incremental GC. Do another slice.
-    JS::PrepareForIncrementalGC(nsJSRuntime::sRuntime);
-    JS::IncrementalGC(nsJSRuntime::sRuntime, aReason, aSliceMillis);
+    JS::PrepareForIncrementalGC(sRuntime);
+    JS::IncrementalGC(sRuntime, aReason, aSliceMillis);
     return;
   }
 
-  JS::PrepareForFullGC(nsJSRuntime::sRuntime);
+  JS::PrepareForFullGC(sRuntime);
   if (aIncremental == IncrementalGC) {
-    JS::IncrementalGC(nsJSRuntime::sRuntime, aReason, aSliceMillis);
+    JS::IncrementalGC(sRuntime, aReason, aSliceMillis);
   } else {
-    JS::GCForReason(nsJSRuntime::sRuntime, aReason);
+    JS::GCForReason(sRuntime, aReason);
   }
 }
 
 //static
 void
 nsJSContext::ShrinkGCBuffersNow()
 {
   PROFILER_LABEL("GC", "ShrinkGCBuffersNow");
 
   KillShrinkGCBuffersTimer();
 
-  JS::ShrinkGCBuffers(nsJSRuntime::sRuntime);
+  JS::ShrinkGCBuffers(sRuntime);
 }
 
 static void
 FinishAnyIncrementalGC()
 {
   if (sCCLockedOut) {
     // We're in the middle of an incremental GC, so finish it.
-    JS::PrepareForIncrementalGC(nsJSRuntime::sRuntime);
-    JS::FinishIncrementalGC(nsJSRuntime::sRuntime, JS::gcreason::CC_FORCED);
+    JS::PrepareForIncrementalGC(sRuntime);
+    JS::FinishIncrementalGC(sRuntime, JS::gcreason::CC_FORCED);
   }
 }
 
 static void
 FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless)
 {
   PRTime startTime = PR_Now();
   FinishAnyIncrementalGC();
@@ -2583,41 +2559,18 @@ nsJSContext::ReportPendingException()
 }
 
 void
 nsJSContext::LikelyShortLivingObjectCreated()
 {
   ++sLikelyShortLivingObjectsNeedingGC;
 }
 
-/**********************************************************************
- * nsJSRuntime implementation
- *********************************************************************/
-
-// QueryInterface implementation for nsJSRuntime
-NS_INTERFACE_MAP_BEGIN(nsJSRuntime)
-  NS_INTERFACE_MAP_ENTRY(nsIScriptRuntime)
-NS_INTERFACE_MAP_END
-
-
-NS_IMPL_ADDREF(nsJSRuntime)
-NS_IMPL_RELEASE(nsJSRuntime)
-
-already_AddRefed<nsIScriptContext>
-nsJSRuntime::CreateContext(bool aGCOnDestruction,
-                           nsIScriptGlobalObject* aGlobalObject)
-{
-  nsCOMPtr<nsIScriptContext> scriptContext =
-    new nsJSContext(sRuntime, aGCOnDestruction, aGlobalObject);
-  return scriptContext.forget();
-}
-
-//static
 void
-nsJSRuntime::Startup()
+mozilla::dom::StartupJSEnvironment()
 {
   // initialize all our statics, so that we can restart XPCOM
   sGCTimer = sFullGCTimer = sCCTimer = nullptr;
   sCCLockedOut = false;
   sCCLockedOutTime = 0;
   sLastCCEndTime = 0;
   sHasRunGC = false;
   sPendingLoadCount = 0;
@@ -2644,81 +2597,81 @@ ReportAllJSExceptionsPrefChangedCallback
   return 0;
 }
 
 static int
 SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   int32_t highwatermark = Preferences::GetInt(aPrefName, 128);
 
-  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
+  JS_SetGCParameter(sRuntime, JSGC_MAX_MALLOC_BYTES,
                     highwatermark * 1024L * 1024L);
   return 0;
 }
 
 static int
 SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   int32_t pref = Preferences::GetInt(aPrefName, -1);
   // handle overflow and negative pref values
   uint32_t max = (pref <= 0 || pref >= 0x1000) ? -1 : (uint32_t)pref * 1024 * 1024;
-  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES, max);
+  JS_SetGCParameter(sRuntime, JSGC_MAX_BYTES, max);
   return 0;
 }
 
 static int
 SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment");
   bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
   JSGCMode mode;
   if (enableIncrementalGC) {
     mode = JSGC_MODE_INCREMENTAL;
   } else if (enableCompartmentGC) {
     mode = JSGC_MODE_COMPARTMENT;
   } else {
     mode = JSGC_MODE_GLOBAL;
   }
-  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MODE, mode);
+  JS_SetGCParameter(sRuntime, JSGC_MODE, mode);
   return 0;
 }
 
 static int
 SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   int32_t pref = Preferences::GetInt(aPrefName, -1);
   // handle overflow and negative pref values
   if (pref > 0 && pref < 100000)
-    JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_SLICE_TIME_BUDGET, pref);
+    JS_SetGCParameter(sRuntime, JSGC_SLICE_TIME_BUDGET, pref);
   return 0;
 }
 
 static int
 SetMemoryGCPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   int32_t pref = Preferences::GetInt(aPrefName, -1);
   // handle overflow and negative pref values
   if (pref >= 0 && pref < 10000)
-    JS_SetGCParameter(nsJSRuntime::sRuntime, (JSGCParamKey)(intptr_t)aClosure, pref);
+    JS_SetGCParameter(sRuntime, (JSGCParamKey)(intptr_t)aClosure, pref);
   return 0;
 }
 
 static int
 SetMemoryGCDynamicHeapGrowthPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool pref = Preferences::GetBool(aPrefName);
-  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_DYNAMIC_HEAP_GROWTH, pref);
+  JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_HEAP_GROWTH, pref);
   return 0;
 }
 
 static int
 SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool pref = Preferences::GetBool(aPrefName);
-  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref);
+  JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref);
   return 0;
 }
 
 JSObject*
 NS_DOMReadStructuredClone(JSContext* cx,
                           JSStructuredCloneReader* reader,
                           uint32_t tag,
                           uint32_t data,
@@ -2779,40 +2732,46 @@ NS_DOMWriteStructuredClone(JSContext* cx
 void
 NS_DOMStructuredCloneError(JSContext* cx,
                            uint32_t errorid)
 {
   // We don't currently support any extensions to structured cloning.
   xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
 }
 
-//static
-nsresult
-nsJSRuntime::Init()
+static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
+
+void
+nsJSContext::EnsureStatics()
 {
   if (sIsInitialized) {
-    if (!nsContentUtils::XPConnect())
-      return NS_ERROR_NOT_AVAILABLE;
-
-    return NS_OK;
+    if (!nsContentUtils::XPConnect()) {
+      MOZ_CRASH();
+    }
+    return;
   }
 
   nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
                                &sSecurityManager);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH();
+  }
 
   rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService);
-  // get the JSRuntime from the runtime svc, if possible
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH();
+  }
 
   rv = sRuntimeService->GetRuntime(&sRuntime);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH();
+  }
 
   // Let's make sure that our main thread is the same as the xpcom main thread.
-  NS_ASSERTION(NS_IsMainThread(), "bad");
+  MOZ_ASSERT(NS_IsMainThread());
 
   sPrevGCSliceCallback = JS::SetGCSliceCallback(sRuntime, DOMGCSliceCallback);
 
   // Set up the structured clone callbacks.
   static JSStructuredCloneCallbacks cloneCallbacks = {
     NS_DOMReadStructuredClone,
     NS_DOMWriteStructuredClone,
     NS_DOMStructuredCloneError
@@ -2877,53 +2836,59 @@ nsJSRuntime::Init()
                                        "javascript.options.mem.gc_allocation_threshold_mb",
                                        (void *)JSGC_ALLOCATION_THRESHOLD);
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
                                        "javascript.options.mem.gc_decommit_threshold_mb",
                                        (void *)JSGC_DECOMMIT_THRESHOLD);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (!obs)
-    return NS_ERROR_FAILURE;
+  if (!obs) {
+    MOZ_CRASH();
+  }
 
   Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
                                "javascript.options.gc_on_memory_pressure",
                                true);
 
   nsIObserver* observer = new nsJSEnvironmentObserver();
   obs->AddObserver(observer, "memory-pressure", false);
   obs->AddObserver(observer, "quit-application", false);
 
+  // We need to explicitly get the nsIDOMScriptObjectFactory service in order
+  // to force its constructor to run, which registers various exceptions
+  // providers and other things. It would be nice to make this more explicit
+  // and less side-effect-y.
+  nsCOMPtr<nsIDOMScriptObjectFactory> factory = do_GetService(kDOMScriptObjectFactoryCID);
+  if (!factory) {
+    MOZ_CRASH();
+  }
+
   sIsInitialized = true;
-
-  return NS_OK;
 }
 
-//static
 nsScriptNameSpaceManager*
-nsJSRuntime::GetNameSpaceManager()
+mozilla::dom::GetNameSpaceManager()
 {
   if (sDidShutdown)
     return nullptr;
 
   if (!gNameSpaceManager) {
     gNameSpaceManager = new nsScriptNameSpaceManager;
     NS_ADDREF(gNameSpaceManager);
 
     nsresult rv = gNameSpaceManager->Init();
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
 
   return gNameSpaceManager;
 }
 
-/* static */
 void
-nsJSRuntime::Shutdown()
+mozilla::dom::ShutdownJSEnvironment()
 {
   KillTimers();
 
   NS_IF_RELEASE(gNameSpaceManager);
 
   if (!sContextCount) {
     // We're being shutdown, and there are no more contexts
     // alive, release the JS runtime service and the security manager.
@@ -2931,29 +2896,16 @@ nsJSRuntime::Shutdown()
     NS_IF_RELEASE(sRuntimeService);
     NS_IF_RELEASE(sSecurityManager);
   }
 
   sShuttingDown = true;
   sDidShutdown = true;
 }
 
-// A factory for the runtime.
-nsresult NS_CreateJSRuntime(nsIScriptRuntime **aRuntime)
-{
-  nsresult rv = nsJSRuntime::Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *aRuntime = new nsJSRuntime();
-  if (*aRuntime == nullptr)
-    return NS_ERROR_OUT_OF_MEMORY;
-  NS_IF_ADDREF(*aRuntime);
-  return NS_OK;
-}
-
 // A fast-array class for JS.  This class supports both nsIJSScriptArray and
 // nsIArray.  If it is JS itself providing and consuming this class, all work
 // can be done via nsIJSScriptArray, and avoid the conversion of elements
 // to/from nsISupports.
 // When consumed by non-JS (eg, another script language), conversion is done
 // on-the-fly.
 class nsJSArgArray MOZ_FINAL : public nsIJSArgArray {
 public:
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; 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/. */
 #ifndef nsJSEnvironment_h
 #define nsJSEnvironment_h
 
 #include "nsIScriptContext.h"
-#include "nsIScriptRuntime.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsCOMPtr.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "nsIObserver.h"
 #include "prtime.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIXPConnect.h"
@@ -29,18 +28,17 @@ template <class> class Maybe;
 
 // The amount of time we wait between a request to GC (due to leaving
 // a page) and doing the actual GC.
 #define NS_GC_DELAY                 4000 // ms
 
 class nsJSContext : public nsIScriptContext
 {
 public:
-  nsJSContext(JSRuntime* aRuntime, bool aGCOnDestruction,
-              nsIScriptGlobalObject* aGlobalObject);
+  nsJSContext(bool aGCOnDestruction, nsIScriptGlobalObject* aGlobalObject);
   virtual ~nsJSContext();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSContext,
                                                          nsIScriptContext)
 
   virtual nsresult EvaluateString(const nsAString& aScript,
                                   JS::Handle<JSObject*> aScopeObject,
@@ -69,21 +67,16 @@ public:
   virtual bool GetProcessingScriptTag() MOZ_OVERRIDE;
   virtual void SetProcessingScriptTag(bool aResult) MOZ_OVERRIDE;
 
   virtual nsresult InitClasses(JS::Handle<JSObject*> aGlobalObj) MOZ_OVERRIDE;
 
   virtual void WillInitializeContext() MOZ_OVERRIDE;
   virtual void DidInitializeContext() MOZ_OVERRIDE;
 
-  virtual nsresult Serialize(nsIObjectOutputStream* aStream,
-                             JS::Handle<JSScript*> aScriptObject) MOZ_OVERRIDE;
-  virtual nsresult Deserialize(nsIObjectInputStream* aStream,
-                               JS::MutableHandle<JSScript*> aResult) MOZ_OVERRIDE;
-
   static void LoadStart();
   static void LoadEnd();
 
   enum IsCompartment {
     CompartmentGC,
     NonCompartmentGC
   };
 
@@ -92,16 +85,19 @@ public:
     NonShrinkingGC
   };
 
   enum IsIncremental {
     IncrementalGC,
     NonIncrementalGC
   };
 
+  // Setup all the statics etc - safe to call multiple times after Startup().
+  void EnsureStatics();
+
   static void GarbageCollectNow(JS::gcreason::Reason reason,
                                 IsIncremental aIncremental = NonIncrementalGC,
                                 IsCompartment aCompartment = NonCompartmentGC,
                                 IsShrinking aShrinking = NonShrinkingGC,
                                 int64_t aSliceMillis = 0);
   static void ShrinkGCBuffersNow();
   // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
   // called even if the previous collection was GC.
@@ -183,37 +179,27 @@ private:
 
   static int JSOptionChangedCallback(const char *pref, void *data);
 
   static bool DOMOperationCallback(JSContext *cx);
 };
 
 class nsIJSRuntimeService;
 
-class nsJSRuntime MOZ_FINAL : public nsIScriptRuntime
-{
-public:
-  // let people who can see us use our runtime for convenience.
-  static JSRuntime *sRuntime;
+namespace mozilla {
+namespace dom {
 
-public:
-  // nsISupports
-  NS_DECL_ISUPPORTS
+void StartupJSEnvironment();
+void ShutdownJSEnvironment();
 
-  virtual already_AddRefed<nsIScriptContext>
-  CreateContext(bool aGCOnDestruction,
-                nsIScriptGlobalObject* aGlobalObject) MOZ_OVERRIDE;
+// Get the NameSpaceManager, creating if necessary
+nsScriptNameSpaceManager* GetNameSpaceManager();
 
-  static void Startup();
-  static void Shutdown();
-  // Setup all the statics etc - safe to call multiple times after Startup()
-  static nsresult Init();
-  // Get the NameSpaceManager, creating if necessary
-  static nsScriptNameSpaceManager* GetNameSpaceManager();
-};
+} // namespace dom
+} // namespace mozilla
 
 // An interface for fast and native conversion to/from nsIArray. If an object
 // supports this interface, JS can reach directly in for the argv, and avoid
 // nsISupports conversion. If this interface is not supported, the object will
 // be queried for nsIArray, and everything converted via xpcom objects.
 #define NS_IJSARGARRAY_IID \
 { 0xb6acdac8, 0xf5c6, 0x432c, \
   { 0xa8, 0x6e, 0x33, 0xee, 0xb1, 0xb0, 0xcd, 0xdc } }
@@ -225,19 +211,16 @@ public:
   // Bug 312003 describes why this must be "void **", but after calling argv
   // may be cast to JS::Value* and the args found at:
   //    ((JS::Value*)argv)[0], ..., ((JS::Value*)argv)[argc - 1]
   virtual nsresult GetArgs(uint32_t *argc, void **argv) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSArgArray, NS_IJSARGARRAY_IID)
 
-/* factory functions */
-nsresult NS_CreateJSRuntime(nsIScriptRuntime **aRuntime);
-
 /* prototypes */
 void NS_ScriptErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
 
 JSObject* NS_DOMReadStructuredClone(JSContext* cx,
                                     JSStructuredCloneReader* reader, uint32_t tag,
                                     uint32_t data, void* closure);
 
 bool NS_DOMWriteStructuredClone(JSContext* cx,
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -816,16 +816,20 @@ DOMInterfaces = {
     'implicitJSContext': [ 'createBuffer' ],
     'resultNotAddRefed': [ 'destination', 'listener' ],
 },
 
 'OfflineResourceList': {
     'nativeType': 'nsDOMOfflineResourceList',
 },
 
+'OscillatorNode': {
+    'resultNotAddRefed': [ 'frequency', 'detune' ],
+},
+
 'PaintRequest': {
     'nativeType': 'nsPaintRequest',
 },
 
 'PaintRequestList': {
     'nativeType': 'nsPaintRequestList',
     'headerFile': 'nsPaintRequest.h',
     'resultNotAddRefed': [ 'item' ]
--- a/dom/src/events/nsJSEventListener.cpp
+++ b/dom/src/events/nsJSEventListener.cpp
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "nsJSEventListener.h"
 #include "nsJSUtils.h"
 #include "nsString.h"
 #include "nsIServiceManager.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptGlobalObject.h"
-#include "nsIScriptRuntime.h"
 #include "nsIXPConnect.h"
 #include "nsGUIEvent.h"
 #include "nsContentUtils.h"
 #include "nsIMutableArray.h"
 #include "nsVariant.h"
 #include "nsIDOMBeforeUnloadEvent.h"
 #include "nsGkAtoms.h"
 #include "xpcpublic.h"
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -62,16 +62,18 @@ interface AudioContext : EventTarget {
     [Creator, Throws]
     ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6);
     [Creator, Throws]
     ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6);
 
     [Creator]
     DynamicsCompressorNode createDynamicsCompressor();
 
+    [Creator]
+    OscillatorNode createOscillator();
     [Creator, Throws]
     PeriodicWave createPeriodicWave(Float32Array real, Float32Array imag);
 
 };
 
 /*
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
new file mode 100644
--- /dev/null
+++ b/dom/webidl/OscillatorNode.webidl
@@ -0,0 +1,68 @@
+/* -*- 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
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+enum OscillatorType {
+  // Hack: Use numbers to support alternate enum values
+  "0", "1", "2", "3", "4",
+
+  "sine",
+  "square",
+  "sawtooth",
+  "triangle",
+  "custom"
+};
+
+[PrefControlled]
+interface OscillatorNode : AudioNode {
+
+    [SetterThrows]
+    attribute OscillatorType type;
+
+    readonly attribute AudioParam frequency; // in Hertz
+    readonly attribute AudioParam detune; // in Cents
+
+    [Throws]
+    void start(double when);
+    [Throws]
+    void stop(double when);
+    void setPeriodicWave(PeriodicWave periodicWave);
+
+    [SetterThrows]
+    attribute EventHandler onended;
+
+};
+
+/*
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
+ */
+partial interface OscillatorNode {
+    // Same as start()
+    [Throws,Pref="media.webaudio.legacy.OscillatorNode"]
+    void noteOn(double when);
+
+    // Same as stop()
+    [Throws,Pref="media.webaudio.legacy.OscillatorNode"]
+    void noteOff(double when);
+
+    [Pref="media.webaudio.legacy.OscillatorNode"]
+    const unsigned short SINE = 0;
+    [Pref="media.webaudio.legacy.OscillatorNode"]
+    const unsigned short SQUARE = 1;
+    [Pref="media.webaudio.legacy.OscillatorNode"]
+    const unsigned short SAWTOOTH = 2;
+    [Pref="media.webaudio.legacy.OscillatorNode"]
+    const unsigned short TRIANGLE = 3;
+    [Pref="media.webaudio.legacy.OscillatorNode"]
+    const unsigned short CUSTOM = 4;
+};
+
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -211,16 +211,17 @@ webidl_files = \
   NodeIterator.webidl \
   NodeList.webidl \
   Notification.webidl \
   NotifyAudioAvailableEvent.webidl \
   NotifyPaintEvent.webidl \
   OfflineAudioCompletionEvent.webidl \
   OfflineAudioContext.webidl \
   OfflineResourceList.webidl \
+  OscillatorNode.webidl \
   PaintRequest.webidl \
   PaintRequestList.webidl \
   PannerNode.webidl \
   ParentNode.webidl \
   Performance.webidl \
   PerformanceNavigation.webidl \
   PerformanceTiming.webidl \
   PeriodicWave.webidl \
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -40,41 +40,46 @@ void
 CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   if (mBuffer &&
       (mBuffer->IsImmutable() || mBuffer->GetSize() != aSize)) {
     RemoveTextureClient(mBuffer);
     mBuffer = nullptr;
   }
 
+  bool bufferCreated = false;
   if (!mBuffer) {
     bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
     gfxASurface::gfxContentType contentType = isOpaque
                                                 ? gfxASurface::CONTENT_COLOR
                                                 : gfxASurface::CONTENT_COLOR_ALPHA;
     gfxASurface::gfxImageFormat format
       = gfxPlatform::GetPlatform()->OptimalFormatForContent(contentType);
     mBuffer = CreateBufferTextureClient(gfx::ImageFormatToSurfaceFormat(format));
     MOZ_ASSERT(mBuffer->AsTextureClientSurface());
     mBuffer->AsTextureClientSurface()->AllocateForSurface(aSize);
 
-    AddTextureClient(mBuffer);
+    bufferCreated = true;
   }
 
   if (!mBuffer->Lock(OPEN_READ_WRITE)) {
     return;
   }
 
   nsRefPtr<gfxASurface> surface = mBuffer->AsTextureClientSurface()->GetAsSurface();
   if (surface) {
     aLayer->UpdateSurface(surface);
   }
 
   mBuffer->Unlock();
 
+  if (bufferCreated) {
+    AddTextureClient(mBuffer);
+  }
+
   if (surface) {
     GetForwarder()->UpdatedTexture(this, mBuffer, nullptr);
     GetForwarder()->UseTexture(this, mBuffer);
   }
 }
 
 TemporaryRef<BufferTextureClient>
 CanvasClient2D::CreateBufferTextureClient(gfx::SurfaceFormat aFormat)
@@ -123,27 +128,23 @@ DeprecatedCanvasClient2D::Update(gfx::In
   gfxASurface* surface = mDeprecatedTextureClient->LockSurface();
   aLayer->UpdateSurface(surface);
   mDeprecatedTextureClient->Unlock();
 }
 
 void
 DeprecatedCanvasClientSurfaceStream::Updated()
 {
-  if (mNeedsUpdate) {
-    mForwarder->UpdateTextureNoSwap(this, 1, mDeprecatedTextureClient->GetDescriptor());
-    mNeedsUpdate = false;
-  }
+  mForwarder->UpdateTextureNoSwap(this, 1, mDeprecatedTextureClient->GetDescriptor());
 }
 
 
 DeprecatedCanvasClientSurfaceStream::DeprecatedCanvasClientSurfaceStream(CompositableForwarder* aFwd,
                                                                          TextureFlags aFlags)
 : CanvasClient(aFwd, aFlags)
-, mNeedsUpdate(false)
 {
   mTextureInfo.mCompositableType = BUFFER_IMAGE_SINGLE;
 }
 
 void
 DeprecatedCanvasClientSurfaceStream::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   if (!mDeprecatedTextureClient) {
@@ -175,30 +176,28 @@ DeprecatedCanvasClientSurfaceStream::Upd
     }
 
     SharedSurface_Gralloc* grallocSurf = SharedSurface_Gralloc::Cast(surf);
     mDeprecatedTextureClient->SetDescriptor(grallocSurf->GetDescriptor());
 #else
     printf_stderr("isCrossProcess, but not MOZ_WIDGET_GONK! Someone needs to write some code!");
     MOZ_ASSERT(false);
 #endif
-    mNeedsUpdate = true;
   } else {
     SurfaceStreamHandle handle = stream->GetShareHandle();
     SurfaceDescriptor *desc = mDeprecatedTextureClient->GetDescriptor();
     if (desc->type() != SurfaceDescriptor::TSurfaceStreamDescriptor ||
         desc->get_SurfaceStreamDescriptor().handle() != handle) {
       *desc = SurfaceStreamDescriptor(handle, false);
 
       // Bug 894405
       //
       // Ref this so the SurfaceStream doesn't disappear unexpectedly. The
       // Compositor will need to unref it when finished.
       aLayer->mGLContext->AddRef();
-      mNeedsUpdate = true;
     }
   }
 
   aLayer->Painted();
 }
 
 }
 }
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -127,15 +127,14 @@ public:
   virtual void SetDescriptorFromReply(TextureIdentifier aTextureId,
                                       const SurfaceDescriptor& aDescriptor) MOZ_OVERRIDE
   {
     mDeprecatedTextureClient->SetDescriptorFromReply(aDescriptor);
   }
 
 private:
   RefPtr<DeprecatedTextureClient> mDeprecatedTextureClient;
-  bool mNeedsUpdate;
 };
 
 }
 }
 
 #endif
--- a/gfx/layers/client/ClientCanvasLayer.cpp
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -81,23 +81,23 @@ ClientCanvasLayer::RenderLayer()
   }
   
   if (!mCanvasClient) {
     TextureFlags flags = TEXTURE_IMMEDIATE_UPLOAD;
     if (mNeedsYFlip) {
       flags |= TEXTURE_NEEDS_Y_FLIP;
     }
 
-    bool isCrossProcess = !(XRE_GetProcessType() == GeckoProcessType_Default);
-    //Append TEXTURE_DEALLOCATE_CLIENT flag for streaming buffer under OOPC case
-    if (isCrossProcess && mGLContext) {
-      GLScreenBuffer* screen = mGLContext->Screen();
-      if (screen && screen->Stream()) {
-        flags |= TEXTURE_DEALLOCATE_CLIENT;
-      }
+    if (!mGLContext) {
+      // GLContext's SurfaceStream handles ownership itself,
+      // and doesn't require layers to do any deallocation.
+      flags |= TEXTURE_DEALLOCATE_HOST;
+
+      // We don't support locking for buffer surfaces currently
+      flags |= TEXTURE_IMMEDIATE_UPLOAD;
     }
     mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(),
                                                      ClientManager(), flags);
     if (!mCanvasClient) {
       return;
     }
     if (HasShadow()) {
       mCanvasClient->Connect();
--- a/gfx/layers/client/ClientImageLayer.cpp
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -121,21 +121,23 @@ ClientImageLayer::RenderLayer()
   }
 
   if (!mImageClient ||
       !mImageClient->UpdateImage(mContainer, GetContentFlags())) {
     CompositableType type = GetImageClientType();
     if (type == BUFFER_UNKNOWN) {
       return;
     }
+    TextureFlags flags = TEXTURE_FLAGS_DEFAULT;
+    if (mDisallowBigImage) {
+      flags |= TEXTURE_DISALLOW_BIGIMAGE;
+    }
     mImageClient = ImageClient::CreateImageClient(type,
                                                   ClientManager(),
-                                                  mDisallowBigImage
-                                                    ? TEXTURE_DISALLOW_BIGIMAGE
-                                                    : 0);
+                                                  flags);
     if (type == BUFFER_BRIDGE) {
       static_cast<ImageClientBridge*>(mImageClient.get())->SetLayer(this);
     }
 
     if (!mImageClient) {
       return;
     }
     if (HasShadow() && !mContainer->IsAsync()) {
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -110,33 +110,38 @@ ImageClientSingle::UpdateImage(ImageCont
       return false;
     }
 
     if (mFrontBuffer && mFrontBuffer->IsImmutable()) {
       RemoveTextureClient(mFrontBuffer);
       mFrontBuffer = nullptr;
     }
 
+    bool bufferCreated = false;
     if (!mFrontBuffer) {
       mFrontBuffer = CreateBufferTextureClient(gfx::FORMAT_YUV);
       gfx::IntSize ySize(data->mYSize.width, data->mYSize.height);
       gfx::IntSize cbCrSize(data->mCbCrSize.width, data->mCbCrSize.height);
       if (!mFrontBuffer->AsTextureClientYCbCr()->AllocateForYCbCr(ySize, cbCrSize, data->mStereoMode)) {
         mFrontBuffer = nullptr;
         return false;
       }
-      AddTextureClient(mFrontBuffer);
+      bufferCreated = true;
     }
 
     if (!mFrontBuffer->Lock(OPEN_READ_WRITE)) {
       return false;
     }
     bool status = mFrontBuffer->AsTextureClientYCbCr()->UpdateYCbCr(*data);
     mFrontBuffer->Unlock();
 
+    if (bufferCreated) {
+      AddTextureClient(mFrontBuffer);
+    }
+
     if (status) {
       GetForwarder()->UpdatedTexture(this, mFrontBuffer, nullptr);
       GetForwarder()->UseTexture(this, mFrontBuffer);
     } else {
       MOZ_ASSERT(false);
       return false;
     }
 
@@ -147,31 +152,37 @@ ImageClientSingle::UpdateImage(ImageCont
     gfx::IntSize size = gfx::IntSize(image->GetSize().width, image->GetSize().height);
 
     if (mFrontBuffer &&
         (mFrontBuffer->IsImmutable() || mFrontBuffer->GetSize() != size)) {
       RemoveTextureClient(mFrontBuffer);
       mFrontBuffer = nullptr;
     }
 
+    bool bufferCreated = false;
     if (!mFrontBuffer) {
       gfxASurface::gfxImageFormat format
         = gfxPlatform::GetPlatform()->OptimalFormatForContent(surface->GetContentType());
       mFrontBuffer = CreateBufferTextureClient(gfx::ImageFormatToSurfaceFormat(format));
       MOZ_ASSERT(mFrontBuffer->AsTextureClientSurface());
       mFrontBuffer->AsTextureClientSurface()->AllocateForSurface(size);
 
-      AddTextureClient(mFrontBuffer);
+      bufferCreated = true;
     }
 
     if (!mFrontBuffer->Lock(OPEN_READ_WRITE)) {
       return false;
     }
     bool status = mFrontBuffer->AsTextureClientSurface()->UpdateSurface(surface);
     mFrontBuffer->Unlock();
+
+    if (bufferCreated) {
+      AddTextureClient(mFrontBuffer);
+    }
+
     if (status) {
       GetForwarder()->UpdatedTexture(this, mFrontBuffer, nullptr);
       GetForwarder()->UseTexture(this, mFrontBuffer);
     } else {
       return false;
     }
   }
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -159,16 +159,17 @@ BufferTextureClient::BufferTextureClient
 
 BufferTextureClient::~BufferTextureClient()
 {}
 
 bool
 BufferTextureClient::UpdateSurface(gfxASurface* aSurface)
 {
   MOZ_ASSERT(aSurface);
+  MOZ_ASSERT(!IsImmutable());
 
   ImageDataSerializer serializer(GetBuffer());
   if (!serializer.IsValid()) {
     return false;
   }
 
   RefPtr<gfxImageSurface> surf = serializer.GetAsThebesSurface();
   if (!surf) {
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -221,17 +221,18 @@ BufferTextureHost::Updated(const nsIntRe
   ++mUpdateSerial;
   if (aRegion) {
     mPartialUpdate = true;
     mMaybeUpdatedRegion = *aRegion;
   } else {
     mPartialUpdate = false;
   }
   if (GetFlags() & TEXTURE_IMMEDIATE_UPLOAD) {
-    MaybeUpload(mPartialUpdate ? &mMaybeUpdatedRegion : nullptr);
+    DebugOnly<bool> result = MaybeUpload(mPartialUpdate ? &mMaybeUpdatedRegion : nullptr);
+    MOZ_ASSERT(result);
   }
 }
 
 void
 BufferTextureHost::SetCompositor(Compositor* aCompositor)
 {
   if (mCompositor == aCompositor) {
     return;
@@ -375,16 +376,19 @@ BufferTextureHost::Upload(nsIntRegion *a
     }
     ImageDataDeserializer deserializer(GetBuffer());
     if (!deserializer.IsValid()) {
       NS_WARNING("failed to open shmem surface");
       return false;
     }
 
     RefPtr<gfx::DataSourceSurface> surf = deserializer.GetAsSurface();
+    if (!surf) {
+      return false;
+    }
 
     if (!mFirstSource->Update(surf.get(), mFlags, aRegion)) {
       NS_WARNING("failed to update the DataTextureSource");
       return false;
     }
   }
   return true;
 }
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -377,16 +377,17 @@ void
 ShadowLayerForwarder::AddTexture(CompositableClient* aCompositable,
                                  TextureClient* aTexture)
 {
   SurfaceDescriptor descriptor;
   if (!aTexture->ToSurfaceDescriptor(descriptor)) {
     NS_WARNING("Failed to serialize a TextureClient");
     return;
   }
+  MOZ_ASSERT(aTexture->GetFlags() != 0);
   mTxn->AddEdit(OpAddTexture(nullptr, aCompositable->GetIPDLActor(),
                              aTexture->GetID(),
                              descriptor,
                              aTexture->GetFlags()));
 }
 
 void
 ShadowLayerForwarder::RemoveTexture(CompositableClient* aCompositable,
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -613,17 +613,21 @@ SurfaceStreamHostOGL::UpdateImpl(const S
 
   // Bug 894405
   //
   // The SurfaceStream's GLContext was refed before being passed up to us, so
   // we need to ensure it gets unrefed when we are finished.
   const SurfaceStreamDescriptor& streamDesc =
       aImage.get_SurfaceStreamDescriptor();
 
-  mStream = SurfaceStream::FromHandle(streamDesc.handle());
+  SurfaceStream *stream = SurfaceStream::FromHandle(streamDesc.handle());
+  if (stream == mStream) {
+    return;
+  }
+  mStream = stream;
   MOZ_ASSERT(mStream);
   mStreamGL = dont_AddRef(mStream->GLContext());
 }
 
 void
 SurfaceStreamHostOGL::Unlock()
 {
   // We don't know what this is unless we're locked
new file mode 100644
--- /dev/null
+++ b/gfx/skia/patches/0026-Bug-901208-Fix-ARM-v4t.patch
@@ -0,0 +1,83 @@
+diff --git a/gfx/skia/src/opts/SkBlitRow_opts_arm.cpp b/gfx/skia/src/opts/SkBlitRow_opts_arm.cpp
+--- a/gfx/skia/src/opts/SkBlitRow_opts_arm.cpp
++++ b/gfx/skia/src/opts/SkBlitRow_opts_arm.cpp
+@@ -26,66 +26,78 @@ static void S32A_D565_Opaque(uint16_t* S
+     asm volatile (
+                   "1:                                   \n\t"
+                   "ldr     r3, [%[src]], #4             \n\t"
+                   "cmp     r3, #0xff000000              \n\t"
+                   "blo     2f                           \n\t"
+                   "and     r4, r3, #0x0000f8            \n\t"
+                   "and     r5, r3, #0x00fc00            \n\t"
+                   "and     r6, r3, #0xf80000            \n\t"
++#ifdef SK_ARM_HAS_EDSP
+                   "pld     [r1, #32]                    \n\t"
++#endif
+                   "lsl     r3, r4, #8                   \n\t"
+                   "orr     r3, r3, r5, lsr #5           \n\t"
+                   "orr     r3, r3, r6, lsr #19          \n\t"
+                   "subs    %[count], %[count], #1       \n\t"
+                   "strh    r3, [%[dst]], #2             \n\t"
+                   "bne     1b                           \n\t"
+                   "b       4f                           \n\t"
+                   "2:                                   \n\t"
+                   "lsrs    r7, r3, #24                  \n\t"
+                   "beq     3f                           \n\t"
+                   "ldrh    r4, [%[dst]]                 \n\t"
+                   "rsb     r7, r7, #255                 \n\t"
+                   "and     r6, r4, #0x001f              \n\t"
+-#if SK_ARM_ARCH == 6
++#if SK_ARM_ARCH <= 6
+                   "lsl     r5, r4, #21                  \n\t"
+                   "lsr     r5, r5, #26                  \n\t"
+ #else
+                   "ubfx    r5, r4, #5, #6               \n\t"
+ #endif
++#ifdef SK_ARM_HAS_EDSP
+                   "pld     [r0, #16]                    \n\t"
++#endif
+                   "lsr     r4, r4, #11                  \n\t"
+ #ifdef SK_ARM_HAS_EDSP
+                   "smulbb  r6, r6, r7                   \n\t"
+                   "smulbb  r5, r5, r7                   \n\t"
+                   "smulbb  r4, r4, r7                   \n\t"
+ #else
+                   "mul     r6, r6, r7                   \n\t"
+                   "mul     r5, r5, r7                   \n\t"
+                   "mul     r4, r4, r7                   \n\t"
+ #endif
++#if SK_ARM_ARCH >= 6
+                   "uxtb    r7, r3, ROR #16              \n\t"
+                   "uxtb    ip, r3, ROR #8               \n\t"
++#else
++                  "mov     ip, #0xff                    \n\t"
++                  "and     r7, ip, r3, ROR #16          \n\t"
++                  "and     ip, ip, r3, ROR #8           \n\t"
++#endif
+                   "and     r3, r3, #0xff                \n\t"
+                   "add     r6, r6, #16                  \n\t"
+                   "add     r5, r5, #32                  \n\t"
+                   "add     r4, r4, #16                  \n\t"
+                   "add     r6, r6, r6, lsr #5           \n\t"
+                   "add     r5, r5, r5, lsr #6           \n\t"
+                   "add     r4, r4, r4, lsr #5           \n\t"
+                   "add     r6, r7, r6, lsr #5           \n\t"
+                   "add     r5, ip, r5, lsr #6           \n\t"
+                   "add     r4, r3, r4, lsr #5           \n\t"
+                   "lsr     r6, r6, #3                   \n\t"
+                   "and     r5, r5, #0xfc                \n\t"
+                   "and     r4, r4, #0xf8                \n\t"
+                   "orr     r6, r6, r5, lsl #3           \n\t"
+                   "orr     r4, r6, r4, lsl #8           \n\t"
+                   "strh    r4, [%[dst]], #2             \n\t"
++#ifdef SK_ARM_HAS_EDSP
+                   "pld     [r1, #32]                    \n\t"
++#endif
+                   "subs    %[count], %[count], #1       \n\t"
+                   "bne     1b                           \n\t"
+                   "b       4f                           \n\t"
+                   "3:                                   \n\t"
+                   "subs    %[count], %[count], #1       \n\t"
+                   "add     %[dst], %[dst], #2           \n\t"
+                   "bne     1b                           \n\t"
+                   "4:                                   \n\t"
--- a/gfx/skia/src/opts/SkBlitRow_opts_arm.cpp
+++ b/gfx/skia/src/opts/SkBlitRow_opts_arm.cpp
@@ -26,66 +26,78 @@ static void S32A_D565_Opaque(uint16_t* S
     asm volatile (
                   "1:                                   \n\t"
                   "ldr     r3, [%[src]], #4             \n\t"
                   "cmp     r3, #0xff000000              \n\t"
                   "blo     2f                           \n\t"
                   "and     r4, r3, #0x0000f8            \n\t"
                   "and     r5, r3, #0x00fc00            \n\t"
                   "and     r6, r3, #0xf80000            \n\t"
+#ifdef SK_ARM_HAS_EDSP
                   "pld     [r1, #32]                    \n\t"
+#endif
                   "lsl     r3, r4, #8                   \n\t"
                   "orr     r3, r3, r5, lsr #5           \n\t"
                   "orr     r3, r3, r6, lsr #19          \n\t"
                   "subs    %[count], %[count], #1       \n\t"
                   "strh    r3, [%[dst]], #2             \n\t"
                   "bne     1b                           \n\t"
                   "b       4f                           \n\t"
                   "2:                                   \n\t"
                   "lsrs    r7, r3, #24                  \n\t"
                   "beq     3f                           \n\t"
                   "ldrh    r4, [%[dst]]                 \n\t"
                   "rsb     r7, r7, #255                 \n\t"
                   "and     r6, r4, #0x001f              \n\t"
-#if SK_ARM_ARCH == 6
+#if SK_ARM_ARCH <= 6
                   "lsl     r5, r4, #21                  \n\t"
                   "lsr     r5, r5, #26                  \n\t"
 #else
                   "ubfx    r5, r4, #5, #6               \n\t"
 #endif
+#ifdef SK_ARM_HAS_EDSP
                   "pld     [r0, #16]                    \n\t"
+#endif
                   "lsr     r4, r4, #11                  \n\t"
 #ifdef SK_ARM_HAS_EDSP
                   "smulbb  r6, r6, r7                   \n\t"
                   "smulbb  r5, r5, r7                   \n\t"
                   "smulbb  r4, r4, r7                   \n\t"
 #else
                   "mul     r6, r6, r7                   \n\t"
                   "mul     r5, r5, r7                   \n\t"
                   "mul     r4, r4, r7                   \n\t"
 #endif
+#if SK_ARM_ARCH >= 6
                   "uxtb    r7, r3, ROR #16              \n\t"
                   "uxtb    ip, r3, ROR #8               \n\t"
+#else
+                  "mov     ip, #0xff                    \n\t"
+                  "and     r7, ip, r3, ROR #16          \n\t"
+                  "and     ip, ip, r3, ROR #8           \n\t"
+#endif
                   "and     r3, r3, #0xff                \n\t"
                   "add     r6, r6, #16                  \n\t"
                   "add     r5, r5, #32                  \n\t"
                   "add     r4, r4, #16                  \n\t"
                   "add     r6, r6, r6, lsr #5           \n\t"
                   "add     r5, r5, r5, lsr #6           \n\t"
                   "add     r4, r4, r4, lsr #5           \n\t"
                   "add     r6, r7, r6, lsr #5           \n\t"
                   "add     r5, ip, r5, lsr #6           \n\t"
                   "add     r4, r3, r4, lsr #5           \n\t"
                   "lsr     r6, r6, #3                   \n\t"
                   "and     r5, r5, #0xfc                \n\t"
                   "and     r4, r4, #0xf8                \n\t"
                   "orr     r6, r6, r5, lsl #3           \n\t"
                   "orr     r4, r6, r4, lsl #8           \n\t"
                   "strh    r4, [%[dst]], #2             \n\t"
+#ifdef SK_ARM_HAS_EDSP
                   "pld     [r1, #32]                    \n\t"
+#endif
                   "subs    %[count], %[count], #1       \n\t"
                   "bne     1b                           \n\t"
                   "b       4f                           \n\t"
                   "3:                                   \n\t"
                   "subs    %[count], %[count], #1       \n\t"
                   "add     %[dst], %[dst], #2           \n\t"
                   "bne     1b                           \n\t"
                   "4:                                   \n\t"
--- a/js/src/build/cl.py
+++ b/js/src/build/cl.py
@@ -14,33 +14,37 @@ def InvokeClWithDependencyGeneration(cmd
     for arg in cmdline:
         if arg.startswith("-Fo"):
             target = arg[3:]
             break
 
     if target == None:
         print >>sys.stderr, "No target set" and sys.exit(1)
 
+    # Assume the source file is the last argument
+    source = cmdline[-1]
+    assert not source.startswith('-')
+
     # The deps target lives here
     depstarget = os.path.basename(target) + ".pp"
 
     cmdline += ['-showIncludes']
     cl = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
 
-    deps = set()
+    deps = set([os.path.normcase(source).replace(os.sep, '/')])
     for line in cl.stdout:
         # cl -showIncludes prefixes every header with "Note: including file:"
         # and an indentation corresponding to the depth (which we don't need)
         if line.startswith(CL_INCLUDES_PREFIX):
             dep = line[len(CL_INCLUDES_PREFIX):].strip()
             # We can't handle pathes with spaces properly in mddepend.pl, but
             # we can assume that anything in a path with spaces is a system
             # header and throw it away.
-            if dep.find(' ') == -1:
-                deps.add(dep)
+            if ' ' not in dep:
+                deps.add(os.path.normcase(dep).replace(os.sep, '/'))
         else:
             sys.stdout.write(line) # Make sure we preserve the relevant output
                                    # from cl
 
     ret = cl.wait()
     if ret != 0 or target == "":
         sys.exit(ret)
 
@@ -49,15 +53,18 @@ def InvokeClWithDependencyGeneration(cmd
     if not os.path.isdir(depsdir):
         try:
             os.makedirs(depsdir)
         except OSError:
             pass # This suppresses the error we get when the dir exists, at the
                  # cost of masking failure to create the directory.  We'll just
                  # die on the next line though, so it's not that much of a loss.
 
-    f = open(depstarget, "w")
-    for dep in sorted(deps):
-        print >>f, "%s: %s" % (target, dep)
-        print >>f, "%s:" % dep
+    with open(depstarget, "w") as f:
+        f.write("%s: %s" % (target, source))
+        for dep in sorted(deps):
+            f.write(" \\\n%s" % dep)
+        f.write('\n')
+        for dep in sorted(deps):
+            f.write("%s:\n" % dep)
 
 if __name__ == "__main__":
     InvokeClWithDependencyGeneration(sys.argv[1:])
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -923,52 +923,54 @@ js::testingFunc_inParallelSection(JSCont
 {
     // If we were actually *in* a parallel section, then this function
     // would be inlined to TRUE in ion-generated code.
     JS_ASSERT(!InParallelSection());
     JS_SET_RVAL(cx, vp, JSVAL_FALSE);
     return true;
 }
 
-static JSObject *objectMetadataFunction = NULL;
+static const char *ObjectMetadataPropertyName = "__objectMetadataFunction__";
 
 static bool
 ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata)
 {
     Value thisv = UndefinedValue();
 
+    RootedValue fun(cx);
+    if (!JS_GetProperty(cx, cx->global(), ObjectMetadataPropertyName, &fun))
+        return false;
+
     RootedValue rval(cx);
-    if (!Invoke(cx, thisv, ObjectValue(*objectMetadataFunction), 0, NULL, &rval))
+    if (!Invoke(cx, thisv, fun, 0, NULL, &rval))
         return false;
 
     if (rval.isObject())
         *pmetadata = &rval.toObject();
 
     return true;
 }
 
 static bool
 SetObjectMetadataCallback(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     args.rval().setUndefined();
 
     if (argc == 0 || !args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
-        if (objectMetadataFunction)
-            JS_RemoveObjectRoot(cx, &objectMetadataFunction);
-        objectMetadataFunction = NULL;
+        if (!JS_DeleteProperty(cx, cx->global(), ObjectMetadataPropertyName))
+            return false;
         js::SetObjectMetadataCallback(cx, NULL);
         return true;
     }
 
-    if (!objectMetadataFunction && !JS_AddObjectRoot(cx, &objectMetadataFunction))
+    if (!JS_DefineProperty(cx, cx->global(), ObjectMetadataPropertyName, args[0], NULL, NULL, 0))
         return false;
 
-    objectMetadataFunction = &args[0].toObject();
     js::SetObjectMetadataCallback(cx, ShellObjectMetadataCallback);
     return true;
 }
 
 static bool
 SetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/config/baseconfig.mk
+++ b/js/src/config/baseconfig.mk
@@ -21,8 +21,14 @@ ifeq ($(HOST_OS_ARCH),WINNT)
 # actually can be found regardless of path-style.
 ifeq (_:,$(.PYMAKE)_$(findstring :,$(srcdir)))
 $(error Windows-style srcdir being used with GNU make. Did you mean to run $(topsrcdir)/build/pymake/make.py instead? [see-also:     https://developer.mozilla.org/en/Gmake_vs._Pymake])
 endif
 ifeq (1_a,$(.PYMAKE)_$(firstword a$(subst /, ,$(srcdir))))
 $(error MSYS-style srcdir being used with Pymake. Did you mean to run GNU Make instead? [see-also: https://developer.mozilla.org/    en/Gmake_vs._Pymake])
 endif
 endif # WINNT
+
+ifdef .PYMAKE
+include_deps = $(eval -includedeps $(1))
+else
+include_deps = $(eval -include $(1))
+endif
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -1616,35 +1616,27 @@ endif
 #   a previous build in the source tree) and thus neglect to create a
 #   dependency directory in the object directory, where we really need
 #   it.
 
 ifneq (,$(filter-out all chrome default export realchrome tools clean clobber clobber_all distclean realclean,$(MAKECMDGOALS)))
 MDDEPEND_FILES		:= $(strip $(wildcard $(foreach file,$(sort $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS) $(TARGETS)),$(MDDEPDIR)/$(notdir $(file)).pp) $(addprefix $(MDDEPDIR)/,$(EXTRA_MDDEPEND_FILES))))
 
 ifneq (,$(MDDEPEND_FILES))
-ifdef .PYMAKE
-includedeps $(MDDEPEND_FILES)
-else
-include $(MDDEPEND_FILES)
-endif
+$(call include_deps,$(MDDEPEND_FILES))
 endif
 
 endif
 
 
 ifneq (,$(filter export,$(MAKECMDGOALS)))
 MDDEPEND_FILES		:= $(strip $(wildcard $(addprefix $(MDDEPDIR)/,$(EXTRA_EXPORT_MDDEPEND_FILES))))
 
 ifneq (,$(MDDEPEND_FILES))
-ifdef .PYMAKE
-includedeps $(MDDEPEND_FILES)
-else
-include $(MDDEPEND_FILES)
-endif
+$(call include_deps,$(MDDEPEND_FILES))
 endif
 
 endif
 
 #############################################################################
 
 -include $(topsrcdir)/$(MOZ_BUILD_APP)/app-rules.mk
 -include $(MY_RULES)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug906885.js
@@ -0,0 +1,5 @@
+ParallelArray([57], function() {
+    return (Math.max(2207764374, (function() {
+        return 1
+    })()))
+})
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -690,31 +690,31 @@ Range::abs(const Range *op)
                      Max(Abs(l), Abs(u)),
                      op->isDecimal(),
                      op->exponent());
 }
 
 Range *
 Range::min(const Range *lhs, const Range *rhs)
 {
-    // If either operand is NaN (implied by isInfinite here), the result is NaN.
-    if (lhs->isInfinite() || rhs->isInfinite())
+    // If either operand is NaN, the result is NaN.
+    if (!lhs->isInt32() || !rhs->isInt32())
         return new Range();
 
     return new Range(Min(lhs->lower(), rhs->lower()),
                      Min(lhs->upper(), rhs->upper()),
                      lhs->isDecimal() || rhs->isDecimal(),
                      Max(lhs->exponent(), rhs->exponent()));
 }
 
 Range *
 Range::max(const Range *lhs, const Range *rhs)
 {
-    // If either operand is NaN (implied by isInfinite here), the result is NaN.
-    if (lhs->isInfinite() || rhs->isInfinite())
+    // If either operand is NaN, the result is NaN.
+    if (!lhs->isInt32() || !rhs->isInt32())
         return new Range();
 
     return new Range(Max(lhs->lower(), rhs->lower()),
                      Max(lhs->upper(), rhs->upper()),
                      lhs->isDecimal() || rhs->isDecimal(),
                      Max(lhs->exponent(), rhs->exponent()));
 }
 
@@ -1056,17 +1056,17 @@ MMod::computeRange()
 {
     if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
     Range lhs(getOperand(0));
     Range rhs(getOperand(1));
 
     // If either operand is a NaN, the result is NaN. This also conservatively
     // handles Infinity cases.
-    if (lhs.isInfinite() || rhs.isInfinite())
+    if (!lhs.isInt32() || !rhs.isInt32())
         return;
 
     // If RHS can be zero, the result can be NaN.
     if (rhs.lower() <= 0 && rhs.upper() >= 0)
         return;
 
     // Math.abs(lhs % rhs) == Math.abs(lhs) % Math.abs(rhs).
     // First, the absolute value of the result will always be less than the
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -444,21 +444,17 @@ class TypedRegisterSet
     }
     void clear() {
         bits_ = 0;
     }
     uint32_t bits() const {
         return bits_;
     }
     uint32_t size() const {
-        uint32_t sum2  = (bits_ & 0x55555555) + ((bits_ & 0xaaaaaaaa) >> 1);
-        uint32_t sum4  = (sum2  & 0x33333333) + ((sum2  & 0xcccccccc) >> 2);
-        uint32_t sum8  = (sum4  & 0x0f0f0f0f) + ((sum4  & 0xf0f0f0f0) >> 4);
-        uint32_t sum16 = (sum8  & 0x00ff00ff) + ((sum8  & 0xff00ff00) >> 8);
-        return sum16;
+        return mozilla::CountPopulation32(bits_);
     }
     bool operator ==(const TypedRegisterSet<T> &other) const {
         return other.bits_ == bits_;
     }
 };
 
 typedef TypedRegisterSet<Register> GeneralRegisterSet;
 typedef TypedRegisterSet<FloatRegister> FloatRegisterSet;
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -144,17 +144,17 @@ nsLayoutStatics::Initialize()
   nsCSSAnonBoxes::AddRefAtoms();
   nsCSSPseudoClasses::AddRefAtoms();
   nsCSSPseudoElements::AddRefAtoms();
   nsCSSKeywords::AddRefTable();
   nsCSSProps::AddRefTable();
   nsColorNames::AddRefTable();
   nsGkAtoms::AddRefAtoms();
 
-  nsJSRuntime::Startup();
+  StartupJSEnvironment();
   rv = nsRegion::InitStatic();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsRegion");
     return rv;
   }
 
   nsGlobalWindow::Init();
   Navigator::Init();
@@ -329,17 +329,17 @@ nsLayoutStatics::Shutdown()
 
   nsTextFragment::Shutdown();
 
   nsAttrValue::Shutdown();
   nsContentUtils::Shutdown();
   nsLayoutStylesheetCache::Shutdown();
   NS_NameSpaceManagerShutdown();
 
-  nsJSRuntime::Shutdown();
+  ShutdownJSEnvironment();
   nsGlobalWindow::ShutDown();
   nsDOMClassInfo::ShutDown();
   nsListControlFrame::Shutdown();
   nsXBLWindowKeyHandler::ShutDown();
   nsXBLService::Shutdown();
   nsAutoCopyListener::Shutdown();
   FrameLayerBuilder::Shutdown();
 
--- a/layout/generic/nsHTMLReflowState.h
+++ b/layout/generic/nsHTMLReflowState.h
@@ -406,17 +406,17 @@ public:
   // Initialize a <b>root</b> reflow state with a rendering context to
   // use for measuring things.
   nsHTMLReflowState(nsPresContext*           aPresContext,
                     nsIFrame*                aFrame,
                     nsRenderingContext*     aRenderingContext,
                     const nsSize&            aAvailableSpace,
                     uint32_t                 aFlags = 0);
 
-  // Initialize a reflow state for a child frames reflow. Some state
+  // Initialize a reflow state for a child frame's reflow. Some state
   // is copied from the parent reflow state; the remaining state is
   // computed. 
   nsHTMLReflowState(nsPresContext*           aPresContext,
                     const nsHTMLReflowState& aParentReflowState,
                     nsIFrame*                aFrame,
                     const nsSize&            aAvailableSpace,
                     // These two are used by absolute positioning code
                     // to override default containing block w & h:
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -1400,22 +1400,24 @@ static int vcmRxStartICE_m(cc_mcapid_t m
   nsRefPtr<sipcc::RemoteSourceStreamInfo> stream =
     pc.impl()->media()->GetRemoteStream(pc_stream_id);
   if (!stream) {
     // This should never happen
     PR_ASSERT(PR_FALSE);
     return VCM_ERROR;
   }
 
-  mozilla::RefPtr<TransportFlow> rtcp_flow =
-    vcmCreateTransportFlow(pc.impl(), level, true,
-                           fingerprint_alg, fingerprint);
-  if (!rtcp_flow) {
-    CSFLogError( logTag, "Could not create RTCP flow");
-    return VCM_ERROR;
+  mozilla::RefPtr<TransportFlow> rtcp_flow = nullptr;
+  if(!attrs->rtcp_mux) {
+    rtcp_flow = vcmCreateTransportFlow(pc.impl(), level, true,
+                                       fingerprint_alg, fingerprint);
+    if (!rtcp_flow) {
+      CSFLogError( logTag, "Could not create RTCP flow");
+      return VCM_ERROR;
+    }
   }
 
   if (CC_IS_AUDIO(mcap_id)) {
     std::vector<mozilla::AudioCodecConfig *> configs;
 
     // Instantiate an appropriate conduit
     mozilla::RefPtr<mozilla::AudioSessionConduit> tx_conduit =
       pc.impl()->media()->GetConduit(level, false);
@@ -2041,22 +2043,24 @@ static int vcmTxStartICE_m(cc_mcapid_t m
   // Create the transport flows
   mozilla::RefPtr<TransportFlow> rtp_flow =
       vcmCreateTransportFlow(pc.impl(), level, false,
                              fingerprint_alg, fingerprint);
   if (!rtp_flow) {
       CSFLogError( logTag, "Could not create RTP flow");
       return VCM_ERROR;
   }
-  mozilla::RefPtr<TransportFlow> rtcp_flow =
-      vcmCreateTransportFlow(pc.impl(), level, true,
-                             fingerprint_alg, fingerprint);
-  if (!rtcp_flow) {
+  mozilla::RefPtr<TransportFlow> rtcp_flow = nullptr;
+  if(!attrs->rtcp_mux) {
+    rtcp_flow = vcmCreateTransportFlow(pc.impl(), level, true,
+                                       fingerprint_alg, fingerprint);
+    if (!rtcp_flow) {
       CSFLogError( logTag, "Could not create RTCP flow");
       return VCM_ERROR;
+    }
   }
 
   if (CC_IS_AUDIO(mcap_id)) {
     mozilla::AudioCodecConfig *config_raw;
     config_raw = new mozilla::AudioCodecConfig(
       payload->remote_rtp_pt,
       ccsdpCodecName(payload->codec_type),
       payload->audio.frequency,
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -117,16 +117,20 @@ class MediaPipeline : public sigslot::ha
       DetachMediaStream();
     }
   }
 
   virtual nsresult Init();
 
   virtual Direction direction() const { return direction_; }
 
+  bool IsDoingRtcpMux() const {
+    return (rtp_transport_ == rtcp_transport_);
+  }
+
   int rtp_packets_sent() const { return rtp_packets_sent_; }
   int rtcp_packets_sent() const { return rtcp_packets_sent_; }
   int rtp_packets_received() const { return rtp_packets_received_; }
   int rtcp_packets_received() const { return rtcp_packets_received_; }
 
   MediaSessionConduit *Conduit() { return conduit_; }
 
   // Thread counting
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -438,16 +438,30 @@ PeerConnectionMedia::IceFailed(NrIceCtx 
 void
 PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream)
 {
   MOZ_ASSERT(aStream);
 
   CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
 }
 
+// This method exists for the unittests.
+// It allows visibility into the pipelines and flows.
+// It returns NULL if no pipeline exists for this track number.
+mozilla::RefPtr<mozilla::MediaPipeline>
+SourceStreamInfo::GetPipeline(int aTrack) {
+  std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it =
+    mPipelines.find(aTrack);
+
+  if (it == mPipelines.end()) {
+    return NULL;
+  }
+
+  return it->second;
+}
 
 void
 LocalSourceStreamInfo::StorePipeline(int aTrack,
   mozilla::RefPtr<mozilla::MediaPipeline> aPipeline)
 {
   MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end());
   if (mPipelines.find(aTrack) != mPipelines.end()) {
     CSFLogError(logTag, "%s: Storing duplicate track", __FUNCTION__);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -157,27 +157,52 @@ class Fake_VideoGenerator {
  private:
   nsCOMPtr<nsITimer> mTimer;
   nsRefPtr<DOMMediaStream> mStream;
   int mCount;
 };
 #endif
 
 
-// TODO(ekr@rtfm.com): Refactor {Local,Remote}SourceStreamInfo
-// bug 837539.
-class LocalSourceStreamInfo {
+class SourceStreamInfo {
 public:
   typedef mozilla::DOMMediaStream DOMMediaStream;
 
-  LocalSourceStreamInfo(DOMMediaStream* aMediaStream, PeerConnectionMedia *aParent)
-      : mMediaStream(aMediaStream), mParent(aParent) {
-    MOZ_ASSERT(aMediaStream);
+  SourceStreamInfo(DOMMediaStream* aMediaStream,
+                   PeerConnectionMedia *aParent)
+      : mMediaStream(aMediaStream),
+        mParent(aParent) {
+    MOZ_ASSERT(mMediaStream);
+  }
+
+  SourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
+                  PeerConnectionMedia *aParent)
+      : mMediaStream(aMediaStream),
+        mParent(aParent) {
+    MOZ_ASSERT(mMediaStream);
   }
 
+  mozilla::RefPtr<mozilla::MediaPipeline> GetPipeline(int aTrack);
+
+protected:
+  std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> > mPipelines;
+  nsRefPtr<DOMMediaStream> mMediaStream;
+  PeerConnectionMedia *mParent;
+};
+
+// TODO(ekr@rtfm.com): Refactor {Local,Remote}SourceStreamInfo
+// bug 837539.
+class LocalSourceStreamInfo : public SourceStreamInfo {
+public:
+  typedef mozilla::DOMMediaStream DOMMediaStream;
+
+  LocalSourceStreamInfo(DOMMediaStream *aMediaStream,
+                        PeerConnectionMedia *aParent)
+      : SourceStreamInfo(aMediaStream, aParent) {}
+
   ~LocalSourceStreamInfo() {
     mMediaStream = NULL;
   }
 
   DOMMediaStream* GetMediaStream() {
     return mMediaStream;
   }
   void StorePipeline(int aTrack, mozilla::RefPtr<mozilla::MediaPipeline> aPipeline);
@@ -186,54 +211,44 @@ public:
   void ExpectVideo(const mozilla::TrackID);
   unsigned AudioTrackCount();
   unsigned VideoTrackCount();
   void DetachTransport_s();
   void DetachMedia_m();
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo)
 private:
-  std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> > mPipelines;
-  nsRefPtr<DOMMediaStream> mMediaStream;
   nsTArray<mozilla::TrackID> mAudioTracks;
   nsTArray<mozilla::TrackID> mVideoTracks;
-  PeerConnectionMedia *mParent;
 };
 
-class RemoteSourceStreamInfo {
+class RemoteSourceStreamInfo : public SourceStreamInfo {
  public:
   typedef mozilla::DOMMediaStream DOMMediaStream;
 
-RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
-                       PeerConnectionMedia *aParent)
-    : mTrackTypeHints(0),
-      mMediaStream(aMediaStream),
-      mPipelines(),
-      mParent(aParent) {
-      MOZ_ASSERT(mMediaStream);
-    }
+  RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
+                         PeerConnectionMedia *aParent)
+    : SourceStreamInfo(aMediaStream, aParent),
+      mTrackTypeHints(0) {}
 
   DOMMediaStream* GetMediaStream() {
     return mMediaStream;
   }
   void StorePipeline(int aTrack, bool aIsVideo,
                      mozilla::RefPtr<mozilla::MediaPipeline> aPipeline);
 
   void DetachTransport_s();
   void DetachMedia_m();
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteSourceStreamInfo)
 
 public:
   DOMMediaStream::TrackTypeHints mTrackTypeHints;
  private:
-  nsRefPtr<DOMMediaStream> mMediaStream;
-  std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> > mPipelines;
   std::map<int, bool> mTypes;
-  PeerConnectionMedia *mParent;
 };
 
 class PeerConnectionMedia : public sigslot::has_slots<> {
  public:
   PeerConnectionMedia(PeerConnectionImpl *parent);
   ~PeerConnectionMedia() {}
 
   nsresult Init(const std::vector<mozilla::NrIceStunServer>& stun_servers,
--- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
@@ -4729,37 +4729,41 @@ gsmsdp_negotiate_media_lines (fsm_fcb_t 
                  */
                 unsupported_line = TRUE;
                 update_local_ret_value = TRUE;
             }
 
             /*
              * Negotiate rtcp-mux
              */
-
-            sdp_res = sdp_attr_get_rtcp_mux_attribute (sdp_p->dest_sdp, i,
-                                              0, SDP_ATTR_RTCP_MUX, 1, &rtcp_mux);
-
-            if (SDP_SUCCESS == sdp_res) {
-            	media->rtcp_mux = TRUE;
+            if(SDP_MEDIA_APPLICATION != media_type) {
+              sdp_res = sdp_attr_get_rtcp_mux_attribute(sdp_p->dest_sdp, i,
+                                                        0, SDP_ATTR_RTCP_MUX,
+                                                        1, &rtcp_mux);
+
+              if (SDP_SUCCESS == sdp_res) {
+                media->rtcp_mux = TRUE;
+              }
             }
 
             if (!unsupported_line) {
 
               if (sdpmode) {
                   int j;
 
                   /* Set ICE */
                   for (j=0; j<media->candidate_ct; j++) {
                     gsmsdp_set_ice_attribute (SDP_ATTR_ICE_CANDIDATE, media->level,
                                               sdp_p->src_sdp, media->candidatesp[j]);
                   }
 
+                  /* Set RTCPMux if we have it turned on in our config
+                     and the other side requests it */
                   config_get_value(CFGID_RTCPMUX, &rtcpmux, sizeof(rtcpmux));
-                  if (rtcpmux) {
+                  if (rtcpmux && media->rtcp_mux) {
                     gsmsdp_set_rtcp_mux_attribute (SDP_ATTR_RTCP_MUX, media->level,
                                                    sdp_p->src_sdp, TRUE);
                   }
 
                   if (notify_stream_added) {
                       /*
                        * Add track to remote streams in dcb
                        */
@@ -5270,17 +5274,17 @@ gsmsdp_add_media_line (fsmdef_dcb_t *dcb
           /*
            * wait until here to set ICE candidates as SDP is now initialized
            */
           for (i=0; i<media->candidate_ct; i++) {
             gsmsdp_set_ice_attribute (SDP_ATTR_ICE_CANDIDATE, level, dcb_p->sdp->src_sdp, media->candidatesp[i]);
           }
 
           config_get_value(CFGID_RTCPMUX, &rtcpmux, sizeof(rtcpmux));
-          if (rtcpmux) {
+          if (SDP_MEDIA_APPLICATION != media_cap->type && rtcpmux) {
             gsmsdp_set_rtcp_mux_attribute (SDP_ATTR_RTCP_MUX, level, dcb_p->sdp->src_sdp, TRUE);
           }
 
 
           /*
            * Since we are initiating an initial offer and opening a
            * receive port, store initial media settings.
            */
--- a/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
@@ -979,16 +979,17 @@ lsm_rx_start (lsm_lcb_t *lcb, const char
                 media->rcv_chan = TRUE; /* recevied channel is created */
                 /* save the source RX port */
                 if (media->is_multicast) {
                     media->multicast_port = open_rcv.port;
                 } else {
                     media->src_port = open_rcv.port;
                 }
 
+                attrs.rtcp_mux = media->rtcp_mux;
                 if ( media->cap_index == CC_VIDEO_1 ) {
                     attrs.video.opaque = media->video;
                 } else {
                     attrs.audio.packetization_period = media->packetization_period;
                     attrs.audio.max_packetization_period = media->max_packetization_period;
                     attrs.audio.avt_payload_type = media->avt_payload_type;
                     attrs.audio.mixing_mode = mix_mode;
                     attrs.audio.mixing_party = mix_party;
@@ -1215,16 +1216,18 @@ lsm_tx_start (lsm_lcb_t *lcb, const char
                           DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname);
         			continue;
             	}
         	}
 
             media->xmit_chan = TRUE;
 
             attrs.mute = FALSE;
+
+            attrs.rtcp_mux = media->rtcp_mux;
             if ( CC_IS_VIDEO(media->cap_index)) {
                 attrs.video.opaque = media->video;
                 if (lcb->vid_mute) {
                     attrs.mute = TRUE;
                 }
 
             } else if ( CC_IS_AUDIO(media->cap_index)){
                 attrs.audio.packetization_period = media->packetization_period;
--- a/media/webrtc/signaling/src/sipcc/core/includes/config.h
+++ b/media/webrtc/signaling/src/sipcc/core/includes/config.h
@@ -174,17 +174,17 @@ static const unsigned char gCallWaiting 
 static const int gDeviceSecurityMode = 1;
 static const int gCcm2_sip_port = 5060;
 static const int gCcm3_sip_port = 5060;
 static const boolean gCcm1_isvalid = TRUE;
 static const int gDscpCallControl = 1;
 static const int gSpeakerEnabled = 1;
 static const char gExternalNumberMask[] = "";
 static const char gVersion[] = "0.1";
-static const boolean gRTCPMUX = FALSE;
+static const boolean gRTCPMUX = TRUE;
 static boolean gRTPSAVPF = TRUE;           /* TRUE = RTP/SAVPF , FALSE = RTP/SAVP */
 static const boolean gMAXAVBITRATE = FALSE;      /* Following six are OPUS fmtp options */
 static const boolean gMAXCODEDAUDIOBW = FALSE;
 static const boolean gUSEDTX = FALSE;
 static const boolean gSTEREO = FALSE;
 static const boolean gUSEINBANDFEC = FALSE;
 static const boolean gCBR = FALSE;
 static const boolean gMAXPTIME = FALSE;
--- a/media/webrtc/signaling/src/sipcc/include/vcm.h
+++ b/media/webrtc/signaling/src/sipcc/include/vcm.h
@@ -326,16 +326,17 @@ typedef struct vcm_audioAttrs_t_ {
 
 /**
  *  vcm_mediaAttrs_t
  *  A structure to contain audio or video media attributes
  */
 typedef struct vcm_attrs_t_ {
   cc_boolean         mute;
   cc_boolean         is_video;
+  cc_boolean         rtcp_mux;
   vcm_audioAttrs_t audio; /**< audio line attribs */
   vcm_videoAttrs_t video; /**< Video Atrribs */
 } vcm_mediaAttrs_t;
 
 //Using C++ for gips. This is required for gips.
 #ifdef __cplusplus
 extern "C" {
 #endif
--- a/media/webrtc/signaling/test/signaling_unittests.cpp
+++ b/media/webrtc/signaling/test/signaling_unittests.cpp
@@ -132,16 +132,25 @@ enum offerAnswerFlags
   ANSWER_NONE  = 0, // Sugar to make function calls clearer.
   ANSWER_AUDIO = (1<<8),
   ANSWER_VIDEO = (1<<9),
 
   OFFER_AV = OFFER_AUDIO | OFFER_VIDEO,
   ANSWER_AV = ANSWER_AUDIO | ANSWER_VIDEO
 };
 
+enum mediaPipelineFlags
+{
+  PIPELINE_LOCAL = (1<<0),
+  PIPELINE_RTCP_MUX = (1<<1),
+  PIPELINE_SEND = (1<<2),
+  PIPELINE_VIDEO = (1<<3)
+};
+
+
 static bool SetupGlobalThread() {
   if (!gThread) {
     nsIThread *thread;
 
     nsresult rv = NS_NewNamedThread("pseudo-main",&thread);
     if (NS_FAILED(rv))
       return false;
 
@@ -450,25 +459,35 @@ class ParsedSDP {
     ice_candidates_(),
     levels_(0),
     num_lines(0)
   {
     sdp_ = sdp;
     Parse();
   }
 
+  void DeleteLine(std::string objType)
+  {
+    ReplaceLine(objType, "");
+  }
 
+  // Replaces the first instance of objType in the SDP with
+  // a new string.
+  // If content is an empty string then the line will be removed
   void ReplaceLine(std::string objType, std::string content)
   {
     std::multimap<std::string, SdpLine>::iterator it;
     it = sdp_map_.find(objType);
     if(it != sdp_map_.end()) {
       SdpLine sdp_line_pair = (*it).second;
       int line_no = sdp_line_pair.first;
       sdp_map_.erase(it);
+      if(content.empty()) {
+        return;
+      }
       std::string value = content.substr(objType.length());
       sdp_map_.insert(std::pair<std::string, SdpLine>(objType,
         std::make_pair(line_no,value)));
     }
   }
 
   void AddLine(std::string content)
   {
@@ -747,17 +766,17 @@ class SignalingAgent {
   }
 
 void CreateAnswer(sipcc::MediaConstraints& constraints, std::string offer,
                     uint32_t offerAnswerFlags,
                     uint32_t sdpCheck = DONT_CHECK_AUDIO|
                                         DONT_CHECK_VIDEO|
                                         DONT_CHECK_DATA,
                     sipcc::PeerConnectionImpl::SignalingState endState =
-                      sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer) {
+                    sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer) {
 
     uint32_t aHintContents = 0;
     if (offerAnswerFlags & ANSWER_AUDIO) {
       aHintContents |= DOMMediaStream::HINT_CONTENTS_AUDIO;
     }
     if (offerAnswerFlags & ANSWER_VIDEO) {
       aHintContents |= DOMMediaStream::HINT_CONTENTS_VIDEO;
     }
@@ -930,16 +949,52 @@ void CreateAnswer(sipcc::MediaConstraint
   void CloseReceiveStreams() {
     std::vector<DOMMediaStream *> streams =
                             pObserver->GetStreams();
     for (size_t i = 0; i < streams.size(); i++) {
       streams[i]->GetStream()->AsSourceStream()->StopStream();
     }
   }
 
+  mozilla::RefPtr<mozilla::MediaPipeline> GetMediaPipeline(
+    bool local, int stream, int track) {
+    sipcc::SourceStreamInfo *streamInfo;
+
+    if (local) {
+      streamInfo = pc->media()->GetLocalStream(stream);
+    } else {
+      streamInfo = pc->media()->GetRemoteStream(stream);
+    }
+
+    if (!streamInfo) {
+      return nullptr;
+    }
+
+    return streamInfo->GetPipeline(track);
+  }
+
+
+  void CheckMediaPipeline(int stream, int track, uint32_t flags) {
+    mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
+      GetMediaPipeline((flags & PIPELINE_LOCAL), stream, track);
+    ASSERT_TRUE(pipeline);
+    ASSERT_EQ(pipeline->IsDoingRtcpMux(), !!(flags & PIPELINE_RTCP_MUX));
+    // We cannot yet test send/recv with video.
+    if (!(flags & PIPELINE_VIDEO)) {
+      if (flags & PIPELINE_SEND) {
+        ASSERT_GE(pipeline->rtp_packets_sent(), 40);
+        ASSERT_GE(pipeline->rtcp_packets_received(), 1);
+      } else {
+        ASSERT_GE(pipeline->rtp_packets_received(), 40);
+        ASSERT_GE(pipeline->rtcp_packets_sent(), 1);
+      }
+    }
+  }
+
+
 public:
   mozilla::RefPtr<sipcc::PeerConnectionImpl> pc;
   nsRefPtr<TestObserver> pObserver;
   char* offer_;
   char* answer_;
   nsRefPtr<DOMMediaStream> domMediaStream_;
   sipcc::IceConfiguration cfg_;
   const std::string name;
@@ -1619,16 +1674,25 @@ TEST_F(SignalingTest, FullCall)
 
   a1_.CloseSendStreams();
   a2_.CloseReceiveStreams();
   // Check that we wrote a bunch of data
   ASSERT_GE(a1_.GetPacketsSent(0), 40);
   //ASSERT_GE(a2_.GetPacketsSent(0), 40);
   //ASSERT_GE(a1_.GetPacketsReceived(0), 40);
   ASSERT_GE(a2_.GetPacketsReceived(0), 40);
+
+  // Check the low-level media pipeline
+  // for RTP and RTCP flows
+  // The first Local pipeline gets stored at 0
+  a1_.CheckMediaPipeline(0, 0,
+    PIPELINE_LOCAL | PIPELINE_RTCP_MUX | PIPELINE_SEND);
+
+  // The first Remote pipeline gets stored at 1
+  a2_.CheckMediaPipeline(0, 1, PIPELINE_RTCP_MUX);
 }
 
 TEST_F(SignalingTest, FullCallAudioOnly)
 {
   sipcc::MediaConstraints constraints;
   OfferAnswer(constraints, constraints, OFFER_AUDIO | ANSWER_AUDIO,
               true, SHOULD_SENDRECV_AUDIO, SHOULD_SENDRECV_AUDIO);
 
@@ -2279,21 +2343,109 @@ TEST_F(SignalingTest, missingUfrag)
       "streams=16\r\n"
     "a=sendrecv\r\n";
 
   // Need to create an offer, since that's currently required by our
   // FSM. This may change in the future.
   a1_.CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
   a1_.SetLocal(TestObserver::OFFER, offer, true);
   // We now detect the missing ICE parameters at SetRemoteDescription
-  a2_.SetRemote(TestObserver::OFFER, offer, true, 
+  a2_.SetRemote(TestObserver::OFFER, offer, true,
     sipcc::PeerConnectionImpl::kSignalingStable);
   ASSERT_TRUE(a2_.pObserver->state == TestObserver::stateError);
 }
 
+TEST_F(SignalingTest, AudioOnlyCalleeNoRtcpMux)
+{
+  sipcc::MediaConstraints constraints;
+
+  a1_.CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO);
+  a1_.SetLocal(TestObserver::OFFER, a1_.offer(), false);
+  ParsedSDP sdpWrapper(a1_.offer());
+  sdpWrapper.DeleteLine("a=rtcp-mux");
+  std::cout << "Modified SDP " << std::endl
+            << indent(sdpWrapper.getSdp()) << std::endl;
+  a2_.SetRemote(TestObserver::OFFER, sdpWrapper.getSdp(), false);
+  a2_.CreateAnswer(constraints, sdpWrapper.getSdp(),
+    OFFER_AUDIO | ANSWER_AUDIO);
+  a2_.SetLocal(TestObserver::ANSWER, a2_.answer(), false);
+  a1_.SetRemote(TestObserver::ANSWER, a2_.answer(), false);
+
+  // Answer should not have a=rtcp-mux
+  ASSERT_EQ(a2_.getLocalDescription().find("\r\na=rtcp-mux"),
+            std::string::npos);
+
+  ASSERT_TRUE_WAIT(a1_.IceCompleted() == true, kDefaultTimeout);
+  ASSERT_TRUE_WAIT(a2_.IceCompleted() == true, kDefaultTimeout);
+
+  PR_Sleep(kDefaultTimeout * 2); // Wait for some data to get written
+
+  a1_.CloseSendStreams();
+  a2_.CloseReceiveStreams();
+
+  ASSERT_GE(a1_.GetPacketsSent(0), 40);
+  ASSERT_GE(a2_.GetPacketsReceived(0), 40);
+
+  // Check the low-level media pipeline
+  // for RTP and RTCP flows
+  // The first Local pipeline gets stored at 0
+  a1_.CheckMediaPipeline(0, 0, PIPELINE_LOCAL | PIPELINE_SEND);
+
+  // The first Remote pipeline gets stored at 1
+  a2_.CheckMediaPipeline(0, 1, 0);
+}
+
+TEST_F(SignalingTest, FullCallAudioNoMuxVideoMux)
+{
+  sipcc::MediaConstraints constraints;
+
+  a1_.CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
+  a1_.SetLocal(TestObserver::OFFER, a1_.offer(), false);
+  ParsedSDP sdpWrapper(a1_.offer());
+  sdpWrapper.DeleteLine("a=rtcp-mux");
+  std::cout << "Modified SDP " << std::endl
+            << indent(sdpWrapper.getSdp()) << std::endl;
+  a2_.SetRemote(TestObserver::OFFER, sdpWrapper.getSdp(), false);
+  a2_.CreateAnswer(constraints, sdpWrapper.getSdp(), OFFER_AV | ANSWER_AV);
+  a2_.SetLocal(TestObserver::ANSWER, a2_.answer(), false);
+  a1_.SetRemote(TestObserver::ANSWER, a2_.answer(), false);
+
+  // Answer should have only one a=rtcp-mux line
+  size_t match = a2_.getLocalDescription().find("\r\na=rtcp-mux");
+  ASSERT_NE(match, std::string::npos);
+  match = a2_.getLocalDescription().find("\r\na=rtcp-mux", match + 1);
+  ASSERT_EQ(match, std::string::npos);
+
+  ASSERT_TRUE_WAIT(a1_.IceCompleted() == true, kDefaultTimeout);
+  ASSERT_TRUE_WAIT(a2_.IceCompleted() == true, kDefaultTimeout);
+
+  PR_Sleep(kDefaultTimeout * 2); // Wait for some data to get written
+
+  a1_.CloseSendStreams();
+  a2_.CloseReceiveStreams();
+
+  ASSERT_GE(a1_.GetPacketsSent(0), 40);
+  ASSERT_GE(a2_.GetPacketsReceived(0), 40);
+
+  // Check the low-level media pipeline
+  // for RTP and RTCP flows
+  // The first Local pipeline gets stored at 0
+  a1_.CheckMediaPipeline(0, 0, PIPELINE_LOCAL | PIPELINE_SEND);
+
+  // Now check video mux.
+  a1_.CheckMediaPipeline(0, 1,
+    PIPELINE_LOCAL | PIPELINE_RTCP_MUX | PIPELINE_SEND | PIPELINE_VIDEO);
+
+  // The first Remote pipeline gets stored at 1
+  a2_.CheckMediaPipeline(0, 1, 0);
+
+  // Now check video mux.
+  a2_.CheckMediaPipeline(0, 2, PIPELINE_RTCP_MUX | PIPELINE_VIDEO);
+}
+
 } // End namespace test.
 
 bool is_color_terminal(const char *terminal) {
   if (!terminal) {
     return false;
   }
   const char *color_terms[] = {
     "xterm",
--- a/mfbt/MathAlgorithms.h
+++ b/mfbt/MathAlgorithms.h
@@ -182,16 +182,26 @@ namespace detail {
   CountTrailingZeroes32(uint32_t u)
   {
     unsigned long index;
     _BitScanForward(&index, static_cast<unsigned long>(u));
     return uint_fast8_t(index);
   }
 
   inline uint_fast8_t
+  CountPopulation32(uint32_t u)
+  {
+     uint32_t sum2  = (u     & 0x55555555) + ((u     & 0xaaaaaaaa) >> 1);
+     uint32_t sum4  = (sum2  & 0x33333333) + ((sum2  & 0xcccccccc) >> 2);
+     uint32_t sum8  = (sum4  & 0x0f0f0f0f) + ((sum4  & 0xf0f0f0f0) >> 4);
+     uint32_t sum16 = (sum8  & 0x00ff00ff) + ((sum8  & 0xff00ff00) >> 8);
+     return sum16;
+  }
+
+  inline uint_fast8_t
   CountLeadingZeroes64(uint64_t u)
   {
 #  if defined(MOZ_BITSCAN_WINDOWS64)
     unsigned long index;
     _BitScanReverse64(&index, static_cast<unsigned __int64>(u));
     return uint_fast8_t(63 - index);
 #  else
     uint32_t hi = uint32_t(u >> 32);
@@ -238,31 +248,38 @@ namespace detail {
 
   inline uint_fast8_t
   CountTrailingZeroes32(uint32_t u)
   {
     return __builtin_ctz(u);
   }
 
   inline uint_fast8_t
+  CountPopulation32(uint32_t u)
+  {
+    return __builtin_popcount(u);
+  }
+
+  inline uint_fast8_t
   CountLeadingZeroes64(uint64_t u)
   {
     return __builtin_clzll(u);
   }
 
   inline uint_fast8_t
   CountTrailingZeroes64(uint64_t u)
   {
     return __builtin_ctzll(u);
   }
 
 #else
 #  error "Implement these!"
   inline uint_fast8_t CountLeadingZeroes32(uint32_t u) MOZ_DELETE;
   inline uint_fast8_t CountTrailingZeroes32(uint32_t u) MOZ_DELETE;
+  inline uint_fast8_t CountPopulation32(uint32_t u) MOZ_DELETE;
   inline uint_fast8_t CountLeadingZeroes64(uint64_t u) MOZ_DELETE;
   inline uint_fast8_t CountTrailingZeroes64(uint64_t u) MOZ_DELETE;
 #endif
 
 } // namespace detail
 
 /**
  * Compute the number of high-order zero bits in the NON-ZERO number |u|.  That
@@ -295,16 +312,25 @@ CountLeadingZeroes32(uint32_t u)
  */
 inline uint_fast8_t
 CountTrailingZeroes32(uint32_t u)
 {
   MOZ_ASSERT(u != 0);
   return detail::CountTrailingZeroes32(u);
 }
 
+/**
+ * Compute the number of one bits in the number |u|,
+ */
+inline uint_fast8_t
+CountPopulation32(uint32_t u)
+{
+  return detail::CountPopulation32(u);
+}
+
 /** Analogous to CountLeadingZeroes32, but for 64-bit numbers. */
 inline uint_fast8_t
 CountLeadingZeroes64(uint64_t u)
 {
   MOZ_ASSERT(u != 0);
   return detail::CountLeadingZeroes64(u);
 }
 
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestCountPopulation.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; 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/. */
+
+#include "mozilla/MathAlgorithms.h"
+
+using mozilla::CountPopulation32;
+
+static void
+TestCountPopulation32()
+{
+  MOZ_ASSERT(CountPopulation32(0xFFFFFFFF) == 32);
+  MOZ_ASSERT(CountPopulation32(0xF0FF1000) == 13);
+  MOZ_ASSERT(CountPopulation32(0x7F8F0001) == 13);
+  MOZ_ASSERT(CountPopulation32(0x3FFF0100) == 15);
+  MOZ_ASSERT(CountPopulation32(0x1FF50010) == 12);
+  MOZ_ASSERT(CountPopulation32(0x00800000) == 1);
+  MOZ_ASSERT(CountPopulation32(0x00400000) == 1);
+  MOZ_ASSERT(CountPopulation32(0x00008000) == 1);
+  MOZ_ASSERT(CountPopulation32(0x00004000) == 1);
+  MOZ_ASSERT(CountPopulation32(0x00000080) == 1);
+  MOZ_ASSERT(CountPopulation32(0x00000040) == 1);
+  MOZ_ASSERT(CountPopulation32(0x00000001) == 1);
+  MOZ_ASSERT(CountPopulation32(0x00000000) == 0);
+}
+
+int main()
+{
+  TestCountPopulation32();
+  return 0;
+}
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -1327,17 +1327,17 @@ abstract public class BrowserApp extends
     private void openReadingList() {
         Tabs.getInstance().loadUrl(ABOUT_HOME, Tabs.LOADURL_READING_LIST);
     }
 
     /* Favicon methods */
     private void loadFavicon(final Tab tab) {
         maybeCancelFaviconLoad(tab);
 
-        int flags = Favicons.FLAG_SCALE | (tab.isPrivate() ? 0 : Favicons.FLAG_PERSIST);
+        int flags = Favicons.FLAG_SCALE | ( (tab.isPrivate() || tab.getErrorType() != Tab.ErrorType.NONE) ? 0 : Favicons.FLAG_PERSIST);
         long id = Favicons.getInstance().loadFavicon(tab.getURL(), tab.getFaviconURL(), flags,
                         new Favicons.OnFaviconLoadedListener() {
 
             @Override
             public void onFaviconLoaded(String pageUrl, Bitmap favicon) {
                 // Leave favicon UI untouched if we failed to load the image
                 // for some reason.
                 if (favicon == null)
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -162,16 +162,17 @@ public class BrowserToolbar extends Geck
     private PropertyAnimator mVisibilityAnimator;
     private static final Interpolator sButtonsInterpolator = new AccelerateInterpolator();
 
     private static final int TABS_CONTRACTED = 1;
     private static final int TABS_EXPANDED = 2;
 
     private static final int FORWARD_ANIMATION_DURATION = 450;
     private final ForegroundColorSpan mUrlColor;
+    private final ForegroundColorSpan mBlockedColor;
     private final ForegroundColorSpan mDomainColor;
     private final ForegroundColorSpan mPrivateDomainColor;
 
     private boolean mShowUrl;
 
     private Integer mPrefObserverId;
 
     public BrowserToolbar(Context context) {
@@ -219,16 +220,17 @@ public class BrowserToolbar extends Geck
                 // We want to be notified of changes to be able to switch mode
                 // without restarting.
                 return true;
             }
         });
 
         Resources res = getResources();
         mUrlColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_urltext));
+        mBlockedColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_blockedtext));
         mDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext));
         mPrivateDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext_private));
 
         registerEventListener("Reader:Click");
         registerEventListener("Reader:LongClick");
 
         mShowSiteSecurity = false;
         mShowReader = false;
@@ -664,16 +666,21 @@ public class BrowserToolbar extends Geck
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     updateBackButton(tab.canDoBack());
                     updateForwardButton(tab.canDoForward());
                     setProgressVisibility(false);
                     // Reset the title in case we haven't navigated to a new page yet.
                     updateTitle();
                 }
                 break;
+            case LOADED:
+                if (Tabs.getInstance().isSelectedTab(tab)) {
+                    updateTitle();
+                }
+                break;
             case RESTORED:
                 // TabCount fixup after OOM
             case SELECTED:
                 updateTabCount(Tabs.getInstance().getDisplayCount());
                 mSwitchingTabs = true;
                 // fall through
             case LOCATION_CHANGE:
             case LOAD_ERROR:
@@ -1081,16 +1088,25 @@ public class BrowserToolbar extends Geck
         }
 
         // Setting a null title will ensure we just see the "Enter Search or Address" placeholder text.
         if ("about:home".equals(url) || "about:privatebrowsing".equals(url)) {
             setTitle(null);
             return;
         }
 
+        // Show the about:blocked page title in red, regardless of prefs
+        if (tab.getErrorType() == Tab.ErrorType.BLOCKED) {
+            String title = tab.getDisplayTitle();
+            SpannableStringBuilder builder = new SpannableStringBuilder(title);
+            builder.setSpan(mBlockedColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+            setTitle(builder);
+            return;
+        }
+
         // If the pref to show the URL isn't set, just use the tab's display title.
         if (!mShowUrl || url == null) {
             setTitle(tab.getDisplayTitle());
             return;
         }
 
         url = StringUtils.stripScheme(url);
         CharSequence title = StringUtils.stripCommonSubdomains(url);
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -648,16 +648,17 @@ RES_DRAWABLE_MDPI = \
   res/drawable-mdpi/desktop_notification.png \
   res/drawable-mdpi/home_tab_menu_strip.9.png \
   res/drawable-mdpi/ic_menu_addons_filler.png \
   res/drawable-mdpi/ic_menu_bookmark_add.png \
   res/drawable-mdpi/ic_menu_bookmark_remove.png \
   res/drawable-mdpi/ic_menu_character_encoding.png \
   res/drawable-mdpi/ic_menu_close_all_tabs.png \
   res/drawable-mdpi/ic_menu_forward.png \
+  res/drawable-mdpi/ic_menu_guest.png \
   res/drawable-mdpi/ic_menu_new_private_tab.png \
   res/drawable-mdpi/ic_menu_new_tab.png \
   res/drawable-mdpi/ic_menu_reload.png \
   res/drawable-mdpi/ic_status_logo.png \
   res/drawable-mdpi/ic_url_bar_go.png \
   res/drawable-mdpi/ic_url_bar_reader.png \
   res/drawable-mdpi/ic_url_bar_search.png \
   res/drawable-mdpi/ic_url_bar_star.png \
@@ -760,16 +761,17 @@ RES_DRAWABLE_HDPI = \
   res/drawable-hdpi/arrow_popup_bg.9.png \
   res/drawable-hdpi/home_tab_menu_strip.9.png \
   res/drawable-hdpi/ic_menu_addons_filler.png \
   res/drawable-hdpi/ic_menu_bookmark_add.png \
   res/drawable-hdpi/ic_menu_bookmark_remove.png \
   res/drawable-hdpi/ic_menu_character_encoding.png \
   res/drawable-hdpi/ic_menu_close_all_tabs.png \
   res/drawable-hdpi/ic_menu_forward.png \
+  res/drawable-hdpi/ic_menu_guest.png \
   res/drawable-hdpi/ic_menu_new_private_tab.png \
   res/drawable-hdpi/ic_menu_new_tab.png \
   res/drawable-hdpi/ic_menu_reload.png \
   res/drawable-hdpi/ic_status_logo.png \
   res/drawable-hdpi/ic_url_bar_go.png \
   res/drawable-hdpi/ic_url_bar_reader.png \
   res/drawable-hdpi/ic_url_bar_search.png \
   res/drawable-hdpi/ic_url_bar_star.png \
@@ -859,16 +861,17 @@ RES_DRAWABLE_XHDPI = \
   res/drawable-xhdpi/arrow_popup_bg.9.png \
   res/drawable-xhdpi/home_tab_menu_strip.9.png \
   res/drawable-xhdpi/ic_menu_addons_filler.png \
   res/drawable-xhdpi/ic_menu_bookmark_add.png \
   res/drawable-xhdpi/ic_menu_bookmark_remove.png \
   res/drawable-xhdpi/ic_menu_close_all_tabs.png \
   res/drawable-xhdpi/ic_menu_character_encoding.png \
   res/drawable-xhdpi/ic_menu_forward.png \
+  res/drawable-xhdpi/ic_menu_guest.png \
   res/drawable-xhdpi/ic_menu_new_private_tab.png \
   res/drawable-xhdpi/ic_menu_new_tab.png \
   res/drawable-xhdpi/ic_menu_reload.png \
   res/drawable-xhdpi/ic_status_logo.png \
   res/drawable-xhdpi/ic_url_bar_go.png \
   res/drawable-xhdpi/ic_url_bar_reader.png \
   res/drawable-xhdpi/ic_url_bar_search.png \
   res/drawable-xhdpi/ic_url_bar_star.png \
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -50,38 +50,45 @@ public class Tab {
     private int mHistoryIndex;
     private int mHistorySize;
     private int mParentId;
     private HomePager.Page mAboutHomePage;
     private boolean mExternal;
     private boolean mBookmark;
     private boolean mReadingListItem;
     private long mFaviconLoadId;
-    private String mDocumentURI;
     private String mContentType;
     private boolean mHasTouchListeners;
     private ZoomConstraints mZoomConstraints;
     private boolean mIsRTL;
     private ArrayList<View> mPluginViews;
     private HashMap<Object, Layer> mPluginLayers;
     private int mBackgroundColor;
     private int mState;
     private Bitmap mThumbnailBitmap;
     private boolean mDesktopMode;
     private boolean mEnteringReaderMode;
     private Context mAppContext;
+    private ErrorType mErrorType = ErrorType.NONE;
     private static final int MAX_HISTORY_LIST_SIZE = 50;
 
     public static final int STATE_DELAYED = 0;
     public static final int STATE_LOADING = 1;
     public static final int STATE_SUCCESS = 2;
     public static final int STATE_ERROR = 3;
 
     private static final int DEFAULT_BACKGROUND_COLOR = Color.WHITE;
 
+    public enum ErrorType {
+        CERT_ERROR,  // Pages with certificate problems
+        BLOCKED,     // Pages blocked for phishing or malware warnings
+        NET_ERROR,   // All other types of error
+        NONE         // Non error pages
+    }
+
     public Tab(Context context, int id, String url, boolean external, int parentId, String title) {
         mAppContext = context.getApplicationContext();
         mId = id;
         mLastUsed = 0;
         mUrl = url;
         mBaseDomain = "";
         mUserSearch = "";
         mExternal = external;
@@ -96,17 +103,16 @@ public class Tab {
         mReaderEnabled = false;
         mEnteringReaderMode = false;
         mThumbnail = null;
         mHistoryIndex = -1;
         mHistorySize = 0;
         mBookmark = false;