Merge central into inbound
authorEhsan Akhgari <ehsan@mozilla.com>
Thu, 14 May 2015 14:16:51 -0400
changeset 276182 2ef80cf97822b7b4e475da1536b282971029c97a
parent 276071 0f1d42a6745a82ef4118c23b2913637e6f92c26c (current diff)
parent 276181 bc345aaf572558b7288d5fd97ee2d49ee7a96dc1 (diff)
child 276183 ec7cf8914e8e26b13c9672e0b9d629a583bf2994
push id897
push userjlund@mozilla.com
push dateMon, 14 Sep 2015 18:56:12 +0000
treeherdermozilla-release@9411e2d2b214 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone41.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 central into inbound
browser/app/profile/firefox.js
dom/workers/test/test_websocket_pref.html
image/decoders/icon/qt/public/moz.build
image/decoders/icon/qt/public/nsGtkQtIconsConverter.idl
image/src/BMPFileHeaders.h
image/src/ClippedImage.cpp
image/src/ClippedImage.h
image/src/DecodePool.cpp
image/src/DecodePool.h
image/src/Decoder.cpp
image/src/Decoder.h
image/src/Downscaler.cpp
image/src/Downscaler.h
image/src/DynamicImage.cpp
image/src/DynamicImage.h
image/src/FrameAnimator.cpp
image/src/FrameAnimator.h
image/src/FrozenImage.cpp
image/src/FrozenImage.h
image/src/ICOFileHeaders.h
image/src/IProgressObserver.h
image/src/Image.cpp
image/src/Image.h
image/src/ImageFactory.cpp
image/src/ImageFactory.h
image/src/ImageMetadata.cpp
image/src/ImageMetadata.h
image/src/ImageOps.cpp
image/src/ImageOps.h
image/src/ImageRegion.h
image/src/ImageURL.h
image/src/ImageWrapper.cpp
image/src/ImageWrapper.h
image/src/MultipartImage.cpp
image/src/MultipartImage.h
image/src/Orientation.h
image/src/OrientedImage.cpp
image/src/OrientedImage.h
image/src/ProgressTracker.cpp
image/src/ProgressTracker.h
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/SVGDocumentWrapper.cpp
image/src/SVGDocumentWrapper.h
image/src/ScriptedNotificationObserver.cpp
image/src/ScriptedNotificationObserver.h
image/src/ShutdownTracker.cpp
image/src/ShutdownTracker.h
image/src/SourceBuffer.cpp
image/src/SourceBuffer.h
image/src/SurfaceCache.cpp
image/src/SurfaceCache.h
image/src/VectorImage.cpp
image/src/VectorImage.h
image/src/imgFrame.cpp
image/src/imgFrame.h
image/src/imgLoader.cpp
image/src/imgLoader.h
image/src/imgRequest.cpp
image/src/imgRequest.h
image/src/imgRequestProxy.cpp
image/src/imgRequestProxy.h
image/src/imgTools.cpp
image/src/imgTools.h
image/src/moz.build
js/src/tests/ecma/Date/15.9.5.2-2-n.js
js/src/tests/ecma/extensions/15.9.5.js
js/src/tests/ecma_2/Exceptions/date-001.js
js/src/tests/js1_4/Regress/date-001-n.js
mobile/android/app/mobile.js
testing/web-platform/meta/websockets/interfaces/CloseEvent/historical.html.ini
--- a/accessible/base/MarkupMap.h
+++ b/accessible/base/MarkupMap.h
@@ -98,16 +98,20 @@ MARKUPMAP(label,
 MARKUPMAP(legend,
           New_HTMLLegend,
           roles::LABEL)
 
 MARKUPMAP(li,
           New_HTMLListitem,
           0)
 
+MARKUPMAP(map,
+          nullptr,
+          roles::TEXT_CONTAINER)
+
 MARKUPMAP(math,
           New_HyperText,
           roles::MATHML_MATH)
 
 MARKUPMAP(mi_,
           New_HyperText,
           roles::MATHML_IDENTIFIER)
 
@@ -299,16 +303,20 @@ MARKUPMAP(optgroup,
           New_HTMLOptgroup,
           0)
 
 MARKUPMAP(output,
           New_HTMLOutput,
           roles::SECTION,
           Attr(live, polite))
 
+MARKUPMAP(p,
+          nullptr,
+          roles::PARAGRAPH)
+
 MARKUPMAP(progress,
           New_HTMLProgress,
           0)
 
 MARKUPMAP(q,
           New_HyperText,
           0)
 
--- a/accessible/base/Role.h
+++ b/accessible/base/Role.h
@@ -976,17 +976,23 @@ enum Role {
    */
   MATHML_STACK_LINE = 167,
 
   /**
    * A group containing radio buttons
    */
   RADIO_GROUP = 168,
 
-  LAST_ROLE = RADIO_GROUP
+  /**
+   * A text container exposing brief amount of information. See related
+   * TEXT_CONTAINER role.
+   */
+  TEXT = 169,
+
+  LAST_ROLE = TEXT
 };
 
 } // namespace role
 
 typedef enum mozilla::a11y::roles::Role role;
 
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/base/RoleMap.h
+++ b/accessible/base/RoleMap.h
@@ -749,17 +749,17 @@ ROLE(TERMINAL,
      ATK_ROLE_TERMINAL,
      NSAccessibilityUnknownRole,
      USE_ROLE_STRING,
      IA2_ROLE_TERMINAL,
      eNoNameRule)
 
 ROLE(TEXT_CONTAINER,
      "text container",
-     ATK_ROLE_TEXT,
+     ATK_ROLE_SECTION,
      NSAccessibilityGroupRole,
      USE_ROLE_STRING,
      IA2_ROLE_TEXT_FRAME,
      eNameFromSubtreeIfReqRule)
 
 ROLE(TOGGLE_BUTTON,
      "toggle button",
      ATK_ROLE_TOGGLE_BUTTON,
@@ -1362,8 +1362,17 @@ ROLE(MATHML_STACK_LINE,
 
 ROLE(RADIO_GROUP,
      "grouping",
      ATK_ROLE_PANEL,
      NSAccessibilityRadioGroupRole,
      ROLE_SYSTEM_GROUPING,
      ROLE_SYSTEM_GROUPING,
      eNoNameRule)
+
+ROLE(TEXT,
+     "text",
+     ATK_ROLE_STATIC,
+     NSAccessibilityGroupRole,
+     USE_ROLE_STRING,
+     IA2_ROLE_TEXT_FRAME,
+     eNameFromSubtreeIfReqRule)
+
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -58,22 +58,21 @@ NS_IMPL_ISUPPORTS_INHERITED0(HyperTextAc
 
 role
 HyperTextAccessible::NativeRole()
 {
   a11y::role r = GetAccService()->MarkupRole(mContent);
   if (r != roles::NOTHING)
     return r;
 
-  // Treat block frames as paragraphs
-  nsIFrame *frame = GetFrame();
-  if (frame && frame->GetType() == nsGkAtoms::blockFrame)
-    return roles::PARAGRAPH;
+  nsIFrame* frame = GetFrame();
+  if (frame && frame->GetType() == nsGkAtoms::inlineFrame)
+    return roles::TEXT;
 
-  return roles::TEXT_CONTAINER; // In ATK this works
+  return roles::TEXT_CONTAINER;
 }
 
 uint64_t
 HyperTextAccessible::NativeState()
 {
   uint64_t states = AccessibleWrap::NativeState();
 
   if (mContent->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
--- a/accessible/interfaces/nsIAccessibleRole.idl
+++ b/accessible/interfaces/nsIAccessibleRole.idl
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 /**
  * Defines cross platform (Gecko) roles.
  */
-[scriptable, uuid(00f9e831-3198-40b7-9186-5251474d4d7a)]
+[scriptable, uuid(94add87a-190c-443e-9549-d11131affb2a)]
 interface nsIAccessibleRole : nsISupports
 {
   /**
    * Used when accessible hans't strong defined role.
    */
   const unsigned long ROLE_NOTHING = 0;
 
   /**
@@ -969,9 +969,15 @@ interface nsIAccessibleRole : nsISupport
    * A MathML line in a stack (msline in MathML).
    */
   const unsigned long ROLE_MATHML_STACK_LINE = 167;
 
   /**
    * A group containing radio buttons
    */
   const unsigned long ROLE_RADIO_GROUP = 168;
+
+  /**
+   * A text container exposing brief amount of information. See related
+   * TEXT_CONTAINER role.
+   */
+  const unsigned long ROLE_TEXT = 169;
 };
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -65,28 +65,28 @@
       // HTML:abbr contained by HTML:td
 
       obj = {
         role: ROLE_CELL,
         attributes: { abbr: "WWW" },
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
         children: [
           {
-            role: ROLE_TEXT_CONTAINER,
+            role: ROLE_TEXT,
             children: [ { role: ROLE_TEXT_LEAF } ]
           }
         ]
       };
       testElm("td_abbr", obj);
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:address
 
       obj = {
-        todo_role: ROLE_PARAGRAPH,
+        role: ROLE_TEXT_CONTAINER,
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
       };
       testElm("address", obj);
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:area@href
 
       obj = {
@@ -183,17 +183,17 @@
       obj = {
         role: ROLE_SECTION,
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
         children: [ { role: ROLE_PARAGRAPH } ]
       };
       testElm("blockquote", obj);
 
       //////////////////////////////////////////////////////////////////////////
-      // HTML:br
+      // HTML:br contained by paragraph
 
       obj = {
         role: ROLE_PARAGRAPH,
         children: [ { role: ROLE_WHITESPACE } ]
       };
       testElm("br_container", obj);
 
       //////////////////////////////////////////////////////////////////////////
@@ -1057,17 +1057,17 @@
         }
       };
       testElm("output_input", obj);
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:pre
 
       obj = {
-        role: ROLE_PARAGRAPH,
+        role: ROLE_TEXT_CONTAINER,
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
       };
       testElm("pre", obj);
 
       ///////////////////////////////////////////////////////////////////////////
       // HTML:progress
 
       obj = {
@@ -1082,17 +1082,17 @@
         states: STATE_MIXED
       };
       testElm("progress_indeterminate", obj);
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:q
 
       obj = {
-        role: ROLE_TEXT_CONTAINER,
+        role: ROLE_TEXT,
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
         children: [
           { role: ROLE_STATICTEXT }, // left quote
           { role: ROLE_TEXT_LEAF }, // quoted text
           { role: ROLE_STATICTEXT } // right quote
         ]
       };
       testElm("q", obj);
@@ -1228,17 +1228,17 @@
         interfaces: [ nsIAccessibleText, nsIAccessibleEditableText ]
       };
       testElm("textarea", obj);
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:time
 
       obj = {
-        role: ROLE_TEXT_CONTAINER,
+        role: ROLE_TEXT,
         attributes: { "xml-roles": "time", "datetime": "2001-05-15 19:00" },
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
       };
       testElm("time", obj);
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:u contained by paragraph
 
--- a/accessible/tests/mochitest/role.js
+++ b/accessible/tests/mochitest/role.js
@@ -106,16 +106,17 @@ const ROLE_SECTION = nsIAccessibleRole.R
 const ROLE_SEPARATOR = nsIAccessibleRole.ROLE_SEPARATOR;
 const ROLE_SLIDER = nsIAccessibleRole.ROLE_SLIDER;
 const ROLE_SPINBUTTON = nsIAccessibleRole.ROLE_SPINBUTTON;
 const ROLE_STATICTEXT = nsIAccessibleRole.ROLE_STATICTEXT;
 const ROLE_STATUSBAR = nsIAccessibleRole.ROLE_STATUSBAR;
 const ROLE_SWITCH = nsIAccessibleRole.ROLE_SWITCH;
 const ROLE_TABLE = nsIAccessibleRole.ROLE_TABLE;
 const ROLE_TERM = nsIAccessibleRole.ROLE_TERM;
+const ROLE_TEXT = nsIAccessibleRole.ROLE_TEXT;
 const ROLE_TEXT_CONTAINER = nsIAccessibleRole.ROLE_TEXT_CONTAINER;
 const ROLE_TEXT_LEAF = nsIAccessibleRole.ROLE_TEXT_LEAF;
 const ROLE_TOGGLE_BUTTON = nsIAccessibleRole.ROLE_TOGGLE_BUTTON;
 const ROLE_TOOLBAR = nsIAccessibleRole.ROLE_TOOLBAR;
 const ROLE_TOOLTIP = nsIAccessibleRole.ROLE_TOOLTIP;
 const ROLE_TREE_TABLE = nsIAccessibleRole.ROLE_TREE_TABLE;
 const ROLE_WHITESPACE = nsIAccessibleRole.ROLE_WHITESPACE;
 
--- a/accessible/tests/mochitest/role/test_aria.html
+++ b/accessible/tests/mochitest/role/test_aria.html
@@ -34,26 +34,26 @@
       testRole("aria_gridcell", ROLE_GRID_CELL);
       testRole("aria_group", ROLE_GROUPING);
       testRole("aria_heading", ROLE_HEADING);
       testRole("aria_img", ROLE_GRAPHIC);
       testRole("aria_link", ROLE_LINK);
       testRole("aria_list", ROLE_LIST);
       testRole("aria_listbox", ROLE_LISTBOX);
       testRole("aria_listitem", ROLE_LISTITEM);
-      testRole("aria_log", ROLE_TEXT_CONTAINER); // weak role
+      testRole("aria_log", ROLE_TEXT); // weak role
       testRole("aria_marquee", ROLE_ANIMATION);
       testRole("aria_math", ROLE_FLAT_EQUATION);
       testRole("aria_menu", ROLE_MENUPOPUP);
       testRole("aria_menubar", ROLE_MENUBAR);
       testRole("aria_menuitem", ROLE_MENUITEM);
       testRole("aria_menuitemcheckbox", ROLE_CHECK_MENU_ITEM);
       testRole("aria_menuitemradio", ROLE_RADIO_MENU_ITEM);
       testRole("aria_note", ROLE_NOTE);
-      testRole("aria_presentation", ROLE_TEXT_CONTAINER); // weak role
+      testRole("aria_presentation", ROLE_TEXT); // weak role
       testRole("aria_progressbar", ROLE_PROGRESSBAR);
       testRole("aria_radio", ROLE_RADIOBUTTON);
       testRole("aria_radiogroup", ROLE_RADIO_GROUP);
       testRole("aria_region", ROLE_PANE);
       testRole("aria_row", ROLE_ROW);
       testRole("aria_rowheader", ROLE_ROWHEADER);
       testRole("aria_scrollbar", ROLE_SCROLLBAR);
       testRole("aria_searchbox", ROLE_ENTRY);
@@ -61,17 +61,17 @@
       testRole("aria_slider", ROLE_SLIDER);
       testRole("aria_spinbutton", ROLE_SPINBUTTON);
       testRole("aria_status", ROLE_STATUSBAR);
       testRole("aria_switch", ROLE_SWITCH);
       testRole("aria_tab", ROLE_PAGETAB);
       testRole("aria_tablist", ROLE_PAGETABLIST);
       testRole("aria_tabpanel", ROLE_PROPERTYPAGE);
       testRole("aria_textbox", ROLE_ENTRY);
-      testRole("aria_timer", ROLE_TEXT_CONTAINER); // weak role
+      testRole("aria_timer", ROLE_TEXT); // weak role
       testRole("aria_toolbar", ROLE_TOOLBAR);
       testRole("aria_tooltip", ROLE_TOOLTIP);
       testRole("aria_tree", ROLE_OUTLINE);
       testRole("aria_treegrid", ROLE_TREE_TABLE);
       testRole("aria_treeitem", ROLE_OUTLINEITEM);
 
       // Note:
       // The phrase "weak foo" here means that there is no good foo-to-platform
--- a/accessible/tests/mochitest/role/test_general.html
+++ b/accessible/tests/mochitest/role/test_general.html
@@ -62,19 +62,19 @@
       testRole("p", ROLE_PARAGRAPH);
 
       // Test dl, dt, dd
       testRole("definitionlist", ROLE_DEFINITION_LIST);
       testRole("definitionterm", ROLE_TERM);
       testRole("definitiondescription", ROLE_DEFINITION);
 
       // Has click, mousedown or mouseup listeners.
-      testRole("span1", ROLE_TEXT_CONTAINER);
-      testRole("span2", ROLE_TEXT_CONTAINER);
-      testRole("span3", ROLE_TEXT_CONTAINER);
+      testRole("span1", ROLE_TEXT);
+      testRole("span2", ROLE_TEXT);
+      testRole("span3", ROLE_TEXT);
 
       // Test role of listbox inside combobox
       testRole("listbox1", ROLE_COMBOBOX_LIST);
       testRole("listbox2", ROLE_COMBOBOX_LIST);
 
       SimpleTest.finish();
     }
 
--- a/accessible/tests/mochitest/tree/test_aria_list.html
+++ b/accessible/tests/mochitest/tree/test_aria_list.html
@@ -30,17 +30,17 @@
 
       //////////////////////////////////////////////////////////////////////////
       // crazy list (mad mix of ARIA and HTML)
 
       accTree = { // div@role="list"
         role: ROLE_LIST,
         children: [
           { // li
-            role: ROLE_PARAGRAPH,
+            role: ROLE_TEXT_CONTAINER,
             children: [
               { // li text leaf
                 role: ROLE_TEXT_LEAF,
                 name: "item1",
                 children: [ ]
               }
             ]
           },
--- a/accessible/tests/mochitest/tree/test_aria_presentation.html
+++ b/accessible/tests/mochitest/tree/test_aria_presentation.html
@@ -68,20 +68,20 @@
           ] }
         ] }
       ] };
     testAccessibleTree("tblfocusable_cnt", tree);
 
     // Presentation list, expose generic accesisble for list items.
     tree =
       { SECTION: [ // container
-        { PARAGRAPH: [ // li generic accessible inside 'presentation' role
+        { TEXT_CONTAINER: [ // li generic accessible inside 'presentation' role
           { TEXT_LEAF: [ ] } // li text
         ] },
-        { PARAGRAPH: [ // li generic accessible inside 'none' role
+        { TEXT_CONTAINER: [ // li generic accessible inside 'none' role
           { TEXT_LEAF: [ ] } // li text
         ] }
       ] };
     testAccessibleTree("list_cnt", tree);
 
     // Has ARIA globals or referred by ARIA relationship, role='presentation'
     // and role='none' are ignored.
     tree =
--- a/accessible/tests/mochitest/tree/test_dockids.html
+++ b/accessible/tests/mochitest/tree/test_dockids.html
@@ -15,25 +15,25 @@
   <script type="application/javascript"
           src="../states.js"></script>
 
   <script type="application/javascript">
   function doTest()
   {
     var tree =
      { DOCUMENT: [
-       { PARAGRAPH: [ // head
-         { PARAGRAPH: [ // link
+       { TEXT_CONTAINER: [ // head
+         { TEXT_CONTAINER: [ // link
            { STATICTEXT: [] }, // generated content
            { STATICTEXT: [] } // generated content
          ] }
        ] },
        { TEXT_LEAF: [ ] }, // body text
        { ENTRY: [ ] }, // input under document element
-       { PARAGRAPH: [ // link under document element
+       { TEXT_CONTAINER: [ // link under document element
          { TEXT_LEAF: [ ] }, // link content
          { STATICTEXT: [ ] }, // generated content
          { STATICTEXT: [ ] } // generated content
        ] },
        { LINK: [ // anchor under document element
          { TEXT_LEAF: [ ] } // anchor content
        ] },
      ] };
--- a/accessible/tests/mochitest/tree/test_invalid_img.xhtml
+++ b/accessible/tests/mochitest/tree/test_invalid_img.xhtml
@@ -15,17 +15,17 @@
 
   <script>
   <![CDATA[
     function doTest()
     {
       document.getElementsByTagName("img")[0].firstChild.data = "2";
 
       var accTree = {
-        role: ROLE_TEXT_CONTAINER,
+        role: ROLE_TEXT,
         children: [ { role: ROLE_TEXT_LEAF } ]
       };
       testAccessibleTree("the_img", accTree);
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
--- a/accessible/tests/mochitest/tree/test_txtcntr.html
+++ b/accessible/tests/mochitest/tree/test_txtcntr.html
@@ -115,17 +115,17 @@
         role: ROLE_SECTION,
         children: [
           { // text leaf
             role: ROLE_TEXT_LEAF,
             name: "This ",
             children: []
           },
           { // abbr tag
-            role: ROLE_TEXT_CONTAINER,
+            role: ROLE_TEXT,
             name: "accessibility",
             children: [
               { // text leaf with actual text
                 role: ROLE_TEXT_LEAF,
                 name: "a11y",
                 children: []
               }
             ]
@@ -145,17 +145,17 @@
         role: ROLE_SECTION,
         children: [
           { // text leaf
             role: ROLE_TEXT_LEAF,
             name: "This ",
             children: []
           },
           { // acronym tag
-            role: ROLE_TEXT_CONTAINER,
+            role: ROLE_TEXT,
             name: "personal computer",
             children: [
               { // text leaf with actual text
                 role: ROLE_TEXT_LEAF,
                 name: "PC",
                 children: []
               }
             ]
--- a/accessible/tests/mochitest/treeupdate/test_textleaf.html
+++ b/accessible/tests/mochitest/treeupdate/test_textleaf.html
@@ -79,45 +79,45 @@
       }
 
       this.getID = function setOnClickAttr_getID()
       {
         return "make " + prettyName(aID) + " linkable";
       }
     }
 
-    function removeTextData(aID)
+    function removeTextData(aID, aRole)
     {
       this.containerNode = getNode(aID);
       this.textNode = this.containerNode.firstChild;
 
       this.eventSeq = [
         new invokerChecker(EVENT_REORDER, this.containerNode)
       ];
 
       this.invoke = function removeTextData_invoke()
       {
         var tree = {
-          role: ROLE_PARAGRAPH,
+          role: aRole,
           children: [
             {
               role: ROLE_TEXT_LEAF,
               name: "text"
             }
           ]
         };
         testAccessibleTree(this.containerNode, tree);
 
         this.textNode.data = "";
       }
 
       this.finalCheck = function removeTextData_finalCheck()
       {
         var tree = {
-          role: ROLE_PARAGRAPH,
+          role: aRole,
           children: []
         };
         testAccessibleTree(this.containerNode, tree);
       }
 
       this.getID = function removeTextData_finalCheck()
       {
         return "remove text data of text node inside '" + aID + "'.";
@@ -142,18 +142,18 @@
       // remove onclick attribute, text leaf shouldn't have any action
       gQueue.push(new removeOnClickAttr("div"));
 
       // set onclick attribute making span accessible, it's inserted into tree
       // and adopts text leaf accessible, text leaf should have an action
       gQueue.push(new setOnClickNRoleAttrs("span"));
 
       // text data removal of text node should remove its text accessible
-      gQueue.push(new removeTextData("p"));
-      gQueue.push(new removeTextData("pre"));
+      gQueue.push(new removeTextData("p", ROLE_PARAGRAPH));
+      gQueue.push(new removeTextData("pre", ROLE_TEXT_CONTAINER));
 
       gQueue.invoke(); // SimpleTest.finish() will be called in the end
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
--- a/accessible/windows/sdn/sdnAccessible.cpp
+++ b/accessible/windows/sdn/sdnAccessible.cpp
@@ -101,18 +101,21 @@ sdnAccessible::get_nodeInfo(BSTR __RPC_F
   *aNameSpaceID = mNode->IsNodeOfType(nsINode::eCONTENT) ?
     static_cast<short>(mNode->AsContent()->GetNameSpaceID()) : 0;
 
   // This is a unique ID for every content node. The 3rd party accessibility
   // application can compare this to the childID we return for events such as
   // focus events, to correlate back to data nodes in their internal object
   // model.
   Accessible* accessible = GetAccessible();
-  *aUniqueID = - NS_PTR_TO_INT32(accessible ? accessible->UniqueID() :
-                                              static_cast<void*>(this));
+  if (accessible) {
+    *aUniqueID = AccessibleWrap::GetChildIDFor(accessible);
+  } else {
+    *aUniqueID = - NS_PTR_TO_INT32(static_cast<void*>(this));
+  }
 
   *aNumChildren = mNode->GetChildCount();
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -45,31 +45,16 @@ var FullScreen = {
 #endif
 
     if (!this._fullScrToggler) {
       this._fullScrToggler = document.getElementById("fullscr-toggler");
       this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
       this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
     }
 
-    // On OS X Lion we don't want to hide toolbars when entering fullscreen, unless
-    // we're entering DOM fullscreen, in which case we should hide the toolbars.
-    // If we're leaving fullscreen, then we'll go through the exit code below to
-    // make sure toolbars are made visible in the case of DOM fullscreen.
-    if (enterFS && this.useLionFullScreen) {
-      if (document.mozFullScreen) {
-        this.showXULChrome("toolbar", false);
-      }
-      else {
-        gNavToolbox.setAttribute("inFullscreen", true);
-        document.documentElement.setAttribute("inFullscreen", true);
-      }
-      return;
-    }
-
     // show/hide menubars, toolbars (except the full screen toolbar)
     this.showXULChrome("toolbar", !enterFS);
 
     if (enterFS) {
       document.addEventListener("keypress", this._keyToggleCallback, false);
       document.addEventListener("popupshown", this._setPopupOpen, false);
       document.addEventListener("popuphidden", this._setPopupOpen, false);
       this._shouldAnimate = true;
@@ -233,19 +218,24 @@ var FullScreen = {
   // Checks whether we are allowed to collapse the chrome
   _isPopupOpen: false,
   _isChromeCollapsed: false,
   _safeToCollapse: function(forceHide)
   {
     if (!gPrefService.getBoolPref("browser.fullscreen.autohide"))
       return false;
 
-    // a popup menu is open in chrome: don't collapse chrome
-    if (!forceHide && this._isPopupOpen)
-      return false;
+    if (!forceHide) {
+      // a popup menu is open in chrome: don't collapse chrome
+      if (this._isPopupOpen)
+        return false;
+      // On OS X Lion we don't want to hide toolbars.
+      if (this.useLionFullScreen)
+        return false;
+    }
 
     // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
     if (document.commandDispatcher.focusedElement &&
         document.commandDispatcher.focusedElement.ownerDocument == document &&
         document.commandDispatcher.focusedElement.localName == "input") {
       if (forceHide)
         // hidden textboxes that still have focus are bad bad bad
         document.commandDispatcher.focusedElement.blur();
@@ -443,17 +433,17 @@ var FullScreen = {
     gNavToolbox.style.marginTop = "";
 
     if (!this._isChromeCollapsed) {
       return;
     }
 
     // Track whether mouse is near the toolbox
     this._isChromeCollapsed = false;
-    if (trackMouse) {
+    if (trackMouse && !this.useLionFullScreen) {
       let rect = gBrowser.mPanelContainer.getBoundingClientRect();
       this._mouseTargetRect = {
         top: rect.top + 50,
         bottom: rect.bottom,
         left: rect.left,
         right: rect.right
       };
       MousePosTracker.addListener(this);
--- a/browser/components/loop/test/mochitest/browser_fxa_login.js
+++ b/browser/components/loop/test/mochitest/browser_fxa_login.js
@@ -6,23 +6,23 @@
  */
 
 "use strict";
 
 const BASE_URL = Services.prefs.getCharPref("loop.server");
 
 function* checkFxA401() {
   let err = MozLoopService.errors.get("login");
-  ise(err.code, 401, "Check error code");
-  ise(err.friendlyMessage, getLoopString("could_not_authenticate"),
-      "Check friendlyMessage");
-  ise(err.friendlyDetails, getLoopString("password_changed_question"),
-      "Check friendlyDetails");
-  ise(err.friendlyDetailsButtonLabel, getLoopString("retry_button"),
-      "Check friendlyDetailsButtonLabel");
+  is(err.code, 401, "Check error code");
+  is(err.friendlyMessage, getLoopString("could_not_authenticate"),
+     "Check friendlyMessage");
+  is(err.friendlyDetails, getLoopString("password_changed_question"),
+     "Check friendlyDetails");
+  is(err.friendlyDetailsButtonLabel, getLoopString("retry_button"),
+     "Check friendlyDetailsButtonLabel");
   let loopButton = document.getElementById("loop-button");
   is(loopButton.getAttribute("state"), "error",
      "state of loop button should be error after a 401 with login");
 
   let loopPanel = document.getElementById("loop-notification-panel");
   yield loadLoopPanel();
   let loopDoc = document.getElementById("loop-panel-iframe").contentDocument;
   is(loopDoc.querySelector(".alert-error .message").textContent,
@@ -60,34 +60,34 @@ add_task(function* checkOAuthParams() {
     content_uri: BASE_URL + "/content",
     oauth_uri: BASE_URL + "/oauth",
     profile_uri: BASE_URL + "/profile",
     state: "state"
   };
   yield promiseOAuthParamsSetup(BASE_URL, params);
   let client = yield MozLoopServiceInternal.promiseFxAOAuthClient();
   for (let key of Object.keys(params)) {
-    ise(client.parameters[key], params[key], "Check " + key + " was passed to the OAuth client");
+    is(client.parameters[key], params[key], "Check " + key + " was passed to the OAuth client");
   }
   let prefName = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
   let padding = "X".repeat(HAWK_TOKEN_LENGTH - params.client_id.length);
-  ise(Services.prefs.getCharPref(prefName), params.client_id + padding, "Check FxA hawk token");
+  is(Services.prefs.getCharPref(prefName), params.client_id + padding, "Check FxA hawk token");
 });
 
 add_task(function* basicAuthorization() {
   let result = yield MozLoopServiceInternal.promiseFxAOAuthAuthorization();
   is(result.code, "code1", "Check code");
   is(result.state, "state", "Check state");
 });
 
 add_task(function* sameOAuthClientForTwoCalls() {
   yield resetFxA();
   let client1 = yield MozLoopServiceInternal.promiseFxAOAuthClient();
   let client2 = yield MozLoopServiceInternal.promiseFxAOAuthClient();
-  ise(client1, client2, "The same client should be returned");
+  is(client1, client2, "The same client should be returned");
 });
 
 add_task(function* paramsInvalid() {
   yield resetFxA();
   // Delete the params so an empty object is returned.
   yield promiseDeletedOAuthParams(BASE_URL);
   let result = null;
   let loginPromise = MozLoopService.logInToFxA();
@@ -115,19 +115,19 @@ add_task(function* params_no_hawk_sessio
   let loginPromise = MozLoopService.logInToFxA();
   let caught = false;
   yield loginPromise.catch(() => {
     ok(true, "The login promise should be rejected due to a lack of a hawk session");
     caught = true;
   });
   ok(caught, "Should have caught the rejection");
   let prefName = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
-  ise(Services.prefs.getPrefType(prefName),
-      Services.prefs.PREF_INVALID,
-      "Check FxA hawk token is not set");
+  is(Services.prefs.getPrefType(prefName),
+     Services.prefs.PREF_INVALID,
+     "Check FxA hawk token is not set");
 });
 
 add_task(function* params_nonJSON() {
   let loopServerUrl = Services.prefs.getCharPref("loop.server");
   Services.prefs.setCharPref("loop.server", "https://localhost:3000/invalid");
   try {
     // Reset after changing the server so a new HawkClient is created
     yield resetFxA();
@@ -294,45 +294,45 @@ add_task(function* basicAuthorizationAnd
   is(MozLoopService.userProfile, null, "profile should be null before log-in");
   let loopButton = document.getElementById("loop-button");
   is(loopButton.getAttribute("state"), "", "state of loop button should be empty when not logged in");
 
   info("Login");
   statusChangedPromise = promiseObserverNotified("loop-status-changed", "login");
   let tokenData = yield MozLoopService.logInToFxA();
   yield statusChangedPromise;
-  ise(tokenData.access_token, "code1_access_token", "Check access_token");
-  ise(tokenData.scope, "profile", "Check scope");
-  ise(tokenData.token_type, "bearer", "Check token_type");
+  is(tokenData.access_token, "code1_access_token", "Check access_token");
+  is(tokenData.scope, "profile", "Check scope");
+  is(tokenData.token_type, "bearer", "Check token_type");
 
   is(MozLoopService.userProfile.email, "test@example.com", "email should exist in the profile data");
   is(MozLoopService.userProfile.uid, "1234abcd", "uid should exist in the profile data");
   is(visibleEmail.textContent, "test@example.com", "the email should be correct on the panel");
   is(loopButton.getAttribute("state"), "active", "state of loop button should be active when logged in");
 
   let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
-  ise(registrationResponse.response.simplePushURLs.calls, "https://localhost/pushUrl/fxa-calls",
-      "Check registered push URL");
-  ise(registrationResponse.response.simplePushURLs.rooms, "https://localhost/pushUrl/fxa-rooms",
-      "Check registered push URL");
+  is(registrationResponse.response.simplePushURLs.calls, "https://localhost/pushUrl/fxa-calls",
+     "Check registered push URL");
+  is(registrationResponse.response.simplePushURLs.rooms, "https://localhost/pushUrl/fxa-rooms",
+     "Check registered push URL");
 
   let loopPanel = document.getElementById("loop-notification-panel");
   loopPanel.hidePopup();
   statusChangedPromise = promiseObserverNotified("loop-status-changed");
   yield loadLoopPanel({stayOnline: true});
   yield statusChangedPromise;
   is(loopButton.getAttribute("state"), "", "state of loop button should return to empty after panel is opened");
   loopPanel.hidePopup();
 
   info("logout");
   yield MozLoopService.logOutFromFxA();
   checkLoggedOutState();
   registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
-  ise(registrationResponse.response, null,
-      "Check registration was deleted on the server");
+  is(registrationResponse.response, null,
+     "Check registration was deleted on the server");
   is(visibleEmail.textContent, "Guest", "Guest should be displayed on the panel again after logout");
   is(MozLoopService.userProfile, null, "userProfile should be null after logout");
 });
 
 add_task(function* loginWithParams401() {
   yield resetFxA();
   let params = {
     client_id: "client_id",
@@ -345,58 +345,58 @@ add_task(function* loginWithParams401() 
   yield promiseOAuthParamsSetup(BASE_URL, params);
   yield MozLoopService.promiseRegisteredWithServers();
 
   let loginPromise = MozLoopService.logInToFxA();
   yield loginPromise.then(tokenData => {
     ok(false, "Promise should have rejected");
   },
   error => {
-    ise(error.code, 401, "Check error code");
+    is(error.code, 401, "Check error code");
     checkFxAOAuthTokenData(null);
   });
 
   yield checkFxA401();
 });
 
 add_task(function* logoutWithIncorrectPushURL() {
   yield resetFxA();
   let pushURL = "http://www.example.com/";
   // Create a fake FxA hawk session token
   const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
   Services.prefs.setCharPref(fxASessionPref, "X".repeat(HAWK_TOKEN_LENGTH));
   yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, "calls", pushURL);
   let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
-  ise(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL");
+  is(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL");
   MozLoopServiceInternal.pushURLs.get(LOOP_SESSION_TYPE.FXA).calls = "http://www.example.com/invalid";
   let caught = false;
   yield MozLoopService.logOutFromFxA().catch((error) => {
     caught = true;
   });
   ok(caught, "Should have caught an error logging out with a mismatched push URL");
   checkLoggedOutState();
   registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
-  ise(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL wasn't deleted");
+  is(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL wasn't deleted");
 });
 
 add_task(function* logoutWithNoPushURL() {
   yield resetFxA();
   let pushURL = "http://www.example.com/";
   // Create a fake FxA hawk session token
   const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
   Services.prefs.setCharPref(fxASessionPref, "X".repeat(HAWK_TOKEN_LENGTH));
 
   yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, "calls", pushURL);
   let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
-  ise(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL");
+  is(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL");
   MozLoopServiceInternal.pushURLs.delete(LOOP_SESSION_TYPE.FXA);
   yield MozLoopService.logOutFromFxA();
   checkLoggedOutState();
   registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
-  ise(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL wasn't deleted");
+  is(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL wasn't deleted");
 });
 
 add_task(function* loginWithRegistration401() {
   yield resetFxA();
   let params = {
     client_id: "client_id",
     content_uri: BASE_URL + "/content",
     oauth_uri: BASE_URL + "/oauth",
@@ -406,17 +406,17 @@ add_task(function* loginWithRegistration
   };
   yield promiseOAuthParamsSetup(BASE_URL, params);
 
   let loginPromise = MozLoopService.logInToFxA();
   yield loginPromise.then(tokenData => {
     ok(false, "Promise should have rejected");
   },
   error => {
-    ise(error.code, 401, "Check error code");
+    is(error.code, 401, "Check error code");
     checkFxAOAuthTokenData(null);
   });
 
   yield checkFxA401();
 });
 
 add_task(function* openFxASettings() {
   yield resetFxA();
--- a/browser/components/loop/test/mochitest/browser_loop_fxa_server.js
+++ b/browser/components/loop/test/mochitest/browser_loop_fxa_server.js
@@ -63,34 +63,34 @@ add_task(function* token_request() {
     content_uri: "https://example.com/content/",
     oauth_uri: "https://example.com/oauth/",
     profile_uri: "https://example.com/profile/",
     state: "my_state"
   };
   yield promiseOAuthParamsSetup(BASE_URL, params);
 
   let request = yield promiseToken("my_code", params.state);
-  ise(request.status, 200, "Check token response status");
-  ise(request.response.access_token, "my_code_access_token", "Check access_token");
-  ise(request.response.scope, "profile", "Check scope");
-  ise(request.response.token_type, "bearer", "Check token_type");
+  is(request.status, 200, "Check token response status");
+  is(request.response.access_token, "my_code_access_token", "Check access_token");
+  is(request.response.scope, "profile", "Check scope");
+  is(request.response.token_type, "bearer", "Check token_type");
 });
 
 add_task(function* token_request_invalid_state() {
   let params = {
     client_id: "my_client_id",
     content_uri: "https://example.com/content/",
     oauth_uri: "https://example.com/oauth/",
     profile_uri: "https://example.com/profile/",
     state: "my_invalid_state"
   };
   yield promiseOAuthParamsSetup(BASE_URL, params);
   let request = yield promiseToken("my_code", "my_state");
-  ise(request.status, 400, "Check token response status");
-  ise(request.response, null, "Check token response body");
+  is(request.status, 400, "Check token response status");
+  is(request.response, null, "Check token response body");
 });
 
 
 // Helper methods
 
 function promiseParams() {
   return new Promise((resolve, reject) => {
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
--- a/browser/components/loop/test/mochitest/head.js
+++ b/browser/components/loop/test/mochitest/head.js
@@ -152,28 +152,28 @@ function* resetFxA() {
   Services.prefs.clearUserPref(fxASessionPref);
   MozLoopService.errors.clear();
   let notified = promiseObserverNotified("loop-status-changed");
   MozLoopServiceInternal.notifyStatusChanged();
   yield notified;
 }
 
 function checkFxAOAuthTokenData(aValue) {
-  ise(MozLoopServiceInternal.fxAOAuthTokenData, aValue, "fxAOAuthTokenData should be " + aValue);
+  is(MozLoopServiceInternal.fxAOAuthTokenData, aValue, "fxAOAuthTokenData should be " + aValue);
 }
 
 function checkLoggedOutState() {
   let global = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
-  ise(global.gFxAOAuthClientPromise, null, "gFxAOAuthClientPromise should be cleared");
-  ise(MozLoopService.userProfile, null, "fxAOAuthProfile should be cleared");
-  ise(global.gFxAOAuthClient, null, "gFxAOAuthClient should be cleared");
+  is(global.gFxAOAuthClientPromise, null, "gFxAOAuthClientPromise should be cleared");
+  is(MozLoopService.userProfile, null, "fxAOAuthProfile should be cleared");
+  is(global.gFxAOAuthClient, null, "gFxAOAuthClient should be cleared");
   checkFxAOAuthTokenData(null);
   const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
-  ise(Services.prefs.getPrefType(fxASessionPref), Services.prefs.PREF_INVALID,
-      "FxA hawk session should be cleared anyways");
+  is(Services.prefs.getPrefType(fxASessionPref), Services.prefs.PREF_INVALID,
+     "FxA hawk session should be cleared anyways");
 }
 
 function promiseDeletedOAuthParams(baseURL) {
   return new Promise((resolve, reject) => {
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
                 createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("DELETE", baseURL + "/setup_params", true);
     xhr.addEventListener("load", () => resolve(xhr));
--- a/browser/components/preferences/in-content/tests/browser_subdialogs.js
+++ b/browser/components/preferences/in-content/tests/browser_subdialogs.js
@@ -33,37 +33,37 @@ let gTests = [{
   run: function* () {
     let rv = { acceptCount: 0 };
     let deferredClose = Promise.defer();
     let dialogPromise = openAndLoadSubDialog(gDialogURL, null, rv,
                                              (aEvent) => dialogClosingCallback(deferredClose, aEvent));
     let dialog = yield dialogPromise;
 
     // Check focus is in the textbox
-    ise(dialog.document.activeElement.value, "Default text", "Textbox with correct text is focused");
+    is(dialog.document.activeElement.value, "Default text", "Textbox with correct text is focused");
 
     // Titlebar
-    ise(content.document.getElementById("dialogTitle").textContent, "Sample sub-dialog",
-       "Dialog title should be correct initially");
+    is(content.document.getElementById("dialogTitle").textContent, "Sample sub-dialog",
+      "Dialog title should be correct initially");
     let receivedEvent = waitForEvent(gBrowser.selectedBrowser, "DOMTitleChanged");
     dialog.document.title = "Updated title";
     // Wait for the title change listener
     yield receivedEvent;
-    ise(content.document.getElementById("dialogTitle").textContent, "Updated title",
-       "Dialog title should be updated with changes");
+    is(content.document.getElementById("dialogTitle").textContent, "Updated title",
+      "Dialog title should be updated with changes");
 
     let closingPromise = promiseDialogClosing(dialog);
 
     // Accept the dialog
     dialog.document.documentElement.acceptDialog();
     let closingEvent = yield closingPromise;
-    ise(closingEvent.detail.button, "accept", "closing event should indicate button was 'accept'");
+    is(closingEvent.detail.button, "accept", "closing event should indicate button was 'accept'");
 
     yield deferredClose.promise;
-    ise(rv.acceptCount, 1, "return value should have been updated");
+    is(rv.acceptCount, 1, "return value should have been updated");
   },
 },
 {
   desc: "Check canceling the dialog",
   run: function* () {
     let rv = { acceptCount: 0 };
     let deferredClose = Promise.defer();
     let dialogPromise = openAndLoadSubDialog(gDialogURL, null, rv,
@@ -71,99 +71,99 @@ let gTests = [{
     let dialog = yield dialogPromise;
 
     let closingPromise = promiseDialogClosing(dialog);
 
     info("cancelling the dialog");
     dialog.document.documentElement.cancelDialog();
 
     let closingEvent = yield closingPromise;
-    ise(closingEvent.detail.button, "cancel", "closing event should indicate button was 'cancel'");
+    is(closingEvent.detail.button, "cancel", "closing event should indicate button was 'cancel'");
 
     yield deferredClose.promise;
-    ise(rv.acceptCount, 0, "return value should NOT have been updated");
+    is(rv.acceptCount, 0, "return value should NOT have been updated");
   },
 },
 {
   desc: "Check window.close on the dialog",
   run: function* () {
     let rv = { acceptCount: 0 };
     let deferredClose = Promise.defer();
     let dialogPromise = openAndLoadSubDialog(gDialogURL, null, rv,
                                              (aEvent) => dialogClosingCallback(deferredClose, aEvent));
     let dialog = yield dialogPromise;
 
     let closingPromise = promiseDialogClosing(dialog);
     info("window.close called on the dialog");
     dialog.window.close();
 
     let closingEvent = yield closingPromise;
-    ise(closingEvent.detail.button, null, "closing event should indicate no button was clicked");
+    is(closingEvent.detail.button, null, "closing event should indicate no button was clicked");
 
     yield deferredClose.promise;
-    ise(rv.acceptCount, 0, "return value should NOT have been updated");
+    is(rv.acceptCount, 0, "return value should NOT have been updated");
   },
 },
 {
   desc: "Check clicking the close button on the dialog",
   run: function* () {
     let rv = { acceptCount: 0 };
     let deferredClose = Promise.defer();
     let dialogPromise = openAndLoadSubDialog(gDialogURL, null, rv,
                                              (aEvent) => dialogClosingCallback(deferredClose, aEvent));
     let dialog = yield dialogPromise;
 
     yield EventUtils.synthesizeMouseAtCenter(content.document.getElementById("dialogClose"), {},
                                              content.window);
 
     yield deferredClose.promise;
-    ise(rv.acceptCount, 0, "return value should NOT have been updated");
+    is(rv.acceptCount, 0, "return value should NOT have been updated");
   },
 },
 {
   desc: "Check that 'back' navigation will close the dialog",
   run: function* () {
     let rv = { acceptCount: 0 };
     let deferredClose = Promise.defer();
     let dialogPromise = openAndLoadSubDialog(gDialogURL, null, rv,
                                              (aEvent) => dialogClosingCallback(deferredClose, aEvent));
     let dialog = yield dialogPromise;
 
     info("cancelling the dialog");
     content.gSubDialog._frame.goBack();
 
     yield deferredClose.promise;
-    ise(rv.acceptCount, 0, "return value should NOT have been updated");
+    is(rv.acceptCount, 0, "return value should NOT have been updated");
   },
 },
 {
   desc: "Hitting escape in the dialog",
   run: function* () {
     let rv = { acceptCount: 0 };
     let deferredClose = Promise.defer();
     let dialogPromise = openAndLoadSubDialog(gDialogURL, null, rv,
                                              (aEvent) => dialogClosingCallback(deferredClose, aEvent));
     let dialog = yield dialogPromise;
 
     EventUtils.synthesizeKey("VK_ESCAPE", {}, content.window);
 
     yield deferredClose.promise;
-    ise(rv.acceptCount, 0, "return value should NOT have been updated");
+    is(rv.acceptCount, 0, "return value should NOT have been updated");
   },
 },
 {
   desc: "Check that width and height from the sub-dialog are used to size the <browser>",
   run: function* () {
     let deferredClose = Promise.defer();
     let dialogPromise = openAndLoadSubDialog(gDialogURL, null, null,
                                              (aEvent) => dialogClosingCallback(deferredClose, aEvent));
     let dialog = yield dialogPromise;
 
-    ise(content.gSubDialog._frame.style.width, "32em", "Width should be set on the frame from the dialog");
-    ise(content.gSubDialog._frame.style.height, "5em", "Height should be set on the frame from the dialog");
+    is(content.gSubDialog._frame.style.width, "32em", "Width should be set on the frame from the dialog");
+    is(content.gSubDialog._frame.style.height, "5em", "Height should be set on the frame from the dialog");
 
     content.gSubDialog.close();
     yield deferredClose.promise;
   },
 },
 {
   desc: "Check that a set width and content causing wrapping still lead to correct scrollHeight-implied height",
   run: function* () {
@@ -189,20 +189,20 @@ let gTests = [{
         architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas
         sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione
         voluptatem sequi nesciunt.`
       doc = null;
     });
 
     let dialog = yield dialogPromise;
 
-    ise(content.gSubDialog._frame.style.width, "32em", "Width should be set on the frame from the dialog");
+    is(content.gSubDialog._frame.style.width, "32em", "Width should be set on the frame from the dialog");
     let docEl = content.gSubDialog._frame.contentDocument.documentElement;
     ok(docEl.scrollHeight > oldHeight, "Content height increased (from " + oldHeight + " to " + docEl.scrollHeight + ").");
-    ise(content.gSubDialog._frame.style.height, docEl.scrollHeight + "px", "Height on the frame should be higher now");
+    is(content.gSubDialog._frame.style.height, docEl.scrollHeight + "px", "Height on the frame should be higher now");
 
     content.gSubDialog.close();
     yield deferredClose.promise;
   },
 },
 {
   desc: "Check that a dialog that is too high gets cut down to size",
   run: function* () {
@@ -212,17 +212,17 @@ let gTests = [{
 
     content.addEventListener("DOMFrameContentLoaded", function frame3Loaded() {
       content.removeEventListener("DOMFrameContentLoaded", frame3Loaded);
       content.gSubDialog._frame.contentDocument.documentElement.style.height = '100000px';
     });
 
     let dialog = yield dialogPromise;
 
-    ise(content.gSubDialog._frame.style.width, "32em", "Width should be set on the frame from the dialog");
+    is(content.gSubDialog._frame.style.width, "32em", "Width should be set on the frame from the dialog");
     let newHeight = content.gSubDialog._frame.contentDocument.documentElement.scrollHeight;
     ok(parseInt(content.gSubDialog._frame.style.height) < window.innerHeight,
        "Height on the frame should be smaller than window's innerHeight");
 
     content.gSubDialog.close();
     yield deferredClose.promise;
   }
 },
@@ -256,19 +256,19 @@ function promiseDialogClosing(dialog) {
   return waitForEvent(dialog, "dialogclosing");
 }
 
 function dialogClosingCallback(aPromise, aEvent) {
   // Wait for the close handler to unload the page
   waitForEvent(content.gSubDialog._frame, "load", 4000).then((aEvt) => {
     info("Load event happened: " + !(aEvt instanceof Error));
     is_element_hidden(content.gSubDialog._overlay, "Overlay is not visible");
-    ise(content.gSubDialog._frame.getAttribute("style"), "",
-        "Check that inline styles were cleared");
-    ise(content.gSubDialog._frame.contentWindow.location.toString(), "about:blank",
-       "Check the sub-dialog was unloaded");
+    is(content.gSubDialog._frame.getAttribute("style"), "",
+       "Check that inline styles were cleared");
+    is(content.gSubDialog._frame.contentWindow.location.toString(), "about:blank",
+      "Check the sub-dialog was unloaded");
     if (gTeardownAfterClose) {
       content.close();
       finish();
     }
     aPromise.resolve();
   }, Cu.reportError);
 }
--- a/browser/components/preferences/in-content/tests/head.js
+++ b/browser/components/preferences/in-content/tests/head.js
@@ -46,32 +46,32 @@ function openAndLoadSubDialog(aURL, aFea
 
 function promiseLoadSubDialog(aURL) {
   return new Promise((resolve, reject) => {
     content.gSubDialog._frame.addEventListener("load", function load(aEvent) {
       if (aEvent.target.contentWindow.location == "about:blank")
         return;
       content.gSubDialog._frame.removeEventListener("load", load);
 
-      ise(content.gSubDialog._frame.contentWindow.location.toString(), aURL,
-          "Check the proper URL is loaded");
+      is(content.gSubDialog._frame.contentWindow.location.toString(), aURL,
+         "Check the proper URL is loaded");
 
       // Check visibility
       is_element_visible(content.gSubDialog._overlay, "Overlay is visible");
 
       // Check that stylesheets were injected
       let expectedStyleSheetURLs = content.gSubDialog._injectedStyleSheets.slice(0);
       for (let styleSheet of content.gSubDialog._frame.contentDocument.styleSheets) {
         let i = expectedStyleSheetURLs.indexOf(styleSheet.href);
         if (i >= 0) {
           info("found " + styleSheet.href);
           expectedStyleSheetURLs.splice(i, 1);
         }
       }
-      ise(expectedStyleSheetURLs.length, 0, "All expectedStyleSheetURLs should have been found");
+      is(expectedStyleSheetURLs.length, 0, "All expectedStyleSheetURLs should have been found");
 
       resolve(content.gSubDialog._frame.contentWindow);
     });
   });
 }
 
 /**
  * Waits a specified number of miliseconds for a specified event to be
--- a/browser/components/uitour/test/browser_UITour.js
+++ b/browser/components/uitour/test/browser_UITour.js
@@ -12,20 +12,20 @@ Components.utils.import("resource:///mod
 function test() {
   UITourTest();
 }
 
 let tests = [
   function test_untrusted_host(done) {
     loadUITourTestPage(function() {
       let bookmarksMenu = document.getElementById("bookmarks-menu-button");
-      ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
+      is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
 
       gContentAPI.showMenu("bookmarks");
-      ise(bookmarksMenu.open, false, "Bookmark menu should not open on a untrusted host");
+      is(bookmarksMenu.open, false, "Bookmark menu should not open on a untrusted host");
 
       done();
     }, "http://mochi.test:8888/");
   },
   function test_testing_host(done) {
     // Add two testing origins intentionally surrounded by whitespace to be ignored.
     Services.prefs.setCharPref("browser.uitour.testingOrigins",
                                "https://test1.example.com, https://test2.example.com:443 ");
@@ -40,20 +40,20 @@ let tests = [
 
     loadUITourTestPage(function() {
       gContentAPI.getConfiguration("appinfo", callback);
     }, "https://test2.example.com/");
   },
   function test_unsecure_host(done) {
     loadUITourTestPage(function() {
       let bookmarksMenu = document.getElementById("bookmarks-menu-button");
-      ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
+      is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
 
       gContentAPI.showMenu("bookmarks");
-      ise(bookmarksMenu.open, false, "Bookmark menu should not open on a unsecure host");
+      is(bookmarksMenu.open, false, "Bookmark menu should not open on a unsecure host");
 
       done();
     }, "http://example.com/");
   },
   function test_unsecure_host_override(done) {
     Services.prefs.setBoolPref("browser.uitour.requireSecure", false);
     loadUITourTestPage(function() {
       let highlight = document.getElementById("UITourHighlight");
@@ -64,20 +64,20 @@ let tests = [
 
       Services.prefs.setBoolPref("browser.uitour.requireSecure", true);
     }, "http://example.com/");
   },
   function test_disabled(done) {
     Services.prefs.setBoolPref("browser.uitour.enabled", false);
 
     let bookmarksMenu = document.getElementById("bookmarks-menu-button");
-    ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
+    is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
 
     gContentAPI.showMenu("bookmarks");
-    ise(bookmarksMenu.open, false, "Bookmark menu should not open when feature is disabled");
+    is(bookmarksMenu.open, false, "Bookmark menu should not open when feature is disabled");
 
     Services.prefs.setBoolPref("browser.uitour.enabled", true);
     done();
   },
   function test_highlight(done) {
     function test_highlight_2() {
       let highlight = document.getElementById("UITourHighlight");
       gContentAPI.hideHighlight();
--- a/browser/components/uitour/test/browser_UITour2.js
+++ b/browser/components/uitour/test/browser_UITour2.js
@@ -65,17 +65,17 @@ let tests = [
           });
         }, "Info should be shown after showInfo() for fixed menu panel items");
       });
     }).then(null, Components.utils.reportError);
   },
   taskify(function* test_bookmarks_menu() {
     let bookmarksMenuButton = document.getElementById("bookmarks-menu-button");
 
-    ise(bookmarksMenuButton.open, false, "Menu should initially be closed");
+    is(bookmarksMenuButton.open, false, "Menu should initially be closed");
     gContentAPI.showMenu("bookmarks");
 
     yield waitForConditionPromise(() => {
       return bookmarksMenuButton.open;
     }, "Menu should be visible after showMenu()");
 
     gContentAPI.hideMenu("bookmarks");
     yield waitForConditionPromise(() => {
--- a/browser/components/uitour/test/browser_UITour_annotation_size_attributes.js
+++ b/browser/components/uitour/test/browser_UITour_annotation_size_attributes.js
@@ -21,31 +21,31 @@ function test() {
 
 let tests = [
   function test_highlight_size_attributes(done) {
     gContentAPI.showHighlight("appMenu");
     waitForElementToBeVisible(highlight, function moveTheHighlight() {
       gContentAPI.showHighlight("urlbar");
       waitForElementToBeVisible(highlight, function checkPanelAttributes() {
         SimpleTest.executeSoon(() => {
-          ise(highlight.height, "", "Highlight panel should have no explicit height set");
-          ise(highlight.width, "", "Highlight panel should have no explicit width set");
+          is(highlight.height, "", "Highlight panel should have no explicit height set");
+          is(highlight.width, "", "Highlight panel should have no explicit width set");
           done();
         });
       }, "Highlight should be moved to the urlbar");
     }, "Highlight should be shown after showHighlight() for the appMenu");
   },
 
   function test_info_size_attributes(done) {
     gContentAPI.showInfo("appMenu", "test title", "test text");
     waitForElementToBeVisible(tooltip, function moveTheTooltip() {
       gContentAPI.showInfo("urlbar", "new title", "new text");
       waitForElementToBeVisible(tooltip, function checkPanelAttributes() {
         SimpleTest.executeSoon(() => {
-          ise(tooltip.height, "", "Info panel should have no explicit height set");
-          ise(tooltip.width, "", "Info panel should have no explicit width set");
+          is(tooltip.height, "", "Info panel should have no explicit height set");
+          is(tooltip.width, "", "Info panel should have no explicit width set");
           done();
         });
       }, "Tooltip should be moved to the urlbar");
     }, "Tooltip should be shown after showInfo() for the appMenu");
   },
 
 ];
--- a/browser/components/uitour/test/browser_UITour_loop.js
+++ b/browser/components/uitour/test/browser_UITour_loop.js
@@ -26,17 +26,17 @@ function runOffline(fun) {
     });
   }
 }
 
 let tests = [
   taskify(function* test_gettingStartedClicked_linkOpenedWithExpectedParams() {
     Services.prefs.setBoolPref("loop.gettingStarted.seen", false);
     Services.prefs.setCharPref("loop.gettingStarted.url", "http://example.com");
-    ise(loopButton.open, false, "Menu should initially be closed");
+    is(loopButton.open, false, "Menu should initially be closed");
     loopButton.click();
 
     yield waitForConditionPromise(() => {
       return loopButton.open;
     }, "Menu should be visible after showMenu()");
 
     gContentAPI.registerPageID("hello-tour_OpenPanel_testPage");
     yield new Promise(resolve => {
@@ -64,17 +64,17 @@ let tests = [
     // Force a refresh of the loop panel since going from seen -> unseen doesn't trigger
     // automatic re-rendering.
     let loopWin = document.getElementById("loop-notification-panel").children[0].contentWindow;
     var event = new loopWin.CustomEvent("GettingStartedSeen");
     loopWin.dispatchEvent(event);
 
     UITour.pageIDsForSession.clear();
     Services.prefs.setCharPref("loop.gettingStarted.url", "http://example.com");
-    ise(loopButton.open, false, "Menu should initially be closed");
+    is(loopButton.open, false, "Menu should initially be closed");
     loopButton.click();
 
     yield waitForConditionPromise(() => {
       return loopButton.open;
     }, "Menu should be visible after showMenu()");
 
 
     gContentAPI.registerPageID("hello-tour_OpenPanel_testPageOldId");
@@ -101,17 +101,17 @@ let tests = [
         gBrowser.currentURI.path + ")");
     yield gBrowser.removeCurrentTab();
 
     checkLoopPanelIsHidden();
   }),
   taskify(function* test_menu_show_hide() {
     // The targets to highlight only appear after getting started is launched.
     Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
-    ise(loopButton.open, false, "Menu should initially be closed");
+    is(loopButton.open, false, "Menu should initially be closed");
     gContentAPI.showMenu("loop");
 
     yield waitForConditionPromise(() => {
       return loopButton.open;
     }, "Menu should be visible after showMenu()");
 
     ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
     ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
@@ -296,17 +296,17 @@ let tests = [
           chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = oldComposeEmail;
         };
         chatWin.document.querySelector(".btn-email").click();
       });
     });
     LoopRooms.open("fakeTourRoom");
   }),
   taskify(function* test_arrow_panel_position() {
-    ise(loopButton.open, false, "Menu should initially be closed");
+    is(loopButton.open, false, "Menu should initially be closed");
     let popup = document.getElementById("UITourTooltip");
 
     yield showMenuPromise("loop");
 
     let currentTarget = "loop-newRoom";
     yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
     is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
 
@@ -339,17 +339,17 @@ let tests = [
 
     // Set the tour URL to be the current page with a different query param
     let gettingStartedURL = gTestTab.linkedBrowser.currentURI.resolve("?gettingstarted=1");
     Services.prefs.setCharPref("loop.gettingStarted.url", gettingStartedURL);
 
     let observationPromise = new Promise((resolve) => {
       gContentAPI.observe((event, params) => {
         is(event, "Loop:IncomingConversation", "Page should have been notified about incoming conversation");
-        ise(params.conversationOpen, false, "conversationOpen should be false");
+        is(params.conversationOpen, false, "conversationOpen should be false");
         is(gBrowser.selectedTab, gTestTab, "The same tab should be selected");
         resolve();
       });
     });
 
     // Now open the menu while that non-owner is in the fake room to trigger resuming the tour
     yield showMenuPromise("loop");
 
--- a/browser/components/uitour/test/browser_UITour_pocket.js
+++ b/browser/components/uitour/test/browser_UITour_pocket.js
@@ -11,17 +11,17 @@ let button;
 Components.utils.import("resource:///modules/UITour.jsm");
 
 function test() {
   UITourTest();
 }
 
 let tests = [
   taskify(function* test_menu_show_navbar() {
-    ise(button.open, false, "Menu should initially be closed");
+    is(button.open, false, "Menu should initially be closed");
     gContentAPI.showMenu("pocket");
 
     // The panel gets created dynamically.
     let widgetPanel = null;
     yield waitForConditionPromise(() => {
       widgetPanel = document.getElementById("customizationui-widget-panel");
       return widgetPanel && widgetPanel.state == "open";
     }, "Menu should be visible after showMenu()");
@@ -31,17 +31,17 @@ let tests = [
     ok(button.hasAttribute("open"), "Pocket button should know that the menu is open");
 
     widgetPanel.hidePopup();
     checkPanelIsHidden(widgetPanel);
   }),
   taskify(function* test_menu_show_appMenu() {
     CustomizableUI.addWidgetToArea("pocket-button", CustomizableUI.AREA_PANEL);
 
-    ise(PanelUI.multiView.hasAttribute("panelopen"), false, "Multiview should initially be closed");
+    is(PanelUI.multiView.hasAttribute("panelopen"), false, "Multiview should initially be closed");
     gContentAPI.showMenu("pocket");
 
     yield waitForConditionPromise(() => {
       return PanelUI.panel.state == "open";
     }, "Menu should be visible after showMenu()");
 
     ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide shouldn't be on the pocket panel");
     ok(PanelUI.multiView.showingSubView, "Subview should be open");
--- a/browser/components/uitour/test/browser_UITour_sync.js
+++ b/browser/components/uitour/test/browser_UITour_sync.js
@@ -45,19 +45,19 @@ let tests = [
 
     loadUITourTestPage(function(contentWindow) {
       let tabBrowser = gBrowser.selectedBrowser;
       // This command will replace the current tab - so add a load event
       // handler which will fire when that happens.
       tabBrowser.addEventListener("load", function onload(evt) {
         tabBrowser.removeEventListener("load", onload, true);
 
-        ise(tabBrowser.contentDocument.location.href,
-            "about:accounts?action=signup&entrypoint=uitour",
-            "about:accounts should have replaced the tab");
+        is(tabBrowser.contentDocument.location.href,
+           "about:accounts?action=signup&entrypoint=uitour",
+           "about:accounts should have replaced the tab");
 
         // the iframe in about:accounts will still be loading, so we stop
         // that before resetting the pref.
         tabBrowser.contentDocument.location.href = "about:blank";
         Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
         done();
       }, true);
 
--- a/browser/devtools/framework/test/browser_target_support.js
+++ b/browser/devtools/framework/test/browser_target_support.js
@@ -9,49 +9,49 @@ let { devtools } =
 let { Task } =
   Cu.import("resource://gre/modules/Task.jsm", {});
 let { WebAudioFront } =
   devtools.require("devtools/server/actors/webaudio");
 
 function* testTarget (client, target) {
   yield target.makeRemote();
 
-  ise(target.hasActor("timeline"), true, "target.hasActor() true when actor exists.");
-  ise(target.hasActor("webaudio"), true, "target.hasActor() true when actor exists.");
-  ise(target.hasActor("notreal"), false, "target.hasActor() false when actor does not exist.");
+  is(target.hasActor("timeline"), true, "target.hasActor() true when actor exists.");
+  is(target.hasActor("webaudio"), true, "target.hasActor() true when actor exists.");
+  is(target.hasActor("notreal"), false, "target.hasActor() false when actor does not exist.");
   // Create a front to ensure the actor is loaded
   let front = new WebAudioFront(target.client, target.form);
 
   let desc = yield target.getActorDescription("webaudio");
-  ise(desc.typeName, "webaudio",
+  is(desc.typeName, "webaudio",
     "target.getActorDescription() returns definition data for corresponding actor");
-  ise(desc.events["start-context"]["type"], "startContext",
+  is(desc.events["start-context"]["type"], "startContext",
     "target.getActorDescription() returns event data for corresponding actor");
 
   desc = yield target.getActorDescription("nope");
-  ise(desc, undefined, "target.getActorDescription() returns undefined for non-existing actor");
+  is(desc, undefined, "target.getActorDescription() returns undefined for non-existing actor");
   desc = yield target.getActorDescription();
-  ise(desc, undefined, "target.getActorDescription() returns undefined for undefined actor");
+  is(desc, undefined, "target.getActorDescription() returns undefined for undefined actor");
 
   let hasMethod = yield target.actorHasMethod("audionode", "getType");
-  ise(hasMethod, true,
+  is(hasMethod, true,
     "target.actorHasMethod() returns true for existing actor with method");
   hasMethod = yield target.actorHasMethod("audionode", "nope");
-  ise(hasMethod, false,
+  is(hasMethod, false,
     "target.actorHasMethod() returns false for existing actor with no method");
   hasMethod = yield target.actorHasMethod("nope", "nope");
-  ise(hasMethod, false,
+  is(hasMethod, false,
     "target.actorHasMethod() returns false for non-existing actor with no method");
   hasMethod = yield target.actorHasMethod();
-  ise(hasMethod, false,
+  is(hasMethod, false,
     "target.actorHasMethod() returns false for undefined params");
 
-  ise(target.getTrait("customHighlighters"), true,
+  is(target.getTrait("customHighlighters"), true,
     "target.getTrait() returns boolean when trait exists");
-  ise(target.getTrait("giddyup"), undefined,
+  is(target.getTrait("giddyup"), undefined,
     "target.getTrait() returns undefined when trait does not exist");
 
   close(target, client);
 }
 
 // Ensure target is closed if client is closed directly
 function test() {
   waitForExplicitFinish();
--- a/browser/devtools/performance/test/browser_perf-front-basic-profiler-01.js
+++ b/browser/devtools/performance/test/browser_perf-front-basic-profiler-01.js
@@ -12,17 +12,17 @@ function spawnTest () {
 
   let startModel = yield front.startRecording();
   let { profilerStartTime, timelineStartTime, memoryStartTime } = startModel;
 
   ok(startModel._profilerStartTime !== undefined,
     "A `_profilerStartTime` property exists in the recording model.");
   ok(startModel._timelineStartTime !== undefined,
     "A `_timelineStartTime` property exists in the recording model.");
-  ise(startModel._memoryStartTime, 0,
+  is(startModel._memoryStartTime, 0,
     "A `_memoryStartTime` property exists in the recording model, but it's 0.");
 
   yield busyWait(WAIT_TIME);
 
   let stopModel = yield front.stopRecording(startModel);
 
   ok(stopModel.getProfile(), "recording model has a profile after stopping.");
   ok(stopModel.getDuration(), "recording model has a duration after stopping.");
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
@@ -36,17 +36,17 @@ add_task(function*() {
 
 function compare (actual, expected, type) {
   actual.forEach(({ value, param }) => {
     value = getGripValue(value);
     if (typeof expected[param] === "function") {
       ok(expected[param](value), type + " has a passing value for " + param);
     }
     else {
-      ise(value, expected[param], type + " has correct default value and type for " + param);
+      is(value, expected[param], type + " has correct default value and type for " + param);
     }
   });
 
   info(Object.keys(expected).join(',') + " - " + JSON.stringify(expected));
 
   is(actual.length, Object.keys(expected).length,
     type + " has correct amount of properties.");
 }
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
@@ -9,39 +9,39 @@ add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   let freq = yield oscNode.getParam("frequency");
   info(typeof freq);
-  ise(freq, 440, "AudioNode:getParam correctly fetches AudioParam");
+  is(freq, 440, "AudioNode:getParam correctly fetches AudioParam");
 
   let type = yield oscNode.getParam("type");
-  ise(type, "sine", "AudioNode:getParam correctly fetches non-AudioParam");
+  is(type, "sine", "AudioNode:getParam correctly fetches non-AudioParam");
 
   type = yield oscNode.getParam("not-a-valid-param");
   ok(type.type === "undefined",
     "AudioNode:getParam correctly returns a grip value for `undefined` for an invalid param.");
 
   let resSuccess = yield oscNode.setParam("frequency", 220);
   freq = yield oscNode.getParam("frequency");
-  ise(freq, 220, "AudioNode:setParam correctly sets a `number` AudioParam");
+  is(freq, 220, "AudioNode:setParam correctly sets a `number` AudioParam");
   is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam");
 
   resSuccess = yield oscNode.setParam("type", "square");
   type = yield oscNode.getParam("type");
-  ise(type, "square", "AudioNode:setParam correctly sets a `string` non-AudioParam");
+  is(type, "square", "AudioNode:setParam correctly sets a `string` non-AudioParam");
   is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam");
 
   try {
     yield oscNode.setParam("frequency", "hello");
     ok(false, "setParam with invalid types should throw");
   } catch (e) {
     ok(/is not a finite floating-point/.test(e.message), "AudioNode:setParam returns error with correct message when attempting an invalid assignment");
     is(e.type, "TypeError", "AudioNode:setParam returns error with correct type when attempting an invalid assignment");
     freq = yield oscNode.getParam("frequency");
-    ise(freq, 220, "AudioNode:setParam does not modify value when an error occurs");
+    is(freq, 220, "AudioNode:setParam does not modify value when an error occurs");
   }
 
   yield removeTab(target.tab);
 });
--- a/browser/devtools/webaudioeditor/test/browser_callwatcher-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_callwatcher-02.js
@@ -21,24 +21,24 @@ add_task(function*() {
   yield waitForGraphRendered(panelWin, 2, 0);
 
   let error = yield evalInDebuggee("throwError()");
   is(error.lineNumber, 21, "error has correct lineNumber");
   is(error.columnNumber, 11, "error has correct columnNumber");
   is(error.name, "TypeError", "error has correct name");
   is(error.message, "Argument 1 is not valid for any of the 2-argument overloads of AudioNode.connect.", "error has correct message");
   is(error.stringified, "TypeError: Argument 1 is not valid for any of the 2-argument overloads of AudioNode.connect.", "error is stringified correctly");
-  ise(error.instanceof, true, "error is correctly an instanceof TypeError");
+  is(error.instanceof, true, "error is correctly an instanceof TypeError");
   is(error.fileName, "http://example.com/browser/browser/devtools/webaudioeditor/test/doc_bug_1112378.html", "error has correct fileName");
 
   error = yield evalInDebuggee("throwDOMException()");
   is(error.lineNumber, 37, "exception has correct lineNumber");
   is(error.columnNumber, 0, "exception has correct columnNumber");
   is(error.code, 9, "exception has correct code");
   is(error.result, 2152923145, "exception has correct result");
   is(error.name, "NotSupportedError", "exception has correct name");
   is(error.message, "Operation is not supported", "exception has correct message");
   is(error.stringified, "NotSupportedError: Operation is not supported", "exception is stringified correctly");
-  ise(error.instanceof, true, "exception is correctly an instance of DOMException");
+  is(error.instanceof, true, "exception is correctly an instance of DOMException");
   is(error.filename, "http://example.com/browser/browser/devtools/webaudioeditor/test/doc_bug_1112378.html", "exception has correct filename");
 
   yield teardown(target);
 });
--- a/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
@@ -14,31 +14,31 @@ add_task(function*() {
   reload(target);
 
   var [actors] = yield Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
 
   var { nodes, edges } = countGraphObjects(panelWin);
-  ise(nodes, 3, "should only be 3 nodes.");
-  ise(edges, 2, "should only be 2 edges.");
+  is(nodes, 3, "should only be 3 nodes.");
+  is(edges, 2, "should only be 2 edges.");
 
   navigate(target, SIMPLE_NODES_URL);
 
   var [actors] = yield Promise.all([
     getN(gFront, "create-node", 15),
     waitForGraphRendered(panelWin, 15, 0)
   ]);
 
   is($("#reload-notice").hidden, true,
     "The 'reload this page' notice should be hidden after context found after navigation.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should be hidden after context found after navigation.");
   is($("#content").hidden, false,
     "The tool's content should reappear without closing and reopening the toolbox.");
 
   var { nodes, edges } = countGraphObjects(panelWin);
-  ise(nodes, 15, "after navigation, should have 15 nodes");
-  ise(edges, 0, "after navigation, should have 0 edges.");
+  is(nodes, 15, "after navigation, should have 15 nodes");
+  is(edges, 0, "after navigation, should have 0 edges.");
 
   yield teardown(target);
 });
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
@@ -14,24 +14,24 @@ add_task(function*() {
   reload(target);
 
   let [actors] = yield Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
 
   let { nodes, edges } = countGraphObjects(panelWin);
-  ise(nodes, 3, "should only be 3 nodes.");
-  ise(edges, 2, "should only be 2 edges.");
+  is(nodes, 3, "should only be 3 nodes.");
+  is(edges, 2, "should only be 2 edges.");
 
   reload(target);
 
   [actors] = yield Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
 
   ({ nodes, edges } = countGraphObjects(panelWin));
-  ise(nodes, 3, "after reload, should only be 3 nodes.");
-  ise(edges, 2, "after reload, should only be 2 edges.");
+  is(nodes, 3, "after reload, should only be 3 nodes.");
+  is(edges, 2, "after reload, should only be 2 edges.");
 
   yield teardown(target);
 });
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
@@ -31,17 +31,17 @@ add_task(function*() {
 
   [actors] = yield Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
   nodeIds = actors.map(actor => actor.actorID);
 
   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
-  ise(InspectorView.getCurrentAudioNode(), null,
+  is(InspectorView.getCurrentAudioNode(), null,
     "InspectorView has no current node set on reset.");
 
   yield clickGraphNode(panelWin, nodeIds[2], true);
   ok(InspectorView.isVisible(),
     "InspectorView visible after selecting a node after a reset.");
   is(InspectorView.getCurrentAudioNode().id, nodeIds[2], "InspectorView has correct node set upon clicking graph node after a reset.");
 
   yield teardown(target);
--- a/browser/devtools/webaudioeditor/test/head.js
+++ b/browser/devtools/webaudioeditor/test/head.js
@@ -273,17 +273,17 @@ function checkVariableView (view, index,
       value = JSON.parse(value);
     }
     catch (e) {}
     if (typeof hash[variable] === "function") {
       ok(hash[variable](value),
         "Passing property value of " + value + " for " + variable + " " + description);
     }
     else {
-      ise(value, hash[variable],
+      is(value, hash[variable],
         "Correct property value of " + hash[variable] + " for " + variable + " " + description);
     }
   });
 }
 
 function modifyVariableView (win, view, index, prop, value) {
   let deferred = Promise.defer();
   let scope = view.getScopeAtIndex(index);
@@ -535,9 +535,9 @@ const NODE_CONSTRUCTORS = {
   "WaveShaperNode": "WaveShaper",
   "PannerNode": "Panner",
   "ConvolverNode": "Convolver",
   "ChannelSplitterNode": "ChannelSplitter",
   "ChannelMergerNode": "ChannelMerger",
   "DynamicsCompressorNode": "DynamicsCompressor",
   "OscillatorNode": "Oscillator",
   "StereoPannerNode": "StereoPanner"
-};
\ No newline at end of file
+};
--- a/browser/devtools/webconsole/test/browser_webconsole_output_05.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_05.js
@@ -59,20 +59,20 @@ let inputTests = [
     printOutput: "Invalid Date",
     inspectable: true,
     variablesViewLabel: "Invalid Date",
   },
 
   // 7
   {
     input: "Date.prototype",
-    output: "Date",
+    output: /Object \{.*\}/,
     printOutput: "Invalid Date",
     inspectable: true,
-    variablesViewLabel: "Date",
+    variablesViewLabel: "Object",
   },
 
   // 8
   {
     input: "new Number(43)",
     output: "43",
     inspectable: true,
   },
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -128,27 +128,16 @@ case "$target" in
     android_platform="$android_ndk"/platforms/android-"$android_version"/arch-"$target_name"
 
     if test -d "$android_platform" ; then
         AC_MSG_RESULT([$android_platform])
     else
         AC_MSG_ERROR([not found. Please check your NDK. With the current configuration, it should be in $android_platform])
     fi
 
-    dnl Old NDK support. If minimum requirement is changed to NDK r8b,
-    dnl please remove this.
-    case "$target_cpu" in
-    i?86)
-        if ! test -e "$android_toolchain"/bin/"$android_tool_prefix"-gcc; then
-            dnl Old NDK toolchain name
-            android_tool_prefix="i686-android-linux"
-        fi
-        ;;
-    esac
-
     dnl set up compilers
     TOOLCHAIN_PREFIX="$android_toolchain/bin/$android_tool_prefix-"
     AS="$android_toolchain"/bin/"$android_tool_prefix"-as
     if test -z "$CC"; then
         CC="$android_toolchain"/bin/"$android_tool_prefix"-gcc
     fi
     if test -z "$CXX"; then
         CXX="$android_toolchain"/bin/"$android_tool_prefix"-g++
@@ -218,28 +207,24 @@ if test "$OS_TARGET" = "Android" -a -z "
         ANDROID_CPU_ARCH=mips
         ;;
     esac
 
     AC_SUBST(ANDROID_CPU_ARCH)
 
     if test -z "$STLPORT_CPPFLAGS$STLPORT_LIBS"; then
         if test -n "$MOZ_ANDROID_LIBSTDCXX" ; then
-            if test -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/libs/$ANDROID_CPU_ARCH/libgnustl_static.a"; then
-                # android-ndk-r8b
-                STLPORT_LIBS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/libs/$ANDROID_CPU_ARCH/ -lgnustl_static"
-                STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/libs/$ANDROID_CPU_ARCH/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version/include/backward"
-            elif test -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/libgnustl_static.a"; then
-                # android-ndk-r7, android-ndk-r7b, android-ndk-r8
-                STLPORT_LIBS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/ -lgnustl_static"
-                STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/include"
-            elif test -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/libstdc++.a"; then
-                # android-ndk-r5c, android-ndk-r6, android-ndk-r6b
-                STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/include"
-                STLPORT_LIBS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/ -lstdc++"
+            # android-ndk-r8b and later
+            ndk_base="$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version"
+            ndk_libs="$ndk_base/libs/$ANDROID_CPU_ARCH"
+            ndk_include="$ndk_base/include"
+
+            if test -e "$ndk_libs/libgnustl_static.a"; then
+                STLPORT_LIBS="-L$ndk_libs -lgnustl_static"
+                STLPORT_CPPFLAGS="-I$ndk_include -I$ndk_include/backward -I$ndk_libs/include"
             else
                 AC_MSG_ERROR([Couldn't find path to gnu-libstdc++ in the android ndk])
             fi
         else
             STLPORT_CPPFLAGS="-isystem $_topsrcdir/build/stlport/stlport -isystem $_topsrcdir/build/stlport/overrides -isystem $android_ndk/sources/cxx-stl/system/include"
         fi
     fi
     CXXFLAGS="$CXXFLAGS $STLPORT_CPPFLAGS"
--- a/configure.in
+++ b/configure.in
@@ -7273,17 +7273,17 @@ fi
 
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
 if test "$OS_TARGET" = Android; then
     MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
     if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
         MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_mutex_trylock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
     fi
-    if test "$MOZ_WIDGET_TOOLKIT" = android; then
+    if test "$MOZ_WIDGET_TOOLKIT" = android -a "$MOZ_ANDROID_MIN_SDK_VERSION" -lt 11; then
         MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=gai_strerror"
     fi
 fi
 
 AC_SUBST_LIST(MOZ_GLUE_WRAP_LDFLAGS)
 export MOZ_GLUE_WRAP_LDFLAGS
 
 dnl ========================================================
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6680,29 +6680,25 @@ nsDocShell::ForceRefreshURIFromTimer(nsI
   return ForceRefreshURI(aURI, aDelay, aMetaRefresh);
 }
 
 bool
 nsDocShell::DoAppRedirectIfNeeded(nsIURI* aURI,
                                   nsIDocShellLoadInfo* aLoadInfo,
                                   bool aFirstParty)
 {
-  uint32_t appId;
-  nsresult rv = GetAppId(&appId);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
+  uint32_t appId = nsIDocShell::GetAppId();
 
   if (appId != nsIScriptSecurityManager::NO_APP_ID &&
       appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
     nsCOMPtr<nsIAppsService> appsService =
       do_GetService(APPS_SERVICE_CONTRACTID);
     NS_ASSERTION(appsService, "No AppsService available");
     nsCOMPtr<nsIURI> redirect;
-    rv = appsService->GetRedirect(appId, aURI, getter_AddRefs(redirect));
+    nsresult rv = appsService->GetRedirect(appId, aURI, getter_AddRefs(redirect));
     if (NS_SUCCEEDED(rv) && redirect) {
       rv = LoadURI(redirect, aLoadInfo, nsIWebNavigation::LOAD_FLAGS_NONE,
                    aFirstParty);
       if (NS_SUCCEEDED(rv)) {
         return true;
       }
     }
   }
@@ -13840,19 +13836,17 @@ nsDocShell::GetAppId(uint32_t* aAppId)
   }
 
   return parent->GetAppId(aAppId);
 }
 
 NS_IMETHODIMP
 nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
 {
-  uint32_t appId;
-  GetAppId(&appId);
-
+  uint32_t appId = nsIDocShell::GetAppId();
   if (appId != nsIScriptSecurityManager::NO_APP_ID &&
       appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
     nsCOMPtr<nsIAppsService> appsService =
       do_GetService(APPS_SERVICE_CONTRACTID);
     NS_ASSERTION(appsService, "No AppsService available");
     appsService->GetManifestURLByLocalId(appId, aAppManifestURL);
   } else {
     aAppManifestURL.SetLength(0);
--- a/docshell/test/chrome/docshell_helpers.js
+++ b/docshell/test/chrome/docshell_helpers.js
@@ -1,12 +1,12 @@
 /**  
  * Import common SimpleTest methods so that they're usable in this window.
  */
-var imports = [ "SimpleTest", "is", "isnot", "ise", "ok", "onerror", "todo",
+var imports = [ "SimpleTest", "is", "isnot", "ok", "onerror", "todo",
   "todo_is", "todo_isnot" ];
 for each (var name in imports) {
   window[name] = window.opener.wrappedJSObject[name];
 }
 
 /**
  * Define global constants and variables.
  */
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -8,16 +8,17 @@
  * Base class for all element classes; this provides an implementation
  * of DOM Core's nsIDOMElement, implements nsIContent, provides
  * utility methods for subclasses, and so forth.
  */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/SegmentedVector.h"
 #include "mozilla/StaticPtr.h"
 
 #include "mozilla/dom/FragmentOrElement.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
@@ -1220,34 +1221,22 @@ FragmentOrElement::FireNodeInserted(nsID
       mozAutoSubtreeModified subtree(aDoc, aParent);
       (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
     }
   }
 }
 
 //----------------------------------------------------------------------
 
-// nsISupports implementation
-
-#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
-
-class ContentUnbinder : public nsRunnable
+class ContentUnbinder
 {
-public:
-  ContentUnbinder()
-  {
-    mLast = this;
-  }
-
-  ~ContentUnbinder()
-  {
-    Run();
-  }
-
-  void UnbindSubtree(nsIContent* aNode)
+  static const size_t kSegmentSize = sizeof(void*) * 512;
+  typedef SegmentedVector<nsCOMPtr<nsIContent>, kSegmentSize, InfallibleAllocPolicy> ContentArray;
+
+  static void UnbindSubtree(nsIContent* aNode)
   {
     if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
         aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
       return;
     }
     FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
     uint32_t childCount = container->mAttrsAndChildren.ChildCount();
     if (childCount) {
@@ -1263,86 +1252,66 @@ public:
           container->mFirstChild = nullptr;
         }
         UnbindSubtree(child);
         child->UnbindFromTree();
       }
     }
   }
 
-  NS_IMETHOD Run()
+  // These two methods are based on DeferredFinalizerImpl.
+
+  static void*
+  AppendContentUnbinderPointer(void* aData, void* aObject)
   {
-    nsAutoScriptBlocker scriptBlocker;
-    uint32_t len = mSubtreeRoots.Length();
-    if (len) {
-      PRTime start = PR_Now();
-      for (uint32_t i = 0; i < len; ++i) {
-        UnbindSubtree(mSubtreeRoots[i]);
-      }
-      mSubtreeRoots.Clear();
-      Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
-                            uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC);
+    ContentArray* contentArray = static_cast<ContentArray*>(aData);
+    if (!contentArray) {
+      contentArray = new ContentArray();
     }
-    nsCycleCollector_dispatchDeferredDeletion();
-    if (this == sContentUnbinder) {
-      sContentUnbinder = nullptr;
-      if (mNext) {
-        nsRefPtr<ContentUnbinder> next;
-        next.swap(mNext);
-        sContentUnbinder = next;
-        next->mLast = mLast;
-        mLast = nullptr;
-        NS_DispatchToMainThread(next);
-      }
-    }
-    return NS_OK;
+
+    contentArray->InfallibleAppend(dont_AddRef(static_cast<nsIContent*>(aObject)));
+    return contentArray;
   }
 
-  static void UnbindAll()
+  static bool
+  DeferredFinalize(uint32_t aSliceBudget, void* aData)
   {
-    nsRefPtr<ContentUnbinder> ub = sContentUnbinder;
-    sContentUnbinder = nullptr;
-    while (ub) {
-      ub->Run();
-      ub = ub->mNext;
+    MOZ_ASSERT(aSliceBudget > 0, "nonsensical/useless call with aSliceBudget == 0");
+    nsAutoScriptBlocker scriptBlocker;
+    ContentArray* contentArray = static_cast<ContentArray*>(aData);
+
+    size_t numToRemove = contentArray->Length();
+    if (aSliceBudget < numToRemove) {
+      numToRemove = aSliceBudget;
     }
-  }
-
-  static void Append(nsIContent* aSubtreeRoot)
-  {
-    if (!sContentUnbinder) {
-      sContentUnbinder = new ContentUnbinder();
-      nsCOMPtr<nsIRunnable> e = sContentUnbinder;
-      NS_DispatchToMainThread(e);
+
+    for (size_t i = 0; i < numToRemove; ++i) {
+      nsCOMPtr<nsIContent> element = contentArray->GetLast().forget();
+      contentArray->PopLast();
+      UnbindSubtree(element);
     }
 
-    if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
-        SUBTREE_UNBINDINGS_PER_RUNNABLE) {
-      sContentUnbinder->mLast->mNext = new ContentUnbinder();
-      sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
+    nsCycleCollector_dispatchDeferredDeletion();
+
+    if (contentArray->Length() == 0) {
+      delete contentArray;
+      return true;
     }
-    sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
+    return false;
   }
 
-private:
-  nsAutoTArray<nsCOMPtr<nsIContent>,
-               SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
-  nsRefPtr<ContentUnbinder>                     mNext;
-  ContentUnbinder*                              mLast;
-  static ContentUnbinder*                       sContentUnbinder;
+public:
+  static void
+  Append(nsIContent* aSubtreeRoot)
+  {
+    nsCOMPtr<nsIContent> root = aSubtreeRoot;
+    mozilla::DeferredFinalize(AppendContentUnbinderPointer, DeferredFinalize, root.forget().take());
+  }
 };
 
-ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
-
-void
-FragmentOrElement::ClearContentUnbinder()
-{
-  ContentUnbinder::UnbindAll();
-}
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
   nsINode::Unlink(tmp);
 
   // The XBL binding is removed by RemoveFromBindingManagerRunnable
   // which is dispatched in UnbindFromTree.
 
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -210,17 +210,16 @@ public:
     return mRefCnt.IsPurple();
   }
 
   virtual void RemovePurple() override
   {
     mRefCnt.RemovePurple();
   }
 
-  static void ClearContentUnbinder();
   static bool CanSkip(nsINode* aNode, bool aRemovingAllowed);
   static bool CanSkipInCC(nsINode* aNode);
   static bool CanSkipThis(nsINode* aNode);
   static void RemoveBlackMarkedNode(nsINode* aNode);
   static void MarkNodeChildren(nsINode* aNode);
   static void InitCCCallbacks();
   static void MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
                            void *aData);
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -1787,63 +1787,16 @@ WebSocket::CreateAndDispatchCloseEvent(b
 
   nsRefPtr<CloseEvent> event =
     CloseEvent::Constructor(this, NS_LITERAL_STRING("close"), init);
   event->SetTrusted(true);
 
   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 }
 
-namespace {
-
-class PrefEnabledRunnable final : public WorkerMainThreadRunnable
-{
-public:
-  explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate)
-    : WorkerMainThreadRunnable(aWorkerPrivate)
-    , mEnabled(false)
-  { }
-
-  bool MainThreadRun() override
-  {
-    AssertIsOnMainThread();
-    mEnabled = Preferences::GetBool("dom.workers.websocket.enabled", false);
-    return true;
-  }
-
-  bool IsEnabled() const
-  {
-    return mEnabled;
-  }
-
-private:
-  bool mEnabled;
-};
-
-} // anonymous namespace
-
-bool
-WebSocket::PrefEnabled(JSContext* /* aCx */, JSObject* /* aGlobal */)
-{
-  // WebSockets are always enabled on main-thread.
-  if (NS_IsMainThread()) {
-    return true;
-  }
-
-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(workerPrivate);
-  workerPrivate->AssertIsOnWorkerThread();
-
-  nsRefPtr<PrefEnabledRunnable> runnable =
-    new PrefEnabledRunnable(workerPrivate);
-  runnable->Dispatch(workerPrivate->GetJSContext());
-
-  return runnable->IsEnabled();
-}
-
 nsresult
 WebSocketImpl::ParseURL(const nsAString& aURL)
 {
   AssertIsOnMainThread();
   NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL);
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -312,18 +312,16 @@ MarkWindowList(nsISimpleEnumerator* aWin
   }
 }
 
 nsresult
 nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
                                  const char16_t* aData)
 {
   if (!strcmp(aTopic, "xpcom-shutdown")) {
-    Element::ClearContentUnbinder();
-
     nsCOMPtr<nsIObserverService> obs =
       mozilla::services::GetObserverService();
     if (!obs)
       return NS_ERROR_FAILURE;
 
     // No need for kungFuDeathGrip here, yay observerservice!
     obs->RemoveObserver(this, "xpcom-shutdown");
     obs->RemoveObserver(this, "cycle-collector-begin");
@@ -338,19 +336,16 @@ nsCCUncollectableMarker::Observe(nsISupp
                !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic");
 
   // JS cleanup can be slow. Do it only if there has been a GC.
   bool cleanupJS =
     nsJSContext::CleanupsSinceLastGC() == 0 &&
     !strcmp(aTopic, "cycle-collector-forget-skippable");
 
   bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");
-  if (prepareForCC) {
-    Element::ClearContentUnbinder();
-  }
 
   // Increase generation to effectively unmark all current objects
   if (!++sGeneration) {
     ++sGeneration;
   }
 
   nsFocusManager::MarkUncollectableForCCGeneration(sGeneration);
 
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -17,39 +17,35 @@
 #include "nsIURI.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMWindow.h"
 #include "nsIContent.h"
 #include "nsCOMArray.h"
 
 NS_IMPL_ISUPPORTS(nsContentPolicy, nsIContentPolicy)
 
-#ifdef PR_LOGGING
 static PRLogModuleInfo* gConPolLog;
-#endif
 
 nsresult
 NS_NewContentPolicy(nsIContentPolicy **aResult)
 {
   *aResult = new nsContentPolicy;
   if (!*aResult)
       return NS_ERROR_OUT_OF_MEMORY;
   NS_ADDREF(*aResult);
   return NS_OK;
 }
 
 nsContentPolicy::nsContentPolicy()
     : mPolicies(NS_CONTENTPOLICY_CATEGORY)
     , mSimplePolicies(NS_SIMPLECONTENTPOLICY_CATEGORY)
 {
-#ifdef PR_LOGGING
     if (! gConPolLog) {
         gConPolLog = PR_NewLogModule("nsContentPolicy");
     }
-#endif
 }
 
 nsContentPolicy::~nsContentPolicy()
 {
 }
 
 #ifdef DEBUG
 #define WARN_IF_URI_UNINITIALIZED(uri,name)                         \
@@ -182,24 +178,22 @@ nsContentPolicy::CheckPolicy(CPMethod   
         }
     }
 
     // everyone returned failure, or no policies: sanitize result
     *decision = nsIContentPolicy::ACCEPT;
     return NS_OK;
 }
 
-#ifdef PR_LOGGING
-
 //uses the parameters from ShouldXYZ to produce and log a message
 //logType must be a literal string constant
 #define LOG_CHECK(logType)                                                    \
   PR_BEGIN_MACRO                                                              \
-    /* skip all this nonsense if the call failed */                           \
-    if (NS_SUCCEEDED(rv)) {                                                   \
+    /* skip all this nonsense if the call failed or logging is disabled */    \
+    if (NS_SUCCEEDED(rv) && PR_LOG_TEST(gConPolLog, PR_LOG_DEBUG)) {          \
       const char *resultName;                                                 \
       if (decision) {                                                         \
         resultName = NS_CP_ResponseName(*decision);                           \
       } else {                                                                \
         resultName = "(null ptr)";                                            \
       }                                                                       \
       nsAutoCString spec("None");                                             \
       if (contentLocation) {                                                  \
@@ -211,22 +205,16 @@ nsContentPolicy::CheckPolicy(CPMethod   
       }                                                                       \
       PR_LOG(gConPolLog, PR_LOG_DEBUG,                                        \
              ("Content Policy: " logType ": <%s> <Ref:%s> result=%s",         \
               spec.get(), refSpec.get(), resultName)                          \
              );                                                               \
     }                                                                         \
   PR_END_MACRO
 
-#else //!defined(PR_LOGGING)
-
-#define LOG_CHECK(logType)
-
-#endif //!defined(PR_LOGGING)
-
 NS_IMETHODIMP
 nsContentPolicy::ShouldLoad(uint32_t          contentType,
                             nsIURI           *contentLocation,
                             nsIURI           *requestingLocation,
                             nsISupports      *requestingContext,
                             const nsACString &mimeType,
                             nsISupports      *extra,
                             nsIPrincipal     *requestPrincipal,
--- a/dom/base/nsContentPolicyUtils.h
+++ b/dom/base/nsContentPolicyUtils.h
@@ -51,17 +51,16 @@ class nsIPrincipal;
 // Offer convenient translations of constants -> const char*
 
 // convenience macro to reduce some repetative typing...
 // name is the name of a constant from this interface
 #define CASE_RETURN(name)          \
   case nsIContentPolicy:: name :   \
     return #name
 
-#ifdef PR_LOGGING
 /**
  * Returns a string corresponding to the name of the response constant, or
  * "<Unknown Response>" if an unknown response value is given.
  *
  * The return value is static and must not be freed.
  *
  * @param response the response code
  * @return the name of the given response code
@@ -114,18 +113,16 @@ NS_CP_ContentTypeName(uint32_t contentTy
     CASE_RETURN( TYPE_BEACON            );
     CASE_RETURN( TYPE_FETCH             );
     CASE_RETURN( TYPE_IMAGESET          );
    default:
     return "<Unknown Type>";
   }
 }
 
-#endif // defined(PR_LOGGING)
-
 #undef CASE_RETURN
 
 /* Passes on parameters from its "caller"'s context. */
 #define CHECK_CONTENT_POLICY(action)                                          \
   PR_BEGIN_MACRO                                                              \
     nsCOMPtr<nsIContentPolicy> policy =                                       \
          do_GetService(NS_CONTENTPOLICY_CONTRACTID);                          \
     if (!policy)                                                              \
--- a/dom/base/nsDOMDataChannel.cpp
+++ b/dom/base/nsDOMDataChannel.cpp
@@ -4,19 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMDataChannel.h"
 
 #include "base/basictypes.h"
 #include "prlog.h"
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* GetDataChannelLog();
-#endif
 #undef LOG
 #define LOG(args) PR_LOG(GetDataChannelLog(), PR_LOG_DEBUG, args)
 
 
 #include "nsDOMDataChannelDeclarations.h"
 #include "nsDOMDataChannel.h"
 #include "nsIDOMDataChannel.h"
 #include "nsIDOMMessageEvent.h"
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -236,20 +236,18 @@
 #include "IPeerConnection.h"
 #endif // MOZ_WEBRTC
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
-#ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
 static PRLogModuleInfo* gCspPRLog;
-#endif
 
 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
 
 nsIdentifierMapEntry::~nsIdentifierMapEntry()
 {
 }
 
 void
@@ -1586,27 +1584,25 @@ nsIDocument::nsIDocument()
 
 nsDocument::nsDocument(const char* aContentType)
   : nsIDocument()
   , mAnimatingImages(true)
   , mViewportType(Unknown)
 {
   SetContentTypeInternal(nsDependentCString(aContentType));
 
-#ifdef PR_LOGGING
   if (!gDocumentLeakPRLog)
     gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak");
 
   if (gDocumentLeakPRLog)
     PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
            ("DOCUMENT %p created", this));
 
   if (!gCspPRLog)
     gCspPRLog = PR_NewLogModule("CSP");
-#endif
 
   // Start out mLastStyleSheetSet as null, per spec
   SetDOMStringToNull(mLastStyleSheetSet);
 
   // void state used to differentiate an empty source from an unselected source
   mPreloadPictureFoundSource.SetIsVoid(true);
 
   if (!sProcessingStack) {
@@ -1635,21 +1631,19 @@ nsIDocument::~nsIDocument()
   }
 
   UnlinkOriginalDocumentIfStatic();
 }
 
 
 nsDocument::~nsDocument()
 {
-#ifdef PR_LOGGING
   if (gDocumentLeakPRLog)
     PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
            ("DOCUMENT %p destroyed", this));
-#endif
 
   NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
 
   // Note: This assert is only non-fatal because mochitest-bc triggers
   // it... as well as the preceding assert about !mIsShowing.
   NS_ASSERTION(!mObservingAppThemeChanged,
                "Document leaked to shutdown, then the observer service dropped "
                "its ref to us so we were able to go away.");
@@ -2304,23 +2298,21 @@ nsDocument::Reset(nsIChannel* aChannel, 
 }
 
 void
 nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
                        nsIPrincipal* aPrincipal)
 {
   NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
 
-#ifdef PR_LOGGING
   if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
     nsAutoCString spec;
     aURI->GetSpec(spec);
     PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get());
   }
-#endif
 
   mSecurityInfo = nullptr;
 
   mDocumentLoadGroup = nullptr;
 
   // Delete references to sub-documents and kill the subdocument map,
   // if any. It holds strong references
   delete mSubDocuments;
@@ -2639,36 +2631,27 @@ WarnIfSandboxIneffective(nsIDocShell* aD
 
 nsresult
 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
                               nsILoadGroup* aLoadGroup,
                               nsISupports* aContainer,
                               nsIStreamListener **aDocListener,
                               bool aReset, nsIContentSink* aSink)
 {
-#ifdef PR_LOGGING
   if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
     nsCOMPtr<nsIURI> uri;
     aChannel->GetURI(getter_AddRefs(uri));
     nsAutoCString spec;
     if (uri)
       uri->GetSpec(spec);
     PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get());
   }
-#endif
-
-#ifdef DEBUG
-  {
-    uint32_t appId;
-    nsresult rv = NodePrincipal()->GetAppId(&appId);
-    NS_ENSURE_SUCCESS(rv, rv);
-    MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
-               "Document should never have UNKNOWN_APP_ID");
-  }
-#endif
+
+  MOZ_ASSERT(NodePrincipal()->GetAppId() != nsIScriptSecurityManager::UNKNOWN_APP_ID,
+             "Document should never have UNKNOWN_APP_ID");
 
   MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
              "Bad readyState");
   SetReadyStateInternal(READYSTATE_LOADING);
 
   if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
     mLoadedAsData = true;
     // We need to disable script & style loading in this case.
@@ -2782,23 +2765,21 @@ AppendCSPFromHeader(nsIContentSecurityPo
   // concatenated into one comma-separated list of policies.
   // See RFC2616 section 4.2 (last paragraph)
   nsresult rv = NS_OK;
   nsCharSeparatedTokenizer tokenizer(aHeaderValue, ',');
   while (tokenizer.hasMoreTokens()) {
       const nsSubstring& policy = tokenizer.nextToken();
       rv = csp->AppendPolicy(policy, aReportOnly);
       NS_ENSURE_SUCCESS(rv, rv);
-#ifdef PR_LOGGING
       {
         PR_LOG(gCspPRLog, PR_LOG_DEBUG,
                 ("CSP refined with policy: \"%s\"",
                 NS_ConvertUTF16toUTF8(policy).get()));
       }
-#endif
   }
   return NS_OK;
 }
 
 bool
 nsDocument::IsLoopDocument(nsIChannel *aChannel)
 {
   nsCOMPtr<nsIURI> chanURI;
@@ -2825,20 +2806,18 @@ nsDocument::IsLoopDocument(nsIChannel *a
   return isLoop;
 }
 
 nsresult
 nsDocument::InitCSP(nsIChannel* aChannel)
 {
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   if (!CSPService::sCSPEnabled) {
-#ifdef PR_LOGGING
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
            ("CSP is disabled, skipping CSP init for document %p", this));
-#endif
     return NS_OK;
   }
 
   nsAutoCString tCspHeaderValue, tCspROHeaderValue;
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     httpChannel->GetResponseHeader(
@@ -2859,55 +2838,52 @@ nsDocument::InitCSP(nsIChannel* aChannel
   bool applyAppDefaultCSP = false;
   bool applyAppManifestCSP = false;
 
   nsAutoString appManifestCSP;
   nsAutoString appDefaultCSP;
   if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
     nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
     if (appsService) {
-      uint32_t appId = 0;
-      if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
-        appsService->GetManifestCSPByLocalId(appId, appManifestCSP);
-        if (!appManifestCSP.IsEmpty()) {
-          applyAppManifestCSP = true;
-        }
-        appsService->GetDefaultCSPByLocalId(appId, appDefaultCSP);
-        if (!appDefaultCSP.IsEmpty()) {
-          applyAppDefaultCSP = true;
-        }
+      uint32_t appId = principal->GetAppId();
+      appsService->GetManifestCSPByLocalId(appId, appManifestCSP);
+      if (!appManifestCSP.IsEmpty()) {
+        applyAppManifestCSP = true;
+      }
+      appsService->GetDefaultCSPByLocalId(appId, appDefaultCSP);
+      if (!appDefaultCSP.IsEmpty()) {
+        applyAppDefaultCSP = true;
       }
     }
   }
 
  // Check if this is part of the Loop/Hello service
  bool applyLoopCSP = IsLoopDocument(aChannel);
 
   // If there's no CSP to apply, go ahead and return early
   if (!applyAppDefaultCSP &&
       !applyAppManifestCSP &&
       !applyLoopCSP &&
       cspHeaderValue.IsEmpty() &&
       cspROHeaderValue.IsEmpty()) {
-#ifdef PR_LOGGING
-    nsCOMPtr<nsIURI> chanURI;
-    aChannel->GetURI(getter_AddRefs(chanURI));
-    nsAutoCString aspec;
-    chanURI->GetAsciiSpec(aspec);
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("no CSP for document, %s, %s",
-            aspec.get(),
-            applyAppDefaultCSP ? "is app" : "not an app"));
-#endif
+    if (PR_LOG_TEST(gCspPRLog, PR_LOG_DEBUG)) {
+      nsCOMPtr<nsIURI> chanURI;
+      aChannel->GetURI(getter_AddRefs(chanURI));
+      nsAutoCString aspec;
+      chanURI->GetAsciiSpec(aspec);
+      PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+             ("no CSP for document, %s, %s",
+              aspec.get(),
+              applyAppDefaultCSP ? "is app" : "not an app"));
+    }
+
     return NS_OK;
   }
 
-#ifdef PR_LOGGING
   PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this));
-#endif
 
   nsresult rv;
 
   // If Document is an app check to see if we already set CSP and return early
   // if that is indeed the case.
   //
   // In general (see bug 947831), we should not be setting CSP on a principal
   // that aliases another document. For non-app code this is not a problem
@@ -2916,32 +2892,28 @@ nsDocument::InitCSP(nsIChannel* aChannel
   // about:srcodoc iframes) and thus won't try to set the CSP again. This
   // check ensures that we do not try to set CSP for an app.
   if (applyAppDefaultCSP || applyAppManifestCSP) {
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     rv = principal->GetCsp(getter_AddRefs(csp));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (csp) {
-#ifdef PR_LOGGING
       PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s",
            "This document is sharing principal with another document.",
            "Since the document is an app, CSP was already set.",
            "Skipping attempt to set CSP."));
-#endif
       return NS_OK;
     }
   }
 
   csp = do_CreateInstance("@mozilla.org/cspcontext;1", &rv);
 
   if (NS_FAILED(rv)) {
-#ifdef PR_LOGGING
     PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
-#endif
     return rv;
   }
 
   // used as a "self" identifier for the CSP.
   nsCOMPtr<nsIURI> selfURI;
   aChannel->GetURI(getter_AddRefs(selfURI));
 
   // Store the request context for violation reports
@@ -2984,20 +2956,18 @@ nsDocument::InitCSP(nsIChannel* aChannel
   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   if (docShell) {
     bool safeAncestry = false;
 
     // PermitsAncestry sends violation reports when necessary
     rv = csp->PermitsAncestry(docShell, &safeAncestry);
 
     if (NS_FAILED(rv) || !safeAncestry) {
-#ifdef PR_LOGGING
       PR_LOG(gCspPRLog, PR_LOG_DEBUG,
               ("CSP doesn't like frame's ancestry, not loading."));
-#endif
       // stop!  ERROR page!
       aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
     }
   }
 
   // ----- Set up any Referrer Policy specified by CSP
   bool hasReferrerPolicy = false;
   uint32_t referrerPolicy = mozilla::net::RP_Default;
@@ -3006,36 +2976,32 @@ nsDocument::InitCSP(nsIChannel* aChannel
   if (hasReferrerPolicy) {
     // Referrer policy spec (section 6.1) says that once the referrer policy
     // is set, any future attempts to change it result in No-Referrer.
     if (!mReferrerPolicySet) {
       mReferrerPolicy = static_cast<ReferrerPolicy>(referrerPolicy);
       mReferrerPolicySet = true;
     } else if (mReferrerPolicy != referrerPolicy) {
       mReferrerPolicy = mozilla::net::RP_No_Referrer;
-#ifdef PR_LOGGING
       {
         PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s",
                 "CSP wants to set referrer, but nsDocument"
                 "already has it set. No referrers will be sent"));
       }
-#endif
     }
 
     // Referrer Policy is set separately for the speculative parser in
     // nsHTMLDocument::StartDocumentLoad() so there's nothing to do here for
     // speculative loads.
   }
 
   rv = principal->SetCsp(csp);
   NS_ENSURE_SUCCESS(rv, rv);
-#ifdef PR_LOGGING
   PR_LOG(gCspPRLog, PR_LOG_DEBUG,
          ("Inserted CSP into principal %p", principal));
-#endif
 
   return NS_OK;
 }
 
 void
 nsDocument::StopDocumentLoad()
 {
   if (mParser) {
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -66,48 +66,37 @@
 #ifndef XP_MACOSX
 #include "nsIScriptError.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::widget;
 
-#ifdef PR_LOGGING
-
 // Two types of focus pr logging are available:
 //   'Focus' for normal focus manager calls
 //   'FocusNavigation' for tab and document navigation
 PRLogModuleInfo* gFocusLog;
 PRLogModuleInfo* gFocusNavigationLog;
 
-#define LOGFOCUS(args) PR_LOG(gFocusLog, 4, args)
-#define LOGFOCUSNAVIGATION(args) PR_LOG(gFocusNavigationLog, 4, args)
+#define LOGFOCUS(args) PR_LOG(gFocusLog, PR_LOG_DEBUG, args)
+#define LOGFOCUSNAVIGATION(args) PR_LOG(gFocusNavigationLog, PR_LOG_DEBUG, args)
 
 #define LOGTAG(log, format, content)                            \
-  {                                                             \
+  if (PR_LOG_TEST(log, PR_LOG_DEBUG)) {                         \
     nsAutoCString tag(NS_LITERAL_CSTRING("(none)"));            \
     if (content) {                                              \
       content->NodeInfo()->NameAtom()->ToUTF8String(tag);       \
     }                                                           \
-    PR_LOG(log, 4, (format, tag.get()));                        \
+    PR_LOG(log, PR_LOG_DEBUG, (format, tag.get()));             \
   }
 
 #define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content)
 #define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content)
 
-#else
-
-#define LOGFOCUS(args)
-#define LOGFOCUSNAVIGATION(args)
-#define LOGCONTENT(format, content)
-#define LOGCONTENTNAVIGATION(format, content)
-
-#endif
-
 struct nsDelayedBlurOrFocusEvent
 {
   nsDelayedBlurOrFocusEvent(uint32_t aType,
                             nsIPresShell* aPresShell,
                             nsIDocument* aDocument,
                             EventTarget* aTarget)
    : mType(aType),
      mPresShell(aPresShell),
@@ -193,20 +182,18 @@ nsFocusManager::~nsFocusManager()
 nsresult
 nsFocusManager::Init()
 {
   nsFocusManager* fm = new nsFocusManager();
   NS_ENSURE_TRUE(fm, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(fm);
   sInstance = fm;
 
-#ifdef PR_LOGGING
   gFocusLog = PR_NewLogModule("Focus");
   gFocusNavigationLog = PR_NewLogModule("FocusNavigation");
-#endif
 
   nsIContent::sTabFocusModelAppliesToXUL =
     Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
                          nsIContent::sTabFocusModelAppliesToXUL);
 
   sMouseFocusesFormControl =
     Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
 
@@ -488,30 +475,28 @@ nsFocusManager::ElementIsFocusable(nsIDO
 }
 
 NS_IMETHODIMP
 nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement,
                           uint32_t aType, uint32_t aFlags, nsIDOMElement** aElement)
 {
   *aElement = nullptr;
 
-#ifdef PR_LOGGING
   LOGFOCUS(("<<MoveFocus begin Type: %d Flags: %x>>", aType, aFlags));
 
   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG) && mFocusedWindow) {
     nsIDocument* doc = mFocusedWindow->GetExtantDoc();
     if (doc && doc->GetDocumentURI()) {
       nsAutoCString spec;
       doc->GetDocumentURI()->GetSpec(spec);
       LOGFOCUS((" Focused Window: %p %s", mFocusedWindow.get(), spec.get()));
     }
   }
 
   LOGCONTENT("  Current Focus: %s", mFocusedContent.get());
-#endif
 
   // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
   // the other focus methods is already set, or we're just moving to the root
   // or caret position.
   if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET &&
       (aFlags & FOCUSMETHOD_MASK) == 0) {
     aFlags |= FLAG_BYMOVEFOCUS;
   }
@@ -648,34 +633,32 @@ nsFocusManager::MoveCaretToFocus(nsIDOMW
 }
 
 NS_IMETHODIMP
 nsFocusManager::WindowRaised(nsIDOMWindow* aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
 
-#ifdef PR_LOGGING
   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
     LOGFOCUS(("Window %p Raised [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
     nsAutoCString spec;
     nsIDocument* doc = window->GetExtantDoc();
     if (doc && doc->GetDocumentURI()) {
       doc->GetDocumentURI()->GetSpec(spec);
       LOGFOCUS(("  Raised Window: %p %s", aWindow, spec.get()));
     }
     if (mActiveWindow) {
       doc = mActiveWindow->GetExtantDoc();
       if (doc && doc->GetDocumentURI()) {
         doc->GetDocumentURI()->GetSpec(spec);
         LOGFOCUS(("  Active Window: %p %s", mActiveWindow.get(), spec.get()));
       }
     }
   }
-#endif
 
   if (mActiveWindow == window) {
     // The window is already active, so there is no need to focus anything,
     // but make sure that the right widget is focused. This is a special case
     // for Windows because when restoring a minimized window, a second
     // activation will occur and the top-level widget could be focused instead
     // of the child we want. We solve this by calling SetFocus to ensure that
     // what the focus manager thinks should be the current widget is actually
@@ -746,34 +729,32 @@ nsFocusManager::WindowRaised(nsIDOMWindo
 }
 
 NS_IMETHODIMP
 nsFocusManager::WindowLowered(nsIDOMWindow* aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
 
-#ifdef PR_LOGGING
   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
     LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
     nsAutoCString spec;
     nsIDocument* doc = window->GetExtantDoc();
     if (doc && doc->GetDocumentURI()) {
       doc->GetDocumentURI()->GetSpec(spec);
       LOGFOCUS(("  Lowered Window: %s", spec.get()));
     }
     if (mActiveWindow) {
       doc = mActiveWindow->GetExtantDoc();
       if (doc && doc->GetDocumentURI()) {
         doc->GetDocumentURI()->GetSpec(spec);
         LOGFOCUS(("  Active Window: %s", spec.get()));
       }
     }
   }
-#endif
 
   if (mActiveWindow != window)
     return NS_OK;
 
   // clear the mouse capture as the active window has changed
   nsIPresShell::SetCapturingContent(nullptr, 0);
 
   // If this is a parent or single process window, send the deactivate event.
@@ -866,17 +847,16 @@ nsFocusManager::ContentRemoved(nsIDocume
 NS_IMETHODIMP
 nsFocusManager::WindowShown(nsIDOMWindow* aWindow, bool aNeedsFocus)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
 
   window = window->GetOuterWindow();
 
-#ifdef PR_LOGGING
   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
     LOGFOCUS(("Window %p Shown [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
     nsAutoCString spec;
     nsIDocument* doc = window->GetExtantDoc();
     if (doc && doc->GetDocumentURI()) {
       doc->GetDocumentURI()->GetSpec(spec);
       LOGFOCUS(("Shown Window: %s", spec.get()));
     }
@@ -884,17 +864,16 @@ nsFocusManager::WindowShown(nsIDOMWindow
     if (mFocusedWindow) {
       doc = mFocusedWindow->GetExtantDoc();
       if (doc && doc->GetDocumentURI()) {
         doc->GetDocumentURI()->GetSpec(spec);
         LOGFOCUS((" Focused Window: %s", spec.get()));
       }
     }
   }
-#endif
 
   if (nsCOMPtr<nsITabChild> child = do_GetInterface(window->GetDocShell())) {
     bool active = static_cast<TabChild*>(child.get())->ParentIsActive();
     ActivateOrDeactivate(window, active);
   }
 
   if (mFocusedWindow != window)
     return NS_OK;
@@ -923,17 +902,16 @@ nsFocusManager::WindowHidden(nsIDOMWindo
   // currently focused window, just return, as the current focus will not
   // be affected.
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
 
   window = window->GetOuterWindow();
 
-#ifdef PR_LOGGING
   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
     LOGFOCUS(("Window %p Hidden [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
     nsAutoCString spec;
     nsIDocument* doc = window->GetExtantDoc();
     if (doc && doc->GetDocumentURI()) {
       doc->GetDocumentURI()->GetSpec(spec);
       LOGFOCUS(("  Hide Window: %s", spec.get()));
     }
@@ -949,17 +927,16 @@ nsFocusManager::WindowHidden(nsIDOMWindo
     if (mActiveWindow) {
       doc = mActiveWindow->GetExtantDoc();
       if (doc && doc->GetDocumentURI()) {
         doc->GetDocumentURI()->GetSpec(spec);
         LOGFOCUS(("  Active Window: %s", spec.get()));
       }
     }
   }
-#endif
 
   if (!IsSameOrAncestor(window, mFocusedWindow))
     return NS_OK;
 
   // at this point, we know that the window being hidden is either the focused
   // window, or an ancestor of the focused window. Either way, the focus is no
   // longer valid, so it needs to be updated.
 
@@ -1794,28 +1771,26 @@ nsFocusManager::Focus(nsPIDOMWindow* aWi
   }
 
   bool clearFirstFocusEvent = false;
   if (!mFirstFocusEvent) {
     mFirstFocusEvent = aContent;
     clearFirstFocusEvent = true;
   }
 
-#ifdef PR_LOGGING
   LOGCONTENT("Element %s has been focused", aContent);
 
   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
     nsIDocument* docm = aWindow->GetExtantDoc();
     if (docm) {
       LOGCONTENT(" from %s", docm->GetRootElement());
     }
     LOGFOCUS((" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]",
              aIsNewDocument, aFocusChanged, aWindowRaised, aFlags));
   }
-#endif
 
   if (aIsNewDocument) {
     // if this is a new document, update the parent chain of frames so that
     // focus can be traversed from the top level down to the newly focused
     // window.
     AdjustWindowFocus(aWindow, false);
   }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -241,19 +241,17 @@ class nsIScriptTimeoutHandler;
 #undef check
 #endif // check
 #include "AccessCheck.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
-#ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
-#endif
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h> // for getpid()
 #endif
 
@@ -1207,21 +1205,19 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
                   gRefCnt,
                   static_cast<void*>(ToCanonicalSupports(this)),
                   getpid(),
                   gSerialCounter,
                   static_cast<void*>(ToCanonicalSupports(aOuterWindow)));
   }
 #endif
 
-#ifdef PR_LOGGING
   if (gDOMLeakPRLog)
     PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG,
            ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
-#endif
 
   NS_ASSERTION(sWindowsById, "Windows hash table must be created!");
   NS_ASSERTION(!sWindowsById->Get(mWindowID),
                "This window shouldn't be in the hash table yet!");
   // We seem to see crashes in release builds because of null |sWindowsById|.
   if (sWindowsById) {
     sWindowsById->Put(mWindowID, this);
   }
@@ -1243,20 +1239,18 @@ void
 nsGlobalWindow::Init()
 {
   AssertIsOnMainThread();
 
   CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
   NS_ASSERTION(gEntropyCollector,
                "gEntropyCollector should have been initialized!");
 
-#ifdef PR_LOGGING
   gDOMLeakPRLog = PR_NewLogModule("DOMLeak");
   NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!");
-#endif
 
   sWindowsById = new WindowByIdTable();
 }
 
 static PLDHashOperator
 DisconnectEventTargetObjects(nsPtrHashKey<DOMEventTargetHelper>* aKey,
                              void* aClosure)
 {
@@ -1301,21 +1295,19 @@ nsGlobalWindow::~nsGlobalWindow()
                   static_cast<void*>(ToCanonicalSupports(this)),
                   getpid(),
                   mSerial,
                   static_cast<void*>(ToCanonicalSupports(outer)),
                   url.get());
   }
 #endif
 
-#ifdef PR_LOGGING
   if (gDOMLeakPRLog)
     PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG,
            ("DOMWINDOW %p destroyed", this));
-#endif
 
   if (IsOuterWindow()) {
     JSObject *proxy = GetWrapperPreserveColor();
     if (proxy) {
       js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr));
     }
 
     // An outer window is destroyed with inner windows still possibly
@@ -2807,25 +2799,23 @@ nsGlobalWindow::ClearStatus()
 }
 
 void
 nsGlobalWindow::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument)
 {
   NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows");
   MOZ_ASSERT(aDocument);
 
-#ifdef PR_LOGGING
   if (gDOMLeakPRLog && PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
     nsIURI *uri = aDocument->GetDocumentURI();
     nsAutoCString spec;
     if (uri)
       uri->GetSpec(spec);
     PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get());
   }
-#endif
 
   mDoc = aDocument;
   ClearDocumentDependentSlots(aCx);
   mFocusedNode = nullptr;
   mLocalStorage = nullptr;
   mSessionStorage = nullptr;
 
 #ifdef DEBUG
@@ -8433,23 +8423,18 @@ nsGlobalWindow::PostMessageMoz(JSContext
 
     nsCOMPtr<nsIScriptSecurityManager> ssm =
       nsContentUtils::GetSecurityManager();
     MOZ_ASSERT(ssm);
 
     nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal();
     MOZ_ASSERT(principal);
 
-    uint32_t appId;
-    if (NS_WARN_IF(NS_FAILED(principal->GetAppId(&appId))))
-      return;
-
-    bool isInBrowser;
-    if (NS_WARN_IF(NS_FAILED(principal->GetIsInBrowserElement(&isInBrowser))))
-      return;
+    uint32_t appId = principal->GetAppId();
+    bool isInBrowser = principal->GetIsInBrowserElement();
 
     // Create a nsIPrincipal inheriting the app/browser attributes from the
     // caller.
     nsresult rv = ssm->GetAppCodebasePrincipal(originURI, appId, isInBrowser,
                                              getter_AddRefs(providedPrincipal));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
@@ -10568,21 +10553,19 @@ nsGlobalWindow::GetSessionStorage(ErrorR
   nsIPrincipal *principal = GetPrincipal();
   nsIDocShell* docShell = GetDocShell();
 
   if (!principal || !docShell || !Preferences::GetBool(kStorageEnabled)) {
     return nullptr;
   }
 
   if (mSessionStorage) {
-#ifdef PR_LOGGING
     if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
       PR_LogPrint("nsGlobalWindow %p has %p sessionStorage", this, mSessionStorage.get());
     }
-#endif
     bool canAccess = mSessionStorage->CanAccess(principal);
     NS_ASSERTION(canAccess,
                  "This window owned sessionStorage "
                  "that could not be accessed!");
     if (!canAccess) {
       mSessionStorage = nullptr;
     }
   }
@@ -10621,33 +10604,29 @@ nsGlobalWindow::GetSessionStorage(ErrorR
                                            getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
     mSessionStorage = static_cast<DOMStorage*>(storage.get());
     MOZ_ASSERT(mSessionStorage);
 
-#ifdef PR_LOGGING
     if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
       PR_LogPrint("nsGlobalWindow %p tried to get a new sessionStorage %p", this, mSessionStorage.get());
     }
-#endif
 
     if (!mSessionStorage) {
       aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return nullptr;
     }
   }
 
-#ifdef PR_LOGGING
   if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
     PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
   }
-#endif
 
   return mSessionStorage;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetSessionStorage(nsISupports** aSessionStorage)
 {
   ErrorResult rv;
@@ -11553,22 +11532,20 @@ nsGlobalWindow::Observe(nsISupports* aSu
       }
 
       if (!check) {
         // This storage event is not coming from our storage or is coming
         // from a different docshell, i.e. it is a clone, ignore this event.
         return NS_OK;
       }
 
-#ifdef PR_LOGGING
       if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
         PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p",
                     this, mSessionStorage.get(), changingStorage.get());
       }
-#endif
 
       fireMozStorageChanged = mSessionStorage == changingStorage;
       break;
     }
 
     case DOMStorage::LocalStorage:
     {
       // Allow event fire only for the same principal storages
--- a/dom/base/nsNodeInfoManager.cpp
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -31,19 +31,17 @@
 #include "nsDocument.h"
 #include "nsNullPrincipal.h"
 
 using namespace mozilla;
 using mozilla::dom::NodeInfo;
 
 #include "prlog.h"
 
-#ifdef PR_LOGGING
 static PRLogModuleInfo* gNodeInfoManagerLeakPRLog;
-#endif
 
 PLHashNumber
 nsNodeInfoManager::GetNodeInfoInnerHashValue(const void *key)
 {
   MOZ_ASSERT(key, "Null key passed to NodeInfo::GetHashValue!");
 
   auto *node = reinterpret_cast<const NodeInfo::NodeInfoInner*>(key);
 
@@ -112,24 +110,22 @@ nsNodeInfoManager::nsNodeInfoManager()
   : mDocument(nullptr),
     mNonDocumentNodeInfos(0),
     mTextNodeInfo(nullptr),
     mCommentNodeInfo(nullptr),
     mDocumentNodeInfo(nullptr)
 {
   nsLayoutStatics::AddRef();
 
-#ifdef PR_LOGGING
   if (!gNodeInfoManagerLeakPRLog)
     gNodeInfoManagerLeakPRLog = PR_NewLogModule("NodeInfoManagerLeak");
 
   if (gNodeInfoManagerLeakPRLog)
     PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
            ("NODEINFOMANAGER %p created", this));
-#endif
 
   mNodeInfoHash = PL_NewHashTable(32, GetNodeInfoInnerHashValue,
                                   NodeInfoInnerKeyCompare,
                                   PL_CompareValues, &allocOps, nullptr);
 }
 
 
 nsNodeInfoManager::~nsNodeInfoManager()
@@ -137,21 +133,19 @@ nsNodeInfoManager::~nsNodeInfoManager()
   if (mNodeInfoHash)
     PL_HashTableDestroy(mNodeInfoHash);
 
   // Note: mPrincipal may be null here if we never got inited correctly
   mPrincipal = nullptr;
 
   mBindingManager = nullptr;
 
-#ifdef PR_LOGGING
   if (gNodeInfoManagerLeakPRLog)
     PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
            ("NODEINFOMANAGER %p destroyed", this));
-#endif
 
   nsLayoutStatics::Release();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager)
@@ -196,21 +190,19 @@ nsNodeInfoManager::Init(nsIDocument *aDo
   if (aDocument) {
     mBindingManager = new nsBindingManager(aDocument);
   }
 
   mDefaultPrincipal = mPrincipal;
 
   mDocument = aDocument;
 
-#ifdef PR_LOGGING
   if (gNodeInfoManagerLeakPRLog)
     PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
            ("NODEINFOMANAGER %p Init document=%p", this, aDocument));
-#endif
 
   return NS_OK;
 }
 
 // static
 int
 nsNodeInfoManager::DropNodeInfoDocument(PLHashEntry *he, int hashIndex, void *arg)
 {
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -101,26 +101,24 @@
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 static const char *kPrefJavaMIME = "plugin.java.mime";
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-#ifdef PR_LOGGING
 static PRLogModuleInfo*
 GetObjectLog()
 {
   static PRLogModuleInfo *sLog;
   if (!sLog)
     sLog = PR_NewLogModule("objlc");
   return sLog;
 }
-#endif
 
 #define LOG(args) PR_LOG(GetObjectLog(), PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(GetObjectLog(), PR_LOG_DEBUG)
 
 static bool
 IsJavaMIME(const nsACString & aMIMEType)
 {
   return
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -49,19 +49,17 @@
 #include "nsContentTypeParser.h"
 #include "nsINetworkPredictor.h"
 #include "ImportManager.h"
 #include "mozilla/dom/EncodingUtils.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/unused.h"
 
-#ifdef PR_LOGGING
 static PRLogModuleInfo* gCspPRLog;
-#endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // The nsScriptLoadRequest is passed as the context to necko, and thus
 // it needs to be threadsafe. Necko won't do anything with this
 // context, but it will AddRef and Release it on other threads.
 NS_IMPL_ISUPPORTS0(nsScriptLoadRequest)
@@ -104,20 +102,18 @@ nsScriptLoader::nsScriptLoader(nsIDocume
   : mDocument(aDocument),
     mBlockerCount(0),
     mEnabled(true),
     mDeferEnabled(false),
     mDocumentParsingDone(false),
     mBlockingDOMContentLoaded(false)
 {
   // enable logging for CSP
-#ifdef PR_LOGGING
   if (!gCspPRLog)
     gCspPRLog = PR_NewLogModule("CSP");
-#endif
 }
 
 nsScriptLoader::~nsScriptLoader()
 {
   mObservers.Clear();
 
   if (mParserBlockingRequest) {
     mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT);
--- a/dom/base/test/test_XHR.html
+++ b/dom/base/test/test_XHR.html
@@ -102,17 +102,17 @@ function checkResponseTextAccessThrows(x
 function checkResponseXMLAccessThrows(xhr) {
   var didthrow = false;
   try { xhr.responseXML } catch (e) { didthrow = true; }
   ok(didthrow, "should have thrown when accessing responseXML");
 }
 function checkSetResponseType(xhr, type) {
   var didthrow = false;
   try { xhr.responseType = type; } catch (e) { didthrow = true; }
-  ise(xhr.responseType, type, "responseType should be " + type);
+  is(xhr.responseType, type, "responseType should be " + type);
   ok(!didthrow, "should not have thrown when setting responseType");
 }
 function checkSetResponseTypeThrows(xhr, type) {
   var didthrow = false;
   try { xhr.responseType = type; } catch (e) { didthrow = true; }
   ok(didthrow, "should have thrown when setting responseType");
 }
 function checkOpenThrows(xhr, method, url, async) {
--- a/dom/base/test/test_bug1075702.html
+++ b/dom/base/test/test_bug1075702.html
@@ -41,29 +41,29 @@ https://bugzilla.mozilla.org/show_bug.cg
   var test1 = document.createElement("div");
   test1.setAttribute("x", "y");
   removedNodeAccordingToEvent = null;
 
   function testremoveNamedItemNS() {
     test1.addEventListener("DOMAttrModified", mutationHandler, true);
     var removedNodeAccordingToRemoveNamedItemNS = test1.attributes.removeNamedItemNS(null, "x");
     test1.removeEventListener("DOMAttrModified", mutationHandler, true);
-    ise(removedNodeAccordingToEvent, removedNodeAccordingToRemoveNamedItemNS, "Node removed according to event is not same as node removed by removeNamedItemNS.");
+    is(removedNodeAccordingToEvent, removedNodeAccordingToRemoveNamedItemNS, "Node removed according to event is not same as node removed by removeNamedItemNS.");
   }
 
   testremoveNamedItemNS();
 
   var test2 = document.createElement("div");
   test2.setAttribute("x", "y");
   removedNodeAccordingToEvent = null;
 
   function testremoveNamedItem() {
     test2.addEventListener("DOMAttrModified", mutationHandler, true);
     var removedNodeAccordingToRemoveNamedItem = test2.attributes.removeNamedItem("x");
     test2.removeEventListener("DOMAttrModified", mutationHandler, true);
-    ise(removedNodeAccordingToEvent, removedNodeAccordingToRemoveNamedItem, "Node removed according to event is not same as node removed by removeNamedItem.");
+    is(removedNodeAccordingToEvent, removedNodeAccordingToRemoveNamedItem, "Node removed according to event is not same as node removed by removeNamedItem.");
   }
 
   testremoveNamedItem();
 
 </script>
 </body>
 </html>
--- a/dom/base/test/test_bug891952.html
+++ b/dom/base/test/test_bug891952.html
@@ -11,40 +11,40 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript">
 
   /** Test for Bug 891952 **/
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(function() {
       var all = document.all;
       is(all["content"], $("content"), "Should find the content");
       ok(!("" in all), "Should not have an empty string prop on document.all");
-      ise(all[""], undefined, "Should not get empty string on document.all");
-      ise(all.namedItem(""), null,
-          "namedItem for empty string should return null on document.all");
+      is(all[""], undefined, "Should not get empty string on document.all");
+      is(all.namedItem(""), null,
+         "namedItem for empty string should return null on document.all");
 
       var divs = document.getElementsByTagName("div");
       ok(!("" in divs), "Should not have an empty string prop on getElementsByTagName");
-      ise(divs[""], undefined, "Should not get empty string on getElementsByTagName");
-      ise(divs.namedItem(""), null,
-          "namedItem for empty string should return null on getElementsByTagName");
+      is(divs[""], undefined, "Should not get empty string on getElementsByTagName");
+      is(divs.namedItem(""), null,
+         "namedItem for empty string should return null on getElementsByTagName");
       var forms = document.forms;
       ok(!("" in forms), "Should not have an empty string prop on document.forms");
-      ise(forms[""], undefined, "Should not get empty string on document.forms");
-      ise(forms.namedItem(""), null,
-          "namedItem for empty string should return null on document.forms");
+      is(forms[""], undefined, "Should not get empty string on document.forms");
+      is(forms.namedItem(""), null,
+         "namedItem for empty string should return null on document.forms");
 
       var form = $("form");
       ok(!("" in form), "Should not have an empty string prop on form");
-      ise(form[""], undefined, "Should not get empty string on form");
+      is(form[""], undefined, "Should not get empty string on form");
 
       var formEls = $("form").elements;
       ok(!("" in formEls), "Should not have an empty string prop on form.elements");
-      ise(formEls[""], undefined, "Should not get empty string on form.elements");
-      ise(formEls.namedItem(""), null,
-          "namedItem for empty string should return null on form.elements");
+      is(formEls[""], undefined, "Should not get empty string on form.elements");
+      is(formEls.namedItem(""), null,
+         "namedItem for empty string should return null on form.elements");
       SimpleTest.finish();
     });
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=891952">Mozilla Bug 891952</a>
 <p id="display"></p>
--- a/dom/base/test/test_bug999456.html
+++ b/dom/base/test/test_bug999456.html
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 999456 **/
 
   SimpleTest.waitForExplicitFinish();
   addEventListener("load", function (e) {
-    ise(e.cancelable, false, "Load events should not be cancelable");
+    is(e.cancelable, false, "Load events should not be cancelable");
     SimpleTest.finish();
   });
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999456">Mozilla Bug 999456</a>
 <p id="display"></p>
--- a/dom/base/test/test_hasFeature.html
+++ b/dom/base/test/test_hasFeature.html
@@ -49,20 +49,20 @@ function testAPIs() {
   ];
 
   var promises = [];
   APIEndPoints.forEach(function(v) {
     promises.push(navigator.hasFeature("api.window." + v.name));
   });
   Promise.all(promises).then(function(values) {
     for (var i = 0; i < values.length; ++i) {
-      ise(values[i], APIEndPoints[i].enabled,
-          "Endpoint " + APIEndPoints[i].name + " resolved with the correct value. " +
-          "If this is failing because you're changing how an API is exposed, you " +
-          "must contact the Marketplace team to let them know about the change.");
+      is(values[i], APIEndPoints[i].enabled,
+         "Endpoint " + APIEndPoints[i].name + " resolved with the correct value. " +
+         "If this is failing because you're changing how an API is exposed, you " +
+         "must contact the Marketplace team to let them know about the change.");
     }
     SimpleTest.finish();
   }, function() {
     ok(false, "The Promise should not be rejected");
     SimpleTest.finish();
   });
 }
 
--- a/dom/base/test/test_history_state_null.html
+++ b/dom/base/test/test_history_state_null.html
@@ -5,17 +5,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 949471</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
   /** Test for Bug 949471 **/
-    ise(history.state, null, "history.state should be null by default");
+    is(history.state, null, "history.state should be null by default");
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=949471">Mozilla Bug 949471</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
--- a/dom/base/test/test_url_empty_port.html
+++ b/dom/base/test/test_url_empty_port.html
@@ -21,33 +21,33 @@ https://bugzilla.mozilla.org/show_bug.cg
 </pre>
   <a id="link" href="http://www.example.com:8080">foobar</a>
   <area id="area" href="http://www.example.com:8080" />
   <script type="application/javascript">
 
   var url = new URL('http://www.example.com:8080');
   is(url.port, '8080', 'URL.port is 8080');
   url.port = '';
-  ise(url.port, '', 'URL.port is \'\'');
+  is(url.port, '', 'URL.port is \'\'');
   url.port = 0;
-  ise(url.port, '0', 'URL.port is 0');
+  is(url.port, '0', 'URL.port is 0');
 
   var link = document.getElementById("link");
   is(link.port, '8080', 'URL.port is 8080');
   link.port = '';
   is(link.href, 'http://www.example.com/', "link.href matches");
-  ise(link.port, '', 'URL.port is \'\'');
+  is(link.port, '', 'URL.port is \'\'');
   link.port = 0;
   is(link.href, 'http://www.example.com:0/', "link.href matches");
-  ise(link.port, '0', 'URL.port is 0');
+  is(link.port, '0', 'URL.port is 0');
 
   var area = document.getElementById("area");
   is(area.port, '8080', 'URL.port is 8080');
   area.port = '';
   is(area.href, 'http://www.example.com/', "area.href matches");
-  ise(area.port, '', 'URL.port is \'\'');
+  is(area.port, '', 'URL.port is \'\'');
   area.port = 0;
   is(area.href, 'http://www.example.com:0/', "area.href matches");
-  ise(area.port, '0', 'URL.port is 0');
+  is(area.port, '0', 'URL.port is 0');
 
   </script>
 </body>
 </html>
--- a/dom/base/test/test_window_define_nonconfigurable.html
+++ b/dom/base/test/test_window_define_nonconfigurable.html
@@ -10,29 +10,29 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1107443 **/
   try {
     Object.defineProperty(window, "nosuchprop", { value: 5 });
     throw "didn't throw";
   } catch (e) {
-    ise(e instanceof TypeError, true,
-        "defineProperty(window) with a non-configurable property should " +
-        "throw a TypeError, instead got: " + e);
-    ise(Object.getOwnPropertyDescriptor(window, "nosuchprop"), undefined,
-        'Window should not have property after an attempt to define it failed');
+    is(e instanceof TypeError, true,
+       "defineProperty(window) with a non-configurable property should " +
+       "throw a TypeError, instead got: " + e);
+    is(Object.getOwnPropertyDescriptor(window, "nosuchprop"), undefined,
+       'Window should not have property after an attempt to define it failed');
   }
 
   Object.defineProperty(window, "nosuchprop", { value: 7, configurable: true });
   var desc = Object.getOwnPropertyDescriptor(window, "nosuchprop");
-  ise(typeof(desc), "object", "Should have a property now");
-  ise(desc.configurable, true, "Property should be configurable");
-  ise(desc.writable, false, "Property should be readonly");
-  ise(desc.value, 7, "Property should have the right value");
+  is(typeof(desc), "object", "Should have a property now");
+  is(desc.configurable, true, "Property should be configurable");
+  is(desc.writable, false, "Property should be readonly");
+  is(desc.value, 7, "Property should have the right value");
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107443">Mozilla Bug 1107443</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
--- a/dom/base/test/test_window_named_frame_enumeration.html
+++ b/dom/base/test/test_window_named_frame_enumeration.html
@@ -53,28 +53,28 @@ https://bugzilla.mozilla.org/show_bug.cg
           "Frame with same-origin changed name should be in GSP own prop list");
     is(names1.indexOf("crossorigin"), -1,
        "Frame with cross-origin changed name should not be in our own prop list");
     is(names2.indexOf("crossorigin"), -1,
        "Frame with cross-origin changed name should not be in our enumeration list");
     is(names3.indexOf("crossorigin"), -1,
        "Frame with cross-origin changed name should not be in GSP own prop list");
 
-    ise(Object.getOwnPropertyDescriptor(gsp, ""), undefined,
-        "Should not have empty string as a named frame");
+    is(Object.getOwnPropertyDescriptor(gsp, ""), undefined,
+       "Should not have empty string as a named frame");
     isnot(Object.getOwnPropertyDescriptor(gsp, "x"), undefined,
         "Should have about:blank subframe as a named frame");
     isnot(Object.getOwnPropertyDescriptor(gsp, "y"), undefined,
         "Should have same-origin subframe as a named frame");
     isnot(Object.getOwnPropertyDescriptor(gsp, "z"), undefined,
         "Should have cross-origin subframe as a named frame");
     isnot(Object.getOwnPropertyDescriptor(gsp, "sameorigin"), undefined,
           "Should have same-origin changed name as a named frame");
-    ise(Object.getOwnPropertyDescriptor(gsp, "crossorigin"), undefined,
-        "Should not have cross-origin-origin changed name as a named frame");
+    is(Object.getOwnPropertyDescriptor(gsp, "crossorigin"), undefined,
+       "Should not have cross-origin-origin changed name as a named frame");
     SimpleTest.finish();
   });
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1019417">Mozilla Bug 1019417</a>
 <p id="display"></p>
 <div id="content" style="display: none">
--- a/dom/base/test/test_xhr_send.html
+++ b/dom/base/test/test_xhr_send.html
@@ -23,53 +23,53 @@ function simpleGetTest() {
     }
   x.send({});
 }
 
 function simplePostTest() {
   var x = new XMLHttpRequest();
   x.open("POST", "echo.sjs");
   x.onload = function() {
-      ise(x.responseText, "somedata", "Should have processed POST");
+      is(x.responseText, "somedata", "Should have processed POST");
       undefinedPostTest();
     }
     x.send({toString: function() { return "somedata"; }});
 }
 
 function undefinedPostTest() {
   var x = new XMLHttpRequest();
   x.open("POST", "echo.sjs");
   x.onload = function() {
-      ise(x.responseText, "undefined", "Should have processed POST");
+      is(x.responseText, "undefined", "Should have processed POST");
       nullPostTest();
     }
     x.send({toString: function() { return undefined; }});
 }
 
 function nullPostTest() {
   var x = new XMLHttpRequest();
   x.open("POST", "echo.sjs");
   x.onload = function() {
-      ise(x.responseText, "null", "Should have processed POST");
+      is(x.responseText, "null", "Should have processed POST");
       testExceptionInToString();
     }
     x.send({toString: function() { return null; }});
 }
 
 function testExceptionInToString() {
   var x = new XMLHttpRequest();
   x.open("GET", "echo.sjs");
   x.onload = function() {
     ok(false);
     SimpleTest.finish();
   }
   try {
     x.send({toString: function() { throw "dummy"; }});
   } catch(ex) {
-    ise(ex, "dummy");
+    is(ex, "dummy");
     SimpleTest.finish();
   }
 }
 
   </script>
 </head>
 <body onload="simpleGetTest()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1096263">Mozilla Bug 1096263</a>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1081,18 +1081,17 @@ class CGHeaders(CGWrapper):
                         #
                         # XXXbz maybe dictionaries with interface members
                         # should just have out-of-line constructors and
                         # destructors?
                         headerSet.add(typeDesc.headerFile)
             elif unrolled.isDictionary():
                 headerSet.add(self.getDeclarationFilename(unrolled.inner))
             elif unrolled.isCallback():
-                # Callbacks are both a type and an object
-                headerSet.add(self.getDeclarationFilename(unrolled))
+                headerSet.add(self.getDeclarationFilename(unrolled.callback))
             elif unrolled.isFloat() and not unrolled.isUnrestricted():
                 # Restricted floats are tested for finiteness
                 bindingHeaders.add("mozilla/FloatingPoint.h")
                 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
             elif unrolled.isEnum():
                 filename = self.getDeclarationFilename(unrolled.inner)
                 declareIncludes.add(filename)
             elif unrolled.isPrimitive():
@@ -1271,17 +1270,17 @@ def UnionTypes(unionTypes, config):
                 elif f.isEnum():
                     # Need to see the actual definition of the enum,
                     # unfortunately.
                     headers.add(CGHeaders.getDeclarationFilename(f.inner))
                 elif f.isCallback():
                     # Callbacks always use strong refs, so we need to include
                     # the right header to be able to Release() in our inlined
                     # code.
-                    headers.add(CGHeaders.getDeclarationFilename(f))
+                    headers.add(CGHeaders.getDeclarationFilename(f.callback))
                 elif f.isMozMap():
                     headers.add("mozilla/dom/MozMap.h")
                     # And add headers for the type we're parametrized over
                     addHeadersForType(f.inner)
 
             if len(config.filenamesPerUnion[t.name]) > 1:
                 implheaders.add("mozilla/dom/UnionTypes.h")
             else:
@@ -5125,17 +5124,18 @@ def getJSToNativeConversionInfo(type, de
                                         dealWithOptional=isOptional)
 
     if type.isCallback():
         assert not isEnforceRange and not isClamp
         assert not type.treatNonCallableAsNull() or type.nullable()
         assert not type.treatNonObjectAsNull() or type.nullable()
         assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
 
-        name = type.unroll().identifier.name
+        callback = type.unroll().callback
+        name = callback.identifier.name
         if type.nullable():
             declType = CGGeneric("nsRefPtr<%s>" % name)
         else:
             declType = CGGeneric("OwningNonNull<%s>" % name)
         conversion = indent(CGCallbackTempRoot(name).define())
 
         if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
             haveCallable = "JS::IsCallable(&${val}.toObject())"
@@ -6228,17 +6228,17 @@ def getRetvalDeclarationForType(returnTy
         conversion = None
         if isMember:
             result = CGGeneric("StrongPtrForMember<%s>::Type" % result.define())
         else:
             conversion = CGGeneric("StrongOrRawPtr<%s>" % result.define())
             result = CGGeneric("auto")
         return result, None, None, None, conversion
     if returnType.isCallback():
-        name = returnType.unroll().identifier.name
+        name = returnType.unroll().callback.identifier.name
         return CGGeneric("nsRefPtr<%s>" % name), None, None, None, None
     if returnType.isAny():
         if isMember:
             return CGGeneric("JS::Value"), None, None, None, None
         return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None
     if returnType.isObject() or returnType.isSpiderMonkeyInterface():
         if isMember:
             return CGGeneric("JSObject*"), None, None, None, None
@@ -8777,17 +8777,17 @@ def getUnionAccessorSignatureType(type, 
 
     if type.isByteString():
         return CGGeneric("const nsCString&")
 
     if type.isEnum():
         return CGGeneric(type.inner.identifier.name)
 
     if type.isCallback():
-        return CGGeneric("%s&" % type.unroll().identifier.name)
+        return CGGeneric("%s&" % type.unroll().callback.identifier.name)
 
     if type.isAny():
         return CGGeneric("JS::Value")
 
     if type.isObject():
         return CGGeneric("JSObject*")
 
     if type.isDictionary():
@@ -12409,17 +12409,17 @@ class CGForwardDeclarations(CGWrapper):
                     try:
                         desc = config.getDescriptor(name, True)
                         builder.add(desc.nativeType)
                     except NoSuchDescriptorError:
                         pass
             # Note: Spidermonkey interfaces are typedefs, so can't be
             # forward-declared
             elif t.isCallback():
-                builder.addInMozillaDom(str(t))
+                builder.addInMozillaDom(t.callback.identifier.name)
             elif t.isDictionary():
                 builder.addInMozillaDom(t.inner.identifier.name, isStruct=True)
             elif t.isCallbackInterface():
                 builder.addInMozillaDom(t.inner.identifier.name)
             elif t.isUnion():
                 # Forward declare both the owning and non-owning version,
                 # since we don't know which one we might want
                 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
@@ -12436,22 +12436,22 @@ class CGForwardDeclarations(CGWrapper):
         # We just about always need NativePropertyHooks
         builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
         builder.addInMozillaDom("ProtoAndIfaceCache")
         # Add the atoms cache type, even if we don't need it.
         for d in descriptors:
             builder.add(d.nativeType + "Atoms", isStruct=True)
 
         for callback in mainCallbacks:
-            forwardDeclareForType(callback)
+            builder.addInMozillaDom(callback.identifier.name)
             for t in getTypesFromCallback(callback):
                 forwardDeclareForType(t, workerness='mainthreadonly')
 
         for callback in workerCallbacks:
-            forwardDeclareForType(callback)
+            builder.addInMozillaDom(callback.identifier.name)
             for t in getTypesFromCallback(callback):
                 forwardDeclareForType(t, workerness='workeronly')
 
         for d in callbackInterfaces:
             builder.add(d.nativeType)
             builder.add(d.nativeType + "Atoms", isStruct=True)
             for t in getTypesFromDescriptor(d):
                 forwardDeclareForType(t)
@@ -12824,17 +12824,17 @@ class CGNativeMember(ClassMethod):
                                    post=">")
             else:
                 result = CGWrapper(result, post="*")
             # Since we always force an owning type for callback return values,
             # our ${declName} is an OwningNonNull or nsRefPtr.  So we can just
             # .forget() to get our already_AddRefed.
             return result.define(), "nullptr", "return ${declName}.forget();\n"
         if type.isCallback():
-            return ("already_AddRefed<%s>" % type.unroll().identifier.name,
+            return ("already_AddRefed<%s>" % type.unroll().callback.identifier.name,
                     "nullptr", "return ${declName}.forget();\n")
         if type.isAny():
             if isMember:
                 # No need for a third element in the isMember case
                 return "JS::Value", None, None
             # Outparam
             return "void", "", "aRetVal.set(${declName});\n"
 
@@ -13054,17 +13054,17 @@ class CGNativeMember(ClassMethod):
                 else:
                     declType = "%s*"
             else:
                 if forceOwningType:
                     declType = "OwningNonNull<%s>"
                 else:
                     declType = "%s&"
             if type.isCallback():
-                name = type.unroll().identifier.name
+                name = type.unroll().callback.identifier.name
             else:
                 name = type.unroll().inner.identifier.name
             return declType % name, False, False
 
         if type.isAny():
             # Don't do the rooting stuff for variadics for now
             if isMember:
                 declType = "JS::Value"
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -43,21 +43,20 @@ class Configuration:
                         "The binding build system doesn't really support "
                         "'implements' statements which don't appear in the "
                         "file in which the left-hand side of the statement is "
                         "defined.  Don't do this unless your right-hand side "
                         "is LegacyQueryInterface.\n"
                         "%s\n"
                         "%s" %
                         (thing.location, thing.implementor.location))
-            # Some toplevel things are sadly types, and those have an
-            # isInterface that doesn't mean the same thing as IDLObject's
-            # isInterface()...
-            if (not isinstance(thing, IDLInterface) and
-                not isinstance(thing, IDLExternalInterface)):
+
+            assert not thing.isType();
+
+            if not thing.isInterface():
                 continue
             iface = thing
             self.interfaces[iface.identifier.name] = iface
             if iface.identifier.name not in config:
                 # Completely skip consequential interfaces with no descriptor
                 # if they have no interface object because chances are we
                 # don't need to do anything interesting with them.
                 if iface.isConsequential() and not iface.hasInterfaceObject():
@@ -795,19 +794,19 @@ def findCallbacksAndDictionaries(inputTy
 
     Note that we assume that our initial invocation already includes all types
     reachable via descriptors in "types", so we only have to deal with things
     that are themeselves reachable via callbacks and dictionaries.
     """
     def doFindCallbacksAndDictionaries(types, callbacks, dictionaries):
         unhandledTypes = set()
         for type in types:
-            if type.isCallback() and type not in callbacks:
-                unhandledTypes |= getFlatTypes(getTypesFromCallback(type))
-                callbacks.add(type)
+            if type.isCallback() and type.callback not in callbacks:
+                unhandledTypes |= getFlatTypes(getTypesFromCallback(type.callback))
+                callbacks.add(type.callback)
             elif type.isDictionary() and type.inner not in dictionaries:
                 d = type.inner
                 unhandledTypes |= getFlatTypes(getTypesFromDictionary(d))
                 while d:
                     dictionaries.add(d)
                     d = d.parent
         if len(unhandledTypes) != 0:
             doFindCallbacksAndDictionaries(unhandledTypes, callbacks, dictionaries)
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -159,16 +159,19 @@ class IDLObject(object):
         return False
 
     def isDictionary(self):
         return False;
 
     def isUnion(self):
         return False
 
+    def isTypedef(self):
+        return False
+
     def getUserData(self, key, default):
         return self.userData.get(key, default)
 
     def setUserData(self, key, value):
         self.userData[key] = value
 
     def addExtendedAttributes(self, attrs):
         assert False # Override me!
@@ -1784,21 +1787,21 @@ class IDLType(IDLObject):
     def isSerializable(self):
         return False
 
     def tag(self):
         assert False # Override me!
 
     def treatNonCallableAsNull(self):
         assert self.tag() == IDLType.Tags.callback
-        return self.nullable() and self.inner._treatNonCallableAsNull
+        return self.nullable() and self.inner.callback._treatNonCallableAsNull
 
     def treatNonObjectAsNull(self):
         assert self.tag() == IDLType.Tags.callback
-        return self.nullable() and self.inner._treatNonObjectAsNull
+        return self.nullable() and self.inner.callback._treatNonObjectAsNull
 
     def addExtendedAttributes(self, attrs):
         assert len(attrs) == 0
 
     def resolveType(self, parentScope):
         pass
 
     def unroll(self):
@@ -1828,21 +1831,27 @@ class IDLUnresolvedType(IDLType):
         try:
             obj = scope._lookupIdentifier(self.name)
         except:
             raise WebIDLError("Unresolved type '%s'." % self.name,
                               [self.location])
 
         assert obj
         if obj.isType():
-            # obj itself might not be complete; deal with that.
-            assert obj != self
-            if not obj.isComplete():
-                obj = obj.complete(scope)
-            return obj
+            print obj
+        assert not obj.isType()
+        if obj.isTypedef():
+            assert self.name.name == obj.identifier.name
+            typedefType = IDLTypedefType(self.location, obj.innerType,
+                                         obj.identifier)
+            assert not typedefType.isComplete()
+            return typedefType.complete(scope)
+        elif obj.isCallback() and not obj.isInterface():
+            assert self.name.name == obj.identifier.name
+            return IDLCallbackType(self.location, obj)
 
         if self._promiseInnerType and not self._promiseInnerType.isComplete():
             self._promiseInnerType = self._promiseInnerType.complete(scope)
 
         name = self.name.resolve(scope, None)
         return IDLWrapperType(self.location, obj, self._promiseInnerType)
 
     def isDistinguishableFrom(self, other):
@@ -2353,33 +2362,27 @@ class IDLArrayType(IDLType):
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isDate() or other.isNonCallbackInterface())
 
     def _getDependentObjects(self):
         return self.inner._getDependentObjects()
 
-class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
+class IDLTypedefType(IDLType):
     def __init__(self, location, innerType, name):
-        IDLType.__init__(self, location, innerType.name)
-
-        identifier = IDLUnresolvedIdentifier(location, name)
-
-        IDLObjectWithIdentifier.__init__(self, location, None, identifier)
-
+        IDLType.__init__(self, location, name)
         self.inner = innerType
-        self.name = name
         self.builtin = False
 
     def __eq__(self, other):
         return isinstance(other, IDLTypedefType) and self.inner == other.inner
 
     def __str__(self):
-        return self.identifier.name
+        return self.name
 
     def nullable(self):
         return self.inner.nullable()
 
     def isPrimitive(self):
         return self.inner.isPrimitive()
 
     def isBoolean(self):
@@ -2437,40 +2440,55 @@ class IDLTypedefType(IDLType, IDLObjectW
         return False
 
     def complete(self, parentScope):
         if not self.inner.isComplete():
             self.inner = self.inner.complete(parentScope)
         assert self.inner.isComplete()
         return self.inner
 
-    def finish(self, parentScope):
-        # Maybe the IDLObjectWithIdentifier for the typedef should be
-        # a separate thing from the type?  If that happens, we can
-        # remove some hackery around avoiding isInterface() in
-        # Configuration.py.
-        self.complete(parentScope)
-
-    def validate(self):
-        pass
-
     # Do we need a resolveType impl?  I don't think it's particularly useful....
 
     def tag(self):
         return self.inner.tag()
 
     def unroll(self):
         return self.inner.unroll()
 
     def isDistinguishableFrom(self, other):
         return self.inner.isDistinguishableFrom(other)
 
     def _getDependentObjects(self):
         return self.inner._getDependentObjects()
 
+class IDLTypedef(IDLObjectWithIdentifier):
+    def __init__(self, location, parentScope, innerType, name):
+        identifier = IDLUnresolvedIdentifier(location, name)
+        IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+        self.innerType = innerType
+
+    def __str__(self):
+        return "Typedef %s %s" % (self.identifier.name, self.innerType)
+
+    def finish(self, parentScope):
+        if not self.innerType.isComplete():
+            self.innerType = self.innerType.complete(parentScope)
+
+    def validate(self):
+        pass
+
+    def isTypedef(self):
+        return True
+
+    def addExtendedAttributes(self, attrs):
+        assert len(attrs) == 0
+
+    def _getDependentObjects(self):
+        return self.innerType._getDependentObjects()
+
 class IDLWrapperType(IDLType):
     def __init__(self, location, inner, promiseInnerType=None):
         IDLType.__init__(self, location, inner.identifier.name)
         self.inner = inner
         self._identifier = inner.identifier
         self.builtin = False
         assert not promiseInnerType or inner.identifier.name == "Promise"
         self._promiseInnerType = promiseInnerType
@@ -3650,22 +3668,20 @@ class IDLArgument(IDLObjectWithIdentifie
         deps = set([self.type])
         if self.defaultValue:
             deps.add(self.defaultValue)
         return deps
 
     def canHaveMissingValue(self):
         return self.optional and not self.defaultValue
 
-class IDLCallbackType(IDLType, IDLObjectWithScope):
+class IDLCallback(IDLObjectWithScope):
     def __init__(self, location, parentScope, identifier, returnType, arguments):
         assert isinstance(returnType, IDLType)
 
-        IDLType.__init__(self, location, identifier.name)
-
         self._returnType = returnType
         # Clone the list
         self._arguments = list(arguments)
 
         IDLObjectWithScope.__init__(self, location, parentScope, identifier)
 
         for (returnType, arguments) in self.signatures():
             for argument in arguments:
@@ -3675,19 +3691,16 @@ class IDLCallbackType(IDLType, IDLObject
         self._treatNonObjectAsNull = False
 
     def isCallback(self):
         return True
 
     def signatures(self):
         return [(self._returnType, self._arguments)]
 
-    def tag(self):
-        return IDLType.Tags.callback
-
     def finish(self, scope):
         if not self._returnType.isComplete():
             type = self._returnType.complete(scope)
 
             assert not isinstance(type, IDLUnresolvedType)
             assert not isinstance(type, IDLTypedefType)
             assert not isinstance(type.name, IDLUnresolvedIdentifier)
             self._returnType = type
@@ -3701,24 +3714,16 @@ class IDLCallbackType(IDLType, IDLObject
             assert not isinstance(type, IDLUnresolvedType)
             assert not isinstance(type, IDLTypedefType)
             assert not isinstance(type.name, IDLUnresolvedIdentifier)
             argument.type = type
 
     def validate(self):
         pass
 
-    def isDistinguishableFrom(self, other):
-        if other.isUnion():
-            # Just forward to the union; it'll deal
-            return other.isDistinguishableFrom(self)
-        return (other.isPrimitive() or other.isString() or other.isEnum() or
-                other.isNonCallbackInterface() or other.isDate() or
-                other.isSequence())
-
     def addExtendedAttributes(self, attrs):
         unhandledAttrs = []
         for attr in attrs:
             if attr.identifier() == "TreatNonCallableAsNull":
                 self._treatNonCallableAsNull = True
             elif attr.identifier() == "TreatNonObjectAsNull":
                 self._treatNonObjectAsNull = True
             else:
@@ -3727,16 +3732,38 @@ class IDLCallbackType(IDLType, IDLObject
             raise WebIDLError("Cannot specify both [TreatNonCallableAsNull] "
                               "and [TreatNonObjectAsNull]", [self.location])
         if len(unhandledAttrs) != 0:
             IDLType.addExtendedAttributes(self, unhandledAttrs)
 
     def _getDependentObjects(self):
         return set([self._returnType] + self._arguments)
 
+class IDLCallbackType(IDLType):
+    def __init__(self, location, callback):
+        IDLType.__init__(self, location, callback.identifier.name)
+        self.callback = callback
+
+    def isCallback(self):
+        return True
+
+    def tag(self):
+        return IDLType.Tags.callback
+
+    def isDistinguishableFrom(self, other):
+        if other.isUnion():
+            # Just forward to the union; it'll deal
+            return other.isDistinguishableFrom(self)
+        return (other.isPrimitive() or other.isString() or other.isEnum() or
+                other.isNonCallbackInterface() or other.isDate() or
+                other.isSequence())
+
+    def _getDependentObjects(self):
+        return self.callback._getDependentObjects()
+
 class IDLMethodOverload:
     """
     A class that represents a single overload of a WebIDL method.  This is not
     quite the same as an element of the "effective overload set" in the spec,
     because separate IDLMethodOverloads are not created based on arguments being
     optional.  Rather, when multiple methods have the same name, there is an
     IDLMethodOverload for each one, all hanging off an IDLMethod representing
     the full set of overloads.
@@ -4733,32 +4760,32 @@ class Parser(Tokenizer):
         """
         p[0] = []
 
     def p_CallbackRest(self, p):
         """
             CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON
         """
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
-        p[0] = IDLCallbackType(self.getLocation(p, 1), self.globalScope(),
-                               identifier, p[3], p[5])
+        p[0] = IDLCallback(self.getLocation(p, 1), self.globalScope(),
+                           identifier, p[3], p[5])
 
     def p_ExceptionMembers(self, p):
         """
             ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
                              |
         """
         pass
 
     def p_Typedef(self, p):
         """
             Typedef : TYPEDEF Type IDENTIFIER SEMICOLON
         """
-        typedef = IDLTypedefType(self.getLocation(p, 1), p[2], p[3])
-        typedef.resolve(self.globalScope())
+        typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(),
+                             p[2], p[3])
         p[0] = typedef
 
     def p_ImplementsStatement(self, p):
         """
             ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON
         """
         assert(p[2] == "implements")
         implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
@@ -5483,18 +5510,22 @@ class Parser(Tokenizer):
                               "parametrized over",
                               [self.getLocation(p, 1)])
 
         type = None
 
         try:
             if self.globalScope()._lookupIdentifier(p[1]):
                 obj = self.globalScope()._lookupIdentifier(p[1])
-                if obj.isType():
-                    type = obj
+                assert not obj.isType()
+                if obj.isTypedef():
+                    type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
+                                          obj.identifier.name)
+                elif obj.isCallback() and not obj.isInterface():
+                    type = IDLCallbackType(self.getLocation(p, 1), obj)
                 else:
                     type = IDLWrapperType(self.getLocation(p, 1), p[1])
                 p[0] = self.handleModifiers(type, p[2])
                 return
         except:
             pass
 
         type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
@@ -5820,19 +5851,17 @@ class Parser(Tokenizer):
 
     def _installBuiltins(self, scope):
         assert isinstance(scope, IDLScope)
 
         # xrange omits the last value.
         for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1):
             builtin = BuiltinTypes[x]
             name = builtin.name
-
-            typedef = IDLTypedefType(BuiltinLocation("<builtin type>"), builtin, name)
-            typedef.resolve(scope)
+            typedef = IDLTypedef(BuiltinLocation("<builtin type>"), scope, builtin, name)
 
     @ staticmethod
     def handleModifiers(type, modifiers):
         for (modifier, modifierLocation) in modifiers:
             assert modifier == IDLMethod.TypeSuffixModifier.QMark or \
                    modifier == IDLMethod.TypeSuffixModifier.Brackets
 
             if modifier == IDLMethod.TypeSuffixModifier.QMark:
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 typedef long myLong;
 typedef TestInterface AnotherNameForTestInterface;
 typedef TestInterface? NullableTestInterface;
+typedef CustomEventInit TestDictionaryTypedef;
 
 interface TestExternalInterface;
 
 [AvailableIn=PrivilegedApps, Pref="xyz"]
 interface TestRenamedInterface {
 };
 
 callback interface TestCallbackInterface {
@@ -1014,16 +1015,17 @@ dictionary Dict : ParentDict {
   sequence<long>? seq5 = [];
 
   long dashed-name;
 
   required long requiredLong;
   required object requiredObject;
 
   CustomEventInit customEventInit;
+  TestDictionaryTypedef dictionaryTypedef;
 };
 
 dictionary ParentDict : GrandparentDict {
   long c = 5;
   TestInterface someInterface;
   TestInterface? someNullableInterface = null;
   TestExternalInterface someExternalInterface;
   any parentAny;
--- a/dom/bindings/test/test_bug707564-chrome.html
+++ b/dom/bindings/test/test_bug707564-chrome.html
@@ -18,24 +18,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 775543 **/
 function test()
 {
   var nav = document.getElementById("t1").contentWindow.navigator;
-  ise(nav.foopy, undefined, "We should have an Xray now");
+  is(nav.foopy, undefined, "We should have an Xray now");
   is(nav.wrappedJSObject.foopy, 5, "We should have the right navigator object");
   var props = Object.getOwnPropertyNames(nav);
   isnot(props.indexOf("mozApps"), -1,
         "Should enumerate a mozApps property on navigator xray");
 
   var nav = document.getElementById("t2").contentWindow.navigator;
-  ise(nav.foopy, undefined, "We should have an Xray now again");
+  is(nav.foopy, undefined, "We should have an Xray now again");
   is(nav.wrappedJSObject.foopy, 5, "We should have the right navigator object again");
   var found = false;
   for (var name in nav) {
     if (name == "mozApps") {
       found = true;
     }
   }
   ok(found, "Should enumerate a mozApps property on navigator xray via for...in");
--- a/dom/bindings/test/test_callback_default_thisval.html
+++ b/dom/bindings/test/test_callback_default_thisval.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 957929 **/
   SimpleTest.waitForExplicitFinish();
 
   function f() {
     "use strict";
-    ise(this, undefined, "Should have undefined this value");
+    is(this, undefined, "Should have undefined this value");
     SimpleTest.finish();
   }
 
   addLoadEvent(function() {
     requestAnimationFrame(f);
   });
   </script>
 </head>
--- a/dom/bindings/test/test_document_location_via_xray_cached.html
+++ b/dom/bindings/test/test_document_location_via_xray_cached.html
@@ -18,17 +18,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 1041731 **/
 
 function test()
 {
   var loc = document.getElementById("t").contentWindow.document.location;
-  ise(loc.toString, loc.toString, "Unforgeable method on the Xray should be cached");
+  is(loc.toString, loc.toString, "Unforgeable method on the Xray should be cached");
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(test);
 
 </script>
 </pre>
--- a/dom/bindings/test/test_dom_xrays.html
+++ b/dom/bindings/test/test_dom_xrays.html
@@ -27,26 +27,26 @@ var Cu = Components.utils;
 // that the value should not be present on that prototype.
 function checkXrayProperty(obj, name, values)
 {
   var instance = obj;
   do {
     var value = values.shift();
     if (typeof value == "undefined") {
       ok(!obj.hasOwnProperty(name), "hasOwnProperty shouldn't see \"" + name + "\" through Xrays");
-      ise(Object.getOwnPropertyDescriptor(obj, name), undefined, "getOwnPropertyDescriptor shouldn't see \"" + name + "\" through Xrays");
+      is(Object.getOwnPropertyDescriptor(obj, name), undefined, "getOwnPropertyDescriptor shouldn't see \"" + name + "\" through Xrays");
       ok(Object.keys(obj).indexOf(name) == -1, "Enumerating the Xray should not return \"" + name + "\"");
     } else {
       ok(obj.hasOwnProperty(name), "hasOwnProperty should see \"" + name + "\" through Xrays");
       var pd = Object.getOwnPropertyDescriptor(obj, name);
       ok(pd, "getOwnPropertyDescriptor should see \"" + name + "\" through Xrays");
       if (pd && pd.get) {
-        ise(pd.get.call(instance), value, "Should get the right value for \"" + name + "\" through Xrays");
+        is(pd.get.call(instance), value, "Should get the right value for \"" + name + "\" through Xrays");
       } else {
-        ise(obj[name], value, "Should get the right value for \"" + name + "\" through Xrays");
+        is(obj[name], value, "Should get the right value for \"" + name + "\" through Xrays");
       }
       if (pd && pd.enumerable) {
         ok(Object.keys(obj).indexOf("" + name) > -1, "Enumerating the Xray should return \"" + name + "\"");
       }
     }
   } while ((obj = Object.getPrototypeOf(obj)));
 }
 
@@ -57,23 +57,23 @@ function checkWindowXrayProperty(obj, na
 
 function test()
 {
   // Window
   var win = document.getElementById("t").contentWindow;
   var doc = document.getElementById("t").contentDocument;
 
   var winProto = Object.getPrototypeOf(win);
-  ise(winProto, win.Window.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+  is(winProto, win.Window.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
 
   var namedPropertiesObject = Object.getPrototypeOf(winProto);
-  ise(Cu.getClassName(namedPropertiesObject, /* unwrap = */ true), "WindowProperties", "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+  is(Cu.getClassName(namedPropertiesObject, /* unwrap = */ true), "WindowProperties", "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
 
   var eventTargetProto = Object.getPrototypeOf(namedPropertiesObject);
-  ise(eventTargetProto, win.EventTarget.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+  is(eventTargetProto, win.EventTarget.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
 
   // Xrays need to filter expandos.
   checkWindowXrayProperty(win, "expando", undefined);
   ok(!("expando" in win), "Xrays should filter expandos");
 
   checkWindowXrayProperty(win, "shadowedIframe", undefined, undefined, doc.getElementById("shadowedIframe").contentWindow);
   ok("shadowedIframe" in win, "Named properties should be exposed through Xrays");
 
@@ -89,54 +89,54 @@ function test()
   checkWindowXrayProperty(win, "self", win, undefined, doc.getElementById("self").contentWindow);
   ok("self" in win, "WebIDL properties should be exposed through Xrays");
 
   // Object.prototype is at the end of the prototype chain.
   var obj = win;
   while ((proto = Object.getPrototypeOf(obj))) {
     obj = proto;
   }
-  ise(obj, win.Object.prototype, "Object.prototype should be at the end of the prototype chain");
+  is(obj, win.Object.prototype, "Object.prototype should be at the end of the prototype chain");
 
   // Named properties shouldn't shadow WebIDL- or ECMAScript-defined properties.
   checkWindowXrayProperty(win, "addEventListener", undefined, undefined, undefined, eventTargetProto.addEventListener);
-  ise(win.addEventListener, eventTargetProto.addEventListener, "Named properties shouldn't shadow WebIDL-defined properties");
+  is(win.addEventListener, eventTargetProto.addEventListener, "Named properties shouldn't shadow WebIDL-defined properties");
 
-  ise(win.toString, win.Object.prototype.toString, "Named properties shouldn't shadow ECMAScript-defined properties");
+  is(win.toString, win.Object.prototype.toString, "Named properties shouldn't shadow ECMAScript-defined properties");
 
   // HTMLDocument
   // Unforgeable properties live on the instance.
   checkXrayProperty(doc, "location", [ win.location ]);
-  ise(String(win.location), document.getElementById("t").src,
-      "Should have the right stringification");
+  is(String(win.location), document.getElementById("t").src,
+     "Should have the right stringification");
 
   // HTMLHtmlElement
   var elem = doc.documentElement;
 
   var elemProto = Object.getPrototypeOf(elem);
-  ise(elemProto, win.HTMLHtmlElement.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+  is(elemProto, win.HTMLHtmlElement.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
 
   elemProto = Object.getPrototypeOf(elemProto);
-  ise(elemProto, win.HTMLElement.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+  is(elemProto, win.HTMLElement.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
 
   elemProto = Object.getPrototypeOf(elemProto);
-  ise(elemProto, win.Element.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+  is(elemProto, win.Element.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
 
   elemProto = Object.getPrototypeOf(elemProto);
-  ise(elemProto, win.Node.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+  is(elemProto, win.Node.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
 
   elemProto = Object.getPrototypeOf(elemProto);
-  ise(elemProto, win.EventTarget.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+  is(elemProto, win.EventTarget.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
 
   // Xrays need to filter expandos.
   ok(!("expando" in elem), "Xrays should filter expandos");
 
   // WebIDL-defined properties live on the prototype.
   checkXrayProperty(elem, "version", [ undefined, "" ]);
-  ise(elem.version, "", "WebIDL properties should be exposed through Xrays");
+  is(elem.version, "", "WebIDL properties should be exposed through Xrays");
 
   // HTMLCollection
   var coll = doc.getElementsByTagName("iframe");
 
   // Named properties live on the instance for non-global objects.
   checkXrayProperty(coll, "iframe", [ doc.getElementById("iframe") ]);
 
   // Indexed properties live on the instance.
--- a/dom/bindings/test/test_exception_options_from_jsimplemented.html
+++ b/dom/bindings/test/test_exception_options_from_jsimplemented.html
@@ -17,45 +17,45 @@ https://bugzilla.mozilla.org/show_bug.cg
   function doTest() {
     var t = new TestInterfaceJS();
     try {
       t.testThrowDOMError();
     } catch (e) {
       ok(e instanceof Error, "Should have an Error here");
       ok(!(e instanceof DOMException), "Should not have DOMException here");
       ok(!("code" in e), "Should not have a 'code' property");
-      ise(e.name, "Error", "Should not have an interesting name here");
-      ise(e.message, "We are a DOMError", "Should have the right message");
-      ise(e.stack,
-          "doTest@http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html:20:7\n",
-          "Exception stack should still only show our code");
-      ise(e.fileName,
-          "http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html",
-          "Should have the right file name");
-      ise(e.lineNumber, 20, "Should have the right line number");
-      ise(e.columnNumber, 6, "Should have the right column number");
+      is(e.name, "Error", "Should not have an interesting name here");
+      is(e.message, "We are a DOMError", "Should have the right message");
+      is(e.stack,
+         "doTest@http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html:20:7\n",
+         "Exception stack should still only show our code");
+      is(e.fileName,
+         "http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html",
+         "Should have the right file name");
+      is(e.lineNumber, 20, "Should have the right line number");
+      is(e.columnNumber, 6, "Should have the right column number");
     }
 
     try {
       t.testThrowDOMException();
     } catch (e) {
       ok(e instanceof Error, "Should also have an Error here");
       ok(e instanceof DOMException, "Should have DOMException here");
-      ise(e.name, "NotSupportedError", "Should have the right name here");
-      ise(e.message, "We are a DOMException",
-          "Should also have the right message");
-      ise(e.code, DOMException.NOT_SUPPORTED_ERR,
-          "Should have the right 'code'");
-      ise(e.stack,
-          "doTest@http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html:38:7\n",
-          "Exception stack should still only show our code");
-      ise(e.filename,
-          "http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html",
-          "Should still have the right file name");
-      ise(e.lineNumber, 38, "Should still have the right line number");
+      is(e.name, "NotSupportedError", "Should have the right name here");
+      is(e.message, "We are a DOMException",
+         "Should also have the right message");
+      is(e.code, DOMException.NOT_SUPPORTED_ERR,
+         "Should have the right 'code'");
+      is(e.stack,
+         "doTest@http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html:38:7\n",
+         "Exception stack should still only show our code");
+      is(e.filename,
+         "http://mochi.test:8888/tests/dom/bindings/test/test_exception_options_from_jsimplemented.html",
+         "Should still have the right file name");
+      is(e.lineNumber, 38, "Should still have the right line number");
       todo_is(e.columnNumber, 7,
               "No column number support for DOMException yet");
     }
     SimpleTest.finish();
   }
 
   SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
                             doTest);
--- a/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
+++ b/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
@@ -10,30 +10,30 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1107592 **/
 
   SimpleTest.waitForExplicitFinish();
 
   function checkExn(lineNumber, name, message, code, filename, testNumber, stack, exn) {
-    ise(exn.lineNumber, lineNumber,
-        "Should have the right line number in test " + testNumber);
-    ise(exn.name, name,
-        "Should have the right exception name in test " + testNumber);
-    ise("filename" in exn ? exn.filename : exn.fileName, filename,
-        "Should have the right file name in test " + testNumber);
-    ise(exn.message, message,
-        "Should have the right message in test " + testNumber);
-    ise(exn.code, code, "Should have the right .code in test " + testNumber);
+    is(exn.lineNumber, lineNumber,
+       "Should have the right line number in test " + testNumber);
+    is(exn.name, name,
+       "Should have the right exception name in test " + testNumber);
+    is("filename" in exn ? exn.filename : exn.fileName, filename,
+       "Should have the right file name in test " + testNumber);
+    is(exn.message, message,
+       "Should have the right message in test " + testNumber);
+    is(exn.code, code, "Should have the right .code in test " + testNumber);
     if (message === "") {
-      ise(exn.name, "NS_ERROR_UNEXPECTED",
-          "Should have one of our synthetic exceptions in test " + testNumber);
+      is(exn.name, "NS_ERROR_UNEXPECTED",
+         "Should have one of our synthetic exceptions in test " + testNumber);
     }
-    ise(exn.stack, stack, "Should have the right stack in test " + testNumber);
+    is(exn.stack, stack, "Should have the right stack in test " + testNumber);
   }
 
   function ensurePromiseFail(testNumber, value) {
     ok(false, "Test " + testNumber + " should not have a fulfilled promise");
   }
 
   function doTest() {
     var t = new TestInterfaceJS();
--- a/dom/bindings/test/test_sequence_detection.html
+++ b/dom/bindings/test/test_sequence_detection.html
@@ -17,26 +17,26 @@ https://bugzilla.mozilla.org/show_bug.cg
     ok(testInterfaceJS, "got a TestInterfaceJS object");
 
     var nonIterableObject = {[Symbol.iterator]: 5};
 
     try {
       testInterfaceJS.testSequenceOverload(nonIterableObject);
       ok(false, "Should have thrown in the overload case");  // see long comment above!
     } catch (e) {
-      ise(e.name, "TypeError", "Should get a TypeError for the overload case");
+      is(e.name, "TypeError", "Should get a TypeError for the overload case");
       ok(e.message.includes("not iterable"),
          "Should have a message about being non-iterable in the overload case");
     }
 
     try {
       testInterfaceJS.testSequenceUnion(nonIterableObject);
       ok(false, "Should have thrown in the union case");
     } catch (e) {
-      ise(e.name, "TypeError", "Should get a TypeError for the union case");
+      is(e.name, "TypeError", "Should get a TypeError for the union case");
       ok(e.message.includes("not iterable"),
          "Should have a message about being non-iterable in the union case");
     }
 
     SimpleTest.finish();
   });
 
   </script>
--- a/dom/bindings/test/test_setWithNamedGetterNoNamedSetter.html
+++ b/dom/bindings/test/test_setWithNamedGetterNoNamedSetter.html
@@ -17,24 +17,24 @@ https://bugzilla.mozilla.org/show_bug.cg
   <input name="action">
 </form>
 </div>
   <script type="application/javascript">
 
   /** Test for Bug 1043690 **/
   var f = document.querySelector("form");
   var i = document.querySelector("input");
-  ise(f.getAttribute("action"), null, "Should have no action attribute");
-  ise(f.action, i, "form.action should be the input");
+  is(f.getAttribute("action"), null, "Should have no action attribute");
+  is(f.action, i, "form.action should be the input");
   f.action = "http://example.org";
-  ise(f.getAttribute("action"), "http://example.org",
-      "Should have an action attribute now");
-  ise(f.action, i, "form.action should still be the input");
+  is(f.getAttribute("action"), "http://example.org",
+     "Should have an action attribute now");
+  is(f.action, i, "form.action should still be the input");
   i.remove();
-  ise(f.action, "http://example.org/",
-      "form.action should no longer be shadowed");
+  is(f.action, "http://example.org/",
+     "form.action should no longer be shadowed");
 
 
   </script>
 <pre id="test">
 </pre>
 </body>
 </html>
--- a/dom/bindings/test/test_treat_non_object_as_null.html
+++ b/dom/bindings/test/test_treat_non_object_as_null.html
@@ -12,23 +12,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   /** Test for Bug 952365 **/
 
     var onvolumechange;
     var x = {};
     
     (function() {
        onvolumechange = x;
-       ise(onvolumechange, x,
-           "Should preserve an object value when assigning to event handler");
+       is(onvolumechange, x,
+          "Should preserve an object value when assigning to event handler");
        // Test that we don't try to actually call the non-callable object
        window.dispatchEvent(new Event("volumechange"));
        onvolumechange = 5;
-       ise(onvolumechange, null,
-           "Non-object values should become null when assigning to event handler");
+       is(onvolumechange, null,
+          "Non-object values should become null when assigning to event handler");
     })();
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=952365">Mozilla Bug 952365</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -459,17 +459,17 @@ public:
     , mState(STATE_INIT)
     , mResult(NS_OK)
     , mExecutingRunOnTarget(false)
   {
     MOZ_ASSERT(mContext);
     // mData may be nullptr
     MOZ_ASSERT(mTarget);
     MOZ_ASSERT(mAction);
-    MOZ_ASSERT(mQuotaInfo.mDir);
+    // mQuotaInfo.mDir may be nullptr if QuotaInitRunnable failed
     MOZ_ASSERT(mInitiatingThread);
   }
 
   nsresult Dispatch()
   {
     NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     MOZ_ASSERT(mState == STATE_INIT);
 
--- a/dom/cache/DBAction.cpp
+++ b/dom/cache/DBAction.cpp
@@ -49,31 +49,31 @@ DBAction::RunOnTarget(Resolver* aResolve
 
   nsCOMPtr<nsIFile> dbDir;
   nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(dbDir));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aResolver->Resolve(rv);
     return;
   }
 
+  rv = dbDir->Append(NS_LITERAL_STRING("cache"));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aResolver->Resolve(rv);
+    return;
+  }
+
   nsCOMPtr<mozIStorageConnection> conn;
 
   // Attempt to reuse the connection opened by a previous Action.
   if (aOptionalData) {
     conn = aOptionalData->GetConnection();
   }
 
   // If there is no previous Action, then we must open one.
   if (!conn) {
-    rv = dbDir->Append(NS_LITERAL_STRING("cache"));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aResolver->Resolve(rv);
-      return;
-    }
-
     rv = OpenConnection(aQuotaInfo, dbDir, getter_AddRefs(conn));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aResolver->Resolve(rv);
       return;
     }
     MOZ_ASSERT(conn);
 
     // Save this connection in the shared Data object so later Actions can
@@ -182,16 +182,19 @@ DBAction::OpenConnection(const QuotaInfo
 }
 
 nsresult
 DBAction::WipeDatabase(nsIFile* aDBFile, nsIFile* aDBDir)
 {
   nsresult rv = aDBFile->Remove(false);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  // Note, the -wal journal file will be automatically deleted by sqlite when
+  // the new database is created.  No need to explicitly delete it here.
+
   // Delete the morgue as well.
   rv = BodyDeleteDir(aDBDir);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 SyncDBAction::SyncDBAction(Mode aMode)
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -23,21 +23,21 @@
 #include "mozilla/dom/ResponseBinding.h"
 #include "nsIContentPolicy.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 namespace db {
 
-const int32_t kMaxWipeSchemaVersion = 7;
+const int32_t kMaxWipeSchemaVersion = 8;
 
 namespace {
 
-const int32_t kLatestSchemaVersion = 7;
+const int32_t kLatestSchemaVersion = 8;
 const int32_t kMaxEntriesPerStatement = 255;
 
 } // anonymous namespace
 
 // If any of the static_asserts below fail, it means that you have changed
 // the corresponding WebIDL enum in a way that may be incompatible with the
 // existing data stored in the DOM Cache.  You would need to update the Cache
 // database schema accordingly and adjust the failing static_assert.
@@ -338,22 +338,22 @@ InitializeConnection(mozIStorageConnecti
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   // This function needs to perform per-connection initialization tasks that
   // need to happen regardless of the schema.
 
   nsAutoCString pragmas(
-#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
-    // Switch the journaling mode to TRUNCATE to avoid changing the directory
-    // structure at the conclusion of every transaction for devices with slower
-    // file systems.
-    "PRAGMA journal_mode = TRUNCATE; "
-#endif
+    "PRAGMA journal_mode = WAL; "
+    // Use default mozStorage 32kb page size for now
+    // WAL journal can grow to 512kb before being flushed to disk
+    "PRAGMA wal_autocheckpoint = 16; "
+    // Always truncate the journal back to 512kb after large transactions
+    "PRAGMA journal_size_limit = 524288; "
     "PRAGMA foreign_keys = ON; "
 
     // Note, the default encoding of UTF-8 is preferred.  mozStorage does all
     // the work necessary to convert UTF-16 nsString values for us.  We don't
     // need ordering and the binary equality operations are correct.  So, do
     // NOT set PRAGMA encoding to UTF-16.
   );
 
--- a/dom/camera/CameraCommon.h
+++ b/dom/camera/CameraCommon.h
@@ -12,22 +12,18 @@
 #define __func__ __FUNCTION__
 #else
 #define __func__ __FILE__
 #endif
 #endif
 
 #include "prlog.h"
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* GetCameraLog();
 #define DOM_CAMERA_LOG( type, ... ) PR_LOG(GetCameraLog(), (PRLogModuleLevel)type, ( __VA_ARGS__ ))
-#else
-#define DOM_CAMERA_LOG( type, ... )
-#endif
 
 #define DOM_CAMERA_LOGA( ... )      DOM_CAMERA_LOG( 0, __VA_ARGS__ )
 
 /**
  * From the least to the most output.
  */
 enum {
   DOM_CAMERA_LOG_NOTHING,
@@ -37,24 +33,20 @@ enum {
   DOM_CAMERA_LOG_TRACE,
   DOM_CAMERA_LOG_REFERENCES
 };
 
 /**
  * DOM_CAMERA_LOGR() can be called before 'gCameraLog' is set, so
  * we need to handle this one a little differently.
  */
-#ifdef PR_LOGGING
 #define DOM_CAMERA_LOGR( ... )                                  \
   do {                                                          \
     if (GetCameraLog()) {                                       \
       DOM_CAMERA_LOG( DOM_CAMERA_LOG_REFERENCES, __VA_ARGS__ ); \
     }                                                           \
   } while (0)
-#else
-#define DOM_CAMERA_LOGR( ... )
-#endif
 #define DOM_CAMERA_LOGT( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_TRACE, __VA_ARGS__ )
 #define DOM_CAMERA_LOGI( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_INFO, __VA_ARGS__ )
 #define DOM_CAMERA_LOGW( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_WARNING, __VA_ARGS__ )
 #define DOM_CAMERA_LOGE( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_ERROR, __VA_ARGS__ )
 
 #endif // DOM_CAMERA_CAMERACOMMON_H
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -87,26 +87,24 @@ CameraControlImpl::OnHardwareStateChange
   //  local binder thread, should the mediaserver process die.
   RwLockAutoEnterRead lock(mListenerLock);
 
   if (aNewState == mHardwareState) {
     DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState);
     return;
   }
 
-#ifdef PR_LOGGING
   const char* state[] = { "uninitialized", "closed", "open", "failed" };
   MOZ_ASSERT(aNewState >= 0);
   if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
     DOM_CAMERA_LOGI("New hardware state is '%s' (reason=0x%x)\n",
       state[aNewState], aReason);
   } else {
     DOM_CAMERA_LOGE("OnHardwareStateChange: got invalid HardwareState value %d\n", aNewState);
   }
-#endif
 
   mHardwareState = aNewState;
   mHardwareStateChangeReason = aReason;
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnHardwareStateChange(mHardwareState, mHardwareStateChangeReason);
   }
@@ -216,25 +214,23 @@ CameraControlImpl::OnPreviewStateChange(
   //  process die.
   RwLockAutoEnterRead lock(mListenerLock);
 
   if (aNewState == mPreviewState) {
     DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState);
     return;
   }
 
-#ifdef PR_LOGGING
   const char* state[] = { "stopped", "paused", "started" };
   MOZ_ASSERT(aNewState >= 0);
   if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
     DOM_CAMERA_LOGI("New preview state is '%s'\n", state[aNewState]);
   } else {
     DOM_CAMERA_LOGE("OnPreviewStateChange: got unknown PreviewState value %d\n", aNewState);
   }
-#endif
 
   mPreviewState = aNewState;
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnPreviewStateChange(mPreviewState);
   }
 }
@@ -275,17 +271,16 @@ CameraControlImpl::OnNewPreviewFrame(lay
 void
 CameraControlImpl::OnUserError(CameraControlListener::UserContext aContext,
                                nsresult aError)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread.
   RwLockAutoEnterRead lock(mListenerLock);
 
-#ifdef PR_LOGGING
   const char* context[] = {
     "StartCamera",
     "StopCamera",
     "AutoFocus",
     "StartFaceDetection",
     "StopFaceDetection",
     "TakePicture",
     "StartRecording",
@@ -300,44 +295,41 @@ CameraControlImpl::OnUserError(CameraCon
   };
   if (static_cast<size_t>(aContext) < sizeof(context) / sizeof(context[0])) {
     DOM_CAMERA_LOGW("CameraControlImpl::OnUserError : aContext='%s' (%d), aError=0x%x\n",
       context[aContext], aContext, aError);
   } else {
     DOM_CAMERA_LOGE("CameraControlImpl::OnUserError : aContext=%d, aError=0x%x\n",
       aContext, aError);
   }
-#endif
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnUserError(aContext, aError);
   }
 }
 
 void
 CameraControlImpl::OnSystemError(CameraControlListener::SystemContext aContext,
                                  nsresult aError)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread.
   RwLockAutoEnterRead lock(mListenerLock);
 
-#ifdef PR_LOGGING
   const char* context[] = {
     "Camera Service"
   };
   if (static_cast<size_t>(aContext) < sizeof(context) / sizeof(context[0])) {
     DOM_CAMERA_LOGW("CameraControlImpl::OnSystemError : aContext='%s' (%d), aError=0x%x\n",
       context[aContext], aContext, aError);
   } else {
     DOM_CAMERA_LOGE("CameraControlImpl::OnSystemError : aContext=%d, aError=0x%x\n",
       aContext, aError);
   }
-#endif
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnSystemError(aContext, aError);
   }
 }
 
 // Camera control asynchronous message; these are dispatched from
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -2241,18 +2241,13 @@ OnShutter(nsGonkCameraControl* gc)
   gc->OnShutter();
 }
 
 void
 OnSystemError(nsGonkCameraControl* gc,
               CameraControlListener::SystemContext aWhere,
               int32_t aArg1, int32_t aArg2)
 {
-#ifdef PR_LOGGING
   DOM_CAMERA_LOGE("OnSystemError : aWhere=%d, aArg1=%d, aArg2=%d\n", aWhere, aArg1, aArg2);
-#else
-  unused << aArg1;
-  unused << aArg2;
-#endif
   gc->OnSystemError(aWhere, NS_ERROR_FAILURE);
 }
 
 } // namespace mozilla
--- a/dom/camera/test/camera_common.js
+++ b/dom/camera/test/camera_common.js
@@ -449,9 +449,9 @@ CameraTestSuite.prototype = {
     return this.expectedError('expected start recording to fail');
   },
 
   _expectedRejectStopRecording: function(p) {
     return this.expectedError('expected stop recording to fail');
   },
 };
 
-ise(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check");
+is(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check");
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -14,16 +14,18 @@
 
 #include "nsContentUtils.h"
 
 #include "nsIDocument.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsSVGEffects.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
+#include "nsWidgetsCID.h"
+#include "nsIAppShell.h"
 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIFrame.h"
 #include "nsError.h"
 
 #include "nsCSSParser.h"
 #include "mozilla/css/StyleRule.h"
 #include "mozilla/css/Declaration.h"
@@ -109,16 +111,17 @@
 #include "SVGContentUtils.h"
 #include "SVGImageContext.h"
 #include "nsIScreenManager.h"
 #include "nsFilterInstance.h"
 #include "nsSVGLength2.h"
 #include "nsDeviceContext.h"
 #include "nsFontMetrics.h"
 #include "Units.h"
+#include "mozilla/Services.h"
 
 #undef free // apparently defined by some windows header, clashing with a free()
             // method in SkTypes.h
 #include "SkiaGLGlue.h"
 #ifdef USE_SKIA
 #include "SurfaceTypes.h"
 #include "GLBlitHelper.h"
 #endif
@@ -174,16 +177,74 @@ public:
       gCanvasAzureMemoryUsed,
       "Memory used by 2D canvases. Each canvas requires "
       "(width * height * 4) bytes.");
   }
 };
 
 NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
 
+class CanvasShutdownObserver : public nsIObserver
+{
+  virtual ~CanvasShutdownObserver() {}
+
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
+    : mCanvas(aCanvas)
+  {
+    nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+    observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
+  }
+
+  void Shutdown() {
+    nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+    observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+  }
+
+  NS_IMETHOD Observe(nsISupports* aSubject,
+                     const char* aTopic,
+                     const char16_t* aData) override
+  {
+    mCanvas->ShutdownTaskQueue();
+    return NS_OK;
+  }
+
+private:
+  CanvasRenderingContext2D* mCanvas;
+};
+
+NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver);
+
+
+static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+void
+CanvasRenderingContext2D::RecordCommand()
+{
+  static uint32_t kBatchSize = 5;
+  if (++mPendingCommands > kBatchSize) {
+    mPendingCommands = 0;
+    FlushDelayedTarget();
+    return;
+  }
+
+  if (mScheduledFlush) {
+    return;
+  }
+
+  mScheduledFlush = true;
+  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &CanvasRenderingContext2D::StableStateReached);
+  appShell->RunInStableState(r);
+}
+
 class CanvasRadialGradient : public CanvasGradient
 {
 public:
   CanvasRadialGradient(CanvasRenderingContext2D* aContext,
                        const Point &aBeginOrigin, Float aBeginRadius,
                        const Point &aEndOrigin, Float aEndRadius)
     : CanvasGradient(aContext, Type::RADIAL)
     , mCenter1(aBeginOrigin)
@@ -388,16 +449,21 @@ public:
       mFinalTarget, mCtx->CurrentState().filter,
       mgfx::Rect(mPostFilterBounds),
       snapshot, mSourceGraphicRect,
       fillPaint, mFillPaintRect,
       strokePaint, mStrokePaintRect,
       mCtx->CurrentState().filterAdditionalImages,
       mPostFilterBounds.TopLeft() - mOffset,
       DrawOptions(1.0f, mCompositionOp));
+
+    // DrawTargetCapture doesn't properly support filter nodes because they are
+    // mutable. Block until drawing is done to avoid races.
+    mCtx->FlushDelayedTarget();
+    mCtx->FinishDelayedRendering();
   }
 
   DrawTarget* DT()
   {
     return mTarget;
   }
 
 private:
@@ -812,16 +878,19 @@ public:
   static void PreTransactionCallback(void* aData)
   {
     CanvasRenderingContext2DUserData* self =
       static_cast<CanvasRenderingContext2DUserData*>(aData);
     CanvasRenderingContext2D* context = self->mContext;
     if (!context || !context->mTarget)
       return;
 
+    context->FlushDelayedTarget();
+    context->FinishDelayedRendering();
+
     // Since SkiaGL default to store drawing command until flush
     // We will have to flush it before present.
     context->mTarget->Flush();
   }
 
   static void DidTransactionCallback(void* aData)
   {
     CanvasRenderingContext2DUserData* self =
@@ -933,35 +1002,48 @@ CanvasRenderingContext2D::CanvasRenderin
 #ifdef USE_SKIA_GPU
   , mVideoTexture(0)
 #endif
   // these are the default values from the Canvas spec
   , mWidth(0), mHeight(0)
   , mZero(false), mOpaque(false)
   , mResetLayer(true)
   , mIPC(false)
+  , mPendingCommands(0)
+  , mScheduledFlush(false)
   , mDrawObserver(nullptr)
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
   , mInvalidateCount(0)
 {
   sNumLivingContexts++;
 
+#ifdef XP_MACOSX
+  // Restrict async rendering to OSX for now until the failures on other
+  // platforms get resolved.
+  mTaskQueue = new MediaTaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("Canvas Rendering"),
+                                                        4));
+  mShutdownObserver = new CanvasShutdownObserver(this);
+#endif
+
   // The default is to use OpenGL mode
   if (!gfxPlatform::GetPlatform()->UseAcceleratedSkiaCanvas()) {
     mRenderingMode = RenderingMode::SoftwareBackendMode;
   }
 
   if (gfxPlatform::GetPlatform()->HaveChoiceOfHWAndSWCanvas()) {
     mDrawObserver = new CanvasDrawObserver(this);
   }
 }
 
 CanvasRenderingContext2D::~CanvasRenderingContext2D()
 {
+  if (mTaskQueue) {
+    ShutdownTaskQueue();
+  }
   RemoveDrawObserver();
   RemovePostRefreshObserver();
   Reset();
   // Drop references from all CanvasRenderingContext2DUserData to this context
   for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
     mUserDatas[i]->Forget();
   }
   sNumLivingContexts--;
@@ -974,16 +1056,29 @@ CanvasRenderingContext2D::~CanvasRenderi
     gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext()->MakeCurrent();
     gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext()->fDeleteTextures(1, &mVideoTexture);
   }
 #endif
 
   RemoveDemotableContext(this);
 }
 
+void
+CanvasRenderingContext2D::ShutdownTaskQueue()
+{
+  mShutdownObserver->Shutdown();
+  mShutdownObserver = nullptr;
+  FlushDelayedTarget();
+  FinishDelayedRendering();
+  mTaskQueue->BeginShutdown();
+  mTaskQueue = nullptr;
+  mDelayedTarget = nullptr;
+}
+
+
 JSObject*
 CanvasRenderingContext2D::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return CanvasRenderingContext2DBinding::Wrap(cx, this, aGivenProto);
 }
 
 bool
 CanvasRenderingContext2D::ParseColor(const nsAString& aString,
@@ -1029,17 +1124,20 @@ CanvasRenderingContext2D::Reset()
   }
 
   // only do this for non-docshell created contexts,
   // since those are the ones that we created a surface for
   if (mTarget && IsTargetValid() && !mDocShell) {
     gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
   }
 
+  FinishDelayedRendering();
   mTarget = nullptr;
+  mDelayedTarget = nullptr;
+  mFinalTarget = nullptr;
 
   // reset hit regions
   mHitRegionsOptions.ClearAndRetainStorage();
 
   // Since the target changes the backing texture will change, and this will
   // no longer be valid.
   mIsEntireFrameInvalid = false;
   mPredictManyRedrawCalls = false;
@@ -1096,16 +1194,18 @@ CanvasRenderingContext2D::StyleColorToSt
     aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
     aStr.Append(')');
   }
 }
 
 nsresult
 CanvasRenderingContext2D::Redraw()
 {
+  RecordCommand();
+
   if (mIsEntireFrameInvalid) {
     return NS_OK;
   }
 
   mIsEntireFrameInvalid = true;
 
   if (!mCanvasElement) {
     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
@@ -1117,16 +1217,17 @@ CanvasRenderingContext2D::Redraw()
   mCanvasElement->InvalidateCanvasContent(nullptr);
 
   return NS_OK;
 }
 
 void
 CanvasRenderingContext2D::Redraw(const mgfx::Rect &r)
 {
+  RecordCommand();
   ++mInvalidateCount;
 
   if (mIsEntireFrameInvalid) {
     return;
   }
 
   if (mPredictManyRedrawCalls ||
     mInvalidateCount > kCanvasMaxInvalidateCount) {
@@ -1139,16 +1240,28 @@ CanvasRenderingContext2D::Redraw(const m
     return;
   }
 
   nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
 
   mCanvasElement->InvalidateCanvasContent(&r);
 }
 
+TemporaryRef<SourceSurface>
+CanvasRenderingContext2D::GetSurfaceSnapshot(bool* aPremultAlpha /* = nullptr */)
+{
+  EnsureTarget();
+  if (aPremultAlpha) {
+    *aPremultAlpha = true;
+  }
+  FlushDelayedTarget();
+  FinishDelayedRendering();
+  return mFinalTarget->Snapshot();
+}
+
 void
 CanvasRenderingContext2D::DidRefresh()
 {
   if (IsTargetValid() && SkiaGLTex()) {
     SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
     MOZ_ASSERT(glue);
 
     auto gl = glue->GetGLContext();
@@ -1156,16 +1269,17 @@ CanvasRenderingContext2D::DidRefresh()
   }
 }
 
 void
 CanvasRenderingContext2D::RedrawUser(const gfxRect& r)
 {
   if (mIsEntireFrameInvalid) {
     ++mInvalidateCount;
+    RecordCommand();
     return;
   }
 
   mgfx::Rect newr =
     mTarget->GetTransform().TransformBounds(ToRect(r));
   Redraw(newr);
 }
 
@@ -1181,17 +1295,17 @@ bool CanvasRenderingContext2D::SwitchRen
       gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext()->MakeCurrent();
       gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext()->fDeleteTextures(1, &mVideoTexture);
     }
 	  mCurrentVideoSize.width = 0;
 	  mCurrentVideoSize.height = 0;
   }
 #endif
 
-  RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+  RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot();
   RefPtr<DrawTarget> oldTarget = mTarget;
   mTarget = nullptr;
   mResetLayer = true;
 
   // Recreate target using the new rendering mode
   RenderingMode attemptedMode = EnsureTarget(aRenderingMode);
   if (!IsTargetValid())
     return false;
@@ -1355,38 +1469,50 @@ CanvasRenderingContext2D::EnsureTarget(R
           gfxPlatform::GetPlatform()->UseAcceleratedSkiaCanvas() &&
           CheckSizeForSkiaGL(size)) {
         DemoteOldestContextIfNecessary();
 
 #if USE_SKIA_GPU
         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
 
         if (glue && glue->GetGrContext() && glue->GetGLContext()) {
-          mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
+          mFinalTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
           if (mTarget) {
             AddDemotableContext(this);
           } else {
             printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
             mode = RenderingMode::SoftwareBackendMode;
           }
         }
 #endif
-        if (!mTarget) {
-          mTarget = layerManager->CreateDrawTarget(size, format);
+        if (!mFinalTarget) {
+          mFinalTarget = layerManager->CreateDrawTarget(size, format);
         }
       } else {
-        mTarget = layerManager->CreateDrawTarget(size, format);
+        mFinalTarget = layerManager->CreateDrawTarget(size, format);
         mode = RenderingMode::SoftwareBackendMode;
       }
      } else {
-        mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
+        mFinalTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
         mode = RenderingMode::SoftwareBackendMode;
      }
   }
 
+  // Restrict async canvas drawing to OSX for now since we get test failures
+  // on other platforms.
+  if (mFinalTarget) {
+#ifdef XP_MACOSX
+    mTarget = mDelayedTarget = mFinalTarget->CreateCaptureDT(size);
+#else
+    mTarget = mFinalTarget;
+#endif
+  }
+
+  mPendingCommands = 0;
+
   if (mTarget) {
     static bool registered = false;
     if (!registered) {
       registered = true;
       RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
     }
 
     gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
@@ -1410,17 +1536,17 @@ CanvasRenderingContext2D::EnsureTarget(R
     if (mCanvasElement) {
       mCanvasElement->InvalidateCanvas();
     }
     // Calling Redraw() tells our invalidation machinery that the entire
     // canvas is already invalid, which can speed up future drawing.
     Redraw();
   } else {
     EnsureErrorTarget();
-    mTarget = sErrorTarget;
+    mTarget = mFinalTarget = sErrorTarget;
   }
 
   return mode;
 }
 
 #ifdef DEBUG
 int32_t
 CanvasRenderingContext2D::GetWidth() const
@@ -1430,16 +1556,61 @@ CanvasRenderingContext2D::GetWidth() con
 
 int32_t
 CanvasRenderingContext2D::GetHeight() const
 {
   return mHeight;
 }
 #endif
 
+class DrawCaptureTask : public nsRunnable
+{
+public:
+  DrawCaptureTask(DrawTargetCapture *aReplay, DrawTarget* aDest)
+    : mReplay(aReplay)
+    , mDest(aDest)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    mDest->DrawCapturedDT(mReplay, Matrix());
+    return NS_OK;
+  }
+
+private:
+  RefPtr<DrawTargetCapture> mReplay;
+  RefPtr<DrawTarget> mDest;
+};
+
+void
+CanvasRenderingContext2D::FlushDelayedTarget()
+{
+  if (!mDelayedTarget) {
+    return;
+  }
+  mPendingCommands = 0;
+
+  nsCOMPtr<nsIRunnable> task = new DrawCaptureTask(mDelayedTarget, mFinalTarget);
+  mTaskQueue->Dispatch(task.forget());
+
+  mDelayedTarget = mFinalTarget->CreateCaptureDT(IntSize(mWidth, mHeight));
+
+  mDelayedTarget->SetTransform(mTarget->GetTransform());
+  mTarget = mDelayedTarget;
+}
+
+void
+CanvasRenderingContext2D::FinishDelayedRendering()
+{
+  if (mTaskQueue) {
+    mTaskQueue->AwaitIdle();
+  }
+}
+
 NS_IMETHODIMP
 CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height)
 {
   ClearTarget();
 
   // Zero sized surfaces can cause problems.
   mZero = false;
   if (height == 0) {
@@ -1579,17 +1750,17 @@ CanvasRenderingContext2D::SetContextOpti
 void
 CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
                                          int32_t* aFormat)
 {
   *aImageBuffer = nullptr;
   *aFormat = 0;
 
   EnsureTarget();
-  RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+  RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot();
   if (!snapshot) {
     return;
   }
 
   RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
   if (!data || data->GetSize() != IntSize(mWidth, mHeight)) {
     return;
   }
@@ -1998,17 +2169,17 @@ CanvasRenderingContext2D::CreatePattern(
   }
 
   EnsureTarget();
 
   // The canvas spec says that createPattern should use the first frame
   // of animated images
   nsLayoutUtils::SurfaceFromElementResult res =
     nsLayoutUtils::SurfaceFromElement(htmlElement,
-      nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget);
+      nsLayoutUtils::SFE_WANT_FIRST_FRAME, mFinalTarget);
 
   if (!res.mSourceSurface) {
     error.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   nsRefPtr<CanvasPattern> pat =
     new CanvasPattern(this, res.mSourceSurface, repeatMode, res.mPrincipal,
@@ -4309,17 +4480,17 @@ CanvasRenderingContext2D::DrawImage(cons
     // The cache lookup can miss even if the image is already in the cache
     // if the image is coming from a different element or cached for a
     // different canvas. This covers the case when we miss due to caching
     // for a different canvas, but CanvasImageCache should be fixed if we
     // see misses due to different elements drawing the same image.
     nsLayoutUtils::SurfaceFromElementResult res =
       CachedSurfaceFromElement(element);
     if (!res.mSourceSurface)
-      res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
+      res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mFinalTarget);
 
     if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
       // The spec says to silently do nothing in the following cases:
       //   - The element is still loading.
       //   - The image is bad, but it's not in the broken state (i.e., we could
       //     decode the headers and get the size).
       if (!res.mIsStillLoading && !res.mHasSize) {
         error.Throw(NS_ERROR_NOT_AVAILABLE);
@@ -4653,17 +4824,22 @@ CanvasRenderingContext2D::DrawWindow(nsG
   }
   nsRefPtr<gfxContext> thebes;
   RefPtr<DrawTarget> drawDT;
   // Rendering directly is faster and can be done if mTarget supports Azure
   // and does not need alpha blending.
   if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget) &&
       GlobalAlpha() == 1.0f)
   {
-    thebes = new gfxContext(mTarget);
+    // Complete any async rendering and use synchronous rendering for DrawWindow
+    // until we're confident it works for all content.
+    FlushDelayedTarget();
+    FinishDelayedRendering();
+
+    thebes = new gfxContext(mFinalTarget);
     thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
                                 matrix._22, matrix._31, matrix._32));
   } else {
     drawDT =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
                                                                    SurfaceFormat::B8G8R8A8);
     if (!drawDT) {
       error.Throw(NS_ERROR_FAILURE);
@@ -4910,17 +5086,17 @@ CanvasRenderingContext2D::GetImageDataAr
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   IntRect srcRect(0, 0, mWidth, mHeight);
   IntRect destRect(aX, aY, aWidth, aHeight);
   IntRect srcReadRect = srcRect.Intersect(destRect);
   RefPtr<DataSourceSurface> readback;
   if (!srcReadRect.IsEmpty() && !mZero) {
-    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+    RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot();
     if (snapshot) {
       readback = snapshot->GetDataSurface();
     }
     if (!readback || !readback->GetData()) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
@@ -5296,17 +5472,17 @@ CanvasRenderingContext2D::GetCanvasLayer
   // layer manager which must NOT happen during a paint.
   if (!mTarget || !IsTargetValid()) {
     // No DidTransactionCallback will be received, so mark the context clean
     // now so future invalidations will be dispatched.
     MarkContextClean();
     return nullptr;
   }
 
-  mTarget->Flush();
+  FlushDelayedTarget();
 
   if (!mResetLayer && aOldLayer) {
     CanvasRenderingContext2DUserData* userData =
       static_cast<CanvasRenderingContext2DUserData*>(
         aOldLayer->GetUserData(&g2DContextLayerUserData));
 
     CanvasLayer::Data data;
 
@@ -5345,34 +5521,33 @@ CanvasRenderingContext2D::GetCanvasLayer
   // The layer will be destroyed when we tear down the presentation
   // (at the latest), at which time this userData will be destroyed,
   // releasing the reference to the element.
   // The userData will receive DidTransactionCallbacks, which flush the
   // the invalidation state to indicate that the canvas is up to date.
   userData = new CanvasRenderingContext2DUserData(this);
   canvasLayer->SetDidTransactionCallback(
           CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
+  canvasLayer->SetPreTransactionCallback(
+          CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
   canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
 
   CanvasLayer::Data data;
   data.mSize = nsIntSize(mWidth, mHeight);
   data.mHasAlpha = !mOpaque;
 
   GLuint skiaGLTex = SkiaGLTex();
   if (skiaGLTex) {
-    canvasLayer->SetPreTransactionCallback(
-            CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
-
     SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
     MOZ_ASSERT(glue);
 
     data.mGLContext = glue->GetGLContext();
     data.mFrontbufferGLTex = skiaGLTex;
   } else {
-    data.mDrawTarget = mTarget;
+    data.mDrawTarget = mFinalTarget;
   }
 
   canvasLayer->Initialize(data);
   uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
   canvasLayer->SetContentFlags(flags);
   canvasLayer->Updated();
 
   mResetLayer = false;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -5,16 +5,17 @@
 #ifndef CanvasRenderingContext2D_h
 #define CanvasRenderingContext2D_h
 
 #include "mozilla/Attributes.h"
 #include <vector>
 #include "nsIDOMCanvasRenderingContext2D.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/Monitor.h"
 #include "nsColor.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "CanvasUtils.h"
 #include "gfxTextRun.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
@@ -22,16 +23,17 @@
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/2D.h"
 #include "gfx2DGlue.h"
 #include "imgIEncoder.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/EnumeratedArray.h"
 #include "FilterSupport.h"
 #include "nsSVGEffects.h"
+#include "MediaTaskQueue.h"
 
 class nsGlobalWindow;
 class nsXULElement;
 
 namespace mozilla {
 namespace gl {
 class SourceSurface;
 }
@@ -47,16 +49,17 @@ class CanvasPath;
 
 extern const mozilla::gfx::Float SIGMA_MAX;
 
 template<typename T> class Optional;
 
 struct CanvasBidiProcessor;
 class CanvasRenderingContext2DUserData;
 class CanvasDrawObserver;
+class CanvasShutdownObserver;
 
 /**
  ** CanvasRenderingContext2D
  **/
 class CanvasRenderingContext2D final :
   public nsICanvasRenderingContextInternal,
   public nsWrapperCache
 {
@@ -437,24 +440,17 @@ public:
   }
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) override;
   NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, int32_t width, int32_t height) override;
 
   NS_IMETHOD GetInputStream(const char* aMimeType,
                             const char16_t* aEncoderOptions,
                             nsIInputStream **aStream) override;
 
-  mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override
-  {
-    EnsureTarget();
-    if (aPremultAlpha) {
-      *aPremultAlpha = true;
-    }
-    return mTarget->Snapshot();
-  }
+  mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override;
 
   NS_IMETHOD SetIsOpaque(bool isOpaque) override;
   bool GetIsOpaque() override { return mOpaque; }
   NS_IMETHOD Reset() override;
   already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                                CanvasLayer *aOldLayer,
                                                LayerManager *aManager) override;
   virtual bool ShouldForceInactiveLayer(LayerManager *aManager) override;
@@ -516,27 +512,43 @@ public:
       mozilla::gfx::Matrix transform = mTarget->GetTransform();
       mDSPathBuilder->BezierTo(transform * aCP1,
                                 transform * aCP2,
                                 transform * aCP3);
     }
   }
 
   friend class CanvasRenderingContext2DUserData;
+  friend class CanvasShutdownObserver;
 
   virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) override;
 
 
   // Given a point, return hit region ID if it exists
   nsString GetHitRegion(const mozilla::gfx::Point& aPoint) override;
 
 
   // return true and fills in the bound rect if element has a hit region.
   bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
 
+  /**
+   * Deferred rendering functions
+   */
+
+  /**
+   * Called when the event loop reaches a stable
+   * state, and trigger us to flush any outstanding
+   * commands to the rendering thread.
+   */
+  void StableStateReached()
+  {
+    mScheduledFlush = false;
+    FlushDelayedTarget();
+  }
+
 protected:
   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                              uint32_t aWidth, uint32_t aHeight,
                              JSObject** aRetval);
 
   nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                  dom::Uint8ClampedArray* aArray,
                                  bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
@@ -545,16 +557,18 @@ protected:
   /**
    * Internal method to complete initialisation, expects mTarget to have been set
    */
   nsresult Initialize(int32_t width, int32_t height);
 
   nsresult InitializeWithTarget(mozilla::gfx::DrawTarget *surface,
                                 int32_t width, int32_t height);
 
+  void ShutdownTaskQueue();
+
   /**
     * The number of living nsCanvasRenderingContexts.  When this goes down to
     * 0, we free the premultiply and unpremultiply tables, if they exist.
     */
   static uint32_t sNumLivingContexts;
 
   /**
     * Lookup table used to speed up GetImageData().
@@ -708,16 +722,64 @@ protected:
   // If mCanvasElement is not provided, then a docshell is
   nsCOMPtr<nsIDocShell> mDocShell;
 
   // This is created lazily so it is necessary to call EnsureTarget before
   // accessing it. In the event of an error it will be equal to
   // sErrorTarget.
   mozilla::RefPtr<mozilla::gfx::DrawTarget> mTarget;
 
+  /**
+   * Deferred rendering implementation
+   */
+
+  // If we are using deferred rendering, then this is the current
+  // deferred rendering target. It is the same pointer as mTarget.
+  mozilla::RefPtr<mozilla::gfx::DrawTargetCapture> mDelayedTarget;
+
+  // If we are using deferred rendering, then this is the actual destination
+  // buffer.
+  mozilla::RefPtr<mozilla::gfx::DrawTarget> mFinalTarget;
+
+  /**
+   * Add the current DelayedDrawTarget to the rendering queue,
+   * schedule a rendering job if required, and create a new
+   * DelayedDrawTarget.
+   */
+  void FlushDelayedTarget();
+
+  /**
+   * Make sure all commands have been flushed to
+   * the rendering thread, and block until they
+   * are completed.
+   */
+  void FinishDelayedRendering();
+
+  /**
+   * Called when a command is added to the current
+   * delayed draw target.
+   *
+   * Either flushes the current batch of commands to
+   * the rendering thread, or ensures that this happens
+   * the next time the event loop reaches a stable state.
+   */
+  void RecordCommand();
+
+  // The number of commands currently waiting to be sent
+  // to the rendering thread.
+  uint32_t mPendingCommands;
+
+  // True if we have scheduled FlushDelayedTarget to be
+  // called in the next browser stable state.
+  bool mScheduledFlush;
+
+  nsRefPtr<MediaTaskQueue> mTaskQueue;
+
+  nsRefPtr<CanvasShutdownObserver> mShutdownObserver;
+
   uint32_t SkiaGLTex() const;
 
   // This observes our draw calls at the beginning of the canvas
   // lifetime and switches to software or GPU mode depending on
   // what it thinks is best
   CanvasDrawObserver* mDrawObserver;
   void RemoveDrawObserver();
 
--- a/dom/contacts/tests/shared.js
+++ b/dom/contacts/tests/shared.js
@@ -244,28 +244,28 @@ function checkStrArray(str1, str2, msg) 
     }
     return v;
   }
   function optArray(val) {
     return Array.isArray(val) ? val : [val];
   }
   str1 = optArray(str1).map(normalize_falsy).filter(v => v != "");
   str2 = optArray(str2).map(normalize_falsy).filter(v => v != "");
-  ise(JSON.stringify(str1), JSON.stringify(str2), msg);
+  is(JSON.stringify(str1), JSON.stringify(str2), msg);
 }
 
 function checkPref(pref1, pref2) {
   // If on Android treat one preference as 0 and the other as undefined as matching
   if (isAndroid) {
     if ((!pref1 && pref2 == undefined) || (pref1 == undefined && !pref2)) {
       pref1 = false;
       pref2 = false;
     }
   }
-  ise(!!pref1, !!pref2, "Same pref");
+  is(!!pref1, !!pref2, "Same pref");
 }
 
 function checkAddress(adr1, adr2) {
   if (adr1 ^ adr2) {
     ok(false, "Expected both adrs to be either present or absent");
     return;
   }
   checkStrArray(adr1.type, adr2.type, "Same type");
@@ -327,17 +327,17 @@ function checkArrayField(array1, array2,
   if (!!array1 ^ !!array2) {
     ok(false, "Expected both arrays to be either present or absent: " + JSON.stringify(array1) + " vs. " + JSON.stringify(array2) + ". (" + msg + ")");
     return;
   }
   if (!array1 && !array2)  {
     ok(true, msg);
     return;
   }
-  ise(array1.length, array2.length, "Same length");
+  is(array1.length, array2.length, "Same length");
   for (var i = 0; i < array1.length; ++i) {
     func(array1[i], array2[i], msg);
   }
 }
 
 function checkContacts(contact1, contact2) {
   if (!!contact1 ^ !!contact2) {
     ok(false, "Expected both contacts to be either present or absent");
--- a/dom/contacts/tests/test_contacts_basics2.html
+++ b/dom/contacts/tests/test_contacts_basics2.html
@@ -697,23 +697,23 @@ var steps = [
     ok(true, "Undefined properties of fields should be treated correctly");
     var c = new mozContact({
       adr: [{streetAddress: undefined}],
       email: [{value: undefined}],
       url: [{value: undefined}],
       impp: [{value: undefined}],
       tel: [{value: undefined}],
     });
-    ise(c.adr[0].streetAddress, undefined, "adr.streetAddress is undefined");
-    ise(c.adr[0].locality, undefined, "adr.locality is undefined");
-    ise(c.adr[0].pref, undefined, "adr.pref is undefined");
-    ise(c.email[0].value, undefined, "email.value is undefined");
-    ise(c.url[0].value, undefined, "url.value is undefined");
-    ise(c.impp[0].value, undefined, "impp.value is undefined");
-    ise(c.tel[0].value, undefined, "tel.value is undefined");
+    is(c.adr[0].streetAddress, undefined, "adr.streetAddress is undefined");
+    is(c.adr[0].locality, undefined, "adr.locality is undefined");
+    is(c.adr[0].pref, undefined, "adr.pref is undefined");
+    is(c.email[0].value, undefined, "email.value is undefined");
+    is(c.url[0].value, undefined, "url.value is undefined");
+    is(c.impp[0].value, undefined, "impp.value is undefined");
+    is(c.tel[0].value, undefined, "tel.value is undefined");
     next();
   },
   function() {
     ok(true, "Setting array properties to an empty array should work");
     var c = new mozContact();
     function testArrayProp(prop) {
       is(c[prop], null, "property is initially null");
       c[prop] = [];
@@ -759,17 +759,17 @@ var steps = [
       for (var i = 0; i < properties1[prop].length; ++i) {
         c[prop].push(properties1[prop][i]);
       }
     }
     req = navigator.mozContacts.save(c);
     req.onsuccess = function() {
       req = navigator.mozContacts.find(defaultOptions);
       req.onsuccess = function() {
-        ise(req.result.length, 1, "Got 1 contact");
+        is(req.result.length, 1, "Got 1 contact");
         checkContacts(req.result[0], properties1);
         next();
       };
       req.onerror = onFailure;
     };
     req.onerror = onFailure;
   },
   clearDatabase,
@@ -783,47 +783,47 @@ var steps = [
     c.init({name: ["Bar"]});
     c.init({name: ["Bar"]});
     SimpleTest.endMonitorConsole();
   },
   function() {
     ok(true, "mozContact.init works as expected");
     var c = new mozContact({name: ["Foo"]});
     c.init({name: ["Bar"]});
-    ise(c.name[0], "Bar", "Same name");
+    is(c.name[0], "Bar", "Same name");
     next();
   },
   function() {
     ok(true, "mozContact.init without parameters");
     var c = new mozContact({name: ["Foo"]});
     c.init();
     next();
   },
   function() {
     ok(true, "mozContact.init resets properties");
     var c = new mozContact({jobTitle: ["Software Engineer"]});
     c.init({nickname: ["Jobless Johnny"]});
-    ise(c.nickname[0], "Jobless Johnny", "Same nickname");
+    is(c.nickname[0], "Jobless Johnny", "Same nickname");
     ok(!c.jobTitle, "jobTitle is not set");
     next();
   },
   function() {
     ok(true, "mozContacts.remove with an ID works");
     var c = new mozContact({name: ["Ephemeral Jimmy"]});
     req = navigator.mozContacts.save(c);
     req.onsuccess = function() {
       req = navigator.mozContacts.remove(c.id);
       req.onsuccess = function() {
         req = navigator.mozContacts.find({
           filterBy: ["id"],
           filterOp: "equals",
           filterValue: c.id
         });
         req.onsuccess = function() {
-          ise(req.result.length, 0, "Successfully removed contact by ID");
+          is(req.result.length, 0, "Successfully removed contact by ID");
           next();
         };
         req.onerror = onFailure;
       };
       req.onerror = onFailure;
     };
     req.onerror = onFailure;
   },
--- a/dom/contacts/tests/test_contacts_blobs.html
+++ b/dom/contacts/tests/test_contacts_blobs.html
@@ -91,18 +91,18 @@ function verifyBlob(blob1, blob2, isLast
   is(blob1 instanceof Blob, true,
      "blob1 is an instance of DOMBlob");
   is(blob2 instanceof Blob, true,
      "blob2 is an instance of DOMBlob");
   isnot(blob1 instanceof File, true,
      "blob1 is an instance of File");
   isnot(blob2 instanceof File, true,
      "blob2 is an instance of File");
-  ise(blob1.size, blob2.size, "Same size");
-  ise(blob1.type, blob2.type, "Same type");
+  is(blob1.size, blob2.size, "Same size");
+  is(blob1.type, blob2.type, "Same type");
 
   var buffer1;
   var buffer2;
 
   var reader1 = new FileReader();
   reader1.readAsArrayBuffer(blob2);
   reader1.onload = function(event) {
     buffer2 = event.target.result;
@@ -120,17 +120,17 @@ function verifyBlob(blob1, blob2, isLast
     }
   }
 }
 
 function verifyBlobArray(blobs1, blobs2)
 {
   is(blobs1 instanceof Array, true, "blobs1 is an array object");
   is(blobs2 instanceof Array, true, "blobs2 is an array object");
-  ise(blobs1.length, blobs2.length, "Same length");
+  is(blobs1.length, blobs2.length, "Same length");
 
   if (!blobs1.length) {
     next();
     return;
   }
 
   for (var i = 0; i < blobs1.length; i++) {
     verifyBlob(blobs1[i], blobs2[i], i == blobs1.length - 1);
--- a/dom/contacts/tests/test_contacts_international.html
+++ b/dom/contacts/tests/test_contacts_international.html
@@ -90,69 +90,69 @@ var steps = [
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
                    filterOp: "startsWith",
                    filterValue: number1.local};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ise(req.result.length, 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
-      ise(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.id, sample_id1, "Same ID");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for international number");
     var options = {filterBy: ["tel"],
                    filterOp: "startsWith",
                    filterValue: number1.international};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ise(req.result.length, 0, "Found exactly 0 contacts.");
+      is(req.result.length, 0, "Found exactly 0 contacts.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for a short number matching the prefix");
     var shortNumber = number1.local.substring(0, 3);
     var options = {filterBy: ["tel"],
                    filterOp: "equals",
                    filterValue: shortNumber};
     req = mozContacts.find(options);
     req.onsuccess = function() {
-      ise(req.result.length, 0, "The prefix short number should not match any contact.");
+      is(req.result.length, 0, "The prefix short number should not match any contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for a short number matching the suffix");
     var shortNumber = number1.local.substring(number1.local.length - 3);
     var options = {filterBy: ["tel"],
                    filterOp: "equals",
                    filterValue: shortNumber};
     req = mozContacts.find(options);
     req.onsuccess = function() {
-      ise(req.result.length, 0, "The suffix short number should not match any contact.");
+      is(req.result.length, 0, "The suffix short number should not match any contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for a short number matching a contact");
     var options = {filterBy: ["tel"],
                    filterOp: "equals",
                    filterValue: shortNumber};
     req = mozContacts.find(options);
     req.onsuccess = function() {
-      ise(req.result.length, 1, "Found the contact equally matching the shortNumber.");
+      is(req.result.length, 1, "Found the contact equally matching the shortNumber.");
       next();
     };
     req.onerror = onFailure;
   },
   function() {
     ok(true, "Modifying number");
     if (!findResult1) {
       SpecialPowers.executeSoon(next);
@@ -166,55 +166,55 @@ var steps = [
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
                    filterOp: "startsWith",
                    filterValue: number1.local};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ise(req.result.length, 0, "Found exactly 0 contact.");
+      is(req.result.length, 0, "Found exactly 0 contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
                    filterOp: "startsWith",
                    filterValue: number1.international};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ise(req.result.length, 0, "Found exactly 0 contact.");
+      is(req.result.length, 0, "Found exactly 0 contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
                    filterOp: "startsWith",
                    filterValue: number2.local};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ise(req.result.length, 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
-      ise(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.id, sample_id1, "Same ID");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
                    filterOp: "startsWith",
                    filterValue: number2.international};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ise(req.result.length, 0, "Found exactly 1 contact.");
+      is(req.result.length, 0, "Found exactly 1 contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Deleting database");
     req = mozContacts.clear();
     req.onsuccess = function () {
@@ -236,19 +236,19 @@ var steps = [
   },
   function () {
     ok(true, "Searching for Brazilian number using local number");
     var options = {filterBy: ["tel"],
                    filterOp: "match",
                    filterValue: number3.local};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ise(req.result.length, 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
-      ise(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.id, sample_id1, "Same ID");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Deleting database");
     req = mozContacts.clear();
     req.onsuccess = function () {
--- a/dom/contacts/tests/test_contacts_substringmatchingCL.html
+++ b/dom/contacts/tests/test_contacts_substringmatchingCL.html
@@ -49,32 +49,32 @@ var steps = [
   function () {
     ok(true, "Searching for Chilean number with prefix");
     req = mozContacts.find({
       filterBy: ["tel"],
       filterOp: "match",
       filterValue: number.international
     });
     req.onsuccess = function () {
-      ise(req.result.length, 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
-      ise(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.id, sample_id1, "Same ID");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for Chilean number using local number");
     req = mozContacts.find({
       filterBy: ["tel"],
       filterOp: "match",
       filterValue: number.local
     });
     req.onsuccess = function () {
-      ise(req.result.length, 1, "Found 0 contacts.");
+      is(req.result.length, 1, "Found 0 contacts.");
       next();
     };
     req.onerror = onFailure;
   },
 
   clearDatabase,
 
   function () {
@@ -87,63 +87,63 @@ var steps = [
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts");
     req = mozContacts.find({});
     req.onsuccess = function () {
-      ise(req.result.length, 1, "One contact.");
+      is(req.result.length, 1, "One contact.");
       findResult1 = req.result[0];
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by last 8 digits");
     req = mozContacts.find({
       filterBy: ["tel"],
       filterOp: "match",
       filterValue: number.international.slice(-8)
     });
     req.onsuccess = function () {
-      ise(req.result.length, 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
-      ise(findResult1.id, sample_id1, "Same ID");
-      ise(findResult1.tel[0].value, number.international, "Same Value");
+      is(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.tel[0].value, number.international, "Same Value");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by last 9 digits");
     req = mozContacts.find({
       filterBy: ["tel"],
       filterOp: "match",
       filterValue: number.international.slice(-9)
     });
     req.onsuccess = function () {
-      ise(req.result.length, 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
-      ise(findResult1.id, sample_id1, "Same ID");
-      ise(findResult1.tel[0].value, number.international, "Same Value");
+      is(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.tel[0].value, number.international, "Same Value");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by last 6 digits");
     req = mozContacts.find({
       filterBy: ["tel"],
       filterOp: "match",
       filterValue: number.international.slice(-6)
     });
     req.onsuccess = function () {
-      ise(req.result.length, 0, "Found exactly zero contacts.");
+      is(req.result.length, 0, "Found exactly zero contacts.");
       next();
     };
     req.onerror = onFailure;
   },
 
   clearDatabase,
 
   function () {
@@ -156,34 +156,34 @@ var steps = [
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts");
     req = mozContacts.find({});
     req.onsuccess = function () {
-      ise(req.result.length, 1, "One contact.");
+      is(req.result.length, 1, "One contact.");
       findResult1 = req.result[0];
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by last 7 digits (local number) with landline calling prefix");
     req = mozContacts.find({
       filterBy: ["tel"],
       filterOp: "match",
       filterValue: "022" + landlineNumber.slice(-7)
     });
     req.onsuccess = function () {
-      ise(req.result.length, 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
-      ise(findResult1.id, sample_id1, "Same ID");
-      ise(findResult1.tel[0].value, landlineNumber, "Same Value");
+      is(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.tel[0].value, landlineNumber, "Same Value");
       next();
     };
     req.onerror = onFailure;
   },
 
   clearDatabase,
 
   function () {
--- a/dom/contacts/tests/test_migration.html
+++ b/dom/contacts/tests/test_migration.html
@@ -81,17 +81,17 @@ var steps = [
   createDB.bind(null, 12),
   setSubstringMatching.bind(null, 7),
 
   function testAccessMozContacts() {
     info("Checking we have the right number of contacts: " + contactsCount);
     var req = mozContacts.getCount();
     req.onsuccess = function onsuccess() {
       ok(true, "Could access the mozContacts API");
-      ise(this.result, contactsCount, "Contacts count is correct");
+      is(this.result, contactsCount, "Contacts count is correct");
       next();
     };
 
     req.onerror = function onerror() {
       ok(false, "Couldn't access the mozContacts API");
       next();
     };
   },
@@ -99,17 +99,17 @@ var steps = [
   function testRetrieveAllContacts() {
     /* if the migration does not work right, either we'll have an error, or the
        contacts won't be migrated properly and thus will fail WebIDL conversion,
        which will manifest as a timeout */
     info("Checking the contacts are corrected to obey WebIDL constraints.  (upgrades 14 to 17)");
     var req = mozContacts.find();
     req.onsuccess = function onsuccess() {
       if (this.result) {
-        ise(this.result.length, contactsCount, "Contacts array length is correct");
+        is(this.result.length, contactsCount, "Contacts array length is correct");
         allContacts = this.result;
         next();
       } else {
         ok(false, "Could access the mozContacts API but got no contacts!");
         next();
       }
     };
 
--- a/dom/downloads/tests/test_downloads_navigator_object.html
+++ b/dom/downloads/tests/test_downloads_navigator_object.html
@@ -43,26 +43,26 @@ var steps = [
       set: [["dom.mozDownloads.enabled", true]]
     }, next);
   },
 
   function() {
     SpecialPowers.pushPermissions([
       {type: "downloads", allow: 0, context: document}
     ], function() {
-      ise(frames[0].navigator.mozDownloadManager, null, "navigator.mozDownloadManager is null when the page doesn't have permissions");
+      is(frames[0].navigator.mozDownloadManager, null, "navigator.mozDownloadManager is null when the page doesn't have permissions");
       next();
     });
   },
 
   function() {
     SpecialPowers.pushPrefEnv({
       set: [["dom.mozDownloads.enabled", false]]
     }, function() {
-      ise(navigator.mozDownloadManager, undefined, "navigator.mozDownloadManager is undefined");
+      is(navigator.mozDownloadManager, undefined, "navigator.mozDownloadManager is undefined");
       next();
     });
   },
 
   function() {
     SimpleTest.finish();
   }
 ];
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -853,23 +853,16 @@ EventDispatcher::CreateEvent(EventTarget
     PageTransitionEventInit init;
     nsRefPtr<PageTransitionEvent> event =
       PageTransitionEvent::Constructor(aOwner, EmptyString(), init);
     event.forget(aDOMEvent);
     return NS_OK;
   }
   if (aEventType.LowerCaseEqualsLiteral("scrollareaevent"))
     return NS_NewDOMScrollAreaEvent(aDOMEvent, aOwner, aPresContext, nullptr);
-  if (aEventType.LowerCaseEqualsLiteral("closeevent")) {
-    CloseEventInit init;
-    nsRefPtr<CloseEvent> event =
-      CloseEvent::Constructor(aOwner, EmptyString(), init);
-    event.forget(aDOMEvent);
-    return NS_OK;
-  }
   // XXXkhuey Chrome supports popstateevent here, even though it provides no
   // initPopStateEvent method.  This is nuts ... but copying it is unlikely to
   // break the web.
   if (aEventType.LowerCaseEqualsLiteral("popstateevent")) {
     AutoJSContext cx;
     RootedDictionary<PopStateEventInit> init(cx);
     nsRefPtr<PopStateEvent> event =
       PopStateEvent::Constructor(aOwner, EmptyString(), init);
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -36,17 +36,16 @@
 #include "nsISupports.h"
 #include "nsPresContext.h"
 
 namespace mozilla {
 
 using namespace dom;
 using namespace widget;
 
-#ifdef PR_LOGGING
 /**
  * When a method is called, log its arguments and/or related static variables
  * with PR_LOG_ALWAYS.  However, if it puts too many logs like
  * OnDestroyPresContext(), should long only when the method actually does
  * something. In this case, the log should start with "ISM: <method name>".
  *
  * When a method quits due to unexpected situation, log the reason with
  * PR_LOG_ERROR.  In this case, the log should start with
@@ -172,38 +171,35 @@ GetNotifyIMEMessageName(IMEMessage aMess
     case REQUEST_TO_COMMIT_COMPOSITION:
       return "REQUEST_TO_COMMIT_COMPOSITION";
     case REQUEST_TO_CANCEL_COMPOSITION:
       return "REQUEST_TO_CANCEL_COMPOSITION";
     default:
       return "unacceptable IME notification message";
   }
 }
-#endif // #ifdef PR_LOGGING
 
 nsIContent* IMEStateManager::sContent = nullptr;
 nsPresContext* IMEStateManager::sPresContext = nullptr;
 bool IMEStateManager::sInstalledMenuKeyboardListener = false;
 bool IMEStateManager::sIsTestingIME = false;
 bool IMEStateManager::sIsGettingNewIMEState = false;
 
 // sActiveIMEContentObserver points to the currently active IMEContentObserver.
 // sActiveIMEContentObserver is null if there is no focused editor.
 IMEContentObserver* IMEStateManager::sActiveIMEContentObserver = nullptr;
 TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
 
 // static
 void
 IMEStateManager::Init()
 {
-#ifdef PR_LOGGING
   if (!sISMLog) {
     sISMLog = PR_NewLogModule("IMEStateManager");
   }
-#endif
 }
 
 // static
 void
 IMEStateManager::Shutdown()
 {
   PR_LOG(sISMLog, PR_LOG_ALWAYS,
     ("ISM: IMEStateManager::Shutdown(), "
@@ -227,25 +223,23 @@ IMEStateManager::OnDestroyPresContext(ns
       sTextCompositions->IndexOf(aPresContext);
     if (i != TextCompositionArray::NoIndex) {
       PR_LOG(sISMLog, PR_LOG_DEBUG,
         ("ISM:   IMEStateManager::OnDestroyPresContext(), "
          "removing TextComposition instance from the array (index=%u)", i));
       // there should be only one composition per presContext object.
       sTextCompositions->ElementAt(i)->Destroy();
       sTextCompositions->RemoveElementAt(i);
-#if defined DEBUG || PR_LOGGING
       if (sTextCompositions->IndexOf(aPresContext) !=
             TextCompositionArray::NoIndex) {
         PR_LOG(sISMLog, PR_LOG_ERROR,
           ("ISM:   IMEStateManager::OnDestroyPresContext(), FAILED to remove "
            "TextComposition instance from the array"));
         MOZ_CRASH("Failed to remove TextComposition instance from the array");
       }
-#endif // #if defined DEBUG || PR_LOGGING
     }
   }
 
   if (aPresContext != sPresContext) {
     return NS_OK;
   }
 
   PR_LOG(sISMLog, PR_LOG_ALWAYS,
@@ -508,25 +502,25 @@ IMEStateManager::OnMouseButtonEventInEdi
       ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
        "the internal event of aMouseEvent isn't WidgetMouseEvent"));
     return false;
   }
 
   bool consumed =
     sActiveIMEContentObserver->OnMouseButtonEvent(aPresContext, internalEvent);
 
-#ifdef PR_LOGGING
-  nsAutoString eventType;
-  aMouseEvent->GetType(eventType);
-  PR_LOG(sISMLog, PR_LOG_ALWAYS,
-    ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
-     "mouse event (type=%s, button=%d) is %s",
-     NS_ConvertUTF16toUTF8(eventType).get(), internalEvent->button,
-     consumed ? "consumed" : "not consumed"));
-#endif
+  if (PR_LOG_TEST(sISMLog, PR_LOG_ALWAYS)) {
+    nsAutoString eventType;
+    aMouseEvent->GetType(eventType);
+    PR_LOG(sISMLog, PR_LOG_ALWAYS,
+      ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
+       "mouse event (type=%s, button=%d) is %s",
+       NS_ConvertUTF16toUTF8(eventType).get(), internalEvent->button,
+       consumed ? "consumed" : "not consumed"));
+  }
 
   return consumed;
 }
 
 // static
 void
 IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
                                  nsIContent* aContent,
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -122,24 +122,26 @@ GetRequestURLFromWorker(const GlobalObje
 } // anonymous namespace
 
 /*static*/ already_AddRefed<Request>
 Request::Constructor(const GlobalObject& aGlobal,
                      const RequestOrUSVString& aInput,
                      const RequestInit& aInit, ErrorResult& aRv)
 {
   nsRefPtr<InternalRequest> request;
+  bool inputRequestHasBody = false;
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 
   if (aInput.IsRequest()) {
     nsRefPtr<Request> inputReq = &aInput.GetAsRequest();
     nsCOMPtr<nsIInputStream> body;
     inputReq->GetBody(getter_AddRefs(body));
     if (body) {
+      inputRequestHasBody = true;
       if (inputReq->BodyUsed()) {
         aRv.ThrowTypeError(MSG_FETCH_BODY_CONSUMED_ERROR);
         return nullptr;
       } else {
         inputReq->SetBodyUsed();
       }
     }
 
@@ -283,26 +285,28 @@ Request::Constructor(const GlobalObject&
     }
   }
 
   requestHeaders->Fill(*headers, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  if (aInit.mBody.WasPassed()) {
+  if (aInit.mBody.WasPassed() || inputRequestHasBody) {
     // HEAD and GET are not allowed to have a body.
     nsAutoCString method;
     request->GetMethod(method);
     // method is guaranteed to be uppercase due to step 14.2 above.
     if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
       aRv.ThrowTypeError(MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD);
       return nullptr;
     }
+  }
 
+  if (aInit.mBody.WasPassed()) {
     const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
     nsCOMPtr<nsIInputStream> stream;
     nsAutoCString contentType;
     aRv = ExtractByteStreamFromBody(bodyInit,
                                     getter_AddRefs(stream), contentType);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -87,25 +87,20 @@
 #include "mozilla/dom/TextTrack.h"
 #include "nsIContentPolicy.h"
 #include "mozilla/Telemetry.h"
 
 #include "ImageContainer.h"
 #include "nsRange.h"
 #include <algorithm>
 
-#ifdef PR_LOGGING
 static PRLogModuleInfo* gMediaElementLog;
 static PRLogModuleInfo* gMediaElementEventsLog;
 #define LOG(type, msg) PR_LOG(gMediaElementLog, type, msg)
 #define LOG_EVENT(type, msg) PR_LOG(gMediaElementEventsLog, type, msg)
-#else
-#define LOG(type, msg)
-#define LOG_EVENT(type, msg)
-#endif
 
 #include "nsIContentSecurityPolicy.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/FloatingPoint.h"
 
 #include "nsIPermissionManager.h"
 #include "nsContentTypeParser.h"
@@ -2089,24 +2084,22 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mCORSMode(CORS_NONE),
     mIsEncrypted(false),
     mDownloadSuspendedByCache(false, "HTMLMediaElement::mDownloadSuspendedByCache"),
     mAudioChannelFaded(false),
     mPlayingThroughTheAudioChannel(false),
     mDisableVideo(false),
     mElementInTreeState(ELEMENT_NOT_INTREE)
 {
-#ifdef PR_LOGGING
   if (!gMediaElementLog) {
     gMediaElementLog = PR_NewLogModule("nsMediaElement");
   }
   if (!gMediaElementEventsLog) {
     gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents");
   }
-#endif
 
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
 
   mPaused.SetOuter(this);
 
   RegisterActivityObserver();
   NotifyOwnerDocumentActivityChanged();
 
@@ -3592,25 +3585,23 @@ HTMLMediaElement::UpdateReadyStateIntern
   if (mDecoder->CanPlayThrough())
   {
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
     return;
   }
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA);
 }
 
-#ifdef PR_LOGGING
 static const char* const gReadyStateToString[] = {
   "HAVE_NOTHING",
   "HAVE_METADATA",
   "HAVE_CURRENT_DATA",
   "HAVE_FUTURE_DATA",
   "HAVE_ENOUGH_DATA"
 };
-#endif
 
 void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
 {
   nsMediaReadyState oldState = mReadyState;
   mReadyState = aState;
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY ||
       oldState == mReadyState) {
@@ -3652,24 +3643,22 @@ void HTMLMediaElement::ChangeReadyState(
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
     DispatchAsyncEvent(NS_LITERAL_STRING("canplaythrough"));
   }
 }
 
-#ifdef PR_LOGGING
 static const char* const gNetworkStateToString[] = {
   "EMPTY",
   "IDLE",
   "LOADING",
   "NO_SOURCE"
  };
-#endif
 
 void HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState)
 {
   if (mNetworkState == aState) {
     return;
   }
 
   nsMediaNetworkState oldState = mNetworkState;
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -35,22 +35,18 @@
 #include "nsISupportsImpl.h"
 #include "nsMappedAttributes.h"
 #include "nsNetUtil.h"
 #include "nsRuleData.h"
 #include "nsStyleConsts.h"
 #include "nsThreadUtils.h"
 #include "nsVideoFrame.h"
 
-#ifdef PR_LOGGING
 static PRLogModuleInfo* gTrackElementLog;
 #define LOG(type, msg) PR_LOG(gTrackElementLog, type, msg)
-#else
-#define LOG(type, msg)
-#endif
 
 // Replace the usual NS_IMPL_NS_NEW_HTML_ELEMENT(Track) so
 // we can return an UnknownElement instead when pref'd off.
 nsGenericHTMLElement*
 NS_NewHTMLTrackElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                        mozilla::dom::FromParser aFromParser)
 {
   if (!mozilla::dom::HTMLTrackElement::IsWebVTTEnabled()) {
@@ -75,21 +71,19 @@ static MOZ_CONSTEXPR nsAttrValue::EnumTa
 
 // The default value for kKindTable is "subtitles"
 static MOZ_CONSTEXPR const char* kKindTableDefaultString = kKindTable->tag;
 
 /** HTMLTrackElement */
 HTMLTrackElement::HTMLTrackElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
 {
-#ifdef PR_LOGGING
   if (!gTrackElementLog) {
     gTrackElementLog = PR_NewLogModule("nsTrackElement");
   }
-#endif
 }
 
 HTMLTrackElement::~HTMLTrackElement()
 {
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLTrackElement)
 
--- a/dom/html/test/forms/test_input_attributes_reflection.html
+++ b/dom/html/test/forms/test_input_attributes_reflection.html
@@ -24,34 +24,34 @@ function testWidthHeight(attr) {
   is(element[attr], 0, attr + ' always returns 0 if not type=image');
   is(element.getAttribute(attr), '42');
   element[attr] = 0;
   is(element.getAttribute(attr), '0', 'setting ' + attr + ' changes the content attribute');
   element[attr] = 12;
   is(element.getAttribute(attr), '12', 'setting ' + attr + ' changes the content attribute');
 
   element.removeAttribute(attr);
-  ise(element.getAttribute(attr), null);
+  is(element.getAttribute(attr), null);
 
   element = document.createElement('input');
   element.type = 'image';
   document.getElementById('content').appendChild(element);
   isnot(element[attr], 0, attr + ' represents the dimension of the element if type=image');
 
   element.setAttribute(attr, '42');
   isnot(element[attr], 0, attr + ' represents the dimension of the element if type=image');
   isnot(element[attr], 42, attr + ' represents the dimension of the element if type=image');
   is(element.getAttribute(attr), '42');
   element[attr] = 0;
   is(element.getAttribute(attr), '0', 'setting ' + attr + ' changes the content attribute');
   element[attr] = 12;
   is(element.getAttribute(attr), '12', 'setting ' + attr + ' changes the content attribute');
 
   element.removeAttribute(attr);
-  ise(element.getAttribute(attr), null);
+  is(element.getAttribute(attr), null);
 }
 
 // .accept
 reflectString({
   element: document.createElement("input"),
   attribute: "accept",
   otherValues: [ "audio/*", "video/*", "image/*", "image/png",
                  "application/msword", "appplication/pdf" ],
--- a/dom/html/test/forms/test_input_autocomplete.html
+++ b/dom/html/test/forms/test_input_autocomplete.html
@@ -61,18 +61,18 @@ var values = [
 var types = [undefined, "hidden", "text", "search"]; // Valid types for all non-multiline hints.
 
 function checkAutocompleteValues(field, type) {
   for (var test of values) {
     if (typeof(test[0]) === "undefined")
       field.removeAttribute("autocomplete");
     else
       field.setAttribute("autocomplete", test[0]);
-    ise(field.autocomplete, test[1], "Checking @autocomplete for @type=" + type + " of: " + test[0]);
-    ise(field.autocomplete, test[1], "Checking cached @autocomplete for @type=" + type + " of: " + test[0]);
+    is(field.autocomplete, test[1], "Checking @autocomplete for @type=" + type + " of: " + test[0]);
+    is(field.autocomplete, test[1], "Checking cached @autocomplete for @type=" + type + " of: " + test[0]);
   }
 }
 
 function start() {
   var inputField = document.getElementById("input-field");
   for (var type of types) {
     // Switch the input type
     if (typeof(type) === "undefined")
--- a/dom/html/test/forms/test_radio_radionodelist.html
+++ b/dom/html/test/forms/test_radio_radionodelist.html
@@ -17,41 +17,41 @@ https://bugzilla.mozilla.org/show_bug.cg
   <input type="checkbox" name="rdo" value="0" id="r0" checked="checked">
   <input type="radio" name="rdo" id="r1">
   <input type="radio" name="rdo" id="r2" value="2">
 </form>
 <script class="testbody" type="text/javascript">
 /** Test for Bug 779723 **/
 
 var rdoList = document.forms[0].elements.namedItem('rdo');
-ise(rdoList.value, "", "The value attribute should be empty");
+is(rdoList.value, "", "The value attribute should be empty");
 
 document.getElementById('r2').checked = true;
 ok(rdoList.value, "2", "The value attribute should be 2");
 
 document.getElementById('r1').checked = true;
 ok(rdoList.value, "on", "The value attribute should be on");
 
 document.getElementById('r1').value = 1;
 ok(rdoList.value, "1", "The value attribute should be 1");
 
-ise(rdoList.value, document.getElementById('r1').value,
-   "The value attribute should be equal to the first checked radio input element's value");
+is(rdoList.value, document.getElementById('r1').value,
+  "The value attribute should be equal to the first checked radio input element's value");
 ok(!document.getElementById('r2').checked,
    "The second radio input element should not be checked");
 
 rdoList.value = '2';
-ise(rdoList.value, document.getElementById('r2').value,
-   "The value attribute should be equal to the second radio input element's value");
+is(rdoList.value, document.getElementById('r2').value,
+  "The value attribute should be equal to the second radio input element's value");
 ok(document.getElementById('r2').checked,
    "The second radio input element should be checked");
 
 rdoList.value = '3';
-ise(rdoList.value, document.getElementById('r2').value,
-   "The value attribute should be the second radio input element's value");
+is(rdoList.value, document.getElementById('r2').value,
+  "The value attribute should be the second radio input element's value");
 ok(document.getElementById('r2').checked,
    "The second radio input element should be checked");
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/html/test/reflect.js
+++ b/dom/html/test/reflect.js
@@ -268,77 +268,77 @@ function reflectLimitedEnumerated(aParam
   var unsupportedValues = aParameters.unsupportedValues !== undefined
                             ? aParameters.unsupportedValues : [];
   var nullable = aParameters.nullable;
 
   ok(idlAttr in element, idlAttr + " should be an IDL attribute of this element");
   if (nullable) {
     // The missing value default is null, which is typeof == "object"
     is(typeof element[idlAttr], "object", "'" + idlAttr + "' IDL attribute should be null, which has typeof == object");
-    ise(element[idlAttr], null, "'" + idlAttr + "' IDL attribute should be null");
+    is(element[idlAttr], null, "'" + idlAttr + "' IDL attribute should be null");
   } else {
     is(typeof element[idlAttr], "string", "'" + idlAttr + "' IDL attribute should be a string");
   }
 
   if (nullable) {
     element.setAttribute(contentAttr, "something");
     // Now it will be a string
     is(typeof element[idlAttr], "string", "'" + idlAttr + "' IDL attribute should be a string");
   }
 
   // Explicitly check the default value.
   element.removeAttribute(contentAttr);
-  ise(element[idlAttr], defaultValueMissing,
-      "When no attribute is set, the value should be the default value.");
+  is(element[idlAttr], defaultValueMissing,
+     "When no attribute is set, the value should be the default value.");
 
   // Check valid values.
   validValues.forEach(function (v) {
     element.setAttribute(contentAttr, v);
-    ise(element[idlAttr], v,
-        "'" + v + "' should be accepted as a valid value for " + idlAttr);
-    ise(element.getAttribute(contentAttr), v,
-        "Content attribute should return the value it has been set to.");
+    is(element[idlAttr], v,
+       "'" + v + "' should be accepted as a valid value for " + idlAttr);
+    is(element.getAttribute(contentAttr), v,
+       "Content attribute should return the value it has been set to.");
     element.removeAttribute(contentAttr);
 
     element.setAttribute(contentAttr, v.toUpperCase());
-    ise(element[idlAttr], v,
-        "Enumerated attributes should be case-insensitive.");
-    ise(element.getAttribute(contentAttr), v.toUpperCase(),
-        "Content attribute should not be lower-cased.");
+    is(element[idlAttr], v,
+       "Enumerated attributes should be case-insensitive.");
+    is(element.getAttribute(contentAttr), v.toUpperCase(),
+       "Content attribute should not be lower-cased.");
     element.removeAttribute(contentAttr);
 
     element[idlAttr] = v;
-    ise(element[idlAttr], v,
-        "'" + v + "' should be accepted as a valid value for " + idlAttr);
-    ise(element.getAttribute(contentAttr), v,
-        "Content attribute should return the value it has been set to.");
+    is(element[idlAttr], v,
+       "'" + v + "' should be accepted as a valid value for " + idlAttr);
+    is(element.getAttribute(contentAttr), v,
+       "Content attribute should return the value it has been set to.");
     element.removeAttribute(contentAttr);
 
     element[idlAttr] = v.toUpperCase();
-    ise(element[idlAttr], v,
-        "Enumerated attributes should be case-insensitive.");
-    ise(element.getAttribute(contentAttr), v.toUpperCase(),
-        "Content attribute should not be lower-cased.");
+    is(element[idlAttr], v,
+       "Enumerated attributes should be case-insensitive.");
+    is(element.getAttribute(contentAttr), v.toUpperCase(),
+       "Content attribute should not be lower-cased.");
     element.removeAttribute(contentAttr);
   });
 
   // Check invalid values.
   invalidValues.forEach(function (v) {
     element.setAttribute(contentAttr, v);
-    ise(element[idlAttr], defaultValueInvalid,
-        "When the content attribute is set to an invalid value, the default value should be returned.");
-    ise(element.getAttribute(contentAttr), v,
-        "Content attribute should not have been changed.");
+    is(element[idlAttr], defaultValueInvalid,
+       "When the content attribute is set to an invalid value, the default value should be returned.");
+    is(element.getAttribute(contentAttr), v,
+       "Content attribute should not have been changed.");
     element.removeAttribute(contentAttr);
 
     element[idlAttr] = v;
-    ise(element[idlAttr], defaultValueInvalid,
-        "When the value is set to an invalid value, the default value should be returned.");
-    ise(element.getAttribute(contentAttr), v,
-        "Content attribute should not have been changed.");
+    is(element[idlAttr], defaultValueInvalid,
+       "When the value is set to an invalid value, the default value should be returned.");
+    is(element.getAttribute(contentAttr), v,
+       "Content attribute should not have been changed.");
     element.removeAttribute(contentAttr);
   });
 
   // Check valid values we currently do not support.
   // Basically, it's like the checks for the valid values but with some todo's.
   unsupportedValues.forEach(function (v) {
     element.setAttribute(contentAttr, v);
     todo_is(element[idlAttr], v,
@@ -365,18 +365,18 @@ function reflectLimitedEnumerated(aParam
     todo_is(element[idlAttr], v,
             "Enumerated attributes should be case-insensitive.");
     is(element.getAttribute(contentAttr), v.toUpperCase(),
        "Content attribute should not be lower-cased.");
     element.removeAttribute(contentAttr);
   });
 
   if (nullable) {
-    ise(defaultValueMissing, null,
-	"Missing default value should be null for nullable attributes");
+    is(defaultValueMissing, null,
+       "Missing default value should be null for nullable attributes");
     ok(validValues.length > 0, "We better have at least one valid value");
     element.setAttribute(contentAttr, validValues[0]);
     ok(element.hasAttribute(contentAttr),
        "Should have content attribute: we just set it");
     element[idlAttr] = null;
     ok(!element.hasAttribute(contentAttr),
        "Should have removed content attribute");
   }
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -64,37 +64,33 @@
 #elif defined(ENABLE_LOGGING)
 #  define LOG(fmt, ...) \
      printf("ProcessPriorityManager - " fmt "\n", ##__VA_ARGS__)
 #  define LOGP(fmt, ...) \
      printf("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " \
        fmt "\n", \
        NameWithComma().get(), \
        static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__)
-
-#elif defined(PR_LOGGING)
+#else
   static PRLogModuleInfo*
   GetPPMLog()
   {
     static PRLogModuleInfo *sLog;
     if (!sLog)
       sLog = PR_NewLogModule("ProcessPriorityManager");
     return sLog;
   }
 #  define LOG(fmt, ...) \
      PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \
             ("ProcessPriorityManager - " fmt, ##__VA_ARGS__))
 #  define LOGP(fmt, ...) \
      PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \
             ("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " fmt, \
             NameWithComma().get(), \
             static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__))
-#else
-#define LOG(fmt, ...)
-#define LOGP(fmt, ...)
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
 namespace {
 
--- a/dom/manifest/test/test_IconsProcessor_density.html
+++ b/dom/manifest/test/test_IconsProcessor_density.html
@@ -30,28 +30,28 @@ var iconDensityValueTests = [null, {},
   '1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
 ];
 
 iconDensityValueTests.forEach((density) => {
   var expected = `Expect density to default to 1.0.`;
   testIcon.icons[0].density = density;
   data.jsonText = JSON.stringify(testIcon);
   var result = processor.process(data);
-  ise(result.icons[0].density, 1.0, expected);
+  is(result.icons[0].density, 1.0, expected);
 });
 
 testIcon = {
   icons: [{
     src: 'test',
     density: undefined
   }]
 };
 
 var parseFloatTests = [3.14, '3.14', `${whiteSpace}3.14${whiteSpace}`, 12e300];
 parseFloatTests.forEach((testNumber) => {
   var expected = `Expect density to be ${parseFloat(testNumber)}.`;
   testIcon.icons[0].density = testNumber;
   data.jsonText = JSON.stringify(testIcon);
   var result = processor.process(data);
-  ise(result.icons[0].density, parseFloat(testNumber), expected);
+  is(result.icons[0].density, parseFloat(testNumber), expected);
 });
   </script>
 </head>
--- a/dom/manifest/test/test_IconsProcessor_sizes.html
+++ b/dom/manifest/test/test_IconsProcessor_sizes.html
@@ -61,41 +61,41 @@ validSizes.forEach(({
   test, expect
 }) => {
   testIcon.icons[0].sizes = test;
   data.jsonText = JSON.stringify(testIcon);
   var result = processor.process(data);
   var sizes = result.icons[0].sizes;
   for (var expectedSize of expect) {
     var expected = `Expect sizes to contain ${expectedSize}`;
-    ise(sizes.has(expectedSize), true, expected);
+    is(sizes.has(expectedSize), true, expected);
   }
   expected = `Expect the size of the set to be ${expect.length}.`;
-  ise(sizes.size, expect.length, expected);
+  is(sizes.size, expect.length, expected);
 });
 
 var testIcon = {
   icons: [{
     src: 'test',
     sizes: undefined
   }]
 };
 
 var invalidSizes = ['invalid', '', ' ', '16 x 16', '32', '21', '16xx16', '16 x x 6'];
 invalidSizes.forEach((invalidSize) => {
   var expected = 'Expect invalid sizes to return an empty set.';
   testIcon.icons[0].sizes = invalidSize;
   data.jsonText = JSON.stringify(testIcon);
   var result = processor.process(data);
   var sizes = result.icons[0].sizes;
-  ise(sizes.size, 0, expected);
+  is(sizes.size, 0, expected);
 });
 
 typeTests.forEach((type) => {
   var expected = `Expect non-string sizes to be empty set: ${typeof type}.`;
   testIcon.icons[0].sizes = type;
   data.jsonText = JSON.stringify(testIcon);
   var result = processor.process(data);
   var sizes = result.icons[0].sizes;
-  ise(sizes.size, 0, expected);
+  is(sizes.size, 0, expected);
 });
   </script>
 </head>
--- a/dom/manifest/test/test_IconsProcessor_src.html
+++ b/dom/manifest/test/test_IconsProcessor_src.html
@@ -33,17 +33,17 @@ var noSrc = {
     sizes: 'any',
     type: 'image/jpg'
   }]
 };
 
 var expected = `Expect icons without a src prop to be filtered out.`;
 data.jsonText = JSON.stringify(noSrc);
 var result = processor.process(data);
-ise(result.icons.length, 0, expected);
+is(result.icons.length, 0, expected);
 
 var invalidSrc = {
   icons: [{
     src: null
   }, {
     src: 1
   }, {
     src: []
@@ -54,57 +54,57 @@ var invalidSrc = {
   }, {
     src: ''
   }]
 };
 
 var expected = `Expect icons with invalid src prop to be filtered out.`;
 data.jsonText = JSON.stringify(noSrc);
 var result = processor.process(data);
-ise(result.icons.length, 0, expected);
+is(result.icons.length, 0, expected);
 
 var expected = `Expect icon's src to be a string.`;
 var withSrc = {
   icons: [{
     src: 'pass'
   }]
 };
 data.jsonText = JSON.stringify(withSrc);
 var result = processor.process(data);
-ise(typeof result.icons[0].src, "string", expected);
+is(typeof result.icons[0].src, "string", expected);
 
 var expected = `Expect only icons with a src prop to be kept.`;
 var withSrc = {
   icons: [{
     src: 'pass'
   }, {
     src: 'pass',
     density: 1
   }, {}, {
     foo: 'foo'
   }]
 };
 data.jsonText = JSON.stringify(withSrc);
 var result = processor.process(data);
-ise(result.icons.length, 2, expected);
+is(result.icons.length, 2, expected);
 
 var expectedURL = new URL('pass', manifestURL);
 for (var icon of result.icons) {
   var expected = `Expect src prop to be ${expectedURL.toString()}`;
-  ise(icon.src.toString(), expectedURL.toString(), expected);
+  is(icon.src.toString(), expectedURL.toString(), expected);
 }
 
 //Resolve URLs relative to manfiest
 var URLs = ['path', '/path', '../../path'];
 
 URLs.forEach((url) => {
   var expected = `Resolve icon src URLs relative to manifest.`;
   data.jsonText = JSON.stringify({
     icons: [{
       src: url
     }]
   });
   var absURL = new URL(url, manifestURL).toString();
   var result = processor.process(data);
-  ise(result.icons[0].src.toString(), absURL, expected);
+  is(result.icons[0].src.toString(), absURL, expected);
 });
   </script>
 </head>
--- a/dom/manifest/test/test_IconsProcessor_type.html
+++ b/dom/manifest/test/test_IconsProcessor_type.html
@@ -24,28 +24,28 @@ var testIcon = {
 };
 
 var invalidMimeTypes = ['application / text', 'test;test', ';test?test', 'application\\text'];
 invalidMimeTypes.forEach((invalidMime) => {
   var expected = `Expect invalid mime to be treated like undefined.`;
   testIcon.icons[0].type = invalidMime;
   data.jsonText = JSON.stringify(testIcon);
   var result = processor.process(data);
-  ise(result.icons[0].type, undefined, expected);
+  is(result.icons[0].type, undefined, expected);
 });
 
 var validTypes = [
   'image/jpeg',
   'IMAGE/jPeG',
   `${whiteSpace}image/jpeg${whiteSpace}`,
   'image/JPEG; whatever=something',
   'image/JPEG;whatever'
 ];
 
 validTypes.forEach((validMime) => {
   var expected = `Expect valid mime to be parsed to : image/jpeg.`;
   testIcon.icons[0].type = validMime;
   data.jsonText = JSON.stringify(testIcon);
   var result = processor.process(data);
-  ise(result.icons[0].type, 'image/jpeg', expected);
+  is(result.icons[0].type, 'image/jpeg', expected);
 });
   </script>
 </head>
--- a/dom/manifest/test/test_ManifestProcessor_JSON.html
+++ b/dom/manifest/test/test_ManifestProcessor_JSON.html
@@ -15,20 +15,20 @@ https://bugzilla.mozilla.org/show_bug.cg
  * https://w3c.github.io/manifest/#processing
  **/
 'use strict';
 var invalidJson = ['', ` \t \n ${whiteSpace} `, '{', '{[[}'];
 invalidJson.forEach((testString) => {
   var expected = `Expect to recover from invalid JSON: ${testString}`;
   data.jsonText = testString;
   var result = processor.process(data);
-  SimpleTest.ise(result.start_url, docURL.href, expected);
+  SimpleTest.is(result.start_url, docURL.href, expected);
 });
 
 var validButUnhelpful = ["1", 1, "", "[{}]", "null"];
 validButUnhelpful.forEach((testString) => {
   var expected = `Expect to recover from invalid JSON: ${testString}`;
   data.jsonText = testString;
   var result = processor.process(data);
-  SimpleTest.ise(result.start_url, docURL.href, expected);
+  SimpleTest.is(result.start_url, docURL.href, expected);
 });
   </script>
 </head>
--- a/dom/manifest/test/test_ManifestProcessor_display.html
+++ b/dom/manifest/test/test_ManifestProcessor_display.html
@@ -17,45 +17,45 @@ https://bugzilla.mozilla.org/show_bug.cg
 'use strict';
 //Type checks
 typeTests.forEach((type) => {
   var expected = `Expect non - string display to default to "browser".`;
   data.jsonText = JSON.stringify({
     display: type
   });
   var result = processor.process(data);
-  ise(result.display, 'browser', expected);
+  is(result.display, 'browser', expected);
 });
 
 /*Test valid modes*/
 var validModes = ['fullscreen', 'standalone', 'minimal-ui', 'browser']
 validModes.forEach((mode) => {
   var expected = `Expect display mode to be ${mode}.`;
   data.jsonText = JSON.stringify({
     display: mode
   });
   var result = processor.process(data);
-  ise(result.display, mode, expected);
+  is(result.display, mode, expected);
 });
 
 //trim tests
 validModes.forEach((display) => {
   var expected = `Expect trimmed display mode to be returned.`;
   var expandedDisplay =  seperators + lineTerminators + display + lineTerminators + seperators;
   data.jsonText = JSON.stringify({
     display: expandedDisplay
   });
   var result = processor.process(data);
-  ise(result.display, display, expected);
+  is(result.display, display, expected);
 });
 
 //Unknown modes
 var invalidModes = ['foo', `fooo${whiteSpace}`, '', 'fullscreen,standalone', 'standalone fullscreen', 'FULLSCreEN'];
 invalidModes.forEach((invalidMode) => {
   var expected = `Expect default display mode "browser" to be returned: '${invalidMode}'`;
   data.jsonText = JSON.stringify({
     display: invalidMode
   });
   var result = processor.process(data);
-  ise(result.display, 'browser', expected);
+  is(result.display, 'browser', expected);
 });
   </script>
 </head>
--- a/dom/manifest/test/test_ManifestProcessor_icons.html
+++ b/dom/manifest/test/test_ManifestProcessor_icons.html
@@ -19,12 +19,12 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 typeTests.forEach((type) => {
   var expected = `Expect non-array icons to be empty array: ${typeof type}.`;
   data.jsonText = JSON.stringify({
     icons: type
   });
   var result = processor.process(data);
   var y = SpecialPowers.unwrap(result.icons);
-  ise(result.icons.length, 0, expected);
+  is(result.icons.length, 0, expected);
 });
   </script>
 </head>
--- a/dom/manifest/test/test_ManifestProcessor_name_and_short_name.html
+++ b/dom/manifest/test/test_ManifestProcessor_name_and_short_name.html
@@ -29,17 +29,17 @@ var props = ['name', 'short_name'];
 
 props.forEach((prop) => {
   trimNamesTests.forEach((trimmableString) => {
     var assetion = `Expecting ${prop} to be trimmed.`;
     var obj = {};
     obj[prop] = trimmableString;
     data.jsonText = JSON.stringify(obj);
     var result = processor.process(data);
-    ise(result[prop], 'pass', assetion);
+    is(result[prop], 'pass', assetion);
   });
 });
 
 /*
  * If the object is not a string, it becomes undefined
  */
 props.forEach((prop) => {
   typeTests.forEach((type) => {
@@ -67,13 +67,13 @@ var acceptableNames = [
 
 props.forEach((prop) => {
   acceptableNames.forEach((name) => {
     var expected = `Expecting name to be acceptable : ${name}`;
     var obj = {};
     obj[prop] = name;
     data.jsonText = JSON.stringify(obj);
     var result = processor.process(data);
-    ise(result[prop], name, expected);
+    is(result[prop], name, expected);
   });
 });
   </script>
 </head>
--- a/dom/manifest/test/test_ManifestProcessor_orientation.html
+++ b/dom/manifest/test/test_ManifestProcessor_orientation.html
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 'use strict';
 
 typeTests.forEach((type) => {
   var expected = `Expect non-string orientation to be empty string : ${typeof type}.`;
   data.jsonText = JSON.stringify({
     orientation: type
   });
   var result = processor.process(data);
-  ise(result.orientation, '', expected);
+  is(result.orientation, '', expected);
 });
 
 
 var validOrientations = [
   'any',
   'natural',
   'landscape',
   'portrait',
@@ -38,17 +38,17 @@ var validOrientations = [
 ];
 
 validOrientations.forEach((orientation) => {
   var expected = `Expect orientation to be returned: ${orientation}.`;
   data.jsonText = JSON.stringify({
     orientation: orientation
   });
   var result = processor.process(data);
-  ise(result.orientation, orientation, expected);
+  is(result.orientation, orientation, expected);
 });
 
 var invalidOrientations = [
   'all',
   'ANY',
   'NaTuRal',
   'portrait-primary portrait-secondary',
   'portrait-primary,portrait-secondary',
@@ -61,23 +61,23 @@ var invalidOrientations = [
 ]
 
 invalidOrientations.forEach((orientation) => {
   var expected = `Expect orientation to be empty string: ${orientation}.`;
   data.jsonText = JSON.stringify({
     orientation: orientation
   });
   var result = processor.process(data);
-  ise(result.orientation, "", expected);
+  is(result.orientation, "", expected);
 });
 
 //Trim tests
 validOrientations.forEach((orientation) => {
   var expected = `Expect trimmed orientation to be returned.`;
   var expandedOrientation = `${seperators}${lineTerminators}${orientation}${lineTerminators}${seperators}`;
   data.jsonText = JSON.stringify({
     orientation: expandedOrientation
   });
   var result = processor.process(data);
-  ise(result.orientation, orientation, expected);
+  is(result.orientation, orientation, expected);
 });
   </script>
 </head>
--- a/dom/manifest/test/test_ManifestProcessor_scope.html
+++ b/dom/manifest/test/test_ManifestProcessor_scope.html
@@ -16,74 +16,74 @@ https://bugzilla.mozilla.org/show_bug.cg
  **/
 'use strict';
 var expected = 'Expect non-string scope to be undefined';
 typeTests.forEach((type) => {
   data.jsonText = JSON.stringify({
     scope: type
   });
   var result = processor.process(data);
-  ise(result.scope, undefined, expected);
+  is(result.scope, undefined, expected);
 });
 
 var expected = 'Expect different origin to be treated as undefined';
 data.jsonText = JSON.stringify({
   scope: 'http://not-same-origin'
 });
 var result = processor.process(data);
-ise(result.scope, undefined, expected);
+is(result.scope, undefined, expected);
 
 var expected = 'Expect the empty string to be treated as undefined.';
 data.jsonText = JSON.stringify({
   scope: ''
 });
 var result = processor.process(data);
-ise(result.scope, undefined, expected);
+is(result.scope, undefined, expected);
 
 var expected = 'Resolve URLs relative to manifest.';
 var URLs = ['path', '/path', '../../path'];
 URLs.forEach((url) => {
   data.jsonText = JSON.stringify({
     scope: url,
     start_url: "/path"
   });
   var absURL = new URL(url, manifestURL).toString();
   var result = processor.process(data);
-  ise(result.scope, absURL, expected);
+  is(result.scope, absURL, expected);
 });
 
 var expected = 'If start URL is not in scope, return undefined.';
 data.jsonText = JSON.stringify({
   scope: 'foo',
   start_url: 'bar'
 });
 var result = processor.process(data);
-ise(result.scope, undefined, expected);
+is(result.scope, undefined, expected);
 
 var expected = 'If start URL is in scope, use the scope.';
 data.jsonText = JSON.stringify({
   start_url: 'foobar',
   scope: 'foo'
 });
 var result = processor.process(data);
-ise(result.scope.toString(), new URL('foo', manifestURL).toString(), expected);
+is(result.scope.toString(), new URL('foo', manifestURL).toString(), expected);
 
 var expected = 'Expect start_url to be ' + new URL('foobar', manifestURL).toString();
-ise(result.start_url.toString(), new URL('foobar', manifestURL).toString(), expected);
+is(result.start_url.toString(), new URL('foobar', manifestURL).toString(), expected);
 
 var expected = 'If start URL is in scope, use the scope.';
 data.jsonText = JSON.stringify({
   start_url: '/foo/',
   scope: '/foo/'
 });
 var result = processor.process(data);
-ise(result.scope.toString(), new URL('/foo/', manifestURL).toString(), expected);
+is(result.scope.toString(), new URL('/foo/', manifestURL).toString(), expected);
 
 var expected = 'If start URL is in scope, use the scope.';
 data.jsonText = JSON.stringify({
   start_url: '.././foo/',
   scope: '../foo/'
 });
 var result = processor.process(data);
-ise(result.scope.toString(), new URL('/foo/', manifestURL).toString(), expected);
+is(result.scope.toString(), new URL('/foo/', manifestURL).toString(), expected);
 
   </script>
 </head>
--- a/dom/manifest/test/test_ManifestProcessor_start_url.html
+++ b/dom/manifest/test/test_ManifestProcessor_start_url.html
@@ -16,44 +16,44 @@ https://bugzilla.mozilla.org/show_bug.cg
  **/
 'use strict';
 typeTests.forEach((type) => {
   var expected = `Expect non - string start_url to be doc's url: ${typeof type}.`;
   data.jsonText = JSON.stringify({
     start_url: type
   });
   var result = processor.process(data);
-  ise(result.start_url.toString(), docURL.toString(), expected);
+  is(result.start_url.toString(), docURL.toString(), expected);
 });
 
 //Not same origin
 var expected = `Expect different origin URLs to become document's URL.`;
 data.jsonText = JSON.stringify({
   start_url: 'http://not-same-origin'
 });
 var result = processor.process(data);
-ise(result.start_url.toString(), docURL.toString(), expected);
+is(result.start_url.toString(), docURL.toString(), expected);
 
 //Empty string test
 var expected = `Expect empty string for start_url to become document's URL.`;
 data.jsonText = JSON.stringify({
   start_url: ''
 });
 var result = processor.process(data);
-ise(result.start_url.toString(), docURL.toString(), expected);
+is(result.start_url.toString(), docURL.toString(), expected);
 
 //Resolve URLs relative to manfiest
 var URLs = ['path', '/path', '../../path',
   `${whiteSpace}path${whiteSpace}`,
   `${whiteSpace}/path`,
   `${whiteSpace}../../path`
 ];
 URLs.forEach((url) => {
   var expected = `Resolve URLs relative to manifest.`;
   data.jsonText = JSON.stringify({
     start_url: url
   });
   var absURL = new URL(url, manifestURL).toString();
   var result = processor.process(data);
-  ise(result.start_url.toString(), absURL, expected);
+  is(result.start_url.toString(), absURL, expected);
 });
   </script>
 </head>
--- a/dom/media/AbstractThread.cpp
+++ b/dom/media/AbstractThread.cpp
@@ -46,17 +46,17 @@ public:
   }
 
   virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                         DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
                         DispatchReason aReason = NormalDispatch) override
   {
     nsCOMPtr<nsIRunnable> r = aRunnable;
     AbstractThread* currentThread;
-    if (aReason != TailDispatch && (currentThread = GetCurrent()) && currentThread->RequiresTailDispatch()) {
+    if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
       currentThread->TailDispatcher().AddTask(this, r.forget(), aFailureHandling);
       return;
     }
 
     nsresult rv = mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
     MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
     unused << rv;
   }
@@ -96,16 +96,24 @@ public:
 
   virtual nsIThread* AsXPCOMThread() override { return mTarget; }
 
 private:
   nsRefPtr<nsIThread> mTarget;
   Maybe<AutoTaskDispatcher> mTailDispatcher;
 };
 
+bool
+AbstractThread::RequiresTailDispatch(AbstractThread* aThread) const
+{
+  // We require tail dispatch if both the source and destination
+  // threads support it.
+  return SupportsTailDispatch() && aThread->SupportsTailDispatch();
+}
+
 AbstractThread*
 AbstractThread::MainThread()
 {
   MOZ_ASSERT(sMainThread);
   return sMainThread;
 }
 
 void
--- a/dom/media/AbstractThread.h
+++ b/dom/media/AbstractThread.h
@@ -35,17 +35,17 @@ class TaskDispatcher;
  */
 class AbstractThread
 {
 public:
   // Returns the AbstractThread that the caller is currently running in, or null
   // if the caller is not running in an AbstractThread.
   static AbstractThread* GetCurrent() { return sCurrentThreadTLS.get(); }
 
-  AbstractThread(bool aRequireTailDispatch) : mRequireTailDispatch(aRequireTailDispatch) {}
+  AbstractThread(bool aSupportsTailDispatch) : mSupportsTailDispatch(aSupportsTailDispatch) {}
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractThread);
 
   enum DispatchFailureHandling { AssertDispatchSuccess, DontAssertDispatchSuccess };
   enum DispatchReason { NormalDispatch, TailDispatch };
   virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                         DispatchFailureHandling aHandling = AssertDispatchSuccess,
                         DispatchReason aReason = NormalDispatch) = 0;
@@ -58,33 +58,36 @@ public:
 
   // Returns a TaskDispatcher that will dispatch its tasks when the currently-
   // running tasks pops off the stack.
   //
   // May only be called when running within the it is invoked up, and only on
   // threads which support it.
   virtual TaskDispatcher& TailDispatcher() = 0;
 
-  // Returns true if this task queue requires all dispatches performed by its
-  // tasks to go through the tail dispatcher.
-  bool RequiresTailDispatch() const { return mRequireTailDispatch; }
+  // Returns true if this supports the tail dispatcher.
+  bool SupportsTailDispatch() const { return mSupportsTailDispatch; }
+
+  // Returns true if this thread requires all dispatches originating from
+  // aThread go through the tail dispatcher.
+  bool RequiresTailDispatch(AbstractThread* aThread) const;
 
   virtual MediaTaskQueue* AsTaskQueue() { MOZ_CRASH("Not a task queue!"); }
   virtual nsIThread* AsXPCOMThread() { MOZ_CRASH("Not an XPCOM thread!"); }
 
   // Convenience method for getting an AbstractThread for the main thread.
   static AbstractThread* MainThread();
 
   // Must be called exactly once during startup.
   static void InitStatics();
 
 protected:
   virtual ~AbstractThread() {}
   static ThreadLocal<AbstractThread*> sCurrentThreadTLS;
 
   // True if we want to require that every task dispatched from tasks running in
   // this queue go through our queue's tail dispatcher.
-  const bool mRequireTailDispatch;
+  const bool mSupportsTailDispatch;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -5,30 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "AudioSink.h"
 #include "MediaDecoderStateMachine.h"
 #include "AudioStream.h"
 #include "prenv.h"
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define SINK_LOG(msg, ...) \
   PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
-#define SINK_LOG_V(msg, ...)                      \
-  PR_BEGIN_MACRO                                  \
-    if (!PR_GetEnv("MOZ_QUIET")) {                \
-      SINK_LOG(msg, ##__VA_ARGS__); \
-    }                                             \
-  PR_END_MACRO
-#else
-#define SINK_LOG(msg, ...)
-#define SINK_LOG_V(msg, ...)
-#endif
+#define SINK_LOG_V(msg, ...) \
+  PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG+1, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
 
 AudioSink::OnAudioEndTimeUpdateTask::OnAudioEndTimeUpdateTask(
                                      MediaDecoderStateMachine* aStateMachine)
   : mMutex("OnAudioEndTimeUpdateTask")
   , mEndTime(0)
   , mStateMachine(aStateMachine)
 {
 }
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -23,23 +23,19 @@
 #endif
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gAudioStreamLog = nullptr;
 // For simple logs
 #define LOG(x) PR_LOG(gAudioStreamLog, PR_LOG_DEBUG, x)
-#else
-#define LOG(x)
-#endif
 
 /**
  * When MOZ_DUMP_AUDIO is set in the environment (to anything),
  * we'll drop a series of files in the current working directory named
  * dumped-audio-<nnn>.wav, one per AudioStream created, containing
  * the audio for the stream including any skips due to underruns.
  */
 static int gDumpedAudioCount = 0;
@@ -1066,21 +1062,18 @@ AudioStream::DataCallback(void* aBuffer,
   // Bug 996162
 
   // callback tells us cubeb succeeded initializing
   if (mState == STARTED) {
     // For low-latency streams, we want to minimize any built-up data when
     // we start getting callbacks.
     // Simple version - contract on first callback only.
     if (mLatencyRequest == LowLatency) {
-#ifdef PR_LOGGING
       uint32_t old_len = mBuffer.Length();
-#endif
       available = mBuffer.ContractTo(FramesToBytes(aFrames));
-#ifdef PR_LOGGING
       TimeStamp now = TimeStamp::Now();
       if (!mStartTime.IsNull()) {
         int64_t timeMs = (now - mStartTime).ToMilliseconds();
         PR_LOG(gAudioStreamLog, PR_LOG_WARNING,
                ("Stream took %lldms to start after first Write() @ %u", timeMs, mOutRate));
       } else {
         PR_LOG(gAudioStreamLog, PR_LOG_WARNING,
           ("Stream started before Write() @ %u", mOutRate));
@@ -1088,17 +1081,16 @@ AudioStream::DataCallback(void* aBuffer,
 
       if (old_len != available) {
         // Note that we may have dropped samples in Write() as well!
         PR_LOG(gAudioStreamLog, PR_LOG_WARNING,
                ("AudioStream %p dropped %u + %u initial frames @ %u", this,
                  mReadPoint, BytesToFrames(old_len - available), mOutRate));
         mReadPoint += BytesToFrames(old_len - available);
       }
-#endif
     }
     mState = RUNNING;
   }
 
   if (available) {
     // When we are playing a low latency stream, and it is the first time we are
     // getting data from the buffer, we prefer to add the silence for an
     // underrun at the beginning of the buffer, so the first buffer is not cut
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -121,19 +121,17 @@ uint32_t GetCubebLatency()
 bool CubebLatencyPrefSet()
 {
   StaticMutexAutoLock lock(sMutex);
   return sCubebLatencyPrefSet;
 }
 
 void InitLibrary()
 {
-#ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
-#endif
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   PrefChanged(PREF_CUBEB_LATENCY, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
 }
 
 void ShutdownLibrary()
 {
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -5,22 +5,18 @@
 
 #include <MediaStreamGraphImpl.h>
 #include "CubebUtils.h"
 
 #ifdef XP_MACOSX
 #include <sys/sysctl.h>
 #endif
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaStreamGraphLog;
 #define STREAM_LOG(type, msg) PR_LOG(gMediaStreamGraphLog, type, msg)
-#else
-#define STREAM_LOG(type, msg)
-#endif
 
 // We don't use NSPR log here because we want this interleaved with adb logcat
 // on Android/B2G
 // #define ENABLE_LIFECYCLE_LOG
 #ifdef ENABLE_LIFECYCLE_LOG
 #ifdef ANDROID
 #include "android/log.h"
 #define LIFECYCLE_LOG(...)  __android_log_print(ANDROID_LOG_INFO, "Gecko - MSG" , __VA_ARGS__); printf(__VA_ARGS__);printf("\n");
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -19,22 +19,18 @@
 #include "nsISeekableStream.h"
 #include "nsIPrincipal.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Services.h"
 #include <algorithm>
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gMediaCacheLog;
 #define CACHE_LOG(type, msg) PR_LOG(gMediaCacheLog, type, msg)
-#else
-#define CACHE_LOG(type, msg)
-#endif
 
 // Readahead blocks for non-seekable streams will be limited to this
 // fraction of the cache space. We don't normally evict such blocks
 // because replacing them requires a seek, but we need to make sure
 // they don't monopolize the cache.
 static const double NONSEEKABLE_READAHEAD_MAX = 0.5;
 
 // Data N seconds before the current playback position is given the same priority
@@ -582,21 +578,19 @@ MediaCache::Init()
   PRFileDesc* fileDesc = nullptr;
   nsresult rv = NS_OpenAnonymousTemporaryFile(&fileDesc);
   NS_ENSURE_SUCCESS(rv,rv);
 
   mFileCache = new FileBlockCache();
   rv = mFileCache->Open(fileDesc);
   NS_ENSURE_SUCCESS(rv,rv);
 
-#ifdef PR_LOGGING
   if (!gMediaCacheLog) {
     gMediaCacheLog = PR_NewLogModule("MediaCache");
   }
-#endif
 
   MediaCacheFlusher::Init();
 
   return NS_OK;
 }
 
 void
 MediaCache::Flush()
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -47,23 +47,19 @@ namespace mozilla {
 // catching up with the download. Having this margin make the
 // MediaDecoder::CanPlayThrough() calculation more stable in the case of
 // fluctuating bitrates.
 static const int64_t CAN_PLAY_THROUGH_MARGIN = 1;
 
 // avoid redefined macro in unified build
 #undef DECODER_LOG
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gMediaDecoderLog;
 #define DECODER_LOG(x, ...) \
   PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, ("Decoder=%p " x, this, ##__VA_ARGS__))
-#else
-#define DECODER_LOG(x, ...)
-#endif
 
 static const char* const gPlayStateStr[] = {
   "START",
   "LOADING",
   "PAUSED",
   "PLAYING",
   "SEEKING",
   "ENDED",
@@ -115,27 +111,29 @@ public:
   }
 };
 
 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
 
 PRLogModuleInfo* gStateWatchingLog;
 PRLogModuleInfo* gMediaPromiseLog;
 PRLogModuleInfo* gMediaTimerLog;
+PRLogModuleInfo* gMediaSampleLog;
 
 void
 MediaDecoder::InitStatics()
 {
   AbstractThread::InitStatics();
 
   // Log modules.
   gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   gMediaPromiseLog = PR_NewLogModule("MediaPromise");
   gStateWatchingLog = PR_NewLogModule("StateWatching");
   gMediaTimerLog = PR_NewLogModule("MediaTimer");
+  gMediaSampleLog = PR_NewLogModule("MediaSample");
 }
 
 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
 
 NS_IMPL_ISUPPORTS(MediaDecoder, nsIObserver)
 
 void MediaDecoder::NotifyOwnerActivityChanged()
 {
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -15,23 +15,19 @@
 #include <stdint.h>
 #include <algorithm>
 
 namespace mozilla {
 
 // Un-comment to enable logging of seek bisections.
 //#define SEEK_LOGGING
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define DECODER_LOG(x, ...) \
   PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, ("Decoder=%p " x, mDecoder, ##__VA_ARGS__))
-#else
-#define DECODER_LOG(x, ...)
-#endif
 
 // Same workaround as MediaDecoderStateMachine.cpp.
 #define DECODER_WARN_HELPER(a, b) NS_WARNING b
 #define DECODER_WARN(x, ...) \
   DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoder, ##__VA_ARGS__).get()))
 
 class VideoQueueMemoryFunctor : public nsDequeFunctor {
 public:
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -45,40 +45,30 @@ namespace mozilla {
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 #define NS_DispatchToMainThread(...) CompileError_UseAbstractThreadDispatchInstead
 
 // avoid redefined macro in unified build
+#undef LOG
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaDecoderLog;
+extern PRLogModuleInfo* gMediaSampleLog;
+#define LOG(m, l, x, ...) \
+  PR_LOG(m, l, ("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__))
 #define DECODER_LOG(x, ...) \
-  PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, ("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__))
-#define VERBOSE_LOG(x, ...)                            \
-    PR_BEGIN_MACRO                                     \
-      if (!PR_GetEnv("MOZ_QUIET")) {                   \
-        DECODER_LOG(x, ##__VA_ARGS__);                 \
-      }                                                \
-    PR_END_MACRO
-#define SAMPLE_LOG(x, ...)                             \
-    PR_BEGIN_MACRO                                     \
-      if (PR_GetEnv("MEDIA_LOG_SAMPLES")) {            \
-        DECODER_LOG(x, ##__VA_ARGS__);                 \
-      }                                                \
-    PR_END_MACRO
-#else
-#define DECODER_LOG(x, ...)
-#define VERBOSE_LOG(x, ...)
-#define SAMPLE_LOG(x, ...)
-#endif
+  LOG(gMediaDecoderLog, PR_LOG_DEBUG, x, ##__VA_ARGS__)
+#define VERBOSE_LOG(x, ...) \
+  LOG(gMediaDecoderLog, PR_LOG_DEBUG+1, x, ##__VA_ARGS__)
+#define SAMPLE_LOG(x, ...) \
+  LOG(gMediaSampleLog, PR_LOG_DEBUG, x, ##__VA_ARGS__)
 
 // Somehow MSVC doesn't correctly delete the comma before ##__VA_ARGS__
 // when __VA_ARGS__ expands to nothing. This is a workaround for it.
 #define DECODER_WARN_HELPER(a, b) NS_WARNING b
 #define DECODER_WARN(x, ...) \
   DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__).get()))
 
 // Certain constants get stored as member variables and then adjusted by various
@@ -2914,27 +2904,23 @@ void MediaDecoderStateMachine::AdvanceFr
   TimeStamp nowTime = TimeStamp::Now();
   // Skip frames up to the frame at the playback position, and figure out
   // the time remaining until it's time to display the next frame.
   int64_t remainingTime = AUDIO_DURATION_USECS;
   NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
   nsRefPtr<VideoData> currentFrame;
   if (VideoQueue().GetSize() > 0) {
     VideoData* frame = VideoQueue().PeekFront();
-#ifdef PR_LOGGING
     int32_t droppedFrames = 0;
-#endif
     while (IsRealTime() || clock_time >= frame->mTime) {
       mVideoFrameEndTime = frame->GetEndTime();
       if (currentFrame) {
         mDecoder->NotifyDecodedFrames(0, 0, 1);
-#ifdef PR_LOGGING
         VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld (%d so far)",
                     currentFrame->mTime, clock_time, ++droppedFrames);
-#endif
       }
       currentFrame = frame;
       nsRefPtr<VideoData> releaseMe = PopVideo();
       // Notify the decode thread that the video queue's buffers may have
       // free'd up space for more frames.
       mDecoder->GetReentrantMonitor().NotifyAll();
       OnPlaybackOffsetUpdate(frame->mOffset);
       if (VideoQueue().GetSize() == 0)
@@ -3248,22 +3234,20 @@ void MediaDecoderStateMachine::StartBuff
   mQuickBuffering =
     !JustExitedQuickBuffering() &&
     decodeDuration < UsecsToDuration(QUICK_BUFFER_THRESHOLD_USECS);
   mBufferingStart = TimeStamp::Now();
 
   SetState(DECODER_STATE_BUFFERING);
   DECODER_LOG("Changed state from DECODING to BUFFERING, decoded for %.3lfs",
               decodeDuration.ToSeconds());
-#ifdef PR_LOGGING
   MediaDecoder::Statistics stats = mDecoder->GetStatistics();
   DECODER_LOG("Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
               stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
               stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
-#endif
 }
 
 void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp)
 {
   AssertCurrentThreadInMonitor();
   mPlayStartTime = aTimeStamp;
   if (!mAudioSink) {
     return;
@@ -3452,14 +3436,15 @@ uint32_t MediaDecoderStateMachine::GetAm
   return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated())
     ? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
     : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
 }
 
 } // namespace mozilla
 
 // avoid redefined macro in unified build
+#undef LOG
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 #undef DECODER_WARN
 #undef DECODER_WARN_HELPER
 
 #undef NS_DispatchToMainThread
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -98,29 +98,25 @@ struct nsIMediaDevice::COMTypeInfo<mozil
 const nsIID nsIMediaDevice::COMTypeInfo<mozilla::AudioDevice, void>::kIID = NS_IMEDIADEVICE_IID;
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
-#ifdef PR_LOGGING
 PRLogModuleInfo*
 GetMediaManagerLog()
 {
   static PRLogModuleInfo *sLog;
   if (!sLog)
     sLog = PR_NewLogModule("MediaManager");
   return sLog;
 }
 #define LOG(msg) PR_LOG(GetMediaManagerLog(), PR_LOG_DEBUG, msg)
-#else
-#define LOG(msg)
-#endif
 
 using dom::MediaStreamConstraints;
 using dom::MediaTrackConstraintSet;
 using dom::MediaTrackConstraints;
 using dom::MediaStreamError;
 using dom::GetUserMediaRequest;
 using dom::Sequence;
 using dom::OwningBooleanOrMediaTrackConstraints;
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -43,22 +43,18 @@
 #endif
 
 namespace mozilla {
 namespace dom {
 struct MediaStreamConstraints;
 struct MediaTrackConstraintSet;
 }
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaManagerLog();
 #define MM_LOG(msg) PR_LOG(GetMediaManagerLog(), PR_LOG_DEBUG, msg)
-#else
-#define MM_LOG(msg)
-#endif
 
 /**
  * This class is an implementation of MediaStreamListener. This is used
  * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
  * are assigned and deassigned in content.
  */
 class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
 {
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -26,22 +26,18 @@
 #include "nsProxyRelease.h"
 #include "nsTArray.h"
 #include "GeckoProfiler.h"
 
 #ifdef LOG
 #undef LOG
 #endif
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gMediaRecorderLog;
 #define LOG(type, msg) PR_LOG(gMediaRecorderLog, type, msg)
-#else
-#define LOG(type, msg)
-#endif
 
 namespace mozilla {
 
 namespace dom {
 
 /**
 + * MediaRecorderReporter measures memory being used by the Media Recorder.
 + *
@@ -754,21 +750,19 @@ MediaRecorder::~MediaRecorder()
 MediaRecorder::MediaRecorder(DOMMediaStream& aSourceMediaStream,
                              nsPIDOMWindow* aOwnerWindow)
   : DOMEventTargetHelper(aOwnerWindow)
   , mState(RecordingState::Inactive)
 {
   MOZ_ASSERT(aOwnerWindow);
   MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
   mDOMStream = &aSourceMediaStream;
-#ifdef PR_LOGGING
   if (!gMediaRecorderLog) {
     gMediaRecorderLog = PR_NewLogModule("MediaRecorder");
   }
-#endif
   RegisterActivityObserver();
 }
 
 MediaRecorder::MediaRecorder(AudioNode& aSrcAudioNode,
                              uint32_t aSrcOutput,
                              nsPIDOMWindow* aOwnerWindow)
   : DOMEventTargetHelper(aOwnerWindow)
   , mState(RecordingState::Inactive)
@@ -787,21 +781,19 @@ MediaRecorder::MediaRecorder(AudioNode& 
                                                       MediaStreamGraph::EXTERNAL_STREAM,
                                                       ctx->SampleRate());
     mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.Stream(),
                                                 MediaInputPort::FLAG_BLOCK_INPUT,
                                                 0,
                                                 aSrcOutput);
   }
   mAudioNode = &aSrcAudioNode;
-  #ifdef PR_LOGGING
   if (!gMediaRecorderLog) {
     gMediaRecorderLog = PR_NewLogModule("MediaRecorder");
   }
-  #endif
   RegisterActivityObserver();
 }
 
 void
 MediaRecorder::RegisterActivityObserver()
 {
   nsPIDOMWindow* window = GetOwner();
   if (window) {
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -28,27 +28,22 @@
 #include "nsICachingChannel.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsContentUtils.h"
 #include "nsHostObjectProtocolHandler.h"
 #include <algorithm>
 #include "nsProxyRelease.h"
 #include "nsIContentPolicy.h"
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gMediaResourceLog;
 #define RESOURCE_LOG(msg, ...) PR_LOG(gMediaResourceLog, PR_LOG_DEBUG, \
                                       (msg, ##__VA_ARGS__))
 // Debug logging macro with object pointer and class name.
 #define CMLOG(msg, ...) \
         RESOURCE_LOG("%p [ChannelMediaResource]: " msg, this, ##__VA_ARGS__)
-#else
-#define RESOURCE_LOG(msg, ...)
-#define CMLOG(msg, ...)
-#endif
 
 static const uint32_t HTTP_OK_CODE = 200;
 static const uint32_t HTTP_PARTIAL_RESPONSE_CODE = 206;
 
 namespace mozilla {
 
 void
 MediaResource::Destroy()
@@ -80,21 +75,19 @@ ChannelMediaResource::ChannelMediaResour
   : BaseMediaResource(aDecoder, aChannel, aURI, aContentType),
     mOffset(0), mSuspendCount(0),
     mReopenOnError(false), mIgnoreClose(false),
     mCacheStream(this),
     mLock("ChannelMediaResource.mLock"),
     mIgnoreResume(false),
     mIsTransportSeekable(true)
 {
-#ifdef PR_LOGGING
   if (!gMediaResourceLog) {
     gMediaResourceLog = PR_NewLogModule("MediaResource");
   }
-#endif
 }
 
 ChannelMediaResource::~ChannelMediaResource()
 {
   if (mListener) {
     // Kill its reference to us since we're going away
     mListener->Revoke();
   }
--- a/dom/media/MediaShutdownManager.cpp
+++ b/dom/media/MediaShutdownManager.cpp
@@ -8,22 +8,18 @@
 #include "nsContentUtils.h"
 #include "mozilla/StaticPtr.h"
 #include "MediaDecoder.h"
 #include "SharedThreadPool.h"
 #include "prlog.h"
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
-#else
-#define DECODER_LOG(type, msg)
-#endif
 
 NS_IMPL_ISUPPORTS(MediaShutdownManager, nsIObserver)
 
 MediaShutdownManager::MediaShutdownManager()
   : mIsObservingShutdown(false),
     mIsDoingXPCOMShutDown(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -36,22 +36,18 @@
 #include "webaudio/blink/HRTFDatabaseLoader.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gMediaStreamGraphLog;
 #define STREAM_LOG(type, msg) PR_LOG(gMediaStreamGraphLog, type, msg)
-#else
-#define STREAM_LOG(type, msg)
-#endif
 
 // #define ENABLE_LIFECYCLE_LOG
 
 // We don't use NSPR log here because we want this interleaved with adb logcat
 // on Android/B2G
 #ifdef ENABLE_LIFECYCLE_LOG
 #  ifdef ANDROID
 #    include "android/log.h"
@@ -387,27 +383,56 @@ GraphTime
 MediaStreamGraphImpl::GetAudioPosition(MediaStream* aStream)
 {
   /* This is correlated to the audio clock when using an AudioCallbackDriver,
    * and is using a system timer otherwise. */
   return IterationEnd();
 }
 
 GraphTime
-MediaStreamGraphImpl::IterationEnd()
+MediaStreamGraphImpl::IterationEnd() const
 {
   return CurrentDriver()->IterationEnd();
 }
 
 void
-MediaStreamGraphImpl::UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime, GraphTime aNextCurrentTime)
+MediaStreamGraphImpl::StreamNotifyOutput(MediaStream* aStream)
+{
+  for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
+    MediaStreamListener* l = aStream->mListeners[j];
+    l->NotifyOutput(this, IterationEnd());
+  }
+}
+
+void
+MediaStreamGraphImpl::StreamReadyToFinish(MediaStream* aStream)
 {
-  nsAutoTArray<MediaStream*, 800> streamsReadyToFinish;
-  nsAutoTArray<MediaStream*, 800> streamsWithOutput;
-
+  MOZ_ASSERT(aStream->mFinished);
+  MOZ_ASSERT(!aStream->mNotifiedFinished);
+
+  // The stream is fully finished when all of its track data has been played
+  // out.
+  if (IterationEnd() >=
+      aStream->StreamTimeToGraphTime(aStream->GetStreamBuffer().GetAllTracksEnd()))  {
+    NS_WARN_IF_FALSE(aStream->mNotifiedBlocked,
+      "Should've notified blocked=true for a fully finished stream");
+    aStream->mNotifiedFinished = true;
+    aStream->mLastPlayedVideoFrame.SetNull();
+    SetStreamOrderDirty();
+    for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
+      MediaStreamListener* l = aStream->mListeners[j];
+      l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
+    }
+  }
+}
+
+void
+MediaStreamGraphImpl::UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime,
+                                                  GraphTime aNextCurrentTime)
+{
   nsTArray<MediaStream*>* runningAndSuspendedPair[2];
   runningAndSuspendedPair[0] = &mStreams;
   runningAndSuspendedPair[1] = &mSuspendedStreams;
 
   for (uint32_t array = 0; array < 2; array++) {
     for (uint32_t i = 0; i < runningAndSuspendedPair[array]->Length(); ++i) {
       MediaStream* stream = (*runningAndSuspendedPair[array])[i];
 
@@ -445,55 +470,29 @@ MediaStreamGraphImpl::UpdateCurrentTimeF
       if (runningAndSuspendedPair[array] == &mStreams) {
         bool streamHasOutput = blockedTime < aNextCurrentTime - aPrevCurrentTime;
         // Make this an assertion when bug 957832 is fixed.
         NS_WARN_IF_FALSE(
           !streamHasOutput || !stream->mNotifiedFinished,
           "Shouldn't have already notified of finish *and* have output!");
 
         if (streamHasOutput) {
-          streamsWithOutput.AppendElement(stream);
+          StreamNotifyOutput(stream);
         }
 
         if (stream->mFinished && !stream->mNotifiedFinished) {
-          streamsReadyToFinish.AppendElement(stream);
+          StreamReadyToFinish(stream);
         }
       }
       STREAM_LOG(PR_LOG_DEBUG + 1,
                  ("MediaStream %p bufferStartTime=%f blockedTime=%f", stream,
                   MediaTimeToSeconds(stream->mBufferStartTime),
                   MediaTimeToSeconds(blockedTime)));
     }
   }
-
-  for (uint32_t i = 0; i < streamsWithOutput.Length(); ++i) {
-    MediaStream* stream = streamsWithOutput[i];
-    for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
-      MediaStreamListener* l = stream->mListeners[j];
-      l->NotifyOutput(this, IterationEnd());
-    }
-  }
-
-  for (uint32_t i = 0; i < streamsReadyToFinish.Length(); ++i) {
-    MediaStream* stream = streamsReadyToFinish[i];
-    // The stream is fully finished when all of its track data has been played
-    // out.
-    if (IterationEnd() >=
-        stream->StreamTimeToGraphTime(stream->GetStreamBuffer().GetAllTracksEnd()))  {
-      NS_WARN_IF_FALSE(stream->mNotifiedBlocked,
-        "Should've notified blocked=true for a fully finished stream");
-      stream->mNotifiedFinished = true;
-      stream->mLastPlayedVideoFrame.SetNull();
-      SetStreamOrderDirty();
-      for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
-        MediaStreamListener* l = stream->mListeners[j];
-        l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
-      }
-    }
-  }
 }
 
 bool
 MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aTime,
                                    GraphTime aEndBlockingDecisions, GraphTime* aEnd)
 {
   // Finished streams can't underrun. ProcessedMediaStreams also can't cause
   // underrun currently, since we'll always be able to produce data for them
@@ -909,42 +908,23 @@ MediaStreamGraphImpl::MarkStreamBlocking
 }
 
 void
 MediaStreamGraphImpl::RecomputeBlockingAt(const nsTArray<MediaStream*>& aStreams,
                                           GraphTime aTime,
                                           GraphTime aEndBlockingDecisions,
                                           GraphTime* aEnd)
 {
-  class MOZ_STACK_CLASS AfterLoop
-  {
-  public:
-    AfterLoop(MediaStream* aStream, GraphTime& aTime)
-      : mStream(aStream)
-      , mTime(aTime)
-    {}
-
-    ~AfterLoop()
-    {
-      mStream->mBlocked.SetAtAndAfter(mTime, mStream->mBlockInThisPhase);
-    }
-
-  private:
-    MediaStream* mStream;
-    GraphTime& mTime;
-  };
-
   for (uint32_t i = 0; i < aStreams.Length(); ++i) {
     MediaStream* stream = aStreams[i];
     stream->mBlockInThisPhase = false;
   }
 
   for (uint32_t i = 0; i < aStreams.Length(); ++i) {
     MediaStream* stream = aStreams[i];
-    AfterLoop al(stream, aTime);
 
     if (stream->mFinished) {
       GraphTime endTime = StreamTimeToGraphTime(stream,
          stream->GetStreamBuffer().GetAllTracksEnd());
       if (endTime <= aTime) {
         STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p is blocked due to being finished", stream));
         // We'll block indefinitely
         MarkStreamBlocking(stream);
@@ -970,18 +950,22 @@ MediaStreamGraphImpl::RecomputeBlockingA
     bool underrun = WillUnderrun(stream, aTime, aEndBlockingDecisions, aEnd);
     if (underrun) {
       // We'll block indefinitely
       MarkStreamBlocking(stream);
       *aEnd = std::min(*aEnd, aEndBlockingDecisions);
       continue;
     }
   }
-
   NS_ASSERTION(*aEnd > aTime, "Failed to advance!");
+
+  for (uint32_t i = 0; i < aStreams.Length(); ++i) {
+    MediaStream* stream = aStreams[i];
+    stream->mBlocked.SetAtAndAfter(aTime, stream->mBlockInThisPhase);
+  }
 }
 
 void
 MediaStreamGraphImpl::NotifyHasCurrentData(MediaStream* aStream)
 {
   if (!aStream->mNotifiedHasCurrentData && aStream->mHasCurrentData) {
     for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
       MediaStreamListener* l = aStream->mListeners[j];
@@ -992,48 +976,55 @@ MediaStreamGraphImpl::NotifyHasCurrentDa
 }
 
 void
 MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTime,
                                                   MediaStream* aStream)
 {
   MOZ_ASSERT(mRealtime, "Should only attempt to create audio streams in real-time mode");
 
+  if (aStream->mAudioOutputs.IsEmpty()) {
+    aStream->mAudioOutputStreams.Clear();
+    return;
+  }
+
+  if (!aStream->GetStreamBuffer().GetAndResetTracksDirty()) {
+    return;
+  }
+
   nsAutoTArray<bool,2> audioOutputStreamsFound;
   for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
     audioOutputStreamsFound.AppendElement(false);
   }
 
-  if (!aStream->mAudioOutputs.IsEmpty()) {
-    for (StreamBuffer::TrackIter tracks(aStream->GetStreamBuffer(), MediaSegment::AUDIO);
-         !tracks.IsEnded(); tracks.Next()) {
-      uint32_t i;
-      for (i = 0; i < audioOutputStreamsFound.Length(); ++i) {
-        if (aStream->mAudioOutputStreams[i].mTrackID == tracks->GetID()) {
-          break;
-        }
+  for (StreamBuffer::TrackIter tracks(aStream->GetStreamBuffer(), MediaSegment::AUDIO);
+       !tracks.IsEnded(); tracks.Next()) {
+    uint32_t i;
+    for (i = 0; i < audioOutputStreamsFound.Length(); ++i) {
+      if (aStream->mAudioOutputStreams[i].mTrackID == tracks->GetID()) {
+        break;
       }
-      if (i < audioOutputStreamsFound.Length()) {
-        audioOutputStreamsFound[i] = true;
-      } else {
-        MediaStream::AudioOutputStream* audioOutputStream =
-          aStream->mAudioOutputStreams.AppendElement();
-        audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
-        audioOutputStream->mBlockedAudioTime = 0;
-        audioOutputStream->mLastTickWritten = 0;
-        audioOutputStream->mTrackID = tracks->GetID();
-
-        if (!CurrentDriver()->AsAudioCallbackDriver() &&
-            !CurrentDriver()->Switching()) {
-          MonitorAutoLock mon(mMonitor);
-          if (mLifecycleState == LIFECYCLE_RUNNING) {
-            AudioCallbackDriver* driver = new AudioCallbackDriver(this);
-            mMixer.AddCallback(driver);
-            CurrentDriver()->SwitchAtNextIteration(driver);
-          }
+    }
+    if (i < audioOutputStreamsFound.Length()) {
+      audioOutputStreamsFound[i] = true;
+    } else {
+      MediaStream::AudioOutputStream* audioOutputStream =
+        aStream->mAudioOutputStreams.AppendElement();
+      audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
+      audioOutputStream->mBlockedAudioTime = 0;
+      audioOutputStream->mLastTickWritten = 0;
+      audioOutputStream->mTrackID = tracks->GetID();
+
+      if (!CurrentDriver()->AsAudioCallbackDriver() &&
+          !CurrentDriver()->Switching()) {
+        MonitorAutoLock mon(mMonitor);
+        if (mLifecycleState == LIFECYCLE_RUNNING) {
+          AudioCallbackDriver* driver = new AudioCallbackDriver(this);
+          mMixer.AddCallback(driver);
+          CurrentDriver()->SwitchAtNextIteration(driver);
         }
       }
     }
   }
 
   for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
     if (!audioOutputStreamsFound[i]) {
       aStream->mAudioOutputStreams.RemoveElementAt(i);
@@ -2968,21 +2959,19 @@ MediaStreamGraphImpl::MediaStreamGraphIm
   , mSelfRef(this)
   , mAudioStreamSizes()
   , mNeedsMemoryReport(false)
 #ifdef DEBUG
   , mCanRunMessagesSynchronously(false)
 #endif
   , mAudioChannel(static_cast<uint32_t>(aChannel))
 {
-#ifdef PR_LOGGING
   if (!gMediaStreamGraphLog) {
     gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
   }
-#endif
 
   if (mRealtime) {
     if (aStartWithAudioDriver) {
       AudioCallbackDriver* driver = new AudioCallbackDriver(this, aChannel);
       mDriver = driver;
       mMixer.AddCallback(driver);
     } else {
       mDriver = new SystemClockDriver(this);
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -30,19 +30,17 @@ template <>
 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
 {
   public:
   static void Release(SpeexResamplerState* aState) { speex_resampler_destroy(aState); }
 };
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaStreamGraphLog;
-#endif
 
 /*
  * MediaStreamGraph is a framework for synchronized audio/video processing
  * and playback. It is designed to be used by other browser components such as
  * HTML media elements, media capture APIs, real-time media streaming APIs,
  * multitrack media APIs, and advanced audio APIs.
  *
  * The MediaStreamGraph uses a dedicated thread to process media --- the media
@@ -308,17 +306,18 @@ class CameraPreviewMediaStream;
  * the DOM wrappers must keep upstream MediaStreams alive as long as they
  * could be being used in the media graph.
  *
  * At any time, however, a set of MediaStream wrappers could be
  * collected via cycle collection. Destroy messages will be sent
  * for those objects in arbitrary order and the MediaStreamGraph has to be able
  * to handle this.
  */
-class MediaStream : public mozilla::LinkedListElement<MediaStream> {
+class MediaStream : public mozilla::LinkedListElement<MediaStream>
+{
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
 
   explicit MediaStream(DOMMediaStream* aWrapper);
   virtual dom::AudioContext::AudioContextId AudioContextId() const { return 0; }
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
@@ -506,17 +505,17 @@ public:
   void RemoveConsumer(MediaInputPort* aPort)
   {
     mConsumers.RemoveElement(aPort);
   }
   uint32_t ConsumerCount()
   {
     return mConsumers.Length();
   }
-  const StreamBuffer& GetStreamBuffer() { return mBuffer; }
+  StreamBuffer& GetStreamBuffer() { return mBuffer; }
   GraphTime GetStreamBufferStartTime() { return mBufferStartTime; }
 
   double StreamTimeToSeconds(StreamTime aTime)
   {
     NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time");
     return static_cast<double>(aTime)/mBuffer.GraphRate();
   }
   int64_t StreamTimeToMicroseconds(StreamTime aTime)
@@ -658,17 +657,18 @@ protected:
   // Maps graph time to the graph update that affected this stream at that time
   TimeVarying<GraphTime,int64_t,0> mGraphUpdateIndices;
 
   // MediaInputPorts to which this is connected
   nsTArray<MediaInputPort*> mConsumers;
 
   // Where audio output is going. There is one AudioOutputStream per
   // audio track.
-  struct AudioOutputStream {
+  struct AudioOutputStream
+  {
     // When we started audio playback for this track.
     // Add mStream->GetPosition() to find the current audio playback position.
     GraphTime mAudioPlaybackStartTime;
     // Amount of time that we've wanted to play silence because of the stream
     // blocking.
     MediaTime mBlockedAudioTime;
     // Last tick written to the audio output.
     StreamTime mLastTickWritten;
@@ -728,17 +728,18 @@ protected:
 };
 
 /**
  * This is a stream into which a decoder can write audio and video.
  *
  * Audio and video can be written on any thread, but you probably want to
  * always write from the same thread to avoid unexpected interleavings.
  */
-class SourceMediaStream : public MediaStream {
+class SourceMediaStream : public MediaStream
+{
 public:
   explicit SourceMediaStream(DOMMediaStream* aWrapper) :
     MediaStream(aWrapper),
     mLastConsumptionState(MediaStreamListener::NOT_CONSUMED),
     mMutex("mozilla::media::SourceMediaStream"),
     mUpdateKnownTracksTime(0),
     mPullEnabled(false),
     mUpdateFinished(false),
@@ -985,17 +986,18 @@ protected:
  *
  * The lifetimes of MediaInputPort are controlled from the main thread.
  * The media graph adds a reference to the port. When a MediaInputPort is no
  * longer needed, main-thread code sends a Destroy message for the port and
  * clears its reference (the last main-thread reference to the object). When
  * the Destroy message is processed on the graph manager thread we disconnect
  * the port and drop the graph's reference, destroying the object.
  */
-class MediaInputPort final {
+class MediaInputPort final
+{
 private:
   // Do not call this constructor directly. Instead call aDest->AllocateInputPort.
   MediaInputPort(MediaStream* aSource, ProcessedMediaStream* aDest,
                  uint32_t aFlags, uint16_t aInputNumber,
                  uint16_t aOutputNumber)
     : mSource(aSource)
     , mDest(aDest)
     , mFlags(aFlags)
@@ -1102,17 +1104,18 @@ private:
   MediaStreamGraphImpl* mGraph;
 };
 
 /**
  * This stream processes zero or more input streams in parallel to produce
  * its output. The details of how the output is produced are handled by
  * subclasses overriding the ProcessInput method.
  */
-class ProcessedMediaStream : public MediaStream {
+class ProcessedMediaStream : public MediaStream
+{
 public:
   explicit ProcessedMediaStream(DOMMediaStream* aWrapper)
     : MediaStream(aWrapper), mAutofinish(false)
   {}
 
   // Control API.
   /**
    * Allocates a new input port attached to source aStream.
@@ -1210,17 +1213,18 @@ protected:
   uint32_t mCycleMarker;
 };
 
 /**
  * Initially, at least, we will have a singleton MediaStreamGraph per
  * process.  Each OfflineAudioContext object creates its own MediaStreamGraph
  * object too.
  */
-class MediaStreamGraph {
+class MediaStreamGraph
+{
 public:
   // We ensure that the graph current time advances in multiples of
   // IdealAudioBlockSize()/AudioStream::PreferredSampleRate(). A stream that
   // never blocks and has a track with the ideal audio rate will produce audio
   // in multiples of the block size.
   //
 
   // Main thread only
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -25,28 +25,30 @@ class LinkedList;
 #ifdef MOZ_WEBRTC
 class AudioOutputObserver;
 #endif
 
 /**
  * A per-stream update message passed from the media graph thread to the
  * main thread.
  */
-struct StreamUpdate {
+struct StreamUpdate
+{
   int64_t mGraphUpdateIndex;
   nsRefPtr<MediaStream> mStream;
   StreamTime mNextMainThreadCurrentTime;
   bool mNextMainThreadFinished;
 };
 
 /**
  * This represents a message passed from the main thread to the graph thread.
  * A ControlMessage always has a weak reference a particular affected stream.
  */
-class ControlMessage {
+class ControlMessage
+{
 public:
   explicit ControlMessage(MediaStream* aStream) : mStream(aStream)
   {
     MOZ_COUNT_CTOR(ControlMessage);
   }
   // All these run on the graph thread
   virtual ~ControlMessage()
   {
@@ -65,32 +67,34 @@ public:
 
 protected:
   // We do not hold a reference to mStream. The graph will be holding
   // a reference to the stream until the Destroy message is processed. The
   // last message referencing a stream is the Destroy message for that stream.
   MediaStream* mStream;
 };
 
-class MessageBlock {
+class MessageBlock
+{
 public:
   int64_t mGraphUpdateIndex;
   nsTArray<nsAutoPtr<ControlMessage> > mMessages;
 };
 
 /**
  * The implementation of a media stream graph. This class is private to this
  * file. It's not in the anonymous namespace because MediaStream needs to
  * be able to friend it.
  *
  * Currently we have one global instance per process, and one per each
  * OfflineAudioContext object.
  */
 class MediaStreamGraphImpl : public MediaStreamGraph,
-                             public nsIMemoryReporter {
+                             public nsIMemoryReporter
+{
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
   /**
    * Set aRealtime to true in order to create a MediaStreamGraph which provides
    * support for real-time audio and video.  Set it to false in order to create
    * a non-realtime instance which just churns through its inputs and produces
@@ -145,17 +149,18 @@ public:
   void ShutdownThreads();
 
   /**
    * Called before the thread runs.
    */
   void Init();
   // The following methods run on the graph thread (or possibly the main thread if
   // mLifecycleState > LIFECYCLE_RUNNING)
-  void AssertOnGraphThreadOrNotRunning() {
+  void AssertOnGraphThreadOrNotRunning() const
+  {
     // either we're on the right thread (and calling CurrentDriver() is safe),
     // or we're going to assert anyways, so don't cross-check CurrentDriver
 #ifdef DEBUG
     // if all the safety checks fail, assert we own the monitor
     if (!mDriver->OnThread()) {
       if (!(mDetectedNotRunning &&
             mLifecycleState > LIFECYCLE_RUNNING &&
             NS_IsMainThread())) {
@@ -168,30 +173,33 @@ public:
    * This does the actual iteration: Message processing, MediaStream ordering,
    * blocking computation and processing.
    */
   void DoIteration();
 
   bool OneIteration(GraphTime aFrom, GraphTime aTo,
                     GraphTime aStateFrom, GraphTime aStateEnd);
 
-  bool Running() {
+  bool Running() const
+  {
     mMonitor.AssertCurrentThreadOwns();
     return mLifecycleState == LIFECYCLE_RUNNING;
   }
 
   // Get the message queue, from the current GraphDriver thread.
-  nsTArray<MessageBlock>& MessageQueue() {
+  nsTArray<MessageBlock>& MessageQueue()
+  {
     mMonitor.AssertCurrentThreadOwns();
     return mFrontMessageQueue;
   }
 
   /* This is the end of the current iteration, that is, the current time of the
    * graph. */
-  GraphTime IterationEnd();
+  GraphTime IterationEnd() const;
+
   /**
    * Ensure there is an event posted to the main thread to run RunInStableState.
    * mMonitor must be held.
    * See EnsureRunInStableState
    */
   void EnsureStableStateEventPosted();
   /**
    * Generate messages to the main thread to update it for all state changes.
@@ -216,17 +224,18 @@ public:
   void UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime,
                                    GraphTime aNextCurrentTime);
   /**
    * Process graph message for this iteration, update stream processing order,
    * and recompute stream blocking until aEndBlockingDecisions.
    */
   void UpdateGraph(GraphTime aEndBlockingDecisions);
 
-  void SwapMessageQueues() {
+  void SwapMessageQueues()
+  {
     mMonitor.AssertCurrentThreadOwns();
     mFrontMessageQueue.SwapElements(mBackMessageQueue);
   }
   /**
    * Do all the processing and play the audio and video, ffrom aFrom to aTo.
    */
   void Process(GraphTime aFrom, GraphTime aTo);
   /**
@@ -299,16 +308,17 @@ public:
   void UpdateStreamOrder();
   /**
    * Compute the blocking states of streams from mStateComputedTime
    * until the desired future time aEndBlockingDecisions.
    * Updates mStateComputedTime and sets MediaStream::mBlocked
    * for all streams.
    */
   void RecomputeBlocking(GraphTime aEndBlockingDecisions);
+
   // The following methods are used to help RecomputeBlocking.
   /**
    * If aStream isn't already in aStreams, add it and recursively call
    * AddBlockingRelatedStreamsToSet on all the streams whose blocking
    * status could depend on or affect the state of aStream.
    */
   void AddBlockingRelatedStreamsToSet(nsTArray<MediaStream*>* aStreams,
                                       MediaStream* aStream);
@@ -357,19 +367,21 @@ public:
    */
   StreamTime GraphTimeToStreamTime(MediaStream* aStream, GraphTime aTime);
   /**
    * Given a graph time aTime, convert it to a stream time taking into
    * account the time during which aStream is scheduled to be blocked, and
    * when we don't know whether it's blocked or not, we assume it's not blocked.
    */
   StreamTime GraphTimeToStreamTimeOptimistic(MediaStream* aStream, GraphTime aTime);
-  enum {
+  enum
+  {
     INCLUDE_TRAILING_BLOCKED_INTERVAL = 0x01
   };
+
   /**
    * Given a stream time aTime, convert it to a graph time taking into
    * account the time during which aStream is scheduled to be blocked.
    * aTime must be <= mStateComputedTime since blocking decisions
    * are only known up to that point.
    * If aTime is exactly at the start of a blocked interval, then the blocked
    * interval is included in the time returned if and only if
    * aFlags includes INCLUDE_TRAILING_BLOCKED_INTERVAL.
@@ -406,26 +418,26 @@ public:
   void FinishStream(MediaStream* aStream);
   /**
    * Compute how much stream data we would like to buffer for aStream.
    */
   StreamTime GetDesiredBufferEnd(MediaStream* aStream);
   /**
    * Returns true when there are no active streams.
    */
-  bool IsEmpty()
+  bool IsEmpty() const
   {
     return mStreams.IsEmpty() && mSuspendedStreams.IsEmpty() && mPortCount == 0;
   }
 
   // For use by control messages, on graph thread only.
   /**
    * Identify which graph update index we are currently processing.
    */
-  int64_t GetProcessingGraphUpdateIndex() { return mProcessingGraphUpdateIndex; }
+  int64_t GetProcessingGraphUpdateIndex() const { return mProcessingGraphUpdateIndex; }
   /**
    * Add aStream to the graph and initializes its graph-specific state.
    */
   void AddStream(MediaStream* aStream);
   /**
    * Remove aStream from the graph. Ensures that pending messages about the
    * stream back to the main thread are flushed.
    */
@@ -438,45 +450,48 @@ public:
    * Mark the media stream order as dirty.
    */
   void SetStreamOrderDirty()
   {
     mStreamOrderDirty = true;
   }
 
   // Always stereo for now.
-  uint32_t AudioChannelCount() { return 2; }
+  uint32_t AudioChannelCount() const { return 2; }
 
-  double MediaTimeToSeconds(GraphTime aTime)
+  double MediaTimeToSeconds(GraphTime aTime) const
   {
     NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time");
     return static_cast<double>(aTime)/GraphRate();
   }
-  GraphTime SecondsToMediaTime(double aS)
+
+  GraphTime SecondsToMediaTime(double aS) const
   {
     NS_ASSERTION(0 <= aS && aS <= TRACK_TICKS_MAX/TRACK_RATE_MAX,
                  "Bad seconds");
     return GraphRate() * aS;
   }
-  GraphTime MillisecondsToMediaTime(int32_t aMS)
+
+  GraphTime MillisecondsToMediaTime(int32_t aMS) const
   {
     return RateConvertTicksRoundDown(GraphRate(), 1000, aMS);
   }
 
   /**
    * Signal to the graph that the thread has paused indefinitly,
    * or resumed.
    */
   void PausedIndefinitly();
   void ResumedFromPaused();
 
   /**
    * Not safe to call off the MediaStreamGraph thread unless monitor is held!
    */
-  GraphDriver* CurrentDriver() {
+  GraphDriver* CurrentDriver() const
+  {
     AssertOnGraphThreadOrNotRunning();
     return mDriver;
   }
 
   bool RemoveMixerCallback(MixerCallbackReceiver* aReceiver)
   {
     return mMixer.RemoveCallback(aReceiver);
   }
@@ -484,34 +499,38 @@ public:
   /**
    * Effectively set the new driver, while we are switching.
    * It is only safe to call this at the very end of an iteration, when there
    * has been a SwitchAtNextIteration call during the iteration. The driver
    * should return and pass the control to the new driver shortly after.
    * We can also switch from Revive() (on MainThread), in which case the
    * monitor is held
    */
-  void SetCurrentDriver(GraphDriver* aDriver) {
+  void SetCurrentDriver(GraphDriver* aDriver)
+  {
     AssertOnGraphThreadOrNotRunning();
     mDriver = aDriver;
   }
 
-  Monitor& GetMonitor() {
+  Monitor& GetMonitor()
+  {
     return mMonitor;
   }
 
-  void EnsureNextIteration() {
+  void EnsureNextIteration()
+  {
     mNeedAnotherIteration = true; // atomic
     if (mGraphDriverAsleep) { // atomic
       MonitorAutoLock mon(mMonitor);
       CurrentDriver()->WakeUp(); // Might not be the same driver; might have woken already
     }
   }
 
-  void EnsureNextIterationLocked() {
+  void EnsureNextIterationLocked()
+  {
     mNeedAnotherIteration = true; // atomic
     if (mGraphDriverAsleep) { // atomic
       CurrentDriver()->WakeUp(); // Might not be the same driver; might have woken already
     }
   }
 
   // Data members
   //
@@ -587,17 +606,18 @@ public:
    * as an atomic unit.
    */
   /* Message queue processed by the MSG thread during an iteration. */
   nsTArray<MessageBlock> mFrontMessageQueue;
   /* Message queue in which the main thread appends messages. */
   nsTArray<MessageBlock> mBackMessageQueue;
 
   /* True if there will messages to process if we swap the message queues. */
-  bool MessagesQueued() {
+  bool MessagesQueued() const
+  {
     mMonitor.AssertCurrentThreadOwns();
     return !mBackMessageQueue.IsEmpty();
   }
   /**
    * This enum specifies where this graph is in its lifecycle. This is used
    * to control shutdown.
    * Shutdown is tricky because it can happen in two different ways:
    * 1) Shutdown due to inactivity. RunThread() detects that it has no
@@ -614,17 +634,18 @@ public:
    * async event to Shutdown() the graph's threads. However the graph object
    * is not deleted. New messages for the graph are processed synchronously on
    * the main thread if necessary. When the last stream is destroyed, the
    * graph object is deleted.
    *
    * This should be kept in sync with the LifecycleState_str array in
    * MediaStreamGraph.cpp
    */
-  enum LifecycleState {
+  enum LifecycleState
+  {
     // The graph thread hasn't started yet.
     LIFECYCLE_THREAD_NOT_STARTED,
     // RunThread() is running normally.
     LIFECYCLE_RUNNING,
     // In the following states, the graph thread is not running so
     // all "graph thread only" state in this class can be used safely
     // on the main thread.
     // RunThread() has exited and we're waiting for the next
@@ -707,16 +728,20 @@ public:
   nsRefPtr<AudioOutputObserver> mFarendObserverRef;
 #endif
 
   uint32_t AudioChannel() const { return mAudioChannel; }
 
 private:
   virtual ~MediaStreamGraphImpl();
 
+  void StreamNotifyOutput(MediaStream* aStream);
+
+  void StreamReadyToFinish(MediaStream* aStream);
+
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   /**
    * Used to signal that a memory report has been requested.
    */
   Monitor mMemoryReportMonitor;
   /**
    * This class uses manual memory management, and all pointers to it are raw
--- a/dom/media/MediaTaskQueue.cpp
+++ b/dom/media/MediaTaskQueue.cpp
@@ -22,16 +22,17 @@ MediaTaskQueue::MediaTaskQueue(Temporary
 {
   MOZ_COUNT_CTOR(MediaTaskQueue);
 }
 
 MediaTaskQueue::~MediaTaskQueue()
 {
   MonitorAutoLock mon(mQueueMonitor);
   MOZ_ASSERT(mIsShutdown);
+  MOZ_DIAGNOSTIC_ASSERT(mTasks.empty());
   MOZ_COUNT_DTOR(MediaTaskQueue);
 }
 
 TaskDispatcher&
 MediaTaskQueue::TailDispatcher()
 {
   MOZ_ASSERT(IsCurrentThreadIn());
   MOZ_ASSERT(mTailDispatcher);
@@ -40,17 +41,17 @@ MediaTaskQueue::TailDispatcher()
 
 nsresult
 MediaTaskQueue::DispatchLocked(already_AddRefed<nsIRunnable> aRunnable,
                                DispatchMode aMode, DispatchFailureHandling aFailureHandling,
                                DispatchReason aReason)
 {
   nsCOMPtr<nsIRunnable> r = aRunnable;
   AbstractThread* currentThread;
-  if (aReason != TailDispatch && (currentThread = GetCurrent()) && currentThread->RequiresTailDispatch()) {
+  if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
     currentThread->TailDispatcher().AddTask(this, r.forget(), aFailureHandling);
     return NS_OK;
   }
 
   mQueueMonitor.AssertCurrentThreadOwns();
   if (mIsFlushing && aMode == AbortIfFlushing) {
     return NS_ERROR_ABORT;
   }
--- a/dom/media/RtspMediaResource.cpp
+++ b/dom/media/RtspMediaResource.cpp
@@ -15,27 +15,22 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamingProtocolService.h"
 #include "nsServiceManagerUtils.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspChannelChild.h"
 #endif
 using namespace mozilla::net;
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gRtspMediaResourceLog;
 #define RTSP_LOG(msg, ...) PR_LOG(gRtspMediaResourceLog, PR_LOG_DEBUG, \
                                   (msg, ##__VA_ARGS__))
 // Debug logging macro with object pointer and class name.
 #define RTSPMLOG(msg, ...) \
         RTSP_LOG("%p [RtspMediaResource]: " msg, this, ##__VA_ARGS__)
-#else
-#define RTSP_LOG(msg, ...)
-#define RTSPMLOG(msg, ...)
-#endif
 
 namespace mozilla {
 
 /* class RtspTrackBuffer: a ring buffer implementation for audio/video track
  * un-decoded data.
  * The ring buffer is divided into BUFFER_SLOT_NUM slots,
  * and each slot's size is fixed(mSlotSize).
  * Even though the ring buffer is divided into fixed size slots, it still can
@@ -504,22 +499,20 @@ RtspMediaResource::RtspMediaResource(Med
   MOZ_CRASH("Should not be called except for B2G platform");
 #else
   MOZ_ASSERT(aChannel);
   mMediaStreamController =
     static_cast<RtspChannelChild*>(aChannel)->GetController();
   MOZ_ASSERT(mMediaStreamController);
   mListener = new Listener(this);
   mMediaStreamController->AsyncOpen(mListener);
-#ifdef PR_LOGGING
   if (!gRtspMediaResourceLog) {
     gRtspMediaResourceLog = PR_NewLogModule("RtspMediaResource");
   }
 #endif
-#endif
 }
 
 RtspMediaResource::~RtspMediaResource()
 {
   RTSPMLOG("~RtspMediaResource");
   if (mListener) {
     // Kill its reference to us since we're going away
     mListener->Revoke();
--- a/dom/media/StateMirroring.h
+++ b/dom/media/StateMirroring.h
@@ -125,17 +125,17 @@ private:
   {
   public:
     using AbstractCanonical<T>::OwnerThread;
 
     Impl(AbstractThread* aThread, const T& aInitialValue, const char* aName)
       : AbstractCanonical<T>(aThread), WatchTarget(aName), mValue(aInitialValue)
     {
       MIRROR_LOG("%s [%p] initialized", mName, this);
-      MOZ_ASSERT(aThread->RequiresTailDispatch(), "Can't get coherency without tail dispatch");
+      MOZ_ASSERT(aThread->SupportsTailDispatch(), "Can't get coherency without tail dispatch");
     }
 
     void AddMirror(AbstractMirror<T>* aMirror) override
     {
       MIRROR_LOG("%s [%p] adding mirror %p", mName, this, aMirror);
       MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
       MOZ_ASSERT(!mMirrors.Contains(aMirror));
       mMirrors.AppendElement(aMirror);
@@ -298,16 +298,17 @@ private:
   {
   public:
     using AbstractMirror<T>::OwnerThread;
 
     Impl(AbstractThread* aThread, const T& aInitialValue, const char* aName)
       : AbstractMirror<T>(aThread), WatchTarget(aName), mValue(aInitialValue)
     {
       MIRROR_LOG("%s [%p] initialized", mName, this);
+      MOZ_ASSERT(aThread->SupportsTailDispatch(), "Can't get coherency without tail dispatch");
     }
 
     operator const T&()
     {
       MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
       return mValue;
     }
 
@@ -329,16 +330,17 @@ private:
 
     bool IsConnected() const { return !!mCanonical; }
 
     void Connect(AbstractCanonical<T>* aCanonical)
     {
       MIRROR_LOG("%s [%p] Connecting to %p", mName, this, aCanonical);
       MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
       MOZ_ASSERT(!IsConnected());
+      MOZ_ASSERT(OwnerThread()->RequiresTailDispatch(aCanonical->OwnerThread()), "Can't get coherency without tail dispatch");
 
       nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<StorensRefPtrPassByPtr<AbstractMirror<T>>>
                                   (aCanonical, &AbstractCanonical<T>::AddMirror, this);
       aCanonical->OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
       mCanonical = aCanonical;
     }
   public:
 
--- a/dom/media/StreamBuffer.cpp
+++ b/dom/media/StreamBuffer.cpp
@@ -4,22 +4,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "StreamBuffer.h"
 #include "prlog.h"
 #include <algorithm>
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaStreamGraphLog;
 #define STREAM_LOG(type, msg) PR_LOG(gMediaStreamGraphLog, type, msg)
-#else
-#define STREAM_LOG(type, msg)
-#endif
 
 #ifdef DEBUG
 void
 StreamBuffer::DumpTrackInfo() const
 {
   STREAM_LOG(PR_LOG_ALWAYS, ("DumpTracks: mTracksKnownTime %lld", mTracksKnownTime));
   for (uint32_t i = 0; i < mTracks.Length(); ++i) {
     Track* track = mTracks[i];
@@ -62,24 +58,40 @@ StreamBuffer::GetAllTracksEnd() const
     t = std::max(t, track->GetEnd());
   }
   return t;
 }
 
 StreamBuffer::Track*
 StreamBuffer::FindTrack(TrackID aID)
 {
-  if (aID == TRACK_NONE)
+  if (aID == TRACK_NONE || mTracks.IsEmpty()) {
     return nullptr;
-  for (uint32_t i = 0; i < mTracks.Length(); ++i) {
-    Track* track = mTracks[i];
-    if (track->GetID() == aID) {
-      return track;
+  }
+
+  // The tracks are sorted by ID. We can use a binary search.
+
+  uint32_t left = 0, right = mTracks.Length() - 1;
+  while (left <= right) {
+    uint32_t middle = (left + right) / 2;
+    if (mTracks[middle]->GetID() == aID) {
+      return mTracks[middle];
+    }
+
+    if (mTracks[middle]->GetID() > aID) {
+      if (middle == 0) {
+        break;
+      }
+
+      right = middle - 1;
+    } else {
+      left = middle + 1;
     }
   }
+
   return nullptr;
 }
 
 void
 StreamBuffer::ForgetUpTo(StreamTime aTime)
 {
   // Only prune if there is a reasonable chunk (50ms @ 48kHz) to forget, so we
   // don't spend too much time pruning segments.
@@ -88,16 +100,17 @@ StreamBuffer::ForgetUpTo(StreamTime aTim
     return;
   }
   mForgottenTime = aTime;
 
   for (uint32_t i = 0; i < mTracks.Length(); ++i) {
     Track* track = mTracks[i];
     if (track->IsEnded() && track->GetEnd() <= aTime) {
       mTracks.RemoveElementAt(i);
+      mTracksDirty = true;
       --i;
       continue;
     }
     StreamTime forgetTo = std::min(track->GetEnd() - 1, aTime);
     track->ForgetUpTo(forgetTo);
   }
 }
 
--- a/dom/media/StreamBuffer.h
+++ b/dom/media/StreamBuffer.h
@@ -45,53 +45,58 @@ inline TrackTicks RateConvertTicksRoundU
  * we know won't be used again. (We prune a whole number of seconds internally.)
  *
  * StreamBuffers should only be used from one thread at a time.
  *
  * A StreamBuffer has a set of tracks that can be of arbitrary types ---
  * the data for each track is a MediaSegment. The set of tracks can vary
  * over the timeline of the StreamBuffer.
  */
-class StreamBuffer {
+class StreamBuffer
+{
 public:
   /**
    * Every track has a start time --- when it started in the StreamBuffer.
    * It has an end flag; when false, no end point is known; when true,
    * the track ends when the data we have for the track runs out.
    * Tracks have a unique ID assigned at creation. This allows us to identify
    * the same track across StreamBuffers. A StreamBuffer should never have
    * two tracks with the same ID (even if they don't overlap in time).
    * TODO Tracks can also be enabled and disabled over time.
    * TODO Add TimeVarying<StreamTime,bool> mEnabled.
    * Takes ownership of aSegment.
    */
-  class Track {
+  class Track final
+  {
     Track(TrackID aID, StreamTime aStart, MediaSegment* aSegment)
       : mStart(aStart),
         mSegment(aSegment),
         mID(aID),
         mEnded(false)
     {
       MOZ_COUNT_CTOR(Track);
 
       NS_ASSERTION(aID > TRACK_NONE, "Bad track ID");
       NS_ASSERTION(0 <= aStart && aStart <= aSegment->GetDuration(), "Bad start position");
     }
+
   public:
     ~Track()
     {
       MOZ_COUNT_DTOR(Track);
     }
+
     template <class T> T* Get() const
     {
       if (mSegment->GetType() == T::StaticType()) {
         return static_cast<T*>(mSegment.get());
       }
       return nullptr;
     }
+
     MediaSegment* GetSegment() const { return mSegment; }
     TrackID GetID() const { return mID; }
     bool IsEnded() const { return mEnded; }
     StreamTime GetStart() const { return mStart; }
     StreamTime GetEnd() const { return mSegment->GetDuration(); }
     MediaSegment::Type GetType() const { return mSegment->GetType(); }
 
     void SetEnded() { mEnded = true; }
@@ -124,42 +129,45 @@ public:
     {
       size_t amount = aMallocSizeOf(this);
       if (mSegment) {
         amount += mSegment->SizeOfIncludingThis(aMallocSizeOf);
       }
       return amount;
     }
 
-  protected:
+  private:
     friend class StreamBuffer;
 
     // Start offset is in ticks at rate mRate
     StreamTime mStart;
     // The segment data starts at the start of the owning StreamBuffer, i.e.,
     // there's mStart silence/no video at the beginning.
     nsAutoPtr<MediaSegment> mSegment;
     // Unique ID
     TrackID mID;
     // True when the track ends with the data in mSegment
     bool mEnded;
   };
 
-  class CompareTracksByID {
+  class MOZ_STACK_CLASS CompareTracksByID final
+  {
   public:
     bool Equals(Track* aA, Track* aB) const {
       return aA->GetID() == aB->GetID();
     }
     bool LessThan(Track* aA, Track* aB) const {
       return aA->GetID() < aB->GetID();
     }
   };
 
   StreamBuffer()
-    : mTracksKnownTime(0), mForgottenTime(0)
+    : mTracksKnownTime(0)
+    , mForgottenTime(0)
+    , mTracksDirty(false)
 #ifdef DEBUG
     , mGraphRateIsSet(false)
 #endif
   {
     MOZ_COUNT_CTOR(StreamBuffer);
   }
   ~StreamBuffer()
   {
@@ -201,26 +209,28 @@ public:
    * aSegment must have aStart worth of null data.
    */
   Track& AddTrack(TrackID aID, StreamTime aStart, MediaSegment* aSegment)
   {
     NS_ASSERTION(!FindTrack(aID), "Track with this ID already exists");
 
     Track* track = new Track(aID, aStart, aSegment);
     mTracks.InsertElementSorted(track, CompareTracksByID());
+    mTracksDirty = true;
 
     if (mTracksKnownTime == STREAM_TIME_MAX) {
       // There exists code like
       // http://mxr.mozilla.org/mozilla-central/source/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp?rev=96b197deb91e&mark=1292-1297#1292
       NS_WARNING("Adding track to StreamBuffer that should have no more tracks");
     } else {
       NS_ASSERTION(mTracksKnownTime <= aStart, "Start time too early");
     }
     return *track;
   }
+
   void AdvanceKnownTracksTime(StreamTime aKnownTime)
   {
     NS_ASSERTION(aKnownTime >= mTracksKnownTime, "Can't move tracks-known time earlier");
     mTracksKnownTime = aKnownTime;
   }
 
   /**
    * The end time for the StreamBuffer is the latest time for which we have
@@ -236,17 +246,18 @@ public:
   StreamTime GetAllTracksEnd() const;
 
 #ifdef DEBUG
   void DumpTrackInfo() const;
 #endif
 
   Track* FindTrack(TrackID aID);
 
-  class TrackIter {
+  class MOZ_STACK_CLASS TrackIter final
+  {
   public:
     /**
      * Iterate through the tracks of aBuffer in order of ID.
      */
     explicit TrackIter(const StreamBuffer& aBuffer) :
       mBuffer(&aBuffer.mTracks), mIndex(0), mMatchType(false) {}
     /**
      * Iterate through the tracks of aBuffer with type aType, in order of ID.
@@ -289,24 +300,38 @@ public:
   /**
    * Returns the latest time passed to ForgetUpTo.
    */
   StreamTime GetForgottenDuration()
   {
     return mForgottenTime;
   }
 
+  bool GetAndResetTracksDirty()
+  {
+    if (!mTracksDirty) {
+      return false;
+    }
+
+    mTracksDirty = false;
+    return true;
+  }
+
 protected:
   TrackRate mGraphRate; // StreamTime per second
   // Any new tracks added will start at or after this time. In other words, the track
   // list is complete and correct for all times less than this time.
   StreamTime mTracksKnownTime;
   StreamTime mForgottenTime;
+
+private:
   // All known tracks for this StreamBuffer
-  nsTArray<nsAutoPtr<Track> > mTracks;
+  nsTArray<nsAutoPtr<Track>> mTracks;
+  bool mTracksDirty;
+
 #ifdef DEBUG
   bool mGraphRateIsSet;
 #endif
 };
 
 }
 
 #endif /* MOZILLA_STREAMBUFFER_H_ */
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -37,31 +37,25 @@ using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 
 #ifdef STREAM_LOG
 #undef STREAM_LOG
 #endif
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gTrackUnionStreamLog;
 #define STREAM_LOG(type, msg) PR_LOG(gTrackUnionStreamLog, type, msg)
-#else
-#define STREAM_LOG(type, msg)
-#endif
 
 TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) :
   ProcessedMediaStream(aWrapper)
 {
-#ifdef PR_LOGGING
   if (!gTrackUnionStreamLog) {
     gTrackUnionStreamLog = PR_NewLogModule("TrackUnionStream");
   }
-#endif
 }
 
   void TrackUnionStream::RemoveInput(MediaInputPort* aPort)
   {
     for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
       if (mTrackMap[i].mInputPort == aPort) {
         EndTrack(i);
         mTrackMap.RemoveElementAt(i);
--- a/dom/media/WebVTTListener.cpp
+++ b/dom/media/WebVTTListener.cpp
@@ -23,32 +23,26 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebVTTListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebVTTListener)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebVTTListener)
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* gTextTrackLog;
 # define VTT_LOG(...) PR_LOG(gTextTrackLog, PR_LOG_DEBUG, (__VA_ARGS__))
-#else
-# define VTT_LOG(msg)
-#endif
 
 WebVTTListener::WebVTTListener(HTMLTrackElement* aElement)
   : mElement(aElement)
 {
   MOZ_ASSERT(mElement, "Must pass an element to the callback");
-#ifdef PR_LOGGING
   if (!gTextTrackLog) {
     gTextTrackLog = PR_NewLogModule("TextTrack");
   }
-#endif
   VTT_LOG("WebVTTListener created.");
 }
 
 WebVTTListener::~WebVTTListener()
 {
   VTT_LOG("WebVTTListener destroyed.");
 }
 
--- a/dom/media/apple/AppleMP3Reader.cpp
+++ b/dom/media/apple/AppleMP3Reader.cpp
@@ -16,26 +16,20 @@
 // per block and power-of-2 allocation sizes.  Since we must pre-allocate the
 // buffer we cannot use AudioCompactor without paying for an additional
 // allocation and copy.  Therefore, choosing a value that divides exactly into
 // 1152 is most memory efficient.
 #define MAX_AUDIO_FRAMES 128
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define LOGE(...) PR_LOG(gMediaDecoderLog, PR_LOG_ERROR, (__VA_ARGS__))
 #define LOGW(...) PR_LOG(gMediaDecoderLog, PR_LOG_WARNING, (__VA_ARGS__))
 #define LOGD(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__))
-#else
-#define LOGE(...)
-#define LOGW(...)
-#define LOGD(...)
-#endif
 
 #define PROPERTY_ID_FORMAT "%c%c%c%c"
 #define PROPERTY_ID_PRINT(x) ((x) >> 24), \
                              ((x) >> 16) & 0xff, \
                              ((x) >> 8) & 0xff, \
                              (x) & 0xff
 
 AppleMP3Reader::AppleMP3Reader(AbstractMediaDecoder *aDecoder)
--- a/dom/media/directshow/AudioSinkFilter.cpp
+++ b/dom/media/directshow/AudioSinkFilter.cpp
@@ -18,22 +18,18 @@
 
 DEFINE_GUID(CLSID_MozAudioSinkFilter, 0x1872d8c8, 0xea8d, 0x4c34, 0xae, 0x96, 0x69, 0xde,
             0xf1, 0x33, 0x7b, 0x33);
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* GetDirectShowLog();
 #define LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#else
-#define LOG(...)
-#endif
 
 AudioSinkFilter::AudioSinkFilter(const wchar_t* aObjectName, HRESULT* aOutResult)
   : BaseFilter(aObjectName, CLSID_MozAudioSinkFilter),
     mFilterCritSec("AudioSinkFilter::mFilterCritSec")
 {
   (*aOutResult) = S_OK;
   mInputPin = new AudioSinkInputPin(L"AudioSinkInputPin",
                                     this,
--- a/dom/media/directshow/AudioSinkInputPin.cpp
+++ b/dom/media/directshow/AudioSinkInputPin.cpp
@@ -10,22 +10,18 @@
 #include "prlog.h"
 
 #include <wmsdkidl.h>
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* GetDirectShowLog();
 #define LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#else
-#define LOG(...)
-#endif
 
 AudioSinkInputPin::AudioSinkInputPin(wchar_t* aObjectName,
                                      AudioSinkFilter* aFilter,
                                      mozilla::CriticalSection* aLock,
                                      HRESULT* aOutResult)
   : BaseInputPin(aObjectName, aFilter, aLock, aOutResult, aObjectName),
     mSegmentStartTime(0)
 {
--- a/dom/media/directshow/DirectShowReader.cpp
+++ b/dom/media/directshow/DirectShowReader.cpp
@@ -12,33 +12,27 @@
 #include "SourceFilter.h"
 #include "SampleSink.h"
 #include "MediaResource.h"
 #include "VideoUtils.h"
 
 namespace mozilla {
 
 
-#ifdef PR_LOGGING
-
 PRLogModuleInfo*
 GetDirectShowLog() {
   static PRLogModuleInfo* log = nullptr;
   if (!log) {
     log = PR_NewLogModule("DirectShowDecoder");
   }
   return log;
 }
 
 #define LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 
-#else
-#define LOG(...)
-#endif
-
 DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder),
     mMP3FrameParser(aDecoder->GetResource()->GetLength()),
 #ifdef DEBUG
     mRotRegister(0),
 #endif
     mNumChannels(0),
     mAudioRate(0),
--- a/dom/media/directshow/DirectShowUtils.cpp
+++ b/dom/media/directshow/DirectShowUtils.cpp
@@ -11,18 +11,16 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/RefPtr.h"
 #include "nsPrintfCString.h"
 
 #define WARN(...) NS_WARNING(nsPrintfCString(__VA_ARGS__).get())
 
 namespace mozilla {
 
-#if defined(PR_LOGGING)
-
 // Create a table which maps GUIDs to a string representation of the GUID.
 // This is useful for debugging purposes, for logging the GUIDs of media types.
 // This is only available when logging is enabled, i.e. not in release builds.
 struct GuidToName {
   const char* name;
   const GUID guid;
 };
 
@@ -43,17 +41,16 @@ GetDirectShowGuidName(const GUID& aGuid)
   const size_t len = ArrayLength(GuidToNameTable);
   for (unsigned i = 0; i < len; i++) {
     if (IsEqualGUID(aGuid, GuidToNameTable[i].guid)) {
       return GuidToNameTable[i].name;
     }
   }
   return "Unknown";
 }
-#endif // PR_LOGGING
 
 void
 RemoveGraphFromRunningObjectTable(DWORD aRotRegister)
 {
   nsRefPtr<IRunningObjectTable> runningObjectTable;
   if (SUCCEEDED(GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)))) {
     runningObjectTable->Revoke(aRotRegister);
   }
--- a/dom/media/directshow/DirectShowUtils.h
+++ b/dom/media/directshow/DirectShowUtils.h
@@ -107,17 +107,14 @@ RefTimeToUsecs(const int64_t hRefTime)
 // Converts from DirectShow "Reference Time" (hundreds of nanoseconds)
 // to seconds.
 inline double
 RefTimeToSeconds(const REFERENCE_TIME aRefTime)
 {
   return double(aRefTime) / 10000000;
 }
 
-
-#if defined(PR_LOGGING)
 const char*
 GetDirectShowGuidName(const GUID& aGuid);
-#endif
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/directshow/SampleSink.cpp
+++ b/dom/media/directshow/SampleSink.cpp
@@ -9,22 +9,18 @@
 #include "AudioSinkInputPin.h"
 #include "VideoUtils.h"
 #include "prlog.h"
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
 PRLogModuleInfo* GetDirectShowLog();
 #define LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#else
-#define LOG(...)
-#endif
 
 SampleSink::SampleSink()
   : mMonitor("SampleSink"),
     mIsFlushing(false),
     mAtEOS(false)
 {
   MOZ_COUNT_CTOR(SampleSink);
 }
@@ -64,23 +60,23 @@ SampleSink::Receive(IMediaSample* aSampl
     }
     if (mAtEOS) {
       return E_UNEXPECTED;
     }
     // Wait until the consumer thread consumes the sample.
     mon.Wait();
   }
 
-#ifdef PR_LOGGING
-  REFERENCE_TIME start = 0, end = 0;
-  HRESULT hr = aSample->GetMediaTime(&start, &end);
-  LOG("SampleSink::Receive() [%4.2lf-%4.2lf]",
-      (double)RefTimeToUsecs(start) / USECS_PER_S,
-      (double)RefTimeToUsecs(end) / USECS_PER_S);
-#endif
+  if (PR_LOG_TEST(GetDirectShowLog(), PR_LOG_DEBUG)) {
+    REFERENCE_TIME start = 0, end = 0;
+    HRESULT hr = aSample->GetMediaTime(&start, &end);
+    LOG("SampleSink::Receive() [%4.2lf-%4.2lf]",
+        (double)RefTimeToUsecs(start) / USECS_PER_S,
+        (double)RefTimeToUsecs(end) / USECS_PER_S);
+  }
 
   mSample = aSample;
   // Notify the signal, to awaken the consumer thread in WaitForSample()
   // if necessary.
   mon.NotifyAll();
   return S_OK;
 }
 
@@ -101,23 +97,23 @@ SampleSink::Extract(RefPtr<IMediaSample>
       // before reporting EOS.
       return E_UNEXPECTED;
     }
     // Wait until the producer thread gives us a sample.
     mon.Wait();
   }
   aOutSample = mSample;
 
-#ifdef PR_LOGGING
-  int64_t start = 0, end = 0;
-  mSample->GetMediaTime(&start, &end);
-  LOG("SampleSink::Extract() [%4.2lf-%4.2lf]",
-      (double)RefTimeToUsecs(start) / USECS_PER_S,
-      (double)RefTimeToUsecs(end) / USECS_PER_S);
-#endif
+  if (PR_LOG_TEST(GetDirectShowLog(), PR_LOG_DEBUG)) {
+    int64_t start = 0, end = 0;
+    mSample->GetMediaTime(&start, &end);
+    LOG("SampleSink::Extract() [%4.2lf-%4.2lf]",
+        (double)RefTimeToUsecs(start) / USECS_PER_S,
+        (double)RefTimeToUsecs(end) / USECS_PER_S);
+  }
 
   mSample = nullptr;
   // Notify the signal, to awaken the producer thread in Receive()
   // if necessary.
   mon.NotifyAll();
   return S_OK;
 }
 
--- a/dom/media/directshow/SourceFilter.cpp
+++ b/dom/media/directshow/SourceFilter.cpp
@@ -14,17 +14,17 @@
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
 // Define to trace what's on...
 //#define DEBUG_SOURCE_TRACE 1
 
-#if defined(PR_LOGGING) && defined (DEBUG_SOURCE_TRACE)
+#if defined (DEBUG_SOURCE_TRACE)
 PRLogModuleInfo* GetDirectShowLog();
 #define DIRECTSHOW_LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define DIRECTSHOW_LOG(...)
 #endif
 
 static HRESULT
 DoGetInterface(IUnknown* aUnknown, void** aInterface)
--- a/dom/media/eme/CDMCaps.cpp
+++ b/dom/media/eme/CDMCaps.cpp
@@ -46,17 +46,16 @@ CDMCaps::AutoLock::AutoLock(CDMCaps& aIn
   mData.Lock();
 }
 
 CDMCaps::AutoLock::~AutoLock()
 {
   mData.Unlock();
 }
 
-#ifdef PR_LOGGING
 static void
 TestCap(uint64_t aFlag,
         uint64_t aCaps,
         const nsACString& aCapName,
         nsACString& aCapStr)
 {
   if (!(aFlag & aCaps)) {
     return;
@@ -72,17 +71,16 @@ CapsToString(uint64_t aCaps)
 {
   nsCString capsStr;
   TestCap(GMP_EME_CAP_DECRYPT_AUDIO, aCaps, NS_LITERAL_CSTRING("DecryptAudio"), capsStr);
   TestCap(GMP_EME_CAP_DECRYPT_VIDEO, aCaps, NS_LITERAL_CSTRING("DecryptVideo"), capsStr);
   TestCap(GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO, aCaps, NS_LITERAL_CSTRING("DecryptAndDecodeAudio"), capsStr);
   TestCap(GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO, aCaps, NS_LITERAL_CSTRING("DecryptAndDecodeVideo"), capsStr);
   return capsStr;
 }
-#endif // PR_LOGGING
 
 void
 CDMCaps::AutoLock::SetCaps(uint64_t aCaps)
 {
   EME_LOG("SetCaps() %s", CapsToString(aCaps).get());
   mData.mMonitor.AssertCurrentThreadOwns();
   mData.mCaps = aCaps;
   for (size_t i = 0; i < mData.mWaitForCaps.Length(); i++) {
--- a/dom/media/eme/EMEUtils.cpp
+++ b/dom/media/eme/EMEUtils.cpp
@@ -3,36 +3,32 @@
 /* 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/EMEUtils.h"
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
-
 PRLogModuleInfo* GetEMELog() {
   static PRLogModuleInfo* log = nullptr;
   if (!log) {
     log = PR_NewLogModule("EME");
   }
   return log;
 }
 
 PRLogModuleInfo* GetEMEVerboseLog() {
   static PRLogModuleInfo* log = nullptr;
   if (!log) {
     log = PR_NewLogModule("EMEV");
   }
   return log;
 }
 
-#endif
-
 static bool
 ContainsOnlyDigits(const nsAString& aString)
 {
   nsAString::const_iterator iter, end;
   aString.BeginReading(iter);
   aString.EndReading(end);
   while (iter != end) {
     char16_t ch = *iter;
--- a/dom/media/eme/EMEUtils.h
+++ b/dom/media/eme/EMEUtils.h
@@ -7,47 +7,34 @@
 #ifndef EME_LOG_H_
 #define EME_LOG_H_
 
 #include "prlog.h"
 #include "nsString.h"
 
 namespace mozilla {
 
-#ifdef PR_LOGGING
-
-  #ifndef EME_LOG
-    PRLogModuleInfo* GetEMELog();
-    #define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
-  #endif
+#ifndef EME_LOG
+  PRLogModuleInfo* GetEMELog();
+  #define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
+  #define EME_LOG_ENABLED() PR_LOG_TEST(GetEMELog(), PR_LOG_DEBUG)
+#endif
 
-  #ifndef EME_VERBOSE_LOG
-    PRLogModuleInfo* GetEMEVerboseLog();
-    #define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-  #else
-    #ifndef EME_LOG
-      #define EME_LOG(...)
-    #endif
-
-    #ifndef EME_VERBOSE_LOG
-      #define EME_VERBOSE_LOG(...)
-    #endif
-  #endif
-
+#ifndef EME_VERBOSE_LOG
+  PRLogModuleInfo* GetEMEVerboseLog();
+  #define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
-
   #ifndef EME_LOG
     #define EME_LOG(...)
   #endif
 
   #ifndef EME_VERBOSE_LOG
     #define EME_VERBOSE_LOG(...)
   #endif
-
-#endif // PR_LOGGING
+#endif
 
 #define NO_CDM_VERSION -1
 
 // Checks a keySystem string against a whitelist, and determines whether
 // the keySystem is in the whitelist, and extracts the requested minimum
 // CDM version.
 //
 // Format of EME keysystems:
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -133,34 +133,34 @@ MediaKeySession::UpdateKeyStatusMap()
   nsTArray<CDMCaps::KeyStatus> keyStatuses;
   {
     CDMCaps::AutoLock caps(mKeys->GetCDMProxy()->Capabilites());
     caps.GetKeyStatusesForSession(mSessionId, keyStatuses);
   }
 
   mKeyStatusMap->Update(keyStatuses);
 
-#ifdef PR_LOGGING
-  nsAutoCString message(
-    nsPrintfCString("MediaKeySession[%p,'%s'] key statuses change {",
-                    this, NS_ConvertUTF16toUTF8(mSessionId).get()));
-  for (const CDMCaps::KeyStatus& status : keyStatuses) {
-    nsAutoCString base64KeyId;
-    nsDependentCSubstring rawKeyId(reinterpret_cast<const char*>(status.mId.Elements()),
-                                   status.mId.Length());
-    nsresult rv = Base64Encode(rawKeyId, base64KeyId);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      continue;
+  if (EME_LOG_ENABLED()) {
+    nsAutoCString message(
+      nsPrintfCString("MediaKeySession[%p,'%s'] key statuses change {",
+                      this, NS_ConvertUTF16toUTF8(mSessionId).get()));
+    for (const CDMCaps::KeyStatus& status : keyStatuses) {
+      nsAutoCString base64KeyId;
+      nsDependentCSubstring rawKeyId(reinterpret_cast<const char*>(status.mId.Elements()),
+                                     status.mId.Length());
+      nsresult rv = Base64Encode(rawKeyId, base64KeyId);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        continue;
+      }
+      message.Append(nsPrintfCString(" (%s,%s)", base64KeyId.get(),
+        MediaKeyStatusValues::strings[status.mStatus].value));
     }
-    message.Append(nsPrintfCString(" (%s,%s)", base64KeyId.get(),
-      MediaKeyStatusValues::strings[status.mStatus].value));
+    message.Append(" }");
+    EME_LOG(message.get());
   }
-  message.Append(" }");
-  EME_LOG(message.get());
-#endif
 }
 
 MediaKeyStatusMap*
 MediaKeySession::KeyStatuses() const
 {
   return mKeyStatusMap;
 }
 
@@ -188,27 +188,27 @@ MediaKeySession::GenerateRequest(const n
       !CopyArrayBufferViewOrArrayBufferData(aInitData, data)) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, "
             "invalid initData or initDataType",
       this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
 
-#ifdef PR_LOGGING
   // Convert initData to base64 for easier logging.
   // Note: UpdateSession() Move()s the data out of the array, so we have
   // to copy it here.
   nsAutoCString base64InitData;
-  nsDependentCSubstring rawInitData(reinterpret_cast<const char*>(data.Elements()),
-    data.Length());
-  if (NS_FAILED(Base64Encode(rawInitData, base64InitData))) {
-    NS_WARNING("Failed to base64 encode initData for logging");
+  if (EME_LOG_ENABLED()) {
+    nsDependentCSubstring rawInitData(reinterpret_cast<const char*>(data.Elements()),
+      data.Length());
+    if (NS_FAILED(Base64Encode(rawInitData, base64InitData))) {
+      NS_WARNING("Failed to base64 encode initData for logging");
+    }
   }
-#endif
 
   PromiseId pid = mKeys->StorePromise(promise);
   mKeys->GetCDMProxy()->CreateSession(Token(),
                                       mSessionType,
                                       pid,
                                       aInitDataType, data);
 
   EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() sent, "
@@ -275,27 +275,27 @@ MediaKeySession::Update(const ArrayBuffe
       !CopyArrayBufferViewOrArrayBufferData(aResponse, data)) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     EME_LOG("MediaKeySession[%p,'%s'] Update() failed, invalid response buffer",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
 
 
-#ifdef PR_LOGGING
   // Convert response to base64 for easier logging.
   // Note: UpdateSession() Move()s the data out of the array, so we have
   // to copy it here.
   nsAutoCString base64Response;
-  nsDependentCSubstring rawResponse(reinterpret_cast<const char*>(data.Elements()),
-    data.Length());
-  if (NS_FAILED(Base64Encode(rawResponse, base64Response))) {
-    NS_WARNING("Failed to base64 encode response for logging");
+  if (EME_LOG_ENABLED()) {
+    nsDependentCSubstring rawResponse(reinterpret_cast<const char*>(data.Elements()),
+      data.Length());
+    if (NS_FAILED(Base64Encode(rawResponse, base64Response))) {
+      NS_WARNING("Failed to base64 encode response for logging");
+    }
   }
-#endif
 
   PromiseId pid = mKeys->StorePromise(promise);
   mKeys->GetCDMProxy()->UpdateSession(mSessionId,
                                       pid,
                                       data);
 
   EME_LOG("MediaKeySession[%p,'%s'] Update() sent to CDM, "
           "promiseId=%d Response(base64)='%s'",
@@ -377,28 +377,28 @@ MediaKeySession::Remove(ErrorResult& aRv
 
   return promise.forget();
 }
 
 void
 MediaKeySession::DispatchKeyMessage(MediaKeyMessageType aMessageType,
                                     const nsTArray<uint8_t>& aMessage)
 {
-#ifdef PR_LOGGING
-  nsAutoCString base64MsgData;
-  nsDependentCSubstring rawMsgData(reinterpret_cast<const char*>(aMessage.Elements()),
-                                   aMessage.Length());
-  if (NS_FAILED(Base64Encode(rawMsgData, base64MsgData))) {
-    NS_WARNING("Failed to base64 encode message for logging");
+  if (EME_LOG_ENABLED()) {
+    nsAutoCString base64MsgData;
+    nsDependentCSubstring rawMsgData(reinterpret_cast<const char*>(aMessage.Elements()),
+                                     aMessage.Length());
+    if (NS_F