Merge inbound to m-c
authorWes Kocher <wkocher@mozilla.com>
Mon, 14 Oct 2013 18:48:14 -0700
changeset 164536 ddd03c32fab13099b81e21cdd38f1a2a4565fb3b
parent 164505 608de8acc55fb180d4d2385db3a1ae05ccb68567 (current diff)
parent 164535 27921f21cddf7abbba678f62e4a348f280312454 (diff)
child 164549 23bd0deec3597094677bf4865fa129e178c31013
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 915002 - Clobber needed for webidl updates for AppNotificationServiceOptions. One more time.
\ No newline at end of file
+Bug 872934 - Clobber needed for webidl updates for style sheet change events. Again and again.
\ No newline at end of file
--- a/Makefile.in
+++ b/Makefile.in
@@ -238,10 +238,13 @@ check::
 	$(call SUBMAKE,$@,js/src)
 endif
 
 ifdef MOZ_PSEUDO_DERECURSE
 # Interdependencies for parallel export.
 js/xpconnect/src/export: dom/bindings/export
 accessible/src/xpcom/export: xpcom/xpidl/export
 js/src/export: mfbt/export
+ifdef ENABLE_CLANG_PLUGIN
+js/src/export config/export: build/clang-plugin/export
 endif
 endif
+endif
--- a/accessible/src/jsat/OutputGenerator.jsm
+++ b/accessible/src/jsat/OutputGenerator.jsm
@@ -6,18 +6,19 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const INCLUDE_DESC = 0x01;
 const INCLUDE_NAME = 0x02;
-const INCLUDE_CUSTOM = 0x04;
-const NAME_FROM_SUBTREE_RULE = 0x08;
+const INCLUDE_VALUE = 0x04;
+const INCLUDE_CUSTOM = 0x08;
+const NAME_FROM_SUBTREE_RULE = 0x10;
 
 const OUTPUT_DESC_FIRST = 0;
 const OUTPUT_DESC_LAST = 1;
 
 const ROLE_LISTITEM = Ci.nsIAccessibleRole.ROLE_LISTITEM;
 const ROLE_STATICTEXT = Ci.nsIAccessibleRole.ROLE_STATICTEXT;
 const ROLE_LINK = Ci.nsIAccessibleRole.ROLE_LINK;
 
@@ -59,18 +60,19 @@ this.OutputGenerator = {
     let addOutput = function addOutput(aAccessible) {
       output.push.apply(output, self.genForObject(aAccessible, aContext));
     };
     let ignoreSubtree = function ignoreSubtree(aAccessible) {
       let roleString = Utils.AccRetrieval.getStringRole(aAccessible.role);
       let nameRule = self.roleRuleMap[roleString] || 0;
       // Ignore subtree if the name is explicit and the role's name rule is the
       // NAME_FROM_SUBTREE_RULE.
-      return (nameRule & NAME_FROM_SUBTREE_RULE) &&
-        (Utils.getAttributes(aAccessible)['explicit-name'] === 'true');
+      return (((nameRule & INCLUDE_VALUE) && aAccessible.value) ||
+              ((nameRule & NAME_FROM_SUBTREE_RULE) &&
+               Utils.getAttributes(aAccessible)['explicit-name'] === 'true'));
     };
 
     let contextStart = this._getContextStart(aContext);
 
     if (this.outputOrder === OUTPUT_DESC_FIRST) {
       contextStart.forEach(addOutput);
       addOutput(aContext.accessible);
       [addOutput(node) for
@@ -251,19 +253,19 @@ this.OutputGenerator = {
     'pagetab': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'graphic': INCLUDE_DESC,
     'pushbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'checkbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'radiobutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'buttondropdown': NAME_FROM_SUBTREE_RULE,
     'combobox': INCLUDE_DESC,
     'droplist': INCLUDE_DESC,
-    'progressbar': INCLUDE_DESC,
-    'slider': INCLUDE_DESC,
-    'spinbutton': INCLUDE_DESC,
+    'progressbar': INCLUDE_DESC | INCLUDE_VALUE,
+    'slider': INCLUDE_DESC | INCLUDE_VALUE,
+    'spinbutton': INCLUDE_DESC | INCLUDE_VALUE,
     'diagram': INCLUDE_DESC,
     'animation': INCLUDE_DESC,
     'equation': INCLUDE_DESC,
     'buttonmenu': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'buttondropdowngrid': NAME_FROM_SUBTREE_RULE,
     'pagetablist': INCLUDE_DESC,
     'canvas': INCLUDE_DESC,
     'check menu item': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
@@ -273,17 +275,17 @@ this.OutputGenerator = {
     'radio menu item': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'table column header': NAME_FROM_SUBTREE_RULE,
     'table row header': NAME_FROM_SUBTREE_RULE,
     'tear off menu item': NAME_FROM_SUBTREE_RULE,
     'toggle button': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'parent menuitem': NAME_FROM_SUBTREE_RULE,
     'header': INCLUDE_DESC,
     'footer': INCLUDE_DESC,
-    'entry': INCLUDE_DESC | INCLUDE_NAME,
+    'entry': INCLUDE_DESC | INCLUDE_NAME | INCLUDE_VALUE,
     'caption': INCLUDE_DESC,
     'document frame': INCLUDE_DESC,
     'heading': INCLUDE_DESC,
     'calendar': INCLUDE_DESC | INCLUDE_NAME,
     'combobox list': INCLUDE_DESC,
     'combobox option': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'listbox option': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'listbox rich option': NAME_FROM_SUBTREE_RULE,
@@ -304,35 +306,46 @@ this.OutputGenerator = {
       if (aFlags & INCLUDE_DESC) {
         let desc = this._getLocalizedStates(aStates);
         let roleStr = this._getLocalizedRole(aRoleStr);
         if (roleStr)
           desc.push(roleStr);
         output.push(desc.join(' '));
       }
 
+      if (aFlags & INCLUDE_VALUE) {
+        let value = aAccessible.value;
+        if (value) {
+          output[this.outputOrder === OUTPUT_DESC_FIRST ?
+                 'push' : 'unshift'](value);
+        }
+      }
+
       this._addName(output, aAccessible, aFlags);
       this._addLandmark(output, aAccessible);
 
       return output;
     },
 
-    entry: function entry(aAccessible, aRoleStr, aStates, aFlags) {
-      let output = [];
-      let desc = this._getLocalizedStates(aStates);
-      desc.push(this._getLocalizedRole(
-                  (aStates.ext & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE) ?
-                    'textarea' : 'entry'));
+    label: function label(aAccessible, aRoleStr, aStates, aFlags, aContext) {
+      if (aContext.isNestedControl ||
+          aContext.accessible == Utils.getEmbeddedControl(aAccessible)) {
+        // If we are on a nested control, or a nesting label,
+        // we don't need the context.
+        return [];
+      }
 
-      output.push(desc.join(' '));
+      return this.objectOutputFunctions.defaultFunc.apply(this, arguments);
+    },
 
-      this._addName(output, aAccessible, aFlags);
-      this._addLandmark(output, aAccessible);
-
-      return output;
+    entry: function entry(aAccessible, aRoleStr, aStates, aFlags) {
+      let rolestr = (aStates.ext & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE) ?
+            'textarea' : 'entry';
+      return this.objectOutputFunctions.defaultFunc.apply(
+        this, [aAccessible, rolestr, aStates, aFlags]);
     },
 
     pagetab: function pagetab(aAccessible, aRoleStr, aStates, aFlags) {
       let localizedRole = this._getLocalizedRole(aRoleStr);
       let itemno = {};
       let itemof = {};
       aAccessible.groupPosition({}, itemof, itemno);
       let output = [];
--- a/accessible/src/jsat/Presentation.jsm
+++ b/accessible/src/jsat/Presentation.jsm
@@ -158,30 +158,31 @@ VisualPresenter.prototype = {
       };
     }
 
     return null;
   },
 
   pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
     this._displayedAccessibles.set(aContext.accessible.document.window,
-                                   { accessible: aContext.accessible,
+                                   { accessible: aContext.accessibleForBounds,
                                      startOffset: aContext.startOffset,
                                      endOffset: aContext.endOffset });
 
-    if (!aContext.accessible)
+    if (!aContext.accessibleForBounds)
       return {type: this.type, details: {method: 'hideBounds'}};
 
     try {
-      aContext.accessible.scrollTo(
+      aContext.accessibleForBounds.scrollTo(
         Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
 
       let bounds = (aContext.startOffset === -1 && aContext.endOffset === -1) ?
-                   aContext.bounds : Utils.getTextBounds(aContext.accessible,
-                                     aContext.startOffset, aContext.endOffset);
+            aContext.bounds : Utils.getTextBounds(aContext.accessibleForBounds,
+                                                  aContext.startOffset,
+                                                  aContext.endOffset);
 
       return {
         type: this.type,
         details: {
           method: 'showBounds',
           bounds: bounds,
           padding: this.BORDER_PADDING
         }
--- a/accessible/src/jsat/TraversalRules.jsm
+++ b/accessible/src/jsat/TraversalRules.jsm
@@ -43,50 +43,65 @@ const ROLE_SLIDER = Ci.nsIAccessibleRole
 const ROLE_HEADING = Ci.nsIAccessibleRole.ROLE_HEADING;
 const ROLE_HEADER = Ci.nsIAccessibleRole.ROLE_HEADER;
 const ROLE_TERM = Ci.nsIAccessibleRole.ROLE_TERM;
 const ROLE_SEPARATOR = Ci.nsIAccessibleRole.ROLE_SEPARATOR;
 const ROLE_TABLE = Ci.nsIAccessibleRole.ROLE_TABLE;
 const ROLE_INTERNAL_FRAME = Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME;
 const ROLE_PARAGRAPH = Ci.nsIAccessibleRole.ROLE_PARAGRAPH;
 const ROLE_SECTION = Ci.nsIAccessibleRole.ROLE_SECTION;
+const ROLE_LABEL = Ci.nsIAccessibleRole.ROLE_LABEL;
 
 this.EXPORTED_SYMBOLS = ['TraversalRules'];
 
 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
 let gSkipEmptyImages = new PrefCache('accessibility.accessfu.skip_empty_images');
 
 function BaseTraversalRule(aRoles, aMatchFunc) {
+  this._explicitMatchRoles = new Set(aRoles);
   this._matchRoles = aRoles;
-  this._matchFunc = aMatchFunc;
+  if (aRoles.indexOf(ROLE_LABEL) < 0) {
+    this._matchRoles.push(ROLE_LABEL);
+  }
+  this._matchFunc = aMatchFunc || function (acc) { return FILTER_MATCH; };
 }
 
 BaseTraversalRule.prototype = {
     getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
       aRules.value = this._matchRoles;
       return aRules.value.length;
     },
 
     preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
     Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE |
     Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN,
 
     match: function BaseTraversalRule_match(aAccessible)
     {
-      if (aAccessible.role == ROLE_INTERNAL_FRAME) {
+      let role = aAccessible.role;
+      if (role == ROLE_INTERNAL_FRAME) {
         return (Utils.getMessageManager(aAccessible.DOMNode)) ?
           FILTER_MATCH  | FILTER_IGNORE_SUBTREE : FILTER_IGNORE;
       }
 
-      if (this._matchFunc)
-        return this._matchFunc(aAccessible);
+      let matchResult = this._explicitMatchRoles.has(role) ?
+          this._matchFunc(aAccessible) : FILTER_IGNORE;
 
-      return FILTER_MATCH;
+      // If we are on a label that nests a checkbox/radio we should land on it.
+      // It is a bigger touch target, and it reduces clutter.
+      if (role == ROLE_LABEL && !(matchResult & FILTER_IGNORE_SUBTREE)) {
+        let control = Utils.getEmbeddedControl(aAccessible);
+        if (control && this._explicitMatchRoles.has(control.role)) {
+          matchResult = this._matchFunc(control) | FILTER_IGNORE_SUBTREE;
+        }
+      }
+
+      return matchResult;
     },
 
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessibleTraversalRule])
 };
 
 var gSimpleTraversalRoles =
   [ROLE_MENUITEM,
    ROLE_LINK,
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -9,16 +9,18 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 const EVENT_STATE_CHANGE = Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE;
 
 const ROLE_CELL = Ci.nsIAccessibleRole.ROLE_CELL;
 const ROLE_COLUMNHEADER = Ci.nsIAccessibleRole.ROLE_COLUMNHEADER;
 const ROLE_ROWHEADER = Ci.nsIAccessibleRole.ROLE_ROWHEADER;
 
+const RELATION_LABEL_FOR = Ci.nsIAccessibleRelation.RELATION_LABEL_FOR;
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, 'Services',
   'resource://gre/modules/Services.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Rect',
   'resource://gre/modules/Geometry.jsm');
 
 this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext', 'PrefCache'];
 
@@ -286,16 +288,30 @@ this.Utils = {
     ];
     let roles = this.getAttributes(aAccessible)['xml-roles'];
     if (!roles) {
       return;
     }
 
     // Looking up a role that would match a landmark.
     return this.matchAttributeValue(roles, landmarks);
+  },
+
+  getEmbeddedControl: function getEmbeddedControl(aLabel) {
+    if (aLabel) {
+      let relation = aLabel.getRelationByType(RELATION_LABEL_FOR);
+      for (let i = 0; i < relation.targetsCount; i++) {
+        let target = relation.getTarget(i);
+        if (target.parent === aLabel) {
+          return target;
+        }
+      }
+    }
+
+    return null;
   }
 };
 
 this.Logger = {
   DEBUG: 0,
   INFO: 1,
   WARNING: 2,
   ERROR: 3,
@@ -420,38 +436,55 @@ this.Logger = {
     for (var i=0; i < aAccessible.childCount; i++)
       this._dumpTreeInternal(aLogLevel, aAccessible.getChildAt(i), aIndent + 1);
     }
 };
 
 /**
  * PivotContext: An object that generates and caches context information
  * for a given accessible and its relationship with another accessible.
+ *
+ * If the given accessible is a label for a nested control, then this
+ * context will represent the nested control instead of the label.
+ * With the exception of bounds calculation, which will use the containing
+ * label. In this case the |accessible| field would be the embedded control,
+ * and the |accessibleForBounds| field would be the label.
  */
 this.PivotContext = function PivotContext(aAccessible, aOldAccessible,
   aStartOffset, aEndOffset, aIgnoreAncestry = false,
   aIncludeInvisible = false) {
   this._accessible = aAccessible;
+  this._nestedControl = Utils.getEmbeddedControl(aAccessible);
   this._oldAccessible =
     this._isDefunct(aOldAccessible) ? null : aOldAccessible;
   this.startOffset = aStartOffset;
   this.endOffset = aEndOffset;
   this._ignoreAncestry = aIgnoreAncestry;
   this._includeInvisible = aIncludeInvisible;
 }
 
 PivotContext.prototype = {
   get accessible() {
-    return this._accessible;
+    // If the current pivot accessible has a nested control,
+    // make this context use it publicly.
+    return this._nestedControl || this._accessible;
   },
 
   get oldAccessible() {
     return this._oldAccessible;
   },
 
+  get isNestedControl() {
+    return !!this._nestedControl;
+  },
+
+  get accessibleForBounds() {
+    return this._accessible;
+  },
+
   get textAndAdjustedOffsets() {
     if (this.startOffset === -1 && this.endOffset === -1) {
       return null;
     }
 
     if (!this._textAndAdjustedOffsets) {
       let result = {startOffset: this.startOffset,
                     endOffset: this.endOffset,
@@ -516,17 +549,17 @@ PivotContext.prototype = {
   },
 
   /**
    * A list of the current accessible's ancestry.
    */
   get currentAncestry() {
     if (!this._currentAncestry) {
       this._currentAncestry = this._ignoreAncestry ? [] :
-        this._getAncestry(this._accessible);
+        this._getAncestry(this.accessible);
     }
     return this._currentAncestry;
   },
 
   /*
    * This is a list of the accessible's ancestry up to the common ancestor
    * of the accessible and the old accessible. It is useful for giving the
    * user context as to where they are in the heirarchy.
@@ -577,17 +610,17 @@ PivotContext.prototype = {
    * list of the accessible's subtree in pre or post order.
    * It only includes the accessible's visible chidren.
    * @param {boolean} aPreorder A flag for traversal order. If true, traverse
    * in preorder; if false, traverse in postorder.
    * @param {function} aStop An optional function, indicating whether subtree
    * traversal should stop.
    */
   subtreeGenerator: function subtreeGenerator(aPreorder, aStop) {
-    return this._traverse(this._accessible, aPreorder, aStop);
+    return this._traverse(this.accessible, aPreorder, aStop);
   },
 
   getCellInfo: function getCellInfo(aAccessible) {
     if (!this._cells) {
       this._cells = new WeakMap();
     }
 
     let domNode = aAccessible.DOMNode;
@@ -672,17 +705,17 @@ PivotContext.prototype = {
     }
 
     this._cells.set(domNode, cellInfo);
     return cellInfo;
   },
 
   get bounds() {
     if (!this._bounds) {
-      this._bounds = Utils.getBounds(this._accessible);
+      this._bounds = Utils.getBounds(this.accessibleForBounds);
     }
 
     return this._bounds.clone();
   },
 
   _isDefunct: function _isDefunct(aAccessible) {
     try {
       let extstate = {};
--- a/accessible/src/jsat/content-script.js
+++ b/accessible/src/jsat/content-script.js
@@ -168,16 +168,21 @@ function activateCurrent(aMessage) {
         aAccessible.role != Ci.nsIAccessibleRole.ROLE_KEY) {
       // Only activate keys, don't do anything on other objects.
       return;
     }
 
     if (aAccessible.actionCount > 0) {
       aAccessible.doAction(0);
     } else {
+      let control = Utils.getEmbeddedControl(aAccessible);
+      if (control && control.actionCount > 0) {
+        control.doAction(0);
+      }
+
       // XXX Some mobile widget sets do not expose actions properly
       // (via ARIA roles, etc.), so we need to generate a click.
       // Could possibly be made simpler in the future. Maybe core
       // engine could expose nsCoreUtiles::DispatchMouseEvent()?
       let docAcc = Utils.AccRetrieval.getAccessibleFor(content.document);
       let docX = {}, docY = {}, docW = {}, docH = {};
       docAcc.getBounds(docX, docY, docW, docH);
 
--- a/accessible/tests/mochitest/jsat/output.js
+++ b/accessible/tests/mochitest/jsat/output.js
@@ -20,17 +20,19 @@ Cu.import("resource://gre/modules/access
 function testContextOutput(expected, aAccOrElmOrID, aOldAccOrElmOrID, aGenerator) {
   aOldAccOrElmOrID = aOldAccOrElmOrID || "root";
   var accessible = getAccessible(aAccOrElmOrID);
   var oldAccessible = getAccessible(aOldAccOrElmOrID);
   var context = new PivotContext(accessible, oldAccessible);
   var output = aGenerator.genForContext(context).output;
 
   isDeeply(output, expected,
-    "Context output is correct for " + aAccOrElmOrID);
+           "Context output is correct for " + aAccOrElmOrID +
+           " (output: " + output.join(", ") + ") ==" +
+           " (expected: " + expected.join(", ") + ")");
 }
 
 /**
  * Test object output generated array that includes names.
  * Note: test ignores outputs without the name.
  *
  * @param aAccOrElmOrID identifier to get an accessible to test.
  * @param aGenerator    the output generator to use when generating accessible
--- a/accessible/tests/mochitest/jsat/test_braille.html
+++ b/accessible/tests/mochitest/jsat/test_braille.html
@@ -51,16 +51,22 @@ https://bugzilla.mozilla.org/show_bug.cg
           accOrElmOrID: "ul_li_one",
           expected: [["*", "ul item 1"], ["*", "ul item 1"]]
         },{
           accOrElmOrID: "ol_li_one",
           expected: [["1.", "ol item 1"], ["1.", "ol item 1"]]
         },{
           accOrElmOrID: "textarea",
           expected: [["txtarea", "Here lies treasure."], ["Here lies treasure.", "txtarea"]]
+        },{
+          accOrElmOrID: "textentry",
+          expected: [["entry", "Mario", "First name:"], ["First name:", "Mario", "entry"]]
+        },{
+          accOrElmOrID: "range",
+          expected: [["slider", "3", "Points:"], ["Points:", "3", "slider"]]
         }];
 
         // Test all possible braille order preference values.
         tests.forEach(function run(test) {
           var brailleOrderValues = [0, 1];
           brailleOrderValues.forEach(
             function testBrailleOrder(brailleOrder) {
               SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, brailleOrder);
@@ -104,11 +110,13 @@ https://bugzilla.mozilla.org/show_bug.cg
         <li id="ul_li_one">ul item 1</li>
         <li id="ul_li_two">ul item 2</li>
         <li id="ul_li_three">ul item 3</li>
         <li id="ul_li_three">ul item 4</li>
       </ul>
       <textarea id="textarea" cols="80" rows="5">
         Here lies treasure.
       </textarea>
+      <label>First name: <input id="textentry" value="Mario"></label>
+      <label>Points: <input id="range" type="range" name="points" min="1" max="10" value="3"></label>
    </div>
   </body>
 </html>
--- a/accessible/tests/mochitest/jsat/test_explicit_names.html
+++ b/accessible/tests/mochitest/jsat/test_explicit_names.html
@@ -23,17 +23,17 @@
       }, {
         accOrElmOrID: "button1",
         expected: ["button", "Press me"]
       }, {
         accOrElmOrID: "button2",
         expected: ["button", "Press me"]
       }, {
         accOrElmOrID: "textarea1",
-        expected: ["text area", "Test Text Area", "This is the text area text."]
+        expected: ["text area", "This is the text area text.", "Test Text Area"]
       }, {
         accOrElmOrID: "textarea2",
         expected: ["text area", "This is the text area text."]
       }, {
         accOrElmOrID: "heading1",
         expected: ["heading level 1", "Test heading", "This is the heading."]
       }, {
         accOrElmOrID: "heading2",
--- a/accessible/tests/mochitest/jsat/test_utterance_order.html
+++ b/accessible/tests/mochitest/jsat/test_utterance_order.html
@@ -134,16 +134,66 @@ https://bugzilla.mozilla.org/show_bug.cg
           accOrElmOrID: 'tab1',
           expected: [['tab list', 'selected tab 1 of 2', 'Account'],
             ['Account', 'selected tab 1 of 2', 'tab list']]
         }, {
           // Test unselected tab
           accOrElmOrID: 'tab2',
           expected: [['tab list', 'tab 2 of 2', 'Advanced'],
             ['Advanced', 'tab 2 of 2', 'tab list']]
+        },
+
+        {
+          // Landing on this label should mimic landing on the checkbox.
+          accOrElmOrID: "label1",
+          expected: [['not checked check button', 'Orange'],
+                     ['Orange', 'not checked check button']]
+        },
+        {
+          // Here we get a top-level view of the form.
+          accOrElmOrID: "form1",
+          expected: [['label', 'not checked check button', 'Orange', 'Orange',
+                      'not checked check button', 'Blue', 'label', 'Blue'],
+                     ['Orange', 'not checked check button', 'Orange', 'label',
+                      'Blue', 'not checked check button', 'Blue', 'label']]
+        },
+        {
+          // This is a non-nesting label.
+          accOrElmOrID: "label2",
+          expected: [['label', 'Blue'], ['Blue', 'label']]
+        },
+        {
+          // This is a distinct control.
+          accOrElmOrID: "input2",
+          expected: [['not checked check button', 'Blue'],
+                     ['Blue', 'not checked check button']]
+        },
+        {
+          // This is a nested control.
+          accOrElmOrID: "input1",
+          expected: [['not checked check button', 'Orange'],
+                     ['Orange', 'not checked check button']]
+        },
+        {
+          // Landing on this label should mimic landing on the entry.
+          accOrElmOrID: "label3",
+          expected: [['entry', 'Joe', 'First name:'],
+                     ['First name:', 'Joe', 'entry']]
+        },
+        {
+          // This is a nested control with a value.
+          accOrElmOrID: "input3",
+          expected: [['entry', 'Joe', 'First name:'],
+                     ['First name:', 'Joe', 'entry']]
+        },
+        {
+          // This is a nested control with a value.
+          accOrElmOrID: "input4",
+          expected: [['slider', '3', 'Points:'],
+                     ['Points:', '3', 'slider']]
         }];
 
         // Test all possible utterance order preference values.
         tests.forEach(function run(test) {
           var utteranceOrderValues = [0, 1];
           utteranceOrderValues.forEach(
             function testUtteranceOrder(utteranceOrder) {
               SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, utteranceOrder);
@@ -213,11 +263,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       <button id="expandedButton" aria-expanded="true">I am expanded</button>
       <button id="collapsedButton" aria-expanded="false">I am collapsed</button>
       <input id="requiredInput" required placeholder="I am required" />
       <button id="hasPopupButton" aria-haspopup="true">I have a popup</button>
       <div role="tablist">
         <a id="tab1" href="#" role="tab" aria-selected="true">Account</a>
         <a id="tab2" href="#" role="tab" aria-selected="false">Advanced</a>
       </div>
+      <form id="form1">
+        <label id="label1"><input id="input1" type="checkbox">Orange</label>
+        <input id="input2" type="checkbox"><label id="label2" for="input2">Blue</label>
+      </form>
+      <label id="label3">First name: <input id="input3" value="Joe"></label>
+      <label id="label4">Points:
+        <input id="input4" type="range" name="points" min="1" max="10" value="3">
+      </label>
     </div>
   </body>
 </html>
--- a/addon-sdk/Makefile.in
+++ b/addon-sdk/Makefile.in
@@ -1,16 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include $(topsrcdir)/config/config.mk
 
-export PYMAKE = $(.PYMAKE)
-
 libs::
 	$(PYTHON) $(srcdir)/copy_source.py $(topsrcdir) $(srcdir)/source/lib $(FINAL_TARGET)/modules/commonjs >copy_source.mk
 	$(MAKE) -f copy_source.mk libs
 
 include $(topsrcdir)/config/rules.mk
 
 TEST_FILES = \
   source/app-extension \
--- a/addon-sdk/copy_source.py
+++ b/addon-sdk/copy_source.py
@@ -1,38 +1,21 @@
 # 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/.
 
 import os
-import posixpath
 import sys
 
-
-def normpath(path):
-    """Ensure UNIX style paths are used with GNU make on Windows.
-
-    This can be removed once we no longer support GNU make on Windows (bug
-    828317).
-    """
-    if os.environ.get('PYMAKE') or os.name not in ('nt', 'ce'):
-        return path
-
-    if len(path) > 2 and path[1] == ':':
-        path = '/' + path[0] + path[2:]
-
-    return posixpath.normpath(path)
-
-
 if len(sys.argv) != 4:
     print >> sys.stderr, "Usage: copy_source.py " \
                          "<topsrcdir> <source directory> <target directory>"
     sys.exit(1)
 
-topsrcdir = normpath(sys.argv[1])
+topsrcdir = sys.argv[1]
 source_dir = sys.argv[2]
 target_dir = sys.argv[3]
 
 print """
 DEPTH     = ..
 topsrcdir = %(topsrcdir)s
 srcdir    = %(topsrcdir)s/addon-sdk
 VPATH     = %(topsrcdir)s/addon-sdk
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -166,20 +166,20 @@ AC_DEFUN([MOZ_COMPILER_OPTS],
   MOZ_ARG_ENABLE_BOOL(release,
   [  --enable-release        Build with more conservative, release engineering-oriented options.
                           This may slow down builds.],
       DEVELOPER_OPTIONS=,
       DEVELOPER_OPTIONS=1)
 
   AC_SUBST(DEVELOPER_OPTIONS)
 
-  if test -n "$DEVELOPER_OPTIONS" -a "${MOZ_PSEUDO_DERECURSE-unset}" = unset; then
+  if test "${MOZ_PSEUDO_DERECURSE-unset}" = unset; then
     dnl Don't enable on pymake, because of bug 918652. Bug 912979 is an annoyance
     dnl with pymake, too.
-    MOZ_PSEUDO_DERECURSE=no-parallel-export,no-pymake,no-skip
+    MOZ_PSEUDO_DERECURSE=no-pymake,no-skip
   fi
 
   MOZ_DEBUGGING_OPTS
   MOZ_RTTI
 if test "$CLANG_CXX"; then
     ## We disable return-type-c-linkage because jsval is defined as a C++ type but is
     ## returned by C functions. This is possible because we use knowledge about the ABI
     ## to typedef it to a C type with the same layout when the headers are included
--- a/build/clang-plugin/Makefile.in
+++ b/build/clang-plugin/Makefile.in
@@ -36,16 +36,21 @@ all: $(PLUGIN) $(TESTS)
 
 TESTFLAGS := -fsyntax-only -Xclang -verify \
 	-Xclang -load -Xclang $(CURDIR)/$(PLUGIN) \
 	-Xclang -add-plugin -Xclang moz-check
 
 $(TESTS): test-%: tests/%.cpp $(PLUGIN)
 	$(CXX) $(TESTFLAGS) $<
 
-compile binaries libs export tools: all
+compile libs export tools: all
 
 distclean clean:
 	rm -f $(OBJS) $(TESTS) $(PLUGIN)
 
 check:
 
-.PHONY: compile binaries libs export tools distclean clean check
+libs: binaries
+
+binaries: all
+	@touch $@
+
+.PHONY: compile libs export tools distclean clean check
--- a/content/events/test/test_all_synthetic_events.html
+++ b/content/events/test/test_all_synthetic_events.html
@@ -348,24 +348,27 @@ const kEventConstructors = {
                                              },
   StorageEvent:                              { create: function (aName, aProps) {
                                                          return new StorageEvent(aName, aProps);
                                                        },
                                              },
   StyleRuleChangeEvent:                      { create: function (aName, aProps) {
                                                          return new StyleRuleChangeEvent(aName, aProps);
                                                        },
+                                               chromeOnly: true,
                                              },
   StyleSheetApplicableStateChangeEvent:      { create: function (aName, aProps) {
                                                          return new StyleSheetApplicableStateChangeEvent(aName, aProps);
                                                        },
+                                               chromeOnly: true,
                                              },
   StyleSheetChangeEvent:                     { create: function (aName, aProps) {
                                                          return new StyleSheetChangeEvent(aName, aProps);
                                                        },
+                                               chromeOnly: true,
                                              },
   SVGZoomEvent:                              { create: function (aName, aProps) {
                                                          var e = document.createEvent("svgzoomevent");
                                                          e.initUIEvent(aName, aProps.bubbles, aProps.cancelable,
                                                                        aProps.view, aProps.detail);
                                                          return e;
                                                        },
                                              },
@@ -405,16 +408,26 @@ const kEventConstructors = {
                                                        },
                                              },
   WheelEvent:                                { create: function (aName, aProps) {
                                                          return new WheelEvent(aName, aProps);
                                                        },
                                              },
 };
 
+for (var name of Object.keys(kEventConstructors)) {
+  if (!kEventConstructors[name].chromeOnly) {
+    continue;
+  }
+  if (window[name]) {
+    ok(false, name + " should be chrome only.");
+  }
+  window[name] = SpecialPowers.unwrap(SpecialPowers.wrap(window)[name]);
+}
+
 var props = Object.getOwnPropertyNames(window);
 for (var i = 0; i < props.length; i++) {
   // Assume that event object must be named as "FooBarEvent".
   if (!props[i].match(/^([A-Z][a-zA-Z]+)?Event$/)) {
     continue;
   }
   if (!kEventConstructors[props[i]]) {
     ok(false, "Unknown event found: " + props[i]);
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -865,16 +865,22 @@ DOMInterfaces = {
     'resultNotAddRefed': [ 'item' ]
 },
 
 'PannerNode': [
 {
     'resultNotAddRefed': [ 'coneGain', 'distanceGain' ],
 }],
 
+'PeerConnectionImpl': {
+    'nativeType': 'sipcc::PeerConnectionImpl',
+    'headerFile': 'PeerConnectionImpl.h',
+    'wrapperCache': False
+},
+
 'Performance': {
     'nativeType': 'nsPerformance',
     'resultNotAddRefed': [ 'timing', 'navigation' ]
 },
 
 'PerformanceTiming': {
     'nativeType': 'nsPerformanceTiming',
     'headerFile': 'nsPerformance.h'
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -71,16 +71,18 @@ CPPSRCS = \
 endif
 
 LOCAL_INCLUDES += -I$(topsrcdir)/js/xpconnect/src \
   -I$(topsrcdir)/js/xpconnect/wrappers \
   -I$(topsrcdir)/js/ipc \
   -I$(topsrcdir)/content/canvas/src \
   -I$(topsrcdir)/content/html/content/src \
   -I$(topsrcdir)/media/webrtc/signaling/src/peerconnection \
+  -I$(topsrcdir)/media/webrtc/signaling/src/common/time_profiling \
+  -I$(topsrcdir)/media/mtransport \
   -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/dom/battery \
   -I$(topsrcdir)/dom/indexedDB \
   -I$(topsrcdir)/content/xslt/src/base \
   -I$(topsrcdir)/content/xslt/src/xpath \
   -I$(topsrcdir)/content/xml/content/src \
   -I$(topsrcdir)/content/xul/content/src \
   -I$(topsrcdir)/content/xul/document/src \
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -5,21 +5,23 @@
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
+const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1";
 const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1";
 const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1";
 const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1";
 
 const PC_CID = Components.ID("{9878b414-afaa-4176-a887-1e02b3b047c2}");
+const PC_OBS_CID = Components.ID("{1d44a18e-4545-4ff3-863d-6dbd6234a583}");
 const PC_ICE_CID = Components.ID("{02b9970c-433d-4cc2-923d-f7028ac66073}");
 const PC_SESSION_CID = Components.ID("{1775081b-b62d-4954-8ffe-a067bbf508a7}");
 const PC_MANAGER_CID = Components.ID("{7293e901-2be3-4c02-b4bd-cbef6fc24f78}");
 
 // Global list of PeerConnection objects, so they can be cleaned up when
 // a page is torn down. (Maps inner window ID to an array of PC objects).
 function GlobalPCList() {
   this._list = [];
@@ -184,18 +186,17 @@ function RTCPeerConnection() {
   this._ongatheringchange = null;
   this._onicechange = null;
 }
 RTCPeerConnection.prototype = {
   classDescription: "mozRTCPeerConnection",
   classID: PC_CID,
   contractID: PC_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer,
-                                         Ci.nsISupportsWeakReference]),
+                                         Ci.nsIDOMGlobalPropertyInitializer]),
   init: function(win) { this._win = win; },
 
   __init: function(rtcConfig) {
     this._trickleIce = Services.prefs.getBoolPref("media.peerconnection.trickle_ice");
     if (!rtcConfig.iceServers ||
         !Services.prefs.getBoolPref("media.peerconnection.use_document_iceservers")) {
       rtcConfig = {iceServers:
         JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers"))};
@@ -212,33 +213,37 @@ RTCPeerConnection.prototype = {
     this.makeGetterSetterEH("onnegotiationneeded");
     this.makeGetterSetterEH("onsignalingstatechange");
     this.makeGetterSetterEH("onremovestream");
     this.makeGetterSetterEH("ondatachannel");
     this.makeGetterSetterEH("onconnection");
     this.makeGetterSetterEH("onclosedconnection");
     this.makeGetterSetterEH("oniceconnectionstatechange");
 
-    this._pc = Cc["@mozilla.org/peerconnection;1"].
-             createInstance(Ci.IPeerConnection);
-    this._observer = new PeerConnectionObserver(this);
+    this._pc = new this._win.PeerConnectionImpl();
+    this._observer = new this._win.PeerConnectionObserver(this);
     this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
 
     // Add a reference to the PeerConnection to global list (before init).
     _globalPCList.addPC(this);
 
     this._queueOrRun({
-      func: this._getPC().initialize,
-      args: [this._observer, this._win, rtcConfig, Services.tm.currentThread],
+      func: this._initialize,
+      args: [rtcConfig],
       // If not trickling, suppress start.
       wait: !this._trickleIce
     });
   },
 
+  _initialize: function(rtcConfig) {
+    this._getPC().initialize(this._observer, this._win, rtcConfig,
+                             Services.tm.currentThread);
+  },
+
   _getPC: function() {
     if (!this._pc) {
       throw new this._win.DOMError("",
           "RTCPeerConnection is gone (did you enter Offline mode?)");
     }
     return this._pc;
   },
 
@@ -296,16 +301,19 @@ RTCPeerConnection.prototype = {
       let ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
       try {
         return ios.newURI(uriStr, null, null);
       } catch (e if (e.result == Cr.NS_ERROR_MALFORMED_URI)) {
         throw new errorCtor("", errorMsg + " - malformed URI: " + uriStr);
       }
     }
     function mustValidateServer(server) {
+      if (!server.url) {
+        throw new errorCtor("", errorMsg + " - missing url");
+      }
       let url = nicerNewURI(server.url, errorMsg);
       if (url.scheme in { turn:1, turns:1 }) {
         if (!server.username) {
           throw new errorCtor("", errorMsg + " - missing username: " + server.url);
         }
         if (!server.credential) {
           throw new errorCtor("", errorMsg + " - missing credential: " +
                               server.url);
@@ -319,85 +327,54 @@ RTCPeerConnection.prototype = {
       let len = rtcConfig.iceServers.length;
       for (let i=0; i < len; i++) {
         mustValidateServer (rtcConfig.iceServers[i], errorMsg);
       }
     }
   },
 
   /**
-   * Constraints look like this:
+   * MediaConstraints look like this:
    *
    * {
    *   mandatory: {"OfferToReceiveAudio": true, "OfferToReceiveVideo": true },
    *   optional: [{"VoiceActivityDetection": true}, {"FooBar": 10}]
    * }
    *
-   * We check for basic structure of constraints and the validity of
-   * mandatory constraints against those we support (fail if we don't).
-   * Unknown optional constraints may be of any type.
+   * WebIDL normalizes the top structure for us, but the mandatory constraints
+   * member comes in as a raw object so we can detect unknown constraints.
+   * We compare its members against ones we support, and fail if not found.
    */
   _mustValidateConstraints: function(constraints, errorMsg) {
-    function isObject(obj) {
-      return obj && (typeof obj === "object");
-    }
-    function isArraylike(obj) {
-      return isObject(obj) && ("length" in obj);
-    }
-    const SUPPORTED_CONSTRAINTS = {
-      OfferToReceiveAudio:1,
-      OfferToReceiveVideo:1,
-      MozDontOfferDataChannel:1
-    };
-    const OTHER_KNOWN_CONSTRAINTS = {
-      VoiceActivityDetection:1,
-      IceTransports:1,
-      RequestIdentity:1
-    };
-    // Parse-aid: Testing for pilot error of missing outer block avoids
-    // otherwise silent no-op since both mandatory and optional are optional
-    if (!isObject(constraints) || Array.isArray(constraints)) {
-      throw new this._win.DOMError("", errorMsg);
-    }
     if (constraints.mandatory) {
-      // Testing for pilot error of using [] on mandatory here throws nicer msg
-      // (arrays would throw in loop below regardless but with more cryptic msg)
-      if (!isObject(constraints.mandatory) || Array.isArray(constraints.mandatory)) {
-        throw new this._win.DOMError("",
-            errorMsg + " - malformed mandatory constraints");
+      let supported;
+      try {
+        // Passing the raw constraints.mandatory here validates its structure
+        supported = this._observer.getSupportedConstraints(constraints.mandatory);
+      } catch (e) {
+        throw new this._win.DOMError("", errorMsg + " - " + e.message);
       }
-      for (let constraint in constraints.mandatory) {
-        if (!(constraint in SUPPORTED_CONSTRAINTS) &&
-            constraints.mandatory.hasOwnProperty(constraint)) {
-          throw new this._win.DOMError("", errorMsg + " - " +
-              ((constraint in OTHER_KNOWN_CONSTRAINTS)? "unsupported" : "unknown") +
-              " mandatory constraint: " + constraint);
+
+      for (let constraint of Object.keys(constraints.mandatory)) {
+        if (!(constraint in supported)) {
+          throw new this._win.DOMError("",
+              errorMsg + " - unknown mandatory constraint: " + constraint);
         }
       }
     }
     if (constraints.optional) {
-      if (!isArraylike(constraints.optional)) {
-        throw new this._win.DOMError("",
-            errorMsg + " - malformed optional constraint array");
-      }
       let len = constraints.optional.length;
-      for (let i = 0; i < len; i += 1) {
-        if (!isObject(constraints.optional[i])) {
-          throw new this._win.DOMError("", errorMsg +
-              " - malformed optional constraint: " + constraints.optional[i]);
-        }
+      for (let i = 0; i < len; i++) {
         let constraints_per_entry = 0;
-        for (let constraint in constraints.optional[i]) {
-          if (constraints.optional[i].hasOwnProperty(constraint)) {
-            if (constraints_per_entry) {
-              throw new this._win.DOMError("", errorMsg +
-                  " - optional constraint must be single key/value pair");
-            }
-            constraints_per_entry += 1;
+        for (let constraint in Object.keys(constraints.optional[i])) {
+          if (constraints_per_entry) {
+            throw new this._win.DOMError("", errorMsg +
+                " - optional constraint must be single key/value pair");
           }
+          constraints_per_entry += 1;
         }
       }
     }
   },
 
   // Ideally, this should be of the form _checkState(state),
   // where the state is taken from an enumeration containing
   // the valid peer connection states defined in the WebRTC
@@ -488,21 +465,21 @@ RTCPeerConnection.prototype = {
     }
     if (!onError) {
       this.deprecated("calling createOffer without failureCallback");
     }
     this._mustValidateConstraints(constraints, "createOffer passed invalid constraints");
     this._onCreateOfferSuccess = onSuccess;
     this._onCreateOfferFailure = onError;
 
-    this._queueOrRun({
-      func: this._getPC().createOffer,
-      args: [constraints],
-      wait: true
-    });
+    this._queueOrRun({ func: this._createOffer, args: [constraints], wait: true });
+  },
+
+  _createOffer: function(constraints) {
+    this._getPC().createOffer(constraints);
   },
 
   _createAnswer: function(onSuccess, onError, constraints, provisional) {
     if (!onError) {
       this.deprecated("calling createAnswer without failureCallback");
     }
     this._onCreateAnswerSuccess = onSuccess;
     this._onCreateAnswerFailure = onError;
@@ -562,23 +539,27 @@ RTCPeerConnection.prototype = {
       case "pranswer":
         throw new this._win.DOMError("", "pranswer not yet implemented");
       default:
         throw new this._win.DOMError("",
             "Invalid type " + desc.type + " provided to setLocalDescription");
     }
 
     this._queueOrRun({
-      func: this._getPC().setLocalDescription,
+      func: this._setLocalDescription,
       args: [type, desc.sdp],
       wait: true,
       type: desc.type
     });
   },
 
+  _setLocalDescription: function(type, sdp) {
+    this._getPC().setLocalDescription(type, sdp);
+  },
+
   setRemoteDescription: function(desc, onSuccess, onError) {
     // TODO -- if we have two setRemoteDescriptions in the
     // queue, this code overwrites the callbacks for the first
     // one with the callbacks for the second one. See Bug 831759.
     this._onSetRemoteDescriptionSuccess = onSuccess;
     this._onSetRemoteDescriptionFailure = onError;
 
     let type;
@@ -592,82 +573,87 @@ RTCPeerConnection.prototype = {
       case "pranswer":
         throw new this._win.DOMError("", "pranswer not yet implemented");
       default:
         throw new this._win.DOMError("",
             "Invalid type " + desc.type + " provided to setRemoteDescription");
     }
 
     this._queueOrRun({
-      func: this._getPC().setRemoteDescription,
+      func: this._setRemoteDescription,
       args: [type, desc.sdp],
       wait: true,
       type: desc.type
     });
   },
 
+  _setRemoteDescription: function(type, sdp) {
+    this._getPC().setRemoteDescription(type, sdp);
+  },
+
   updateIce: function(config, constraints) {
     throw new this._win.DOMError("", "updateIce not yet implemented");
   },
 
   addIceCandidate: function(cand, onSuccess, onError) {
     if (!cand.candidate && !cand.sdpMLineIndex) {
       throw new this._win.DOMError("",
           "Invalid candidate passed to addIceCandidate!");
     }
     this._onAddIceCandidateSuccess = onSuccess || null;
     this._onAddIceCandidateError = onError || null;
 
-    this._queueOrRun({
-      func: this._getPC().addIceCandidate,
-      args: [cand.candidate, cand.sdpMid || "",
-             (cand.sdpMLineIndex === null)? 0 : cand.sdpMLineIndex + 1],
-      wait: true
-    });
+    this._queueOrRun({ func: this._addIceCandidate, args: [cand], wait: true });
+  },
+
+  _addIceCandidate: function(cand) {
+    this._getPC().addIceCandidate(cand.candidate, cand.sdpMid || "",
+                                  (cand.sdpMLineIndex === null)? 0 :
+                                      cand.sdpMLineIndex + 1);
   },
 
   addStream: function(stream, constraints) {
     if (stream.currentTime === undefined) {
       throw new this._win.DOMError("", "Invalid stream passed to addStream!");
     }
     // TODO: Implement constraints.
-    this._queueOrRun({
-      func: this._getPC().addStream,
-      args: [stream],
-      wait: false
-    });
+    this._queueOrRun({ func: this._addStream, args: [stream], wait: false });
+  },
+
+  _addStream: function(stream) {
+    this._getPC().addStream(stream);
   },
 
   removeStream: function(stream) {
      //Bug 844295: Not implementing this functionality.
      throw new this._win.DOMError("", "removeStream not yet implemented");
   },
 
   getStreamById: function(id) {
     throw new this._win.DOMError("", "getStreamById not yet implemented");
   },
 
   close: function() {
-    this._queueOrRun({
-      func: this._getPC().close,
-      args: [false],
-      wait: false
-    });
+    this._queueOrRun({ func: this._close, args: [false], wait: false });
     this._closed = true;
     this.changeIceConnectionState("closed");
   },
 
+  _close: function() {
+    this._getPC().close();
+  },
+
   getLocalStreams: function() {
     this._checkClosed();
-    return this._getPC().localStreams;
+    return this._getPC().getLocalStreams();
   },
 
   getRemoteStreams: function() {
     this._checkClosed();
-    return this._getPC().remoteStreams;
+    return this._getPC().getRemoteStreams();
   },
 
   // Backwards-compatible attributes
   get localStreams() {
     this.deprecated("localStreams");
     return this.getLocalStreams();
   },
   get remoteStreams() {
@@ -693,34 +679,31 @@ RTCPeerConnection.prototype = {
     }
     return new this._win.mozRTCSessionDescription({ type: this._remoteType,
                                                     sdp: sdp });
   },
 
   get iceGatheringState()  { return this._iceGatheringState; },
   get iceConnectionState() { return this._iceConnectionState; },
 
-  // Corresponds to constants in IPeerConnection.idl
-  _signalingStateMap: [
-    'invalid',
-    'stable',
-    'have-local-offer',
-    'have-remote-offer',
-    'have-local-pranswer',
-    'have-remote-pranswer',
-    'closed'
-  ],
-
   get signalingState() {
     // checking for our local pc closed indication
     // before invoking the pc methods.
     if(this._closed) {
       return "closed";
     }
-    return this._signalingStateMap[this._getPC().signalingState];
+    return {
+      "SignalingInvalid":            "",
+      "SignalingStable":             "stable",
+      "SignalingHaveLocalOffer":     "have-local-offer",
+      "SignalingHaveRemoteOffer":    "have-remote-offer",
+      "SignalingHaveLocalPranswer":  "have-local-pranswer",
+      "SignalingHaveRemotePranswer": "have-remote-pranswer",
+      "SignalingClosed":             "closed"
+    }[this._getPC().signalingState];
   },
 
   changeIceGatheringState: function(state) {
     this._iceGatheringState = state;
   },
 
   changeIceConnectionState: function(state) {
     this._iceConnectionState = state;
@@ -809,20 +792,24 @@ RTCPeerConnection.prototype = {
     return channel;
   },
 
   connectDataConnection: function(localport, remoteport, numstreams) {
     if (numstreams == undefined || numstreams <= 0) {
       numstreams = 16;
     }
     this._queueOrRun({
-      func: this._getPC().connectDataConnection,
+      func: this._connectDataConnection,
       args: [localport, remoteport, numstreams],
       wait: false
     });
+  },
+
+  _connectDataConnection: function(localport, remoteport, numstreams) {
+    this._getPC().connectDataConnection(localport, remoteport, numstreams);
   }
 };
 
 function RTCError(code, message) {
   this.name = this.reasonName[Math.min(code, this.reasonName.length - 1)];
   this.message = (typeof message === "string")? message : this.name;
   this.__exposedProps__ = { name: "rw", message: "rw" };
 }
@@ -838,22 +825,30 @@ RTCError.prototype = {
     "INCOMPATIBLE_SESSION_DESCRIPTION",
     "INCOMPATIBLE_CONSTRAINTS",
     "INCOMPATIBLE_MEDIASTREAMTRACK",
     "INTERNAL_ERROR"
   ]
 };
 
 // This is a separate object because we don't want to expose it to DOM.
-function PeerConnectionObserver(dompc) {
-  this._dompc = dompc;
+function PeerConnectionObserver() {
+  this._dompc = null;
 }
 PeerConnectionObserver.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.IPeerConnectionObserver,
-                                         Ci.nsISupportsWeakReference]),
+  classDescription: "PeerConnectionObserver",
+  classID: PC_OBS_CID,
+  contractID: PC_OBS_CONTRACT,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
+                                         Ci.nsIDOMGlobalPropertyInitializer]),
+  init: function(win) { this._win = win; },
+
+  __init: function(dompc) {
+    this._dompc = dompc;
+  },
 
   dispatchEvent: function(event) {
     this._dompc.dispatchEvent(event);
   },
 
   callCB: function(callback, arg) {
     if (callback) {
       try {
@@ -995,33 +990,29 @@ PeerConnectionObserver.prototype = {
   //                 flaky network.
   //
   //   closed        The ICE Agent has shut down and is no longer responding to
   //                 STUN requests.
 
   handleIceStateChanges: function(iceState) {
     var histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
 
-    const STATE_MAP = [
-      // Ci.IPeerConnection.kIceGathering:
+    const STATE_MAP = {
+      IceGathering:
         { gathering: "gathering" },
-      // Ci.IPeerConnection.kIceWaiting:
+      IceWaiting:
         { connection: "new",  gathering: "complete", legacy: "starting" },
-      // Ci.IPeerConnection.kIceChecking:
+      IceChecking:
         { connection: "checking", legacy: "checking" },
-      // Ci.IPeerConnection.kIceConnected:
+      IceConnected:
         { connection: "connected", legacy: "connected", success: true },
-      // Ci.IPeerConnection.kIceFailed:
+      IceFailed:
         { connection: "failed", legacy: "failed", success: false }
-    ];
-
-    if (iceState < 0 || iceState > STATE_MAP.length) {
-      this._dompc.reportWarning("Unhandled ice state: " + iceState, null, 0);
-      return;
-    }
+    };
+    // These are all the allowed inputs.
 
     let transitions = STATE_MAP[iceState];
 
     if ("connection" in transitions) {
         this._dompc.changeIceConnectionState(transitions.connection);
     }
     if ("gathering" in transitions) {
       this._dompc.changeIceGatheringState(transitions.gathering);
@@ -1031,51 +1022,51 @@ PeerConnectionObserver.prototype = {
     // Handle deprecated "onicechange" callback
     if ("legacy" in transitions) {
       this.callCB(this._onicechange, transitions.legacy);
     }
     if ("success" in transitions) {
       histogram.add(transitions.success);
     }
 
-    if (iceState == Ci.IPeerConnection.kIceWaiting) {
+    if (iceState == "IceWaiting") {
       if (!this._dompc._trickleIce) {
         // If we are not trickling, then the queue is in a pending state
         // waiting for ICE gathering and executeNext frees it
         this._dompc._executeNext();
       }
       else if (this.localDescription) {
         // If we are trickling but we have already done setLocal,
         // then we need to send a final foundIceCandidate(null) to indicate
         // that we are done gathering.
         this.foundIceCandidate(null);
       }
     }
   },
 
   onStateChange: function(state) {
     switch (state) {
-      case Ci.IPeerConnectionObserver.kSignalingState:
+      case "SignalingState":
         this.callCB(this._dompc.onsignalingstatechange,
                     this._dompc.signalingState);
         break;
 
-      case Ci.IPeerConnectionObserver.kIceState:
+      case "IceState":
         this.handleIceStateChanges(this._dompc._pc.iceState);
         break;
 
-      case Ci.IPeerConnectionObserver.kSdpState:
+      case "SdpState":
         // No-op
         break;
 
-      case Ci.IPeerConnectionObserver.kReadyState:
+      case "ReadyState":
         // No-op
         break;
 
-      case Ci.IPeerConnectionObserver.kSipccState:
+      case "SipccState":
         // No-op
         break;
 
       default:
         this._dompc.reportWarning("Unhandled state type: " + state, null, 0);
         break;
     }
   },
@@ -1101,14 +1092,19 @@ PeerConnectionObserver.prototype = {
   },
 
   notifyConnection: function() {
     this.dispatchEvent(new this._dompc._win.Event("connection"));
   },
 
   notifyClosedConnection: function() {
     this.dispatchEvent(new this._dompc._win.Event("closedconnection"));
+  },
+
+  getSupportedConstraints: function(dict) {
+    return dict;
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
-  [GlobalPCList, RTCIceCandidate, RTCSessionDescription, RTCPeerConnection]
+  [GlobalPCList, RTCIceCandidate, RTCSessionDescription, RTCPeerConnection,
+   PeerConnectionObserver]
 );
--- a/dom/media/PeerConnection.manifest
+++ b/dom/media/PeerConnection.manifest
@@ -1,9 +1,11 @@
 component {9878b414-afaa-4176-a887-1e02b3b047c2} PeerConnection.js
+component {1d44a18e-4545-4ff3-863d-6dbd6234a583} PeerConnection.js
 component {02b9970c-433d-4cc2-923d-f7028ac66073} PeerConnection.js
 component {1775081b-b62d-4954-8ffe-a067bbf508a7} PeerConnection.js
 component {7293e901-2be3-4c02-b4bd-cbef6fc24f78} PeerConnection.js
 
 contract @mozilla.org/dom/peerconnection;1 {9878b414-afaa-4176-a887-1e02b3b047c2}
+contract @mozilla.org/dom/peerconnectionobserver;1 {1d44a18e-4545-4ff3-863d-6dbd6234a583}
 contract @mozilla.org/dom/rtcicecandidate;1 {02b9970c-433d-4cc2-923d-f7028ac66073}
 contract @mozilla.org/dom/rtcsessiondescription;1 {1775081b-b62d-4954-8ffe-a067bbf508a7}
 contract @mozilla.org/dom/peerconnectionmanager;1 {7293e901-2be3-4c02-b4bd-cbef6fc24f78}
--- a/dom/media/bridge/IPeerConnection.idl
+++ b/dom/media/bridge/IPeerConnection.idl
@@ -19,55 +19,22 @@ interface IPeerConnectionManager : nsISu
 %}
 
 /* Do not confuse with nsIDOMRTCPeerConnection. This interface is purely for
  * communication between the PeerConnection JS DOM binding and the C++
  * implementation in SIPCC.
  *
  * See media/webrtc/signaling/include/PeerConnectionImpl.h
  */
-[scriptable, uuid(896dc16a-05d6-45e4-bdbf-aba57123ed3e)]
+[scriptable, uuid(d7dfe148-0416-446b-a128-66a7c71ae8d3)]
 interface IPeerConnectionObserver : nsISupports
 {
-  /* Constants */
-  const long kReadyState = 0x1;
-  const long kIceState = 0x2;
-  const long kSdpState = 0x3;
-  const long kSipccState = 0x4;
-  const long kSignalingState = 0x5;
-
-  /* JSEP callbacks */
-  void onCreateOfferSuccess(in string offer);
-  void onCreateOfferError(in unsigned long name, in string message);
-  void onCreateAnswerSuccess(in string answer);
-  void onCreateAnswerError(in unsigned long name, in string message);
-  void onSetLocalDescriptionSuccess();
-  void onSetRemoteDescriptionSuccess();
-  void onSetLocalDescriptionError(in unsigned long name, in string message);
-  void onSetRemoteDescriptionError(in unsigned long name, in string message);
-  void onAddIceCandidateSuccess();
-  void onAddIceCandidateError(in unsigned long name, in string message);
-  void onIceCandidate(in unsigned short level, in string mid, in string candidate);
-
-  /* Data channel callbacks */
-  void notifyDataChannel(in nsIDOMDataChannel channel);
-  void notifyConnection();
-  void notifyClosedConnection();
-
-  /* Notification of one of several types of state changed */
-  void onStateChange(in unsigned long state);
-
-  /* Changes to MediaStreams */
-  void onAddStream(in nsIDOMMediaStream stream);
-  void onRemoveStream();
-  void onAddTrack();
-  void onRemoveTrack();
 };
 
-[scriptable, uuid(930dce8b-7c5e-4393-b8c0-cb3a928f68bd)]
+[scriptable, uuid(c9c31639-1a49-4533-8429-f6a348c4d8c3)]
 interface IPeerConnection : nsISupports
 {
   const unsigned long kHintAudio = 0x00000001;
   const unsigned long kHintVideo = 0x00000002;
 
   const long kActionNone = -1;
   const long kActionOffer = 0;
   const long kActionAnswer = 1;
@@ -81,25 +48,16 @@ interface IPeerConnection : nsISupports
 
   /* for readyState on Peer Connection */
   const long kNew = 0;
   const long kNegotiating = 1;
   const long kActive = 2;
   const long kClosing = 3;
   const long kClosed = 4;
 
-  /* RTCSignalingState from WebRTC spec */
-  const long kSignalingInvalid            = 0;
-  const long kSignalingStable             = 1;
-  const long kSignalingHaveLocalOffer     = 2;
-  const long kSignalingHaveRemoteOffer    = 3;
-  const long kSignalingHaveLocalPranswer  = 4;
-  const long kSignalingHaveRemotePranswer = 5;
-  const long kSignalingClosed             = 6;
-
   /* for 'type' in DataChannelInit dictionary */
   const unsigned short kDataChannelReliable = 0;
   const unsigned short kDataChannelPartialReliableRexmit = 1;
   const unsigned short kDataChannelPartialReliableTimed = 2;
 
   /* Constants for 'name' in error callbacks */
   const unsigned long kNoError                          = 0; // Test driver only
   const unsigned long kInvalidConstraintsType           = 1;
@@ -107,55 +65,9 @@ interface IPeerConnection : nsISupports
   const unsigned long kInvalidMediastreamTrack          = 3;
   const unsigned long kInvalidState                     = 4;
   const unsigned long kInvalidSessionDescription        = 5;
   const unsigned long kIncompatibleSessionDescription   = 6;
   const unsigned long kIncompatibleConstraints          = 7;
   const unsigned long kIncompatibleMediaStreamTrack     = 8;
   const unsigned long kInternalError                    = 9;
   const unsigned long kMaxErrorType                     = 9; // Same as final error
-
-  /* Must be called first. Observer events will be dispatched on the thread provided */
-  [implicit_jscontext] void initialize(in IPeerConnectionObserver observer, in nsIDOMWindow window,
-                  [optional] in jsval iceServers,
-                  [optional] in nsIThread thread);
-
-  /* JSEP calls */
-  [implicit_jscontext] void createOffer(in jsval constraints);
-  [implicit_jscontext] void createAnswer(in jsval constraints);
-  void setLocalDescription(in long action, in string sdp);
-  void setRemoteDescription(in long action, in string sdp);
-
-  /* Adds the stream created by GetUserMedia */
-  void addStream(in nsIDOMMediaStream stream);
-  void removeStream(in nsIDOMMediaStream stream);
-  void closeStreams();
-
-  [implicit_jscontext] readonly attribute jsval localStreams; // MediaStream[]
-  [implicit_jscontext] readonly attribute jsval remoteStreams; // MediaStream[]
-
-  /* As the ICE candidates roll in this one should be called each time
-   * in order to keep the candidate list up-to-date for the next SDP-related
-   * call PeerConnectionImpl does not parse ICE candidates, just sticks them
-   * into the SDP.
-   */
-  void addIceCandidate(in string candidate, in string mid, in unsigned short level);
-
-  /* Puts the SIPCC engine back to 'kIdle', shuts down threads, deletes state */
-  void close();
-
-  /* Attributes */
-  readonly attribute string localDescription;
-  readonly attribute string remoteDescription;
-
-  readonly attribute unsigned long iceState;
-  readonly attribute unsigned long readyState;
-  readonly attribute unsigned long signalingState;
-  readonly attribute unsigned long sipccState;
-
-  /* Data channels */
-  nsIDOMDataChannel createDataChannel(in ACString label, in ACString protocol,
-    in unsigned short type, in boolean outOfOrderAllowed,
-    in unsigned short maxTime, in unsigned short maxNum,
-    in boolean externalNegotiated, in unsigned short stream);
-  void connectDataConnection(in unsigned short localport,
-    in unsigned short remoteport, in unsigned short numstreams);
 };
--- a/dom/media/tests/mochitest/test_peerConnection_bug835370.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug835370.html
@@ -22,36 +22,27 @@
 
     var exception = null;
     try { pconnects.createOffer(step1, failed); } catch (e) { exception = e; }
     ok(!exception, "createOffer(step1, failed) succeeds");
     exception = null;
     try { pconnect.createOffer(step1, failed, 1); } catch (e) { exception = e; }
     ok(exception, "createOffer(step1, failed, 1) throws");
     exception = null;
-    try { pconnect.createOffer(step1, failed, []); } catch (e) { exception = e; }
-    ok(exception, "createOffer(step1, failed, []) throws");
-    exception = null;
     try { pconnects.createOffer(step1, failed, {}); } catch (e) { exception = e; }
     ok(!exception, "createOffer(step1, failed, {}) succeeds");
     exception = null;
-    try { pconnect.createOffer(step1, failed, { mandatory: [] }); } catch (e) { exception = e; }
-    ok(exception, "createOffer(step1, failed, { mandatory: [] }) throws");
-    exception = null;
     try {
         pconnect.createOffer(step1, failed, { mandatory: { FooBar: true } });
     } catch (e) {
         ok(e.message.indexOf("FooBar") > 0, "createOffer has readable exceptions");
         exception = e;
     }
     ok(exception, "createOffer(step1, failed, { mandatory: { FooBar: true } }) throws");
     exception = null;
-    try { pconnect.createOffer(step1, failed, { optional: {} }); } catch (e) { exception = e; }
-    ok(exception, "createOffer(step1, failed, { optional: {} }) throws");
-    exception = null;
     try { pconnects.createOffer(step1, failed, { optional: [] }); } catch (e) { exception = e; }
     ok(!exception, "createOffer(step1, failed, { optional: [] }) succeeds");
     exception = null;
     try { pconnect.createOffer(step1, failed, { optional: [1] }); } catch (e) { exception = e; }
     ok(exception, "createOffer(step1, failed, { optional: [1] }) throws");
     exception = null;
     try { pconnect.createOffer(step1, failed, { optional: [{ OfferToReceiveVideo: false, OfferToReceiveAudio: true, }] }); } catch (e) { exception = e; }
     ok(exception, "createOffer(step1, failed, { optional: [{ OfferToReceiveVideo: false, OfferToReceiveAudio: true, }] }) throws");
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -406,20 +406,17 @@ var interfaceNamesInGlobalScope =
     "SpeechRecognitionEvent",
     "SpeechSynthesisEvent",
     {name: "SpeechSynthesis", b2g: true},
     {name: "SpeechSynthesisUtterance", b2g: true},
     {name: "SpeechSynthesisVoice", b2g: true},
     {name: "SpecialPowers", xbl: false},
     "Storage",
     "StorageEvent",
-    "StyleRuleChangeEvent",
     "StyleSheet",
-    "StyleSheetApplicableStateChangeEvent",
-    "StyleSheetChangeEvent",
     "StyleSheetList",
     "SVGAElement",
     "SVGAltGlyphElement",
     "SVGAngle",
     "SVGAnimatedAngle",
     "SVGAnimatedBoolean",
     "SVGAnimatedEnumeration",
     "SVGAnimatedInteger",
--- a/dom/webidl/MediaStreamList.webidl
+++ b/dom/webidl/MediaStreamList.webidl
@@ -1,11 +1,11 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[NoInterfaceObject]
+[ChromeOnly]
 interface MediaStreamList {
   getter MediaStream? (unsigned long index);
   readonly attribute unsigned long length;
 };
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PeerConnectionImpl.webidl
@@ -0,0 +1,77 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * PeerConnection.js' interface to the C++ PeerConnectionImpl.
+ *
+ * Do not confuse with mozRTCPeerConnection. This interface is purely for
+ * communication between the PeerConnection JS DOM binding and the C++
+ * implementation in SIPCC.
+ *
+ * See media/webrtc/signaling/include/PeerConnectionImpl.h
+ *
+ */
+
+interface Window;
+interface nsISupports;
+
+/* Must be created first. Observer events will be dispatched on the thread provided */
+[ChromeOnly, Constructor]
+interface PeerConnectionImpl  {
+  /* Must be called first. Observer events dispatched on the thread provided */
+  [Throws]
+  void initialize(PeerConnectionObserver observer, Window window,
+                  RTCConfiguration iceServers,
+                  nsISupports thread);
+  /* JSEP calls */
+  [Throws]
+  void createOffer(optional MediaConstraintsInternal constraints);
+  [Throws]
+  void createAnswer(optional MediaConstraintsInternal constraints);
+  [Throws]
+  void setLocalDescription(long action, DOMString sdp);
+  [Throws]
+  void setRemoteDescription(long action, DOMString sdp);
+
+  /* Adds the stream created by GetUserMedia */
+  [Throws]
+  void addStream(MediaStream stream);
+  [Throws]
+  void removeStream(MediaStream stream);
+  [Throws]
+  void closeStreams();
+
+  sequence<MediaStream> getLocalStreams();
+  sequence<MediaStream> getRemoteStreams();
+
+  /* As the ICE candidates roll in this one should be called each time
+   * in order to keep the candidate list up-to-date for the next SDP-related
+   * call PeerConnectionImpl does not parse ICE candidates, just sticks them
+   * into the SDP.
+   */
+  [Throws]
+  void addIceCandidate(DOMString candidate, DOMString mid, unsigned short level);
+
+  /* Puts the SIPCC engine back to 'kIdle', shuts down threads, deletes state */
+  void close();
+
+  /* Attributes */
+  readonly attribute DOMString localDescription;
+  readonly attribute DOMString remoteDescription;
+
+  readonly attribute PCImplIceState iceState;
+  readonly attribute PCImplReadyState readyState;
+  readonly attribute PCImplSignalingState signalingState;
+  readonly attribute PCImplSipccState sipccState;
+
+  /* Data channels */
+  [Throws]
+  DataChannel createDataChannel(DOMString label, DOMString protocol,
+    unsigned short type, boolean outOfOrderAllowed,
+    unsigned short maxTime, unsigned short maxNum,
+    boolean externalNegotiated, unsigned short stream);
+  [Throws]
+  void connectDataConnection(unsigned short localport,
+    unsigned short remoteport, unsigned short numstreams);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PeerConnectionImplEnums.webidl
@@ -0,0 +1,41 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This is in a separate file so it can be shared with unittests.
+ */
+
+enum PCImplReadyState {
+  "New",
+  "Negotiating",
+  "Active",
+  "Closing",
+  "Closed"
+};
+
+/* Must be in the same order as comparable fsmdef_states_t in fsmdef_states.h */
+enum PCImplSignalingState {
+  "SignalingInvalid",
+  "SignalingStable",
+  "SignalingHaveLocalOffer",
+  "SignalingHaveRemoteOffer",
+  "SignalingHaveLocalPranswer",
+  "SignalingHaveRemotePranswer",
+  "SignalingClosed",
+};
+
+enum PCImplSipccState {
+  "Idle",
+  "Starting",
+  "Started"
+};
+
+// TODO(ekr@rtfm.com): make this conform to the specifications
+enum PCImplIceState {
+  "IceGathering",
+  "IceWaiting",
+  "IceChecking",
+  "IceConnected",
+  "IceFailed"
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PeerConnectionObserver.webidl
@@ -0,0 +1,46 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[ChromeOnly,
+ JSImplementation="@mozilla.org/dom/peerconnectionobserver;1",
+ Constructor (object domPC)]
+interface PeerConnectionObserver
+{
+  /* JSEP callbacks */
+  void onCreateOfferSuccess(DOMString offer);
+  void onCreateOfferError(unsigned long name, DOMString message);
+  void onCreateAnswerSuccess(DOMString answer);
+  void onCreateAnswerError(unsigned long name, DOMString message);
+  void onSetLocalDescriptionSuccess();
+  void onSetRemoteDescriptionSuccess();
+  void onSetLocalDescriptionError(unsigned long name, DOMString message);
+  void onSetRemoteDescriptionError(unsigned long name, DOMString message);
+  void onAddIceCandidateSuccess();
+  void onAddIceCandidateError(unsigned long name, DOMString message);
+  void onIceCandidate(unsigned short level, DOMString mid, DOMString candidate);
+
+  /* Data channel callbacks */
+  void notifyDataChannel(DataChannel channel);
+  void notifyConnection();
+  void notifyClosedConnection();
+
+  /* Notification of one of several types of state changed */
+  void onStateChange(PCObserverStateType state);
+
+  /* Changes to MediaStreams */
+  void onAddStream(MediaStream stream);
+  void onRemoveStream();
+  void onAddTrack();
+  void onRemoveTrack();
+
+  /* Helper function to access supported constraints defined in webidl. Needs to
+   * be in a separate webidl object we hold, so putting it here was convenient.
+   */
+// TODO: Bug 863949
+//  MediaConstraintSet getSupportedConstraints(optional
+  object getSupportedConstraints(optional
+      MediaConstraintSet constraints);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PeerConnectionObserverEnums.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This is in a separate file so it can be shared with unittests.
+ */
+
+enum PCObserverStateType {
+    "None",
+    "ReadyState",
+    "IceState",
+    "SdpState",
+    "SipccState",
+    "SignalingState"
+};
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -46,50 +46,76 @@ dictionary RTCDataChannelInit {
 
   // these are deprecated due to renaming in the spec, but still supported for Fx22
   boolean outOfOrderAllowed; // now ordered, and the default changes to keep behavior the same
   unsigned short maxRetransmitNum; // now maxRetransmits
   boolean preset; // now negotiated
   unsigned short stream; // now id
 };
 
+// Misnomer dictionaries housing PeerConnection-specific constraints.
+//
+// Important! Do not ever add members that might need tracing (e.g. object)
+// to MediaConstraintSet or any dictionary marked XxxInternal here
+
+dictionary MediaConstraintSet {
+  boolean OfferToReceiveAudio;
+  boolean OfferToReceiveVideo;
+  boolean MozDontOfferDataChannel;
+};
+
+// MediaConstraint = single-property-subset of MediaConstraintSet
+// Implemented as full set. Test Object.keys(pair).length == 1
+
+// typedef MediaConstraintSet MediaConstraint; // TODO: Bug 913053
+
+dictionary MediaConstraints {
+  object mandatory; // so we can see unknown + unsupported constraints
+  sequence<MediaConstraintSet> _optional; // a.k.a. MediaConstraint
+};
+
+dictionary MediaConstraintsInternal {
+  MediaConstraintSet mandatory; // holds only supported constraints
+  sequence<MediaConstraintSet> _optional; // a.k.a. MediaConstraint
+};
+
 interface RTCDataChannel;
 
 [Pref="media.peerconnection.enabled",
  JSImplementation="@mozilla.org/dom/peerconnection;1",
  Constructor (optional RTCConfiguration configuration,
               optional object? constraints)]
 // moz-prefixed until sufficiently standardized.
 interface mozRTCPeerConnection : EventTarget  {
   void createOffer (RTCSessionDescriptionCallback successCallback,
                     RTCPeerConnectionErrorCallback? failureCallback, // for apprtc
-                    optional object? constraints);
+                    optional MediaConstraints constraints);
   void createAnswer (RTCSessionDescriptionCallback successCallback,
                      RTCPeerConnectionErrorCallback? failureCallback, // for apprtc
-                     optional object? constraints);
+                     optional MediaConstraints constraints);
   void setLocalDescription (mozRTCSessionDescription description,
                             optional VoidFunction successCallback,
                             optional RTCPeerConnectionErrorCallback failureCallback);
   void setRemoteDescription (mozRTCSessionDescription description,
                              optional VoidFunction successCallback,
                              optional RTCPeerConnectionErrorCallback failureCallback);
   readonly attribute mozRTCSessionDescription? localDescription;
   readonly attribute mozRTCSessionDescription? remoteDescription;
   readonly attribute RTCSignalingState signalingState;
   void updateIce (optional RTCConfiguration configuration,
-                  optional object? constraints);
+                  optional MediaConstraints constraints);
   void addIceCandidate (mozRTCIceCandidate candidate,
                         optional VoidFunction successCallback,
                         optional RTCPeerConnectionErrorCallback failureCallback);
   readonly attribute RTCIceGatheringState iceGatheringState;
   readonly attribute RTCIceConnectionState iceConnectionState;
   sequence<MediaStream> getLocalStreams ();
   sequence<MediaStream> getRemoteStreams ();
   MediaStream? getStreamById (DOMString streamId);
-  void addStream (MediaStream stream, optional object? constraints);
+  void addStream (MediaStream stream, optional MediaConstraints constraints);
   void removeStream (MediaStream stream);
   void close ();
   attribute EventHandler onnegotiationneeded;
   attribute EventHandler onicecandidate;
   attribute EventHandler onsignalingstatechange;
   attribute EventHandler onaddstream;
   attribute EventHandler onremovestream;
   attribute EventHandler oniceconnectionstatechange;
--- a/dom/webidl/StyleRuleChangeEvent.webidl
+++ b/dom/webidl/StyleRuleChangeEvent.webidl
@@ -1,16 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 interface CSSRule;
 
-[Constructor(DOMString type, optional StyleRuleChangeEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
+[ChromeOnly, Constructor(DOMString type, optional StyleRuleChangeEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
 interface StyleRuleChangeEvent : Event
 {
   readonly attribute CSSStyleSheet? stylesheet;
   readonly attribute CSSRule? rule;
 };
 
 dictionary StyleRuleChangeEventInit : EventInit
 {
--- a/dom/webidl/StyleSheetApplicableStateChangeEvent.webidl
+++ b/dom/webidl/StyleSheetApplicableStateChangeEvent.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[Constructor(DOMString type, optional StyleSheetApplicableStateChangeEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
+[ChromeOnly, Constructor(DOMString type, optional StyleSheetApplicableStateChangeEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
 interface StyleSheetApplicableStateChangeEvent : Event
 {
   readonly attribute CSSStyleSheet? stylesheet;
   readonly attribute boolean applicable;
 };
 
 dictionary StyleSheetApplicableStateChangeEventInit : EventInit
 {
--- a/dom/webidl/StyleSheetChangeEvent.webidl
+++ b/dom/webidl/StyleSheetChangeEvent.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[Constructor(DOMString type, optional StyleSheetChangeEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
+[ChromeOnly, Constructor(DOMString type, optional StyleSheetChangeEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
 interface StyleSheetChangeEvent : Event
 {
   readonly attribute CSSStyleSheet? stylesheet;
   readonly attribute boolean documentSheet;
 };
 
 dictionary StyleSheetChangeEventInit : EventInit
 {
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -237,16 +237,20 @@ WEBIDL_FILES = [
     'OfflineAudioCompletionEvent.webidl',
     'OfflineAudioContext.webidl',
     'OfflineResourceList.webidl',
     'OscillatorNode.webidl',
     'PaintRequest.webidl',
     'PaintRequestList.webidl',
     'PannerNode.webidl',
     'ParentNode.webidl',
+    'PeerConnectionImpl.webidl',
+    'PeerConnectionImplEnums.webidl',
+    'PeerConnectionObserver.webidl',
+    'PeerConnectionObserverEnums.webidl',
     'Performance.webidl',
     'PerformanceNavigation.webidl',
     'PerformanceTiming.webidl',
     'PeriodicWave.webidl',
     'PhoneNumberService.webidl',
     'Plugin.webidl',
     'PluginArray.webidl',
     'Position.webidl',
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -94,20 +94,20 @@ CanvasClient2D::Update(gfx::IntSize aSiz
 
   if (surface) {
     GetForwarder()->UpdatedTexture(this, mBuffer, nullptr);
     GetForwarder()->UseTexture(this, mBuffer);
   }
 }
 
 TemporaryRef<BufferTextureClient>
-CanvasClient2D::CreateBufferTextureClient(gfx::SurfaceFormat aFormat)
+CanvasClient2D::CreateBufferTextureClient(gfx::SurfaceFormat aFormat, TextureFlags aFlags)
 {
   return CompositableClient::CreateBufferTextureClient(aFormat,
-                                                       mTextureInfo.mTextureFlags);
+                                                       mTextureInfo.mTextureFlags | aFlags);
 }
 
 void
 DeprecatedCanvasClient2D::Updated()
 {
   mForwarder->UpdateTexture(this, 1, mDeprecatedTextureClient->LockSurfaceDescriptor());
 }
 
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -78,17 +78,18 @@ public:
 
   virtual bool AddTextureClient(TextureClient* aTexture) MOZ_OVERRIDE
   {
     MOZ_ASSERT((mTextureInfo.mTextureFlags & aTexture->GetFlags()) == mTextureInfo.mTextureFlags);
     return CompositableClient::AddTextureClient(aTexture);
   }
 
   virtual TemporaryRef<BufferTextureClient>
-  CreateBufferTextureClient(gfx::SurfaceFormat aFormat) MOZ_OVERRIDE;
+  CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+                            TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT) MOZ_OVERRIDE;
 
   virtual void OnDetach() MOZ_OVERRIDE
   {
     mBuffer = nullptr;
   }
 
 private:
   RefPtr<TextureClient> mBuffer;
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -202,22 +202,16 @@ CompositableClient::CreateBufferTextureC
   if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) {
     RefPtr<BufferTextureClient> result = new MemoryTextureClient(this, aFormat, aTextureFlags);
     return result.forget();
   }
   RefPtr<BufferTextureClient> result = new ShmemTextureClient(this, aFormat, aTextureFlags);
   return result.forget();
 }
 
-TemporaryRef<BufferTextureClient>
-CompositableClient::CreateBufferTextureClient(gfx::SurfaceFormat aFormat)
-{
-  return CreateBufferTextureClient(aFormat, TEXTURE_FLAGS_DEFAULT);
-}
-
 bool
 CompositableClient::AddTextureClient(TextureClient* aClient)
 {
   ++mNextTextureID;
   // 0 is always an invalid ID
   if (mNextTextureID == 0) {
     ++mNextTextureID;
   }
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -80,20 +80,18 @@ public:
 
   LayersBackend GetCompositorBackendType() const;
 
   TemporaryRef<DeprecatedTextureClient>
   CreateDeprecatedTextureClient(DeprecatedTextureClientType aDeprecatedTextureClientType,
                                 gfxContentType aContentType = GFX_CONTENT_SENTINEL);
 
   virtual TemporaryRef<BufferTextureClient>
-  CreateBufferTextureClient(gfx::SurfaceFormat aFormat, TextureFlags aFlags);
-
-  virtual TemporaryRef<BufferTextureClient>
-  CreateBufferTextureClient(gfx::SurfaceFormat aFormat);
+  CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+                            TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT);
 
   virtual void SetDescriptorFromReply(TextureIdentifier aTextureId,
                                       const SurfaceDescriptor& aDescriptor)
   {
     MOZ_CRASH("If you want to call this, you should have implemented it");
   }
 
   /**
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -288,23 +288,16 @@ ImageClientSingle::AddTextureClient(Text
 }
 
 TemporaryRef<BufferTextureClient>
 ImageClientSingle::CreateBufferTextureClient(gfx::SurfaceFormat aFormat, TextureFlags aFlags)
 {
   return CompositableClient::CreateBufferTextureClient(aFormat, mTextureFlags | aFlags);
 }
 
-TemporaryRef<BufferTextureClient>
-ImageClientSingle::CreateBufferTextureClient(gfx::SurfaceFormat aFormat)
-{
-  return CompositableClient::CreateBufferTextureClient(aFormat,
-    mTextureFlags | TEXTURE_FLAGS_DEFAULT);
-}
-
 void
 ImageClientSingle::OnDetach()
 {
   mFrontBuffer = nullptr;
 }
 
 void
 ImageClientBuffered::OnDetach()
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -87,20 +87,18 @@ public:
 
   virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags);
 
   virtual void OnDetach() MOZ_OVERRIDE;
 
   virtual bool AddTextureClient(TextureClient* aTexture) MOZ_OVERRIDE;
 
   virtual TemporaryRef<BufferTextureClient>
-  CreateBufferTextureClient(gfx::SurfaceFormat aFormat, TextureFlags aFlags) MOZ_OVERRIDE;
-
-  virtual TemporaryRef<BufferTextureClient>
-  CreateBufferTextureClient(gfx::SurfaceFormat aFormat) MOZ_OVERRIDE;
+  CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+                            TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT) MOZ_OVERRIDE;
 
   virtual TextureInfo GetTextureInfo() const MOZ_OVERRIDE;
 
   virtual already_AddRefed<Image> CreateImage(const uint32_t *aFormats,
                                               uint32_t aNumFormats) MOZ_OVERRIDE;
 
   virtual void FlushImage() MOZ_OVERRIDE;
 
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -709,17 +709,17 @@ nsEventStatus AsyncPanZoomController::On
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
-  if (controller && mAllowZoom) {
+  if (controller && !mAllowZoom) {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, mFrameMetrics.mZoom);
     controller->HandleSingleTap(gfx::RoundedToInt(point));
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
new file mode 100644
--- /dev/null
+++ b/intl/icu-patches/bug-901348
@@ -0,0 +1,38 @@
+# HG changeset patch
+# User Nomis101
+# Date 1380136873 -7200
+#      Wed Sep 25 21:21:13 2013 +0200
+# Node ID 2921e2256ba8a8ac1ca8b5b0e48eb04511545d41
+# Parent  39f30376058cf20823534f2d510430eaa31844bf
+Bug 901348 - [10.9] Duplicate symbol errors building --with-intl-api
+
+diff --git a/intl/icu/source/common/umutex.h b/intl/icu/source/common/umutex.h
+--- a/intl/icu/source/common/umutex.h
++++ b/intl/icu/source/common/umutex.h
+@@ -43,26 +43,18 @@
+ # define NOIME
+ # define NOMCX
+ # include <windows.h>
+ #endif  /* 0 */
+ #define U_WINDOWS_CRIT_SEC_SIZE 64
+ #endif  /* win32 */
+ 
+ #if U_PLATFORM_IS_DARWIN_BASED
+-#if defined(__STRICT_ANSI__)
+-#define UPRV_REMAP_INLINE
+-#define inline
+-#endif
+ #include <libkern/OSAtomic.h>
+ #define USE_MAC_OS_ATOMIC_INCREMENT 1
+-#if defined(UPRV_REMAP_INLINE)
+-#undef inline
+-#undef UPRV_REMAP_INLINE
+-#endif
+ #endif
+ 
+ /*
+  * If we do not compile with dynamic_annotations.h then define
+  * empty annotation macros.
+  *  See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations
+  */
+ #ifndef ANNOTATE_HAPPENS_BEFORE
--- a/intl/icu/source/common/umutex.h
+++ b/intl/icu/source/common/umutex.h
@@ -43,26 +43,18 @@
 # define NOIME
 # define NOMCX
 # include <windows.h>
 #endif  /* 0 */
 #define U_WINDOWS_CRIT_SEC_SIZE 64
 #endif  /* win32 */
 
 #if U_PLATFORM_IS_DARWIN_BASED
-#if defined(__STRICT_ANSI__)
-#define UPRV_REMAP_INLINE
-#define inline
-#endif
 #include <libkern/OSAtomic.h>
 #define USE_MAC_OS_ATOMIC_INCREMENT 1
-#if defined(UPRV_REMAP_INLINE)
-#undef inline
-#undef UPRV_REMAP_INLINE
-#endif
 #endif
 
 /*
  * If we do not compile with dynamic_annotations.h then define
  * empty annotation macros.
  *  See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations
  */
 #ifndef ANNOTATE_HAPPENS_BEFORE
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -553,19 +553,19 @@ struct JSClass {
 // ECMA-262 requires that most constructors used internally create objects
 // with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
 // member initial value.  The "original ... value" verbiage is there because
 // in ECMA-262, global properties naming class objects are read/write and
 // deleteable, for the most part.
 //
 // Implementing this efficiently requires that global objects have classes
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
-// prevously allowed, but is now an ES5 violation and thus unsupported.
+// previously allowed, but is now an ES5 violation and thus unsupported.
 //
-#define JSCLASS_GLOBAL_SLOT_COUNT      (JSProto_LIMIT * 3 + 26)
+#define JSCLASS_GLOBAL_SLOT_COUNT      (3 + JSProto_LIMIT * 3 + 26)
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                                    \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
 
--- a/js/src/build/autoconf/compiler-opts.m4
+++ b/js/src/build/autoconf/compiler-opts.m4
@@ -166,20 +166,20 @@ AC_DEFUN([MOZ_COMPILER_OPTS],
   MOZ_ARG_ENABLE_BOOL(release,
   [  --enable-release        Build with more conservative, release engineering-oriented options.
                           This may slow down builds.],
       DEVELOPER_OPTIONS=,
       DEVELOPER_OPTIONS=1)
 
   AC_SUBST(DEVELOPER_OPTIONS)
 
-  if test -n "$DEVELOPER_OPTIONS" -a "${MOZ_PSEUDO_DERECURSE-unset}" = unset; then
+  if test "${MOZ_PSEUDO_DERECURSE-unset}" = unset; then
     dnl Don't enable on pymake, because of bug 918652. Bug 912979 is an annoyance
     dnl with pymake, too.
-    MOZ_PSEUDO_DERECURSE=no-parallel-export,no-pymake,no-skip
+    MOZ_PSEUDO_DERECURSE=no-pymake,no-skip
   fi
 
   MOZ_DEBUGGING_OPTS
   MOZ_RTTI
 if test "$CLANG_CXX"; then
     ## We disable return-type-c-linkage because jsval is defined as a C++ type but is
     ## returned by C functions. This is possible because we use knowledge about the ABI
     ## to typedef it to a C type with the same layout when the headers are included
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -2033,25 +2033,25 @@ js_InitIntlClass(JSContext *cx, HandleOb
         if (!InitCollatorClass(cx, Intl, global))
             return nullptr;
         if (!InitNumberFormatClass(cx, Intl, global))
             return nullptr;
         if (!InitDateTimeFormatClass(cx, Intl, global))
             return nullptr;
     }
 
-    MarkStandardClassInitializedNoProto(global, &IntlClass);
+    global->markStandardClassInitializedNoProto(&IntlClass);
 
     return Intl;
 }
 
 bool
 GlobalObject::initIntlObject(JSContext *cx, Handle<GlobalObject*> global)
 {
     RootedObject Intl(cx);
     Intl = NewObjectWithGivenProto(cx, &IntlClass, global->getOrCreateObjectPrototype(cx),
                                    global, SingletonObject);
     if (!Intl)
         return false;
 
-    global->setReservedSlot(JSProto_Intl, ObjectValue(*Intl));
+    global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
     return true;
 }
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -117,17 +117,17 @@ JS_StopProfiling(const char *profileName
  * Start or stop whatever platform- and configuration-specific profiling
  * backends are available.
  */
 static bool
 ControlProfilers(bool toState)
 {
     bool ok = true;
 
-    if (! Probes::ProfilingActive && toState) {
+    if (! probes::ProfilingActive && toState) {
 #ifdef __APPLE__
 #if defined(MOZ_SHARK) || defined(MOZ_INSTRUMENTS)
         const char* profiler;
 #ifdef MOZ_SHARK
         ok = Shark::Start();
         profiler = "Shark";
 #endif
 #ifdef MOZ_INSTRUMENTS
@@ -140,34 +140,34 @@ ControlProfilers(bool toState)
 #endif
 #endif
 #ifdef MOZ_CALLGRIND
         if (! js_StartCallgrind()) {
             UnsafeError("Failed to start Callgrind");
             ok = false;
         }
 #endif
-    } else if (Probes::ProfilingActive && ! toState) {
+    } else if (probes::ProfilingActive && ! toState) {
 #ifdef __APPLE__
 #ifdef MOZ_SHARK
         Shark::Stop();
 #endif
 #ifdef MOZ_INSTRUMENTS
         Instruments::Pause();
 #endif
 #endif
 #ifdef MOZ_CALLGRIND
         if (! js_StopCallgrind()) {
             UnsafeError("failed to stop Callgrind");
             ok = false;
         }
 #endif
     }
 
-    Probes::ProfilingActive = toState;
+    probes::ProfilingActive = toState;
 
     return ok;
 }
 
 /*
  * Pause/resume whatever profiling mechanism is currently compiled
  * in, if applicable. This will not affect things like dtrace.
  *
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -481,26 +481,26 @@ static JSObject *
 SetupAndGetPrototypeObjectForComplexTypeInstance(JSContext *cx,
                                                  HandleObject complexTypeGlobal)
 {
     RootedObject global(cx, cx->compartment()->maybeGlobal());
     RootedValue complexTypePrototypeVal(cx);
     RootedValue complexTypePrototypePrototypeVal(cx);
 
     if (!JSObject::getProperty(cx, complexTypeGlobal, complexTypeGlobal,
-                               cx->names().classPrototype, &complexTypePrototypeVal))
+                               cx->names().prototype, &complexTypePrototypeVal))
         return nullptr;
 
     JS_ASSERT(complexTypePrototypeVal.isObject()); // immutable binding
     RootedObject complexTypePrototypeObj(cx,
         &complexTypePrototypeVal.toObject());
 
     if (!JSObject::getProperty(cx, complexTypePrototypeObj,
                                complexTypePrototypeObj,
-                               cx->names().classPrototype,
+                               cx->names().prototype,
                                &complexTypePrototypePrototypeVal))
         return nullptr;
 
     RootedObject prototypeObj(cx,
         NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, global));
 
     JS_ASSERT(complexTypePrototypePrototypeVal.isObject()); // immutable binding
     RootedObject proto(cx, &complexTypePrototypePrototypeVal.toObject());
@@ -1328,17 +1328,17 @@ GlobalObject::initDataObject(JSContext *
 
     if (!LinkConstructorAndPrototype(cx, DataCtor, DataProto))
         return false;
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_Data,
                                        DataCtor, DataProto))
         return false;
 
-    global->setReservedSlot(JSProto_Data, ObjectValue(*DataCtor));
+    global->setConstructor(JSProto_Data, ObjectValue(*DataCtor));
     return true;
 }
 
 bool
 GlobalObject::initTypeObject(JSContext *cx, Handle<GlobalObject *> global)
 {
     RootedObject TypeProto(cx, global->getOrCreateDataObject(cx));
     if (!TypeProto)
@@ -1353,28 +1353,28 @@ GlobalObject::initTypeObject(JSContext *
 
     if (!LinkConstructorAndPrototype(cx, TypeCtor, TypeProto))
         return false;
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_Type,
                                        TypeCtor, TypeProto))
         return false;
 
-    global->setReservedSlot(JSProto_Type, ObjectValue(*TypeCtor));
+    global->setConstructor(JSProto_Type, ObjectValue(*TypeCtor));
     return true;
 }
 
 bool
 GlobalObject::initArrayTypeObject(JSContext *cx, Handle<GlobalObject *> global)
 {
     RootedFunction ctor(cx,
         global->createConstructor(cx, ArrayType::construct,
                                   cx->names().ArrayType, 2));
 
-    global->setReservedSlot(JSProto_ArrayTypeObject, ObjectValue(*ctor));
+    global->setConstructor(JSProto_ArrayTypeObject, ObjectValue(*ctor));
     return true;
 }
 
 static JSObject *
 SetupComplexHeirarchy(JSContext *cx, Handle<GlobalObject*> global, JSProtoKey protoKey,
                       HandleObject complexObject, MutableHandleObject proto,
                       MutableHandleObject protoProto)
 {
@@ -1388,17 +1388,17 @@ SetupComplexHeirarchy(JSContext *cx, Han
         return nullptr;
 
     RootedObject DataObject(cx, global->getOrCreateDataObject(cx));
     if (!DataObject)
         return nullptr;
 
     RootedValue DataProtoVal(cx);
     if (!JSObject::getProperty(cx, DataObject, DataObject,
-                               cx->names().classPrototype, &DataProtoVal))
+                               cx->names().prototype, &DataProtoVal))
         return nullptr;
 
     RootedObject DataProto(cx, &DataProtoVal.toObject());
     if (!DataProto)
         return nullptr;
 
     RootedObject prototypeObj(cx,
         NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, global));
@@ -1438,17 +1438,17 @@ InitType(JSContext *cx, HandleObject glo
     JS_ASSERT(globalObj->isNative());
     Rooted<GlobalObject*> global(cx, &globalObj->as<GlobalObject>());
     RootedObject ctor(cx, global->getOrCreateTypeObject(cx));
     if (!ctor)
         return false;
 
     RootedValue protoVal(cx);
     if (!JSObject::getProperty(cx, ctor, ctor,
-                               cx->names().classPrototype, &protoVal))
+                               cx->names().prototype, &protoVal))
         return false;
 
     JS_ASSERT(protoVal.isObject());
     RootedObject protoObj(cx, &protoVal.toObject());
 
     if (!JS_DefineFunction(cx, protoObj, "equivalent", TypeEquivalent, 0, 0))
         return false;
 
@@ -1681,17 +1681,17 @@ BinaryBlock::obj_finalize(js::FreeOp *op
 
 /*static*/ JSObject *
 BinaryBlock::createNull(JSContext *cx, HandleObject type, HandleValue owner)
 {
     JS_ASSERT(IsBinaryType(type));
 
     RootedValue protoVal(cx);
     if (!JSObject::getProperty(cx, type, type,
-                               cx->names().classPrototype, &protoVal))
+                               cx->names().prototype, &protoVal))
         return nullptr;
 
     RootedObject obj(cx,
         NewObjectWithClassProto(cx, &class_, &protoVal.toObject(), nullptr));
     obj->initFixedSlot(SLOT_DATATYPE, ObjectValue(*type));
     obj->initFixedSlot(SLOT_BLOCKREFOWNER, owner);
 
     // Tag the type object for this instance with the type
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1363,19 +1363,16 @@ ICUpdatedStub::addUpdateStubForValue(JSC
                                      HandleId id, HandleValue val)
 {
     if (numOptimizedStubs_ >= MAX_OPTIMIZED_STUBS) {
         // TODO: if the TypeSet becomes unknown or has the AnyObject type,
         // replace stubs with a single stub to handle these.
         return true;
     }
 
-    if (!obj->getType(cx))
-        return false;
-
     types::EnsureTrackPropertyTypes(cx, obj, id);
 
     if (val.isPrimitive()) {
         JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType();
 
         // Check for existing TypeUpdate stub.
         ICTypeUpdate_PrimitiveSet *existingStub = nullptr;
         for (ICStubConstIterator iter = firstUpdateStub_; !iter.atEnd(); iter++) {
@@ -5321,16 +5318,20 @@ ICIn_Fallback::Compiler::generateStubCod
 static bool
 TryAttachGlobalNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *stub,
                         HandleObject global, HandlePropertyName name)
 {
     JS_ASSERT(global->is<GlobalObject>());
 
     RootedId id(cx, NameToId(name));
 
+    // Instantiate this global property, for use during Ion compilation.
+    if (IsIonEnabled(cx))
+        types::EnsureTrackPropertyTypes(cx, global, NameToId(name));
+
     // The property must be found, and it must be found as a normal data property.
     RootedShape shape(cx, global->nativeLookup(cx, id));
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return true;
 
     JS_ASSERT(shape->slot() >= global->numFixedSlots());
     uint32_t slot = shape->slot() - global->numFixedSlots();
 
@@ -5378,16 +5379,20 @@ TryAttachScopeNameStub(JSContext *cx, Ha
             break;
 
         scopeChain = scopeChain->enclosingScope();
     }
 
     if (!IsCacheableGetPropReadSlot(scopeChain, scopeChain, shape))
         return true;
 
+    // Instantiate properties on singleton scope chain objects, for use during Ion compilation.
+    if (scopeChain->hasSingletonType() && IsIonEnabled(cx))
+        types::EnsureTrackPropertyTypes(cx, scopeChain, NameToId(name));
+
     bool isFixedSlot;
     uint32_t offset;
     GetFixedOrDynamicSlotOffset(scopeChain, shape->slot(), &isFixedSlot, &offset);
 
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     ICStub *newStub;
 
     switch (shapes.length()) {
@@ -5805,16 +5810,20 @@ TryAttachNativeGetPropStub(JSContext *cx
         return true;
 
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     if (!isDOMProxy && IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
         GetFixedOrDynamicSlotOffset(holder, shape->slot(), &isFixedSlot, &offset);
 
+        // Instantiate this property for singleton holders, for use during Ion compilation.
+        if (IsIonEnabled(cx))
+            types::EnsureTrackPropertyTypes(cx, holder, NameToId(name));
+
         ICStub::Kind kind = (obj == holder) ? ICStub::GetProp_Native
                                             : ICStub::GetProp_NativePrototype;
 
         IonSpew(IonSpew_BaselineIC, "  Generating GetProp(%s %s) stub",
                     isDOMProxy ? "DOMProxy" : "Native",
                     (obj == holder) ? "direct" : "prototype");
         ICGetPropNativeCompiler compiler(cx, kind, monitorStub, obj, holder, isFixedSlot, offset);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
@@ -5914,16 +5923,20 @@ TryAttachStringGetPropStub(JSContext *cx
 {
     JS_ASSERT(!*attached);
     JS_ASSERT(val.isString());
 
     RootedObject stringProto(cx, script->global().getOrCreateStringPrototype(cx));
     if (!stringProto)
         return false;
 
+    // Instantiate this property, for use during Ion compilation.
+    if (IsIonEnabled(cx))
+        types::EnsureTrackPropertyTypes(cx, stringProto, NameToId(name));
+
     // For now, only look for properties directly set on String.prototype
     RootedId propId(cx, NameToId(name));
     RootedShape shape(cx, stringProto->nativeLookup(cx, propId));
     if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
@@ -7460,16 +7473,21 @@ TryAttachCallStub(JSContext *cx, ICCall_
             // Before adding new stub, unlink all previous Call_Scripted.
             stub->unlinkStubsWithKind(cx, ICStub::Call_Scripted);
 
             // Add new generalized stub.
             stub->addNewStub(newStub);
             return true;
         }
 
+        // Keep track of the function's |prototype| property in type
+        // information, for use during Ion compilation.
+        if (IsIonEnabled(cx))
+            types::EnsureTrackPropertyTypes(cx, fun, NameToId(cx->names().prototype));
+
         IonSpew(IonSpew_BaselineIC,
                 "  Generating Call_Scripted stub (fun=%p, %s:%d, cons=%s)",
                 fun.get(), fun->nonLazyScript()->filename(), fun->nonLazyScript()->lineno,
                 constructing ? "yes" : "no");
         ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
                                         calleeScript, constructing, pc - script->code);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2146,17 +2146,17 @@ CodeGenerator::visitApplyArgsGeneric(LAp
     emitPushArguments(apply, copyreg);
 
     masm.checkStackAlignment();
 
     // If the function is known to be uncompilable, only emit the call to InvokeFunction.
     ExecutionMode executionMode = gen->info().executionMode();
     if (apply->hasSingleTarget()) {
         JSFunction *target = apply->getSingleTarget();
-        if (!CanIonCompile(target, executionMode)) {
+        if (target->isNative()) {
             if (!emitCallInvokeFunction(apply, copyreg))
                 return false;
             emitPopArguments(apply, copyreg);
             return true;
         }
     }
 
     Label end, invoke;
--- a/js/src/jit/ExecutionModeInlines.h
+++ b/js/src/jit/ExecutionModeInlines.h
@@ -69,22 +69,16 @@ CanIonCompile(JSScript *script, Executio
       case DefinitePropertiesAnalysis: return true;
       default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
     return false;
 }
 
 static inline bool
-CanIonCompile(JSFunction *fun, ExecutionMode cmode)
-{
-    return fun->isInterpreted() && CanIonCompile(fun->nonLazyScript(), cmode);
-}
-
-static inline bool
 CompilingOffThread(JSScript *script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->isIonCompilingOffThread();
       case ParallelExecution: return script->isParallelIonCompilingOffThread();
       default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1917,17 +1917,17 @@ AnalyzePoppedThis(JSContext *cx, types::
         MCallSetProperty *setprop = ins->toCallSetProperty();
 
         if (setprop->object() != thisValue)
             return true;
 
         // Don't use GetAtomId here, we need to watch for SETPROP on
         // integer properties and bail out. We can't mark the aggregate
         // JSID_VOID type property as being in a definite slot.
-        if (setprop->name() == cx->names().classPrototype ||
+        if (setprop->name() == cx->names().prototype ||
             setprop->name() == cx->names().proto ||
             setprop->name() == cx->names().constructor)
         {
             return true;
         }
 
         // Ignore assignments to properties that were already written to.
         if (baseobj->nativeLookup(cx, NameToId(setprop->name()))) {
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -266,16 +266,23 @@ IonBuilder::canInlineTarget(JSFunction *
         return false;
     }
 
     if (target->getParent() != &script()->global()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch");
         return false;
     }
 
+    // Allow constructing lazy scripts when performing the definite properties
+    // analysis, as baseline has not been used to warm the caller up yet.
+    if (target->isInterpretedLazy() && info().executionMode() == DefinitePropertiesAnalysis) {
+        if (!target->getOrCreateScript(cx))
+            return false;
+    }
+
     if (!target->hasScript()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to lack of Non-Lazy script");
         return false;
     }
 
     if (constructing && !target->isInterpretedConstructor()) {
         IonSpew(IonSpew_Inlining, "Cannot inline because callee is not a constructor");
         return false;
@@ -4579,21 +4586,21 @@ IonBuilder::createThisScripted(MDefiniti
     // - First try an idempotent property cache.
     // - Upon failing idempotent property cache, we can't use a non-idempotent cache,
     //   therefore we fallback to CallGetProperty
     //
     // Note: both CallGetProperty and GetPropertyCache can trigger a GC,
     //       and thus invalidation.
     MInstruction *getProto;
     if (!invalidatedIdempotentCache()) {
-        MGetPropertyCache *getPropCache = MGetPropertyCache::New(callee, cx->names().classPrototype);
+        MGetPropertyCache *getPropCache = MGetPropertyCache::New(callee, cx->names().prototype);
         getPropCache->setIdempotent();
         getProto = getPropCache;
     } else {
-        MCallGetProperty *callGetProp = MCallGetProperty::New(callee, cx->names().classPrototype);
+        MCallGetProperty *callGetProp = MCallGetProperty::New(callee, cx->names().prototype);
         callGetProp->setIdempotent();
         getProto = callGetProp;
     }
     current->add(getProto);
 
     // Create this from prototype
     MCreateThisWithProto *createThis = MCreateThisWithProto::New(callee, getProto);
     current->add(createThis);
@@ -4605,17 +4612,17 @@ JSObject *
 IonBuilder::getSingletonPrototype(JSFunction *target)
 {
     if (!target || !target->hasSingletonType())
         return nullptr;
     types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
     if (targetType->unknownProperties())
         return nullptr;
 
-    jsid protoid = NameToId(cx->names().classPrototype);
+    jsid protoid = NameToId(cx->names().prototype);
     types::HeapTypeSetKey protoProperty = targetType->property(protoid);
 
     return protoProperty.singleton(constraints());
 }
 
 MDefinition *
 IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
 {
@@ -5442,17 +5449,17 @@ IonBuilder::jsop_initelem_array()
     // to them during initialization.
     bool needStub = false;
     types::TypeObjectKey *initializer = obj->resultTypeSet()->getObject(0);
     if (value->isConstant() && value->toConstant()->value().isMagic(JS_ELEMENTS_HOLE)) {
         if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
             needStub = true;
     } else if (!initializer->unknownProperties()) {
         types::HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
-        if (!TypeSetIncludes(elemTypes.actualTypes, value->type(), value->resultTypeSet())) {
+        if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
             elemTypes.freeze(constraints());
             needStub = true;
         }
     }
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
@@ -5989,17 +5996,17 @@ IonBuilder::testSingletonProperty(JSObje
     // Ensure the property does not appear anywhere on the prototype chain
     // before |holder|, and that |holder| only has the result object for its
     // property.
     while (true) {
         types::TypeObjectKey *objType = types::TypeObjectKey::get(obj);
         if (objType->unknownProperties())
             return true;
 
-        types::HeapTypeSetKey property = objType->property(idRoot);
+        types::HeapTypeSetKey property = objType->property(NameToId(name), context());
         if (obj != holder) {
             if (property.notEmpty(constraints()))
                 return true;
         } else {
             if (property.singleton(constraints()) != singleton)
                 return true;
             break;
         }
@@ -6071,17 +6078,17 @@ IonBuilder::testSingletonPropertyTypes(M
         // has the singleton property, the access will not be on a missing property.
         for (unsigned i = 0; i < types->getObjectCount(); i++) {
             types::TypeObjectKey *object = types->getObject(i);
             if (!object)
                 continue;
 
             if (object->unknownProperties())
                 return true;
-            types::HeapTypeSetKey property = object->property(NameToId(name));
+            types::HeapTypeSetKey property = object->property(NameToId(name), context());
             if (property.notEmpty(constraints()))
                 return true;
 
             if (JSObject *proto = object->proto().toObjectOrNull()) {
                 // Test this type.
                 bool thoughtConstant = false;
                 if (!testSingletonProperty(proto, singleton, name, &thoughtConstant))
                     return false;
@@ -6208,27 +6215,27 @@ IonBuilder::getStaticName(JSObject *stat
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) {
         *psucceeded = false;
         return true;
     }
 
     types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
     Maybe<types::HeapTypeSetKey> propertyTypes;
     if (!staticType->unknownProperties()) {
-        propertyTypes.construct(staticType->property(id));
+        propertyTypes.construct(staticType->property(id, context()));
         if (propertyTypes.ref().configured(constraints(), staticType)) {
             // The property has been reconfigured as non-configurable, non-enumerable
             // or non-writable.
             *psucceeded = false;
             return true;
         }
     }
 
     types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), staticType,
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), staticType,
                                                 name, baseTypes, /* updateObserved = */ true);
     types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
     // If the property is permanent, a shape guard isn't necessary.
 
     JSObject *singleton = types->getSingleton();
 
     JSValueType knownType = types->getKnownTypeTag();
@@ -6261,16 +6268,19 @@ IonBuilder::getStaticName(JSObject *stat
 
     return loadSlot(obj, shape, rvalType, barrier, types);
 }
 
 // Whether 'types' includes all possible values represented by input/inputTypes.
 bool
 jit::TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *inputTypes)
 {
+    if (!types)
+        return inputTypes && inputTypes->empty();
+
     switch (input) {
       case MIRType_Undefined:
       case MIRType_Null:
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Float32:
       case MIRType_String:
@@ -6319,17 +6329,17 @@ IonBuilder::setStaticName(JSObject *stat
 
     types::HeapTypeSetKey propertyTypes = staticType->property(id);
     if (propertyTypes.configured(constraints(), staticType)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_setprop(name);
     }
 
-    if (!TypeSetIncludes(propertyTypes.actualTypes, value->type(), value->resultTypeSet()))
+    if (!TypeSetIncludes(propertyTypes.maybeTypes(), value->type(), value->resultTypeSet()))
         return jsop_setprop(name);
 
     current->pop();
 
     // Pop the bound object on the stack.
     MDefinition *obj = current->pop();
     JS_ASSERT(&obj->toConstant()->value().toObject() == staticObject);
 
@@ -6728,17 +6738,17 @@ IonBuilder::getElemTryCache(bool *emitte
     // of this getelem.
     bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
     if (index->mightBeType(MIRType_Int32) && nonNativeGetElement)
         return true;
 
     // Emit GetElementCache.
 
     types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), obj, nullptr, baseTypes);
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
     types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
     // Always add a barrier if the index might be a string, so that the cache
     // can attach stubs for particular properties.
     if (index->mightBeType(MIRType_String))
         barrier = true;
 
     // See note about always needing a barrier in jsop_getprop.
@@ -6777,17 +6787,17 @@ IonBuilder::jsop_getelem_dense(MDefiniti
     if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String) && baseTypes->noConstraints()) {
         // Indexed call on an element of an array. Populate the observed types
         // with any objects that could be in the array, to avoid extraneous
         // type barriers.
         if (!AddObjectsForPropertyRead(cx, obj, nullptr, baseTypes))
             return false;
     }
 
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), obj, nullptr, baseTypes);
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
     types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
     bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
 
     // Reads which are on holes in the object do not have to bail out if
     // undefined values have been observed at this access site and the access
     // cannot hit another indexed property on the object or its prototypes.
     bool readOutOfBounds =
@@ -7571,17 +7581,18 @@ IonBuilder::getDefiniteSlot(types::Tempo
 
     types::TypeObjectKey *type = types->getObject(0);
     if (type->unknownProperties())
         return false;
 
     jsid id = NameToId(name);
 
     *property = type->property(id);
-    return property->actualTypes->definiteProperty() &&
+    return property->maybeTypes() &&
+           property->maybeTypes()->definiteProperty() &&
            !property->configured(constraints(), type);
 }
 
 bool
 IonBuilder::jsop_runonce()
 {
     MRunOncePrologue *ins = MRunOncePrologue::New();
     current->add(ins);
@@ -7610,17 +7621,17 @@ TestClassHasAccessorHook(const Class *cl
     return false;
 }
 
 static inline bool
 TestTypeHasOwnProperty(types::TypeObjectKey *typeObj, PropertyName *name, bool &cont)
 {
     cont = true;
     types::HeapTypeSetKey propSet = typeObj->property(NameToId(name));
-    if (!propSet.actualTypes->empty())
+    if (propSet.maybeTypes() && !propSet.maybeTypes()->empty())
         cont = false;
     // Note: Callers must explicitly freeze the property type set later on if optimizing.
     return true;
 }
 
 static inline bool
 TestCommonAccessorProtoChain(JSContext *cx, PropertyName *name,
                              bool isGetter, JSObject *foundProto,
@@ -8027,33 +8038,34 @@ IonBuilder::jsop_getprop(PropertyName *n
 {
     bool emitted = false;
 
     // Try to optimize arguments.length.
     if (!getPropTryArgumentsLength(&emitted) || emitted)
         return emitted;
 
     types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(),
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
                                                 current->peek(-1), name, baseTypes);
     types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
     // Try to hardcode known constants.
     if (!getPropTryConstant(&emitted, name, types) || emitted)
         return emitted;
 
     // Except when loading constants above, always use a call if we are doing
     // the definite properties analysis and not actually emitting code, to
-    // simplify later analysis.
-    if (info().executionMode() == DefinitePropertiesAnalysis) {
+    // simplify later analysis. Also skip deeper analysis if there are no known
+    // types for this operation, as it will always invalidate when executing.
+    if (info().executionMode() == DefinitePropertiesAnalysis || baseTypes->empty()) {
         MDefinition *obj = current->pop();
         MCallGetProperty *call = MCallGetProperty::New(obj, name);
         current->add(call);
         current->push(call);
-        return resumeAfter(call);
+        return resumeAfter(call) && pushTypeBarrier(call, types, true);
     }
 
     // Try to emit loads from known binary data blocks
     if (!getPropTryTypedObject(&emitted, name, types) || emitted)
         return emitted;
 
     // Try to emit loads from definite slots.
     if (!getPropTryDefiniteSlot(&emitted, name, barrier, types) || emitted)
@@ -8263,17 +8275,17 @@ IonBuilder::getPropTryDefiniteSlot(bool 
     MDefinition *obj = current->pop();
     MDefinition *useObj = obj;
     if (obj->type() != MIRType_Object) {
         MGuardObject *guard = MGuardObject::New(obj);
         current->add(guard);
         useObj = guard;
     }
 
-    MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, property.actualTypes->definiteSlot());
+    MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, property.maybeTypes()->definiteSlot());
     if (!barrier)
         fixed->setResultType(MIRTypeFromValueType(types->getKnownTypeTag()));
 
     current->add(fixed);
     current->push(fixed);
 
     if (!pushTypeBarrier(fixed, types, barrier))
         return false;
@@ -8743,17 +8755,17 @@ IonBuilder::setPropTryDefiniteSlot(bool 
 
     if (barrier)
         return true;
 
     types::HeapTypeSetKey property;
     if (!getDefiniteSlot(obj->resultTypeSet(), name, &property))
         return true;
 
-    MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, property.actualTypes->definiteSlot(), value);
+    MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, property.maybeTypes()->definiteSlot(), value);
     current->add(fixed);
     current->push(value);
 
     if (property.needsBarrier(constraints()))
         fixed->setNeedsBarrier();
 
     if (!resumeAfter(fixed))
         return false;
@@ -9390,17 +9402,17 @@ IonBuilder::jsop_instanceof()
         if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction())
             break;
 
         types::TypeObjectKey *rhsType = types::TypeObjectKey::get(rhsObject);
         if (rhsType->unknownProperties())
             break;
 
         types::HeapTypeSetKey protoProperty =
-            rhsType->property(NameToId(cx->names().classPrototype));
+            rhsType->property(NameToId(cx->names().prototype));
         JSObject *protoObject = protoProperty.singleton(constraints());
         if (!protoObject)
             break;
 
         rhs->setFoldedUnchecked();
 
         MInstanceOf *ins = new MInstanceOf(obj, protoObject);
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -685,16 +685,26 @@ class IonBuilder : public MIRGenerator
     types::CompilerConstraintList *constraints() {
         return constraints_;
     }
 
     bool isInlineBuilder() const {
         return callerBuilder_ != nullptr;
     }
 
+    JSContext *context() {
+        // JSContexts are only available to IonBuilder when running on the main
+        // thread, which after bug 785905 will only occur when doing eager
+        // analyses with no available baseline information. Until this bug is
+        // completed, both the |cx| member and |context()| may be used.
+        if (info().executionMode() == DefinitePropertiesAnalysis)
+            return cx;
+        return NULL;
+    }
+
   private:
     bool init();
 
     JSContext *cx;
     BaselineFrame *baselineFrame_;
     AbortReason abortReason_;
     ScopedJSDeletePtr<TypeRepresentationSetHash> reprSetHash_;
 
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -549,17 +549,17 @@ HandleException(ResumeFromException *rfe
                 }
 
                 JS_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME);
 
                 // When profiling, each frame popped needs a notification that
                 // the function has exited, so invoke the probe that a function
                 // is exiting.
                 JSScript *script = frames.script();
-                Probes::exitScript(cx, script, script->function(), nullptr);
+                probes::ExitScript(cx, script, script->function(), nullptr);
                 if (!frames.more())
                     break;
                 ++frames;
             }
 
             IonScript *ionScript = nullptr;
             if (iter.checkInvalidation(&ionScript))
                 ionScript->decref(cx->runtime()->defaultFreeOp());
@@ -569,17 +569,17 @@ HandleException(ResumeFromException *rfe
             bool calledDebugEpilogue = false;
 
             HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue);
             if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
                 return;
 
             // Unwind profiler pseudo-stack
             JSScript *script = iter.script();
-            Probes::exitScript(cx, script, script->function(), iter.baselineFrame());
+            probes::ExitScript(cx, script, script->function(), iter.baselineFrame());
             // After this point, any pushed SPS frame would have been popped if it needed
             // to be.  Unset the flag here so that if we call DebugEpilogue below,
             // it doesn't try to pop the SPS frame again.
             iter.baselineFrame()->unsetPushedSPSFrame();
  
             if (cx->compartment()->debugMode() && !calledDebugEpilogue) {
                 // If DebugEpilogue returns |true|, we have to perform a forced
                 // return, e.g. return frame->returnValue() to the caller.
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -1059,17 +1059,17 @@ class MacroAssembler : public MacroAssem
         storePtr(temp2, Address(temp, ProfileEntry::offsetOfString()));
 
         loadPtr(script, temp2);
         storePtr(temp2, Address(temp, ProfileEntry::offsetOfScript()));
 
         storePtr(ImmPtr(nullptr), Address(temp, ProfileEntry::offsetOfStackAddress()));
 
         // Store 0 for PCIdx because that's what interpreter does.
-        // (See Probes::enterScript, which calls spsProfiler.enter, which pushes an entry
+        // (See probes::EnterScript, which calls spsProfiler.enter, which pushes an entry
         //  with 0 pcIdx).
         store32(Imm32(0), Address(temp, ProfileEntry::offsetOfPCIdx()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
         movePtr(ImmPtr(p->addressOfSizePointer()), temp);
         loadPtr(Address(temp, 0), temp);
         add32(Imm32(1), Address(temp, 0));
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -216,17 +216,17 @@ IonBuilder::inlineArray(CallInfo &callIn
         if (!baseType)
             return InliningStatus_Error;
         types::TypeObjectKey *type = types::TypeObjectKey::get(baseType);
         if (!type->unknownProperties()) {
             types::HeapTypeSetKey elemTypes = type->property(JSID_VOID);
 
             for (uint32_t i = 0; i < initLength; i++) {
                 MDefinition *value = callInfo.getArg(i);
-                if (!TypeSetIncludes(elemTypes.actualTypes, value->type(), value->resultTypeSet())) {
+                if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
                     elemTypes.freeze(constraints());
                     return InliningStatus_NotInlined;
                 }
             }
         }
     }
 
     // A single integer argument denotes initial length.
@@ -327,17 +327,17 @@ IonBuilder::inlineArrayPopShift(CallInfo
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     types::StackTypeSet *returnTypes = getOriginalInlineReturnTypeSet();
     bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
 
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(),
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
                                                 callInfo.thisArg(), nullptr, returnTypes);
     if (barrier)
         returnType = MIRType_Value;
 
     MArrayPopShift *ins = MArrayPopShift::New(callInfo.thisArg(), mode,
                                               needsHoleCheck, maybeUndefined);
     current->add(ins);
     current->push(ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2775,22 +2775,22 @@ PropertyReadNeedsTypeBarrier(JSContext *
                              types::TypeSet *observed)
 {
     // If the object being read from has types for the property which haven't
     // been observed at this access site, the read could produce a new type and
     // a barrier is needed. Note that this only covers reads from properties
     // which are accounted for by type information, i.e. native data properties
     // and elements.
 
-    if (object->unknownProperties())
+    if (object->unknownProperties() || observed->empty())
         return true;
 
     jsid id = name ? NameToId(name) : JSID_VOID;
     types::HeapTypeSetKey property = object->property(id);
-    if (!TypeSetIncludes(observed, MIRType_Value, property.actualTypes))
+    if (property.maybeTypes() && !TypeSetIncludes(observed, MIRType_Value, property.maybeTypes()))
         return true;
 
     // Type information for singleton objects is not required to reflect the
     // initial 'undefined' value for native properties, in particular global
     // variables declared with 'var'. Until the property is assigned a value
     // other than undefined, a barrier is required.
     if (name && object->singleton() && object->singleton()->isNative()) {
         Shape *shape = object->singleton()->nativeLookup(cx, name);
@@ -2802,60 +2802,69 @@ PropertyReadNeedsTypeBarrier(JSContext *
         }
     }
 
     property.freeze(constraints);
     return false;
 }
 
 bool
-jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
+                                  types::CompilerConstraintList *constraints,
                                   types::TypeObjectKey *object, PropertyName *name,
                                   types::StackTypeSet *observed, bool updateObserved)
 {
     // If this access has never executed, try to add types to the observed set
     // according to any property which exists on the object or its prototype.
     if (updateObserved && observed->empty() && observed->noConstraints() && name) {
         JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull();
 
         while (obj) {
             if (!obj->isNative())
                 break;
 
-            Value v;
-            if (HasDataProperty(cx, obj, NameToId(name), &v)) {
-                if (v.isUndefined())
-                    break;
-                observed->addType(cx, types::GetValueType(v));
+            types::TypeObjectKey *typeObj = types::TypeObjectKey::get(obj);
+            if (!typeObj->unknownProperties()) {
+                types::HeapTypeSetKey property = typeObj->property(NameToId(name), propertycx);
+                if (property.maybeTypes()) {
+                    types::TypeSet::TypeList types;
+                    if (!property.maybeTypes()->enumerateTypes(&types))
+                        return false;
+                    if (types.length()) {
+                        observed->addType(cx, types[0]);
+                        break;
+                    }
+                }
             }
 
             obj = obj->getProto();
         }
     }
 
     return PropertyReadNeedsTypeBarrier(cx, constraints, object, name, observed);
 }
 
 bool
-jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
+                                  types::CompilerConstraintList *constraints,
                                   MDefinition *obj, PropertyName *name,
                                   types::StackTypeSet *observed)
 {
     if (observed->unknown())
         return false;
 
     types::TypeSet *types = obj->resultTypeSet();
     if (!types || types->unknownObject())
         return true;
 
     bool updateObserved = types->getObjectCount() == 1;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObjectKey *object = types->getObject(i);
         if (object) {
-            if (PropertyReadNeedsTypeBarrier(cx, constraints, object, name,
+            if (PropertyReadNeedsTypeBarrier(cx, propertycx, constraints, object, name,
                                              observed, updateObserved))
             {
                 return true;
             }
         }
     }
 
     return false;
@@ -2978,29 +2987,32 @@ TryAddTypeBarrierForWrite(types::Compile
         if (!object)
             continue;
 
         if (object->unknownProperties())
             return false;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         types::HeapTypeSetKey property = object->property(id);
-        if (TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
+        if (!property.maybeTypes())
+            return false;
+
+        if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet()))
             return false;
 
         // This freeze is not required for correctness, but ensures that we
         // will recompile if the property types change and the barrier can
         // potentially be removed.
         property.freeze(constraints);
 
         if (aggregateProperty.empty()) {
             aggregateProperty.construct(property);
         } else {
-            if (!aggregateProperty.ref().actualTypes->isSubset(property.actualTypes) ||
-                !property.actualTypes->isSubset(aggregateProperty.ref().actualTypes))
+            if (!aggregateProperty.ref().maybeTypes()->isSubset(property.maybeTypes()) ||
+                !property.maybeTypes()->isSubset(aggregateProperty.ref().maybeTypes()))
             {
                 return false;
             }
         }
     }
 
     JS_ASSERT(!aggregateProperty.empty());
 
@@ -3025,17 +3037,17 @@ TryAddTypeBarrierForWrite(types::Compile
       }
       default:;
     }
 
     if ((*pvalue)->type() != MIRType_Value)
         return false;
 
     types::TemporaryTypeSet *types =
-        aggregateProperty.ref().actualTypes->clone(GetIonContext()->temp->lifoAlloc());
+        aggregateProperty.ref().maybeTypes()->clone(GetIonContext()->temp->lifoAlloc());
     if (!types)
         return false;
 
     MInstruction *ins = MMonitorTypes::New(*pvalue, types);
     current->add(ins);
     return true;
 }
 
@@ -3080,17 +3092,17 @@ jit::PropertyWriteNeedsTypeBarrier(types
 
         // TI doesn't track TypedArray objects and should never insert a type
         // barrier for them.
         if (IsTypedArrayClass(object->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         types::HeapTypeSetKey property = object->property(id);
-        if (!TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
+        if (!TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
             // Either pobj or pvalue needs to be modified to filter out the
             // types which the value could have but are not in the property,
             // or a VM call is required. A VM call is always required if pobj
             // and pvalue cannot be modified.
             if (!canModify)
                 return true;
             success = TryAddTypeBarrierForWrite(constraints, current, types, name, pvalue);
             break;
@@ -3112,20 +3124,20 @@ jit::PropertyWriteNeedsTypeBarrier(types
         types::TypeObjectKey *object = types->getObject(i);
         if (!object || object->unknownProperties())
             continue;
         if (IsTypedArrayClass(object->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         types::HeapTypeSetKey property = object->property(id);
-        if (TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
+        if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet()))
             continue;
 
-        if (!property.actualTypes->empty() || excluded)
+        if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
             return true;
         excluded = object->isTypeObject() ? object->asTypeObject() : object->asSingleObject()->getType(GetIonContext()->cx);
     }
 
     JS_ASSERT(excluded);
 
     *pobj = AddTypeGuard(current, *pobj, excluded, /* bailOnEquality = */ true);
     return false;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8988,20 +8988,22 @@ typedef Vector<MDefinition *, 8, IonAllo
 
 bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
 bool ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
                                ScalarTypeRepresentation::Type *arrayType);
 bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj);
 bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
                                           MDefinition *obj);
 MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj);
-bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
+                                  types::CompilerConstraintList *constraints,
                                   types::TypeObjectKey *object, PropertyName *name,
                                   types::StackTypeSet *observed, bool updateObserved);
-bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
+                                  types::CompilerConstraintList *constraints,
                                   MDefinition *obj, PropertyName *name,
                                   types::StackTypeSet *observed);
 bool PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
                                              MDefinition *obj, PropertyName *name,
                                              types::TemporaryTypeSet *observed);
 bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
                               MDefinition *obj, PropertyName *name);
 bool AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -711,17 +711,17 @@ DebugEpilogue(JSContext *cx, BaselineFra
         JS_ASSERT_IF(frame->hasCallObj(), frame->scopeChain()->as<CallObject>().isForEval());
         DebugScopes::onPopStrictEvalScope(frame);
     }
 
     // If the frame has a pushed SPS frame, make sure to pop it.
     if (frame->hasPushedSPSFrame()) {
         cx->runtime()->spsProfiler.exit(cx, frame->script(), frame->maybeFun());
         // Unset the pushedSPSFrame flag because DebugEpilogue may get called before
-        // Probes::exitScript in baseline during exception handling, and we don't
+        // probes::ExitScript in baseline during exception handling, and we don't
         // want to double-pop SPS frames.
         frame->unsetPushedSPSFrame();
     }
 
     if (!ok) {
         // Pop this frame by updating ionTop, so that the exception handling
         // code will start at the previous frame.
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1548,16 +1548,17 @@ JS_ResolveStandardClass(JSContext *cx, H
     JSRuntime *rt;
     JSAtom *atom;
     const JSStdName *stdnm;
     unsigned i;
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
+    JS_ASSERT(obj->is<GlobalObject>());
     *resolved = false;
 
     rt = cx->runtime();
     if (!rt->hasContexts() || !JSID_IS_ATOM(id))
         return true;
 
     RootedString idstr(cx, JSID_TO_STRING(id));
 
@@ -1617,36 +1618,36 @@ JS_ResolveStandardClass(JSContext *cx, H
         }
     }
 
     if (stdnm) {
         /*
          * If this standard class is anonymous, then we don't want to resolve
          * by name.
          */
-        JS_ASSERT(obj->is<GlobalObject>());
         if (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS)
             return true;
 
-        if (IsStandardClassResolved(obj, stdnm->clasp))
+        if (obj->as<GlobalObject>().isStandardClassResolved(stdnm->clasp))
             return true;
 
         if (!stdnm->init(cx, obj))
             return false;
         *resolved = true;
     }
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
+    MOZ_ASSERT(obj->is<GlobalObject>());
 
     /*
      * Check whether we need to bind 'undefined' and define it if so.
      * Since ES5 15.1.1.3 undefined can't be deleted.
      */
     HandlePropertyName undefinedName = cx->names().undefined;
     RootedValue undefinedValue(cx, UndefinedValue());
     if (!obj->nativeContains(cx, undefinedName) &&
@@ -1654,17 +1655,17 @@ JS_EnumerateStandardClasses(JSContext *c
                                   JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY)) {
         return false;
     }
 
     /* Initialize any classes that have not been initialized yet. */
     for (unsigned i = 0; standard_class_atoms[i].init; i++) {
         const JSStdName &stdnm = standard_class_atoms[i];
-        if (!js::IsStandardClassResolved(obj, stdnm.clasp)) {
+        if (!obj->as<GlobalObject>().isStandardClassResolved(stdnm.clasp)) {
             if (!stdnm.init(cx, obj))
                 return false;
         }
     }
 
     return true;
 }
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3187,17 +3187,17 @@ NewArray(ExclusiveContext *cxArg, uint32
     if (entry != -1) {
         cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, &ArrayObject::class_,
                                                                    cxArg->global(), allocKind, arr);
     }
 
     if (allocateCapacity && !EnsureNewArrayElements(cxArg, arr, length))
         return nullptr;
 
-    Probes::createObject(cxArg, arr);
+    probes::CreateObject(cxArg, arr);
     return arr;
 }
 
 ArrayObject * JS_FASTCALL
 js::NewDenseEmptyArray(JSContext *cx, JSObject *proto /* = nullptr */,
                        NewObjectKind newKind /* = GenericObject */)
 {
     return NewArray<false>(cx, 0, proto, newKind);
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -532,17 +532,17 @@ Exception(JSContext *cx, unsigned argc, 
      * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
      * called as functions, without operator new.  But as we do not give
      * each constructor a distinct JSClass, whose .name member is used by
      * NewNativeClassInstance to find the class prototype, we must get the
      * class prototype ourselves.
      */
     RootedObject callee(cx, &args.callee());
     RootedValue protov(cx);
-    if (!JSObject::getProperty(cx, callee, callee, cx->names().classPrototype, &protov))
+    if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
         return false;
 
     if (!protov.isObject()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE, "Error");
         return false;
     }
 
     RootedObject obj(cx, NewObjectWithGivenProto(cx, &ErrorObject::class_, &protov.toObject(),
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -160,17 +160,17 @@ static bool
 fun_enumerate(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->is<JSFunction>());
 
     RootedId id(cx);
     bool found;
 
     if (!obj->isBoundFunction()) {
-        id = NameToId(cx->names().classPrototype);
+        id = NameToId(cx->names().prototype);
         if (!JSObject::hasProperty(cx, obj, id, &found, 0))
             return false;
     }
 
     id = NameToId(cx->names().length);
     if (!JSObject::hasProperty(cx, obj, id, &found, 0))
         return false;
 
@@ -219,17 +219,17 @@ ResolveInterpretedFunctionPrototype(JSCo
 
     RootedObject proto(cx, NewObjectWithGivenProto(cx, clasp, objProto, nullptr, SingletonObject));
     if (!proto)
         return nullptr;
 
     // Per ES5 15.3.5.2 a user-defined function's .prototype property is
     // initially non-configurable, non-enumerable, and writable.
     RootedValue protoVal(cx, ObjectValue(*proto));
-    if (!JSObject::defineProperty(cx, obj, cx->names().classPrototype,
+    if (!JSObject::defineProperty(cx, obj, cx->names().prototype,
                                   protoVal, JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
     // Per ES5 13.2 the prototype's .constructor property is configurable,
     // non-enumerable, and writable.  However, per the 15 July 2013 ES6 draft,
@@ -251,17 +251,17 @@ bool
 js::fun_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
                 MutableHandleObject objp)
 {
     if (!JSID_IS_ATOM(id))
         return true;
 
     RootedFunction fun(cx, &obj->as<JSFunction>());
 
-    if (JSID_IS_ATOM(id, cx->names().classPrototype)) {
+    if (JSID_IS_ATOM(id, cx->names().prototype)) {
         /*
          * Built-in functions do not have a .prototype property per ECMA-262,
          * or (Object.prototype, Function.prototype, etc.) have that property
          * created eagerly.
          *
          * ES5 15.3.4: the non-native function object named Function.prototype
          * does not have a .prototype property.
          *
@@ -469,17 +469,17 @@ static bool
 fun_hasInstance(JSContext *cx, HandleObject objArg, MutableHandleValue v, bool *bp)
 {
     RootedObject obj(cx, objArg);
 
     while (obj->is<JSFunction>() && obj->isBoundFunction())
         obj = obj->as<JSFunction>().getBoundFunctionTarget();
 
     RootedValue pval(cx);
-    if (!JSObject::getProperty(cx, obj, obj, cx->names().classPrototype, &pval))
+    if (!JSObject::getProperty(cx, obj, obj, cx->names().prototype, &pval))
         return false;
 
     if (pval.isPrimitive()) {
         /*
          * Throw a runtime error if instanceof is called on a function that
          * has a non-object as its .prototype value.
          */
         RootedValue val(cx, ObjectValue(*obj));
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -44,17 +44,17 @@ using namespace js::analyze;
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 using mozilla::PodArrayZero;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 
 static inline jsid
 id_prototype(JSContext *cx) {
-    return NameToId(cx->names().classPrototype);
+    return NameToId(cx->names().prototype);
 }
 
 static inline jsid
 id___proto__(JSContext *cx) {
     return NameToId(cx->names().proto);
 }
 
 static inline jsid
@@ -331,55 +331,59 @@ TypeSet::isSubset(TypeSet *other)
             if (!other->hasType(Type::ObjectType(obj)))
                 return false;
         }
     }
 
     return true;
 }
 
+bool
+TypeSet::enumerateTypes(TypeList *list)
+{
+    /* If any type is possible, there's no need to worry about specifics. */
+    if (flags & TYPE_FLAG_UNKNOWN)
+        return list->append(Type::UnknownType());
+
+    /* Enqueue type set members stored as bits. */
+    for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
+        if (flags & flag) {
+            Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
+            if (!list->append(type))
+                return false;
+        }
+    }
+
+    /* If any object is possible, skip specifics. */
+    if (flags & TYPE_FLAG_ANYOBJECT)
+        return list->append(Type::AnyObjectType());
+
+    /* Enqueue specific object types. */
+    unsigned count = getObjectCount();
+    for (unsigned i = 0; i < count; i++) {
+        TypeObjectKey *object = getObject(i);
+        if (object) {
+            if (!list->append(Type::ObjectType(object)))
+                return false;
+        }
+    }
+
+    return true;
+}
+
 inline void
 TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint)
 {
     /*
      * Build all types in the set into a vector before triggering the
      * constraint, as doing so may modify this type set.
      */
-    Vector<Type> types(cx);
-
-    /* If any type is possible, there's no need to worry about specifics. */
-    if (flags & TYPE_FLAG_UNKNOWN) {
-        if (!types.append(Type::UnknownType()))
-            cx->compartment()->types.setPendingNukeTypes(cx);
-    } else {
-        /* Enqueue type set members stored as bits. */
-        for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
-            if (flags & flag) {
-                Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
-                if (!types.append(type))
-                    cx->compartment()->types.setPendingNukeTypes(cx);
-            }
-        }
-
-        /* If any object is possible, skip specifics. */
-        if (flags & TYPE_FLAG_ANYOBJECT) {
-            if (!types.append(Type::AnyObjectType()))
-                cx->compartment()->types.setPendingNukeTypes(cx);
-        } else {
-            /* Enqueue specific object types. */
-            unsigned count = getObjectCount();
-            for (unsigned i = 0; i < count; i++) {
-                TypeObjectKey *object = getObject(i);
-                if (object) {
-                    if (!types.append(Type::ObjectType(object)))
-                        cx->compartment()->types.setPendingNukeTypes(cx);
-                }
-            }
-        }
-    }
+    TypeList types;
+    if (!enumerateTypes(&types))
+        cx->compartment()->types.setPendingNukeTypes(cx);
 
     for (unsigned i = 0; i < types.length(); i++)
         constraint->newType(cx, this, types[i]);
 }
 
 void
 TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
 {
@@ -561,31 +565,29 @@ class types::CompilerConstraint
 
     // Contents of the property at the point when the query was performed. This
     // may differ from the actual property types later in compilation as the
     // main thread performs side effects.
     TemporaryTypeSet *expected;
 
     CompilerConstraint(const HeapTypeSetKey &property)
       : property(property),
-        expected(property.actualTypes->clone(IonAlloc()))
-    {
-        // Note: CompilerConstraintList::add watches for OOM under clone().
-    }
+        expected(property.maybeTypes() ? property.maybeTypes()->clone(IonAlloc()) : NULL)
+    {}
 
     // Generate the type constraint recording the assumption made by this
     // compilation. Returns true if the assumption originally made still holds.
     virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0;
 };
 
 void
 CompilerConstraintList::add(CompilerConstraint *constraint)
 {
 #ifdef JS_ION
-    if (!constraint || !constraint->expected || !constraints.append(constraint))
+    if (!constraint || !constraints.append(constraint))
         setFailed();
 #else
     MOZ_CRASH();
 #endif
 }
 
 namespace {
 
@@ -636,24 +638,27 @@ class TypeCompilerConstraint : public Ty
             cx->compartment()->types.addPendingRecompile(cx, compilation);
     }
 };
 
 template <typename T>
 bool
 CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo)
 {
-    if (property.actualObject->unknownProperties())
+    if (property.object()->unknownProperties())
+        return false;
+
+    if (!property.instantiate(cx))
         return false;
 
     if (!data.constraintHolds(cx, property, expected))
         return false;
 
-    property.actualTypes->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
-                              /* callExisting = */ false);
+    property.maybeTypes()->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
+                               /* callExisting = */ false);
     return true;
 }
 
 } /* anonymous namespace */
 
 const Class *
 TypeObjectKey::clasp()
 {
@@ -678,47 +683,72 @@ TypeObjectKey::newScript()
     if (isTypeObject()) {
         TypeObjectAddendum *addendum = asTypeObject()->addendum;
         if (addendum && addendum->isNewScript())
             return addendum->asNewScript();
     }
     return nullptr;
 }
 
+TypeObject *
+TypeObjectKey::maybeType()
+{
+    if (isTypeObject())
+        return asTypeObject();
+    if (asSingleObject()->hasLazyType())
+        return NULL;
+    return asSingleObject()->type();
+}
+
 bool
 TypeObjectKey::unknownProperties()
 {
-#ifdef JS_ION
-    JSContext *cx = jit::GetIonContext()->cx;
-    TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
-    if (!type)
-        MOZ_CRASH();
-    return type->unknownProperties();
-#else
-    MOZ_CRASH();
-#endif
+    if (TypeObject *type = maybeType())
+        return type->unknownProperties();
+    return false;
 }
 
 HeapTypeSetKey
-TypeObjectKey::property(jsid id)
+TypeObjectKey::property(jsid id, JSContext *maybecx /* = NULL */)
 {
-#ifdef JS_ION
-    JSContext *cx = jit::GetIonContext()->cx;
-    TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
-    if (!type)
-        MOZ_CRASH();
+    JS_ASSERT(!unknownProperties());
+
     HeapTypeSetKey property;
-    property.actualObject = type;
-    property.actualTypes = type->getProperty(cx, id);
-    if (!property.actualTypes)
-        MOZ_CRASH();
+    property.object_ = this;
+    property.id_ = id;
+    if (TypeObject *type = maybeType())
+        property.maybeTypes_ = type->maybeGetProperty(id);
+
+#ifdef JS_ION
+    // If we are accessing a lazily defined property which actually exists in
+    // the VM and has not been instantiated yet, instantiate it now if we are
+    // on the main thread and able to do so.
+    if (maybecx && !property.maybeTypes() && !JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) {
+        JS_ASSERT(CurrentThreadCanAccessRuntime(maybecx->runtime()));
+        JSObject *singleton = isSingleObject() ? asSingleObject() : asTypeObject()->singleton;
+        if (singleton && singleton->isNative() && singleton->nativeLookupPure(id)) {
+            EnsureTrackPropertyTypes(maybecx, singleton, id);
+            if (TypeObject *type = maybeType())
+                property.maybeTypes_ = type->maybeGetProperty(id);
+        }
+    }
+#endif // JS_ION
+
     return property;
-#else
-    MOZ_CRASH();
-#endif
+}
+
+bool
+HeapTypeSetKey::instantiate(JSContext *cx)
+{
+    if (maybeTypes())
+        return true;
+    if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx))
+        return false;
+    maybeTypes_ = object()->maybeType()->getProperty(cx, id());
+    return maybeTypes_ != NULL;
 }
 
 bool
 types::FinishCompilation(JSContext *cx, JSScript *script, ExecutionMode executionMode,
                          CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
 {
     if (constraints->failed())
         return false;
@@ -733,22 +763,27 @@ types::FinishCompilation(JSContext *cx, 
     }
 
     uint32_t index = types.constrainedOutputs->length();
     if (!types.constrainedOutputs->append(co))
         return false;
 
     *precompileInfo = RecompileInfo(index);
 
+    bool succeeded = true;
+
     for (size_t i = 0; i < constraints->length(); i++) {
         CompilerConstraint *constraint = constraints->get(i);
-        if (!constraint->generateTypeConstraint(cx, *precompileInfo)) {
-            types.constrainedOutputs->back().invalidate();
-            return false;
-        }
+        if (!constraint->generateTypeConstraint(cx, *precompileInfo))
+            succeeded = false;
+    }
+
+    if (!succeeded) {
+        types.constrainedOutputs->back().invalidate();
+        return false;
     }
 
     return true;
 }
 
 namespace {
 
 // Constraint which triggers recompilation of a script if any type is added to a type set. */
@@ -761,17 +796,19 @@ class ConstraintDataFreeze
 
     bool invalidateOnNewType(Type type) { return true; }
     bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
     bool invalidateOnNewObjectState(TypeObject *object) { return false; }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        return property.actualTypes->isSubset(expected);
+        return expected
+               ? property.maybeTypes()->isSubset(expected)
+               : property.maybeTypes()->empty();
     }
 };
 
 } /* anonymous namespace */
 
 void
 HeapTypeSetKey::freeze(CompilerConstraintList *constraints)
 {
@@ -837,55 +874,61 @@ TemporaryTypeSet::mightBeType(JSValueTyp
         return unknownObject() || baseObjectCount() != 0;
 
     return baseFlags() & PrimitiveTypeFlag(type);
 }
 
 JSValueType
 HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints)
 {
-    if (actualTypes->unknown())
+    TypeSet *types = maybeTypes();
+
+    if (!types || types->unknown())
         return JSVAL_TYPE_UNKNOWN;
 
-    TypeFlags flags = actualTypes->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
+    TypeFlags flags = types->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
     JSValueType type;
 
-    if (actualTypes->unknownObject() || actualTypes->getObjectCount())
+    if (types->unknownObject() || types->getObjectCount())
         type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
     else
         type = GetValueTypeFromTypeFlags(flags);
 
     if (type != JSVAL_TYPE_UNKNOWN)
         freeze(constraints);
 
     /*
      * If the type set is totally empty then it will be treated as unknown,
      * but we still need to record the dependency as adding a new type can give
      * it a definite type tag. This is not needed if there are enough types
      * that the exact tag is unknown, as it will stay unknown as more types are
      * added to the set.
      */
-    JS_ASSERT_IF(actualTypes->empty(), type == JSVAL_TYPE_UNKNOWN);
+    JS_ASSERT_IF(types->empty(), type == JSVAL_TYPE_UNKNOWN);
 
     return type;
 }
 
 bool
 HeapTypeSetKey::notEmpty(CompilerConstraintList *constraints)
 {
-    if (!actualTypes->empty())
+    if (maybeTypes() && !maybeTypes()->empty())
         return true;
     freeze(constraints);
     return false;
 }
 
 bool
 HeapTypeSetKey::knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other)
 {
-    if (!actualTypes->isSubset(other.actualTypes))
+    if (!maybeTypes() || maybeTypes()->empty()) {
+        freeze(constraints);
+        return true;
+    }
+    if (!other.maybeTypes() || !maybeTypes()->isSubset(other.maybeTypes()))
         return false;
     freeze(constraints);
     return true;
 }
 
 JSObject *
 TemporaryTypeSet::getSingleton()
 {
@@ -893,33 +936,38 @@ TemporaryTypeSet::getSingleton()
         return nullptr;
 
     return getSingleObject(0);
 }
 
 JSObject *
 HeapTypeSetKey::singleton(CompilerConstraintList *constraints)
 {
-    if (actualTypes->baseFlags() != 0 || actualTypes->getObjectCount() != 1)
+    TypeSet *types = maybeTypes();
+
+    if (!types || types->baseFlags() != 0 || types->getObjectCount() != 1)
         return nullptr;
 
-    JSObject *obj = actualTypes->getSingleObject(0);
+    JSObject *obj = types->getSingleObject(0);
 
     if (obj)
         freeze(constraints);
 
     return obj;
 }
 
 bool
 HeapTypeSetKey::needsBarrier(CompilerConstraintList *constraints)
 {
-    bool result = actualTypes->unknownObject()
-               || actualTypes->getObjectCount() > 0
-               || actualTypes->hasAnyFlag(TYPE_FLAG_STRING);
+    TypeSet *types = maybeTypes();
+    if (!types)
+        return false;
+    bool result = types->unknownObject()
+               || types->getObjectCount() > 0
+               || types->hasAnyFlag(TYPE_FLAG_STRING);
     if (!result)
         freeze(constraints);
     return result;
 }
 
 namespace {
 
 // Constraint which triggers recompilation if an object acquires particular flags.
@@ -941,41 +989,35 @@ class ConstraintDataFreezeObjectFlags
     bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
     bool invalidateOnNewObjectState(TypeObject *object) {
         return object->hasAnyFlags(flags);
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        return !invalidateOnNewObjectState(property.actualObject);
+        return !invalidateOnNewObjectState(property.object()->maybeType());
     }
 };
 
 } /* anonymous namespace */
 
 bool
 TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
 {
-#ifdef JS_ION
     JS_ASSERT(flags);
 
-    JSContext *cx = jit::GetIonContext()->cx;
-    TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
-    if (!type)
-        MOZ_CRASH();
-    if (type->hasAnyFlags(flags))
-        return true;
+    if (TypeObject *type = maybeType()) {
+        if (type->hasAnyFlags(flags))
+            return true;
+    }
 
     HeapTypeSetKey objectProperty = property(JSID_EMPTY);
     constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(flags)));
     return false;
-#else
-    MOZ_CRASH();
-#endif
 }
 
 bool
 TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
 {
     if (unknownObject())
         return true;
 
@@ -1042,17 +1084,17 @@ class ConstraintDataFreezeObjectForNewSc
     bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
     bool invalidateOnNewObjectState(TypeObject *object) {
         return !object->hasNewScript() || object->newScript()->allocKind != allocKind;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        return !invalidateOnNewObjectState(property.actualObject);
+        return !invalidateOnNewObjectState(property.object()->maybeType());
     }
 };
 
 // Constraint which triggers recompilation when the underlying data pointer for
 // a typed array changes.
 class ConstraintDataFreezeObjectForTypedArrayBuffer
 {
     void *viewData;
@@ -1068,17 +1110,17 @@ class ConstraintDataFreezeObjectForTyped
     bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
     bool invalidateOnNewObjectState(TypeObject *object) {
         return object->singleton->as<TypedArrayObject>().viewData() != viewData;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        return !invalidateOnNewObjectState(property.actualObject);
+        return !invalidateOnNewObjectState(property.object()->maybeType());
     }
 };
 
 } /* anonymous namespace */
 
 void
 TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints)
 {
@@ -1165,26 +1207,26 @@ class ConstraintDataFreezeConfiguredProp
             if (type->hasNewScript()) {
                 CheckNewScriptProperties(cx, type, type->newScript()->fun);
             } else {
                 JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED);
                 type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
             }
         }
 
-        return !property.actualTypes->configuredProperty();
+        return !property.maybeTypes()->configuredProperty();
     }
 };
 
 } /* anonymous namespace */
 
 bool
 HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type)
 {
-    if (actualTypes->configuredProperty())
+    if (maybeTypes() && maybeTypes()->configuredProperty())
         return true;
 
     constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeConfiguredProperty> >(*this, ConstraintDataFreezeConfiguredProperty(type)));
     return false;
 }
 
 bool
 TypeObject::incrementTenureCount()
@@ -1250,17 +1292,18 @@ TemporaryTypeSet::convertDoubleElements(
 
         HeapTypeSetKey property = type->property(JSID_VOID);
         property.freeze(constraints);
 
         // We can't convert to double elements for objects which do not have
         // double in their element types (as the conversion may render the type
         // information incorrect), nor for non-array objects (as their elements
         // may point to emptyObjectElements, which cannot be converted).
-        if (!property.actualTypes->hasType(Type::DoubleType()) ||
+        if (!property.maybeTypes() ||
+            !property.maybeTypes()->hasType(Type::DoubleType()) ||
             type->clasp() != &ArrayObject::class_)
         {
             dontConvert = true;
             alwaysConvert = false;
             continue;
         }
 
         // Only bother with converting known packed arrays whose possible
@@ -1758,25 +1801,19 @@ PrototypeHasIndexedProperty(CompilerCons
 
     return false;
 }
 
 bool
 types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints,
                                         HandleScript script)
 {
-#ifdef JS_ION
-    JSObject *proto = script->global().getOrCreateArrayPrototype(jit::GetIonContext()->cx);
-    if (!proto)
-        return true;
-
-    return PrototypeHasIndexedProperty(constraints, proto);
-#else
-    MOZ_CRASH();
-#endif
+    if (JSObject *proto = script->global().maybeGetArrayPrototype())
+        return PrototypeHasIndexedProperty(constraints, proto);
+    return true;
 }
 
 bool
 types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
                                          TemporaryTypeSet *types)
 {
     const Class *clasp = types->getKnownClass();
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -172,18 +172,17 @@ namespace jit {
 namespace analyze {
     class ScriptAnalysis;
 }
 
 namespace types {
 
 class TypeCompartment;
 class TypeSet;
-
-struct TypeObjectKey;
+class TypeObjectKey;
 
 /*
  * Information about a single concrete type. We pack this into a single word,
  * where small values are particular primitive or other singleton types, and
  * larger values are either specific JS objects or type objects.
  */
 class Type
 {
@@ -538,16 +537,20 @@ class TypeSet
      * Add a type to this set, calling any constraint handlers if this is a new
      * possible type.
      */
     inline void addType(ExclusiveContext *cx, Type type);
 
     /* Mark this type set as representing a configured property. */
     inline void setConfiguredProperty(ExclusiveContext *cx);
 
+    /* Get a list of all types in this set. */
+    typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
+    bool enumerateTypes(TypeList *list);
+
     /*
      * Iterate through the objects in this set. getObjectCount overapproximates
      * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
      * may return nullptr.
      */
     inline unsigned getObjectCount() const;
     inline TypeObjectKey *getObject(unsigned i) const;
     inline JSObject *getSingleObject(unsigned i) const;
@@ -1214,18 +1217,19 @@ struct ObjectTableKey;
 struct ObjectTableEntry;
 typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable;
 
 struct AllocationSiteKey;
 typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable;
 
 class HeapTypeSetKey;
 
-/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */
-struct TypeObjectKey {
+// Type set entry for either a JSObject with singleton type or a non-singleton TypeObject.
+struct TypeObjectKey
+{
     static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; }
     static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; }
 
     static TypeObjectKey *get(JSObject *obj) {
         JS_ASSERT(obj);
         return (TypeObjectKey *) (uintptr_t(obj) | 1);
     }
     static TypeObjectKey *get(TypeObject *obj) {
@@ -1254,24 +1258,50 @@ struct TypeObjectKey {
     JSObject *singleton();
     TypeNewScript *newScript();
 
     bool unknownProperties();
     bool hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags);
     void watchStateChangeForInlinedCall(CompilerConstraintList *constraints);
     void watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints);
     void watchStateChangeForTypedArrayBuffer(CompilerConstraintList *constraints);
-    HeapTypeSetKey property(jsid id);
+    HeapTypeSetKey property(jsid id, JSContext *maybecx = NULL);
+
+    TypeObject *maybeType();
 };
 
+// Representation of a heap type property which may or may not be instantiated.
+// Heap properties for singleton types are instantiated lazily as they are used
+// by the compiler, but this is only done on the main thread. If we are
+// compiling off thread and use a property which has not yet been instantiated,
+// it will be treated as empty and non-configured and will be instantiated when
+// rejoining to the main thread. If it is in fact not empty, the compilation
+// will fail; to avoid this, we try to instantiate singleton property types
+// during generation of baseline caches.
 class HeapTypeSetKey
 {
+    friend class TypeObjectKey;
+
+    // Object and property being accessed.
+    TypeObjectKey *object_;
+    jsid id_;
+
+    // If instantiated, the underlying heap type set.
+    HeapTypeSet *maybeTypes_;
+
   public:
-    TypeObject *actualObject;
-    HeapTypeSet *actualTypes;
+    HeapTypeSetKey()
+      : object_(NULL), id_(JSID_EMPTY), maybeTypes_(NULL)
+    {}
+
+    TypeObjectKey *object() const { return object_; }
+    jsid id() const { return id_; }
+    HeapTypeSet *maybeTypes() const { return maybeTypes_; }
+
+    bool instantiate(JSContext *cx);
 
     void freeze(CompilerConstraintList *constraints);
     JSValueType knownTypeTag(CompilerConstraintList *constraints);
     bool configured(CompilerConstraintList *constraints, TypeObjectKey *type);
     bool notEmpty(CompilerConstraintList *constraints);
     bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
     JSObject *singleton(CompilerConstraintList *constraints);
     bool needsBarrier(CompilerConstraintList *constraints);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -417,26 +417,29 @@ TrackPropertyTypes(ExclusiveContext *cx,
         return false;
 
     return true;
 }
 
 inline void
 EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
 {
-    JS_ASSERT(!obj->hasLazyType());
-
-    if (!cx->typeInferenceEnabled() || obj->type()->unknownProperties())
+    if (!cx->typeInferenceEnabled())
         return;
 
     id = IdToTypeId(id);
 
     if (obj->hasSingletonType()) {
         AutoEnterAnalysis enter(cx);
-        obj->type()->getProperty(cx, id);
+        if (obj->hasLazyType() && !obj->getType(cx)) {
+            cx->compartment()->types.setPendingNukeTypes(cx);
+            return;
+        }
+        if (!obj->type()->unknownProperties())
+            obj->type()->getProperty(cx, id);
     }
 
     JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id));
 }
 
 inline bool
 HasTypePropertyId(JSObject *obj, jsid id, Type type)
 {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1552,17 +1552,17 @@ js_NewGenerator(JSContext *cx, const Fra
 
     Rooted<GlobalObject*> global(cx, &stackfp->global());
     RootedObject obj(cx);
     if (stackfp->script()->isStarGenerator()) {
         RootedValue pval(cx);
         RootedObject fun(cx, stackfp->fun());
         // FIXME: This would be faster if we could avoid doing a lookup to get
         // the prototype for the instance.  Bug 906600.
-        if (!JSObject::getProperty(cx, fun, fun, cx->names().classPrototype, &pval))
+        if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval))
             return nullptr;
         JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr;
         if (!proto) {
             proto = global->getOrCreateStarGeneratorObjectPrototype(cx);
             if (!proto)
                 return nullptr;
         }
         obj = NewObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global);
@@ -1944,30 +1944,30 @@ GlobalObject::initIteratorClasses(JSCont
                                                           JSFunction::NATIVE_CTOR, global, name,
                                                           &function.toObject()));
         if (!genFunction)
             return false;
         if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
             return false;
 
         global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
-        global->setSlot(JSProto_GeneratorFunction, ObjectValue(*genFunction));
-        global->setSlot(JSProto_GeneratorFunction + JSProto_LIMIT, ObjectValue(*genFunctionProto));
+        global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
+        global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto));
     }
 
     if (global->getPrototype(JSProto_StopIteration).isUndefined()) {
         proto = global->createBlankPrototype(cx, &StopIterationObject::class_);
         if (!proto || !JSObject::freeze(cx, proto))
             return false;
 
         /* This should use a non-JSProtoKey'd slot, but this is easier for now. */
         if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
             return false;
 
-        MarkStandardClassInitializedNoProto(global, &StopIterationObject::class_);
+        global->markStandardClassInitializedNoProto(&StopIterationObject::class_);
     }
 
     return true;
 }
 
 JSObject *
 js_InitIteratorClasses(JSContext *cx, HandleObject obj)
 {
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -1490,12 +1490,12 @@ js_InitMathClass(JSContext *cx, HandleOb
         return nullptr;
     }
 
     if (!JS_DefineFunctions(cx, Math, math_static_methods))
         return nullptr;
     if (!JS_DefineConstDoubles(cx, Math, math_constants))
         return nullptr;
 
-    MarkStandardClassInitializedNoProto(obj, &MathClass);
+    obj->as<GlobalObject>().markStandardClassInitializedNoProto(&MathClass);
 
     return Math;
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1304,17 +1304,17 @@ NewObject(ExclusiveContext *cx, const Cl
                     "JSCLASS_IMPLEMENTS_BARRIERS flag. Please ensure that it correctly\n"
                     "implements write barriers and then set the flag.\n",
                     clasp->name);
             MOZ_CRASH();
         }
 #endif
     }
 
-    Probes::createObject(cx, obj);
+    probes::CreateObject(cx, obj);
     return obj;
 }
 
 void
 NewObjectCache::fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto proto,
                           gc::AllocKind kind, JSObject *obj)
 {
     JS_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
@@ -1538,17 +1538,17 @@ js::NewReshapedObject(JSContext *cx, Han
 
     return res;
 }
 
 JSObject*
 js::CreateThis(JSContext *cx, const Class *newclasp, HandleObject callee)
 {
     RootedValue protov(cx);
-    if (!JSObject::getProperty(cx, callee, callee, cx->names().classPrototype, &protov))
+    if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
         return nullptr;
 
     JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : nullptr;
     JSObject *parent = callee->getParent();
     gc::AllocKind kind = NewObjectGCKind(newclasp);
     return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
 }
 
@@ -1603,17 +1603,17 @@ js::CreateThisForFunctionWithProto(JSCon
 
     return res;
 }
 
 JSObject *
 js::CreateThisForFunction(JSContext *cx, HandleObject callee, bool newType)
 {
     RootedValue protov(cx);
-    if (!JSObject::getProperty(cx, callee, callee, cx->names().classPrototype, &protov))
+    if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
         return nullptr;
     JSObject *proto;
     if (protov.isObject())
         proto = &protov.toObject();
     else
         proto = nullptr;
     NewObjectKind newKind = newType ? SingletonObject : GenericObject;
     JSObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
@@ -2181,18 +2181,19 @@ DefineStandardSlot(JSContext *cx, Handle
          * Initializing an actual standard class on a global object. If the
          * property is not yet present, force it into a new one bound to a
          * reserved slot. Otherwise, go through the normal property path.
          */
         JS_ASSERT(obj->is<GlobalObject>());
         JS_ASSERT(obj->isNative());
 
         if (!obj->nativeLookup(cx, id)) {
-            uint32_t slot = 2 * JSProto_LIMIT + key;
-            obj->setReservedSlot(slot, v);
+            obj->as<GlobalObject>().setConstructorPropertySlot(key, v);
+
+            uint32_t slot = GlobalObject::constructorPropertySlot(key);
             if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0))
                 return false;
             AddTypePropertyId(cx, obj, id, v);
 
             named = true;
             return true;
         }
     }
@@ -2204,29 +2205,29 @@ DefineStandardSlot(JSContext *cx, Handle
 
 static void
 SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
 {
     JS_ASSERT(!obj->getParent());
     if (!obj->is<GlobalObject>())
         return;
 
-    obj->setReservedSlot(key, ObjectOrNullValue(cobj));
-    obj->setReservedSlot(JSProto_LIMIT + key, ObjectOrNullValue(proto));
+    obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
+    obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
 }
 
 static void
 ClearClassObject(JSObject *obj, JSProtoKey key)
 {
     JS_ASSERT(!obj->getParent());
     if (!obj->is<GlobalObject>())
         return;
 
-    obj->setSlot(key, UndefinedValue());
-    obj->setSlot(JSProto_LIMIT + key, UndefinedValue());
+    obj->as<GlobalObject>().setConstructor(key, UndefinedValue());
+    obj->as<GlobalObject>().setPrototype(key, UndefinedValue());
 }
 
 JSObject *
 js::DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
                                   JSObject *protoProto, const Class *clasp,
                                   Native constructor, unsigned nargs,
                                   const JSPropertySpec *ps, const JSFunctionSpec *fs,
                                   const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
@@ -2356,47 +2357,16 @@ bad:
         bool succeeded;
         JSObject::deleteByValue(cx, obj, StringValue(atom), &succeeded);
     }
     if (cached)
         ClearClassObject(obj, key);
     return nullptr;
 }
 
-/*
- * Lazy standard classes need a way to indicate if they have been initialized.
- * Otherwise, when we delete them, we might accidentally recreate them via a
- * lazy initialization. We use the presence of a ctor or proto in the
- * global object's slot to indicate that they've been constructed, but this only
- * works for classes which have a proto and ctor. Classes which don't have one
- * can call MarkStandardClassInitializedNoProto(), and we can always check
- * whether a class is initialized by calling IsStandardClassResolved().
- */
-bool
-js::IsStandardClassResolved(JSObject *obj, const js::Class *clasp)
-{
-    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
-
-    /* If the constructor is undefined, then it hasn't been initialized. */
-    return (obj->getReservedSlot(key) != UndefinedValue());
-}
-
-void
-js::MarkStandardClassInitializedNoProto(JSObject *obj, const js::Class *clasp)
-{
-    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
-
-    /*
-     * We use True so that it's obvious what we're doing (instead of, say,
-     * Null, which might be miscontrued as an error in setting Undefined).
-     */
-    if (obj->getReservedSlot(key) == UndefinedValue())
-        obj->setSlot(key, BooleanValue(true));
-}
-
 JSObject *
 js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto_,
              const Class *clasp, Native constructor, unsigned nargs,
              const JSPropertySpec *ps, const JSFunctionSpec *fs,
              const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
              JSObject **ctorp, AllocKind ctorKind)
 {
     RootedObject protoProto(cx, protoProto_);
@@ -3039,23 +3009,19 @@ js::SetClassAndProto(JSContext *cx, Hand
 
     obj->setType(type);
     return true;
 }
 
 bool
 js_GetClassObject(ExclusiveContext *cxArg, JSObject *obj, JSProtoKey key, MutableHandleObject objp)
 {
-    RootedObject global(cxArg, &obj->global());
-    if (!global->is<GlobalObject>()) {
-        objp.set(nullptr);
-        return true;
-    }
-
-    Value v = global->getReservedSlot(key);
+    Rooted<GlobalObject*> global(cxArg, &obj->global());
+
+    Value v = global->getConstructor(key);
     if (v.isObject()) {
         objp.set(&v.toObject());
         return true;
     }
 
     // Classes can only be initialized on the main thread.
     if (!cxArg->shouldBeJSContext())
         return false;
@@ -3069,17 +3035,17 @@ js_GetClassObject(ExclusiveContext *cxAr
         objp.set(nullptr);
         return true;
     }
 
     RootedObject cobj(cx, nullptr);
     if (ClassInitializerOp init = lazy_prototype_init[key]) {
         if (!init(cx, global))
             return false;
-        v = global->getReservedSlot(key);
+        v = global->getConstructor(key);
         if (v.isObject())
             cobj = &v.toObject();
     }
 
     objp.set(cobj);
     return true;
 }
 
@@ -3092,18 +3058,18 @@ js_IdentifyClassPrototype(JSObject *obj)
     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
     if (key == JSProto_Null)
         return JSProto_Null;
 
     // Now, see if the cached object matches |obj|.
     //
     // Note that standard class objects are cached in the range [0, JSProto_LIMIT),
     // and the prototypes are cached in [JSProto_LIMIT, 2*JSProto_LIMIT).
-    JSObject &global = obj->global();
-    Value v = global.getReservedSlot(JSProto_LIMIT + key);
+    GlobalObject &global = obj->global();
+    Value v = global.getPrototype(key);
     if (v.isObject() && obj == &v.toObject())
         return key;
 
     // False alarm - just an instance.
     return JSProto_Null;
 }
 
 bool
@@ -5234,17 +5200,17 @@ js::IsDelegate(JSContext *cx, HandleObje
 
 JSObject *
 js::GetClassPrototypePure(GlobalObject *global, JSProtoKey protoKey)
 {
     JS_ASSERT(JSProto_Null <= protoKey);
     JS_ASSERT(protoKey < JSProto_LIMIT);
 
     if (protoKey != JSProto_Null) {
-        const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
+        const Value &v = global->getPrototype(protoKey);
         if (v.isObject())
             return &v.toObject();
     }
 
     return nullptr;
 }
 
 /*
@@ -5263,22 +5229,22 @@ js_GetClassPrototype(ExclusiveContext *c
     RootedValue v(cx);
     if (!js_FindClassObject(cx, protoKey, &v, clasp))
         return false;
 
     if (IsFunctionObject(v)) {
         RootedObject ctor(cx, &v.get().toObject());
         if (cx->isJSContext()) {
             if (!JSObject::getProperty(cx->asJSContext(),
-                                       ctor, ctor, cx->names().classPrototype, &v))
+                                       ctor, ctor, cx->names().prototype, &v))
             {
                 return false;
             }
         } else {
-            Shape *shape = ctor->nativeLookup(cx, cx->names().classPrototype);
+            Shape *shape = ctor->nativeLookup(cx, cx->names().prototype);
             if (!shape || !NativeGetPureInline(ctor, shape, v.address()))
                 return false;
         }
     }
 
     protop.set(v.get().isObject() ? &v.get().toObject() : nullptr);
     return true;
 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1246,22 +1246,16 @@ DenseRangeRef::mark(JSTracer *trc)
 template <AllowGC allowGC>
 extern bool
 HasOwnProperty(JSContext *cx, LookupGenericOp lookup,
                typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
                typename MaybeRooted<jsid, allowGC>::HandleType id,
                typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
                typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp);
 
-bool
-IsStandardClassResolved(JSObject *obj, const js::Class *clasp);
-
-void
-MarkStandardClassInitializedNoProto(JSObject *obj, const js::Class *clasp);
-
 typedef JSObject *(*ClassInitializerOp)(JSContext *cx, JS::HandleObject obj);
 
 } /* namespace js */
 
 /*
  * Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp.
  */
 extern const char js_watch_str[];
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -70,17 +70,17 @@ JSObject::deleteSpecial(JSContext *cx, j
     js::types::MarkTypePropertyConfigured(cx, obj, id);
     js::DeleteSpecialOp op = obj->getOps()->deleteSpecial;
     return (op ? op : js::baseops::DeleteSpecial)(cx, obj, sid, succeeded);
 }
 
 inline void
 JSObject::finalize(js::FreeOp *fop)
 {
-    js::Probes::finalizeObject(this);
+    js::probes::FinalizeObject(this);
 
 #ifdef DEBUG
     JS_ASSERT(isTenured());
     if (!IsBackgroundFinalized(tenuredGetAllocKind())) {
         /* Assert we're on the main thread. */
         JS_ASSERT(CurrentThreadCanAccessRuntime(fop->runtime()));
     }
 #endif
@@ -931,25 +931,25 @@ DefineConstructorAndPrototype(JSContext 
     JS_ASSERT(!global->nativeEmpty()); /* reserved slots already allocated */
     JS_ASSERT(ctor);
     JS_ASSERT(proto);
 
     RootedId id(cx, NameToId(ClassName(key, cx)));
     JS_ASSERT(!global->nativeLookup(cx, id));
 
     /* Set these first in case AddTypePropertyId looks for this class. */
-    global->setSlot(key, ObjectValue(*ctor));
-    global->setSlot(key + JSProto_LIMIT, ObjectValue(*proto));
-    global->setSlot(key + JSProto_LIMIT * 2, ObjectValue(*ctor));
+    global->setConstructor(key, ObjectValue(*ctor));
+    global->setPrototype(key, ObjectValue(*proto));
+    global->setConstructorPropertySlot(key, ObjectValue(*ctor));
 
     types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
-    if (!global->addDataProperty(cx, id, key + JSProto_LIMIT * 2, 0)) {
-        global->setSlot(key, UndefinedValue());
-        global->setSlot(key + JSProto_LIMIT, UndefinedValue());
-        global->setSlot(key + JSProto_LIMIT * 2, UndefinedValue());
+    if (!global->addDataProperty(cx, id, GlobalObject::constructorPropertySlot(key), 0)) {
+        global->setConstructor(key, UndefinedValue());
+        global->setPrototype(key, UndefinedValue());
+        global->setConstructorPropertySlot(key, UndefinedValue());
         return false;
     }
 
     return true;
 }
 
 inline bool
 ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -881,12 +881,12 @@ js_InitJSONClass(JSContext *cx, HandleOb
 
     if (!JS_DefineProperty(cx, global, js_JSON_str, OBJECT_TO_JSVAL(JSON),
                            JS_PropertyStub, JS_StrictPropertyStub, 0))
         return nullptr;
 
     if (!JS_DefineFunctions(cx, JSON, json_static_methods))
         return nullptr;
 
-    MarkStandardClassInitializedNoProto(global, &JSONClass);
+    global->markStandardClassInitializedNoProto(&JSONClass);
 
     return JSON;
 }
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -3288,24 +3288,24 @@ static const JSFunctionSpec static_metho
     JS_FN("create",         proxy_create,          2, 0),
     JS_FN("createFunction", proxy_createFunction,  3, 0),
     JS_FS_END
 };
 
 JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, HandleObject obj)
 {
-    Rooted<GlobalObject*> global(cx);
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2);
     if (!ctor)
         return nullptr;
 
     if (!JS_DefineFunctions(cx, ctor, static_methods))
         return nullptr;
     if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(ctor),
                            JS_PropertyStub, JS_StrictPropertyStub, 0)) {
         return nullptr;
     }
 
-    MarkStandardClassInitializedNoProto(obj, &ProxyObject::uncallableClass_);
+    global->markStandardClassInitializedNoProto(&ProxyObject::uncallableClass_);
     return ctor;
 }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4865,17 +4865,17 @@ InitDOMObject(HandleObject obj)
 
 static bool
 dom_constructor(JSContext* cx, unsigned argc, JS::Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject callee(cx, &args.callee());
     RootedValue protov(cx);
-    if (!JSObject::getProperty(cx, callee, callee, cx->names().classPrototype, &protov))
+    if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
         return false;
 
     if (!protov.isObject()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE, "FakeDOMObject");
         return false;
     }
 
     RootedObject domObj(cx, JS_NewObject(cx, &dom_class, &protov.toObject(), nullptr));
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -22,17 +22,16 @@
     macro(byteOffset, byteOffset, "byteOffset") \
     macro(bytes, bytes, "bytes") \
     macro(BYTES_PER_ELEMENT, BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \
     macro(call, call, "call") \
     macro(callee, callee, "callee") \
     macro(caller, caller, "caller") \
     macro(callFunction, callFunction, "callFunction") \
     macro(caseFirst, caseFirst, "caseFirst") \
-    macro(classPrototype, classPrototype, "prototype") \
     macro(Collator, Collator, "Collator") \
     macro(CollatorCompareGet, CollatorCompareGet, "Intl_Collator_compare_get") \
     macro(columnNumber, columnNumber, "columnNumber") \
     macro(compare, compare, "compare") \
     macro(configurable, configurable, "configurable") \
     macro(construct, construct, "construct") \
     macro(constructor, constructor, "constructor") \
     macro(currency, currency, "currency") \
@@ -119,16 +118,17 @@
     macro(offset, offset, "offset") \
     macro(outOfMemory, outOfMemory, "out of memory") \
     macro(parseFloat, parseFloat, "parseFloat") \
     macro(parseInt, parseInt, "parseInt") \
     macro(pattern, pattern, "pattern") \
     macro(preventExtensions, preventExtensions, "preventExtensions") \
     macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
     macro(proto, proto, "__proto__") \
+    macro(prototype, prototype, "prototype") \
     macro(return, return_, "return") \
     macro(sensitivity, sensitivity, "sensitivity") \
     macro(set, set, "set") \
     macro(shape, shape, "shape") \
     macro(source, source, "source") \
     macro(stack, stack, "stack") \
     macro(sticky, sticky, "sticky") \
     macro(style, style, "style") \
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2082,17 +2082,17 @@ Debugger::construct(JSContext *cx, unsig
                                  "Debugger");
             return false;
         }
     }
 
     /* Get Debugger.prototype. */
     RootedValue v(cx);
     RootedObject callee(cx, &args.callee());
-    if (!JSObject::getProperty(cx, callee, callee, cx->names().classPrototype, &v))
+    if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &v))
         return false;
     RootedObject proto(cx, &v.toObject());
     JS_ASSERT(proto->getClass() == &Debugger::jsclass);
 
     /*
      * Make the new Debugger object. Each one has a reference to
      * Debugger.{Frame,Object,Script}.prototype in reserved slots. The rest of
      * the reserved slots are for hooks; they default to undefined.
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -356,19 +356,19 @@ GlobalObject::initFunctionAndObjectClass
         !LinkConstructorAndPrototype(cx, functionCtor, functionProto) ||
         !DefinePropertiesAndBrand(cx, functionProto, nullptr, function_methods) ||
         !DefinePropertiesAndBrand(cx, functionCtor, nullptr, nullptr))
     {
         return nullptr;
     }
 
     /* Add the global Function and Object properties now. */
-    if (!self->addDataProperty(cx, cx->names().Object, JSProto_Object + JSProto_LIMIT * 2, 0))
+    if (!self->addDataProperty(cx, cx->names().Object, constructorPropertySlot(JSProto_Object), 0))
         return nullptr;
-    if (!self->addDataProperty(cx, cx->names().Function, JSProto_Function + JSProto_LIMIT * 2, 0))
+    if (!self->addDataProperty(cx, cx->names().Function, constructorPropertySlot(JSProto_Function), 0))
         return nullptr;
 
     /* Heavy lifting done, but lingering tasks remain. */
 
     /* ES5 15.1.2.1. */
     RootedId evalId(cx, NameToId(cx->names().eval));
     JSObject *evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS);
     if (!evalobj)
@@ -546,17 +546,17 @@ GlobalObject::createBlankPrototypeInheri
 bool
 js::LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor_, JSObject *proto_)
 {
     RootedObject ctor(cx, ctor_), proto(cx, proto_);
 
     RootedValue protoVal(cx, ObjectValue(*proto));
     RootedValue ctorVal(cx, ObjectValue(*ctor));
 
-    return JSObject::defineProperty(cx, ctor, cx->names().classPrototype,
+    return JSObject::defineProperty(cx, ctor, cx->names().prototype,
                                     protoVal, JS_PropertyStub, JS_StrictPropertyStub,
                                     JSPROP_PERMANENT | JSPROP_READONLY) &&
            JSObject::defineProperty(cx, proto, cx->names().constructor,
                                     ctorVal, JS_PropertyStub, JS_StrictPropertyStub, 0);
 }
 
 bool
 js::DefinePropertiesAndBrand(JSContext *cx, JSObject *obj_,
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -30,49 +30,57 @@ js_InitTypedObjectClasses(JSContext *cx,
 
 namespace js {
 
 class Debugger;
 
 /*
  * Global object slots are reserved as follows:
  *
- * [0, JSProto_LIMIT)
+ * [0, APPLICATION_SLOTS)
+ *   Pre-reserved slots in all global objects set aside for the embedding's
+ *   use. As with all reserved slots these start out as UndefinedValue() and
+ *   are traced for GC purposes. Apart from that the engine never touches
+ *   these slots, so the embedding can do whatever it wants with them.
+ * [APPLICATION_SLOTS, APPLICATION_SLOTS + JSProto_LIMIT)
  *   Stores the original value of the constructor for the corresponding
  *   JSProtoKey.
- * [JSProto_LIMIT, 2 * JSProto_LIMIT)
+ * [APPLICATION_SLOTS + JSProto_LIMIT, APPLICATION_SLOTS + 2 * JSProto_LIMIT)
  *   Stores the prototype, if any, for the constructor for the corresponding
  *   JSProtoKey offset from JSProto_LIMIT.
- * [2 * JSProto_LIMIT, 3 * JSProto_LIMIT)
+ * [APPLICATION_SLOTS + 2 * JSProto_LIMIT, APPLICATION_SLOTS + 3 * JSProto_LIMIT)
  *   Stores the current value of the global property named for the JSProtoKey
  *   for the corresponding JSProtoKey offset from 2 * JSProto_LIMIT.
- * [3 * JSProto_LIMIT, RESERVED_SLOTS)
+ * [APPLICATION_SLOTS + 3 * JSProto_LIMIT, RESERVED_SLOTS)
  *   Various one-off values: ES5 13.2.3's [[ThrowTypeError]], RegExp statics,
  *   the original eval for this global object (implementing |var eval =
  *   otherWindow.eval; eval(...)| as an indirect eval), a bit indicating
  *   whether this object has been cleared (see JS_ClearScope), and a cache for
  *   whether eval is allowed (per the global's Content Security Policy).
  *
- * The first two ranges are necessary to implement js::FindClassObject,
- * and spec language speaking in terms of "the original Array prototype
- * object", or "as if by the expression new Array()" referring to the original
- * Array constructor. The third range stores the (writable and even deletable)
- * Object, Array, &c. properties (although a slot won't be used again if its
- * property is deleted and readded).
+ * The first two JSProto_LIMIT-sized ranges are necessary to implement
+ * js::FindClassObject, and spec language speaking in terms of "the original
+ * Array prototype object", or "as if by the expression new Array()" referring
+ * to the original Array constructor. The third range stores the (writable and
+ * even deletable) Object, Array, &c. properties (although a slot won't be used
+ * again if its property is deleted and readded).
  */
 class GlobalObject : public JSObject
 {
+    /* Count of slots set aside for application use. */
+    static const unsigned APPLICATION_SLOTS = 3;
+
     /*
      * Count of slots to store built-in constructors, prototypes, and initial
      * visible properties for the constructors.
      */
     static const unsigned STANDARD_CLASS_SLOTS  = JSProto_LIMIT * 3;
 
     /* Various function values needed by the engine. */
-    static const unsigned EVAL                    = STANDARD_CLASS_SLOTS;
+    static const unsigned EVAL                    = APPLICATION_SLOTS + STANDARD_CLASS_SLOTS;
     static const unsigned CREATE_DATAVIEW_FOR_THIS = EVAL + 1;
     static const unsigned THROWTYPEERROR          = CREATE_DATAVIEW_FOR_THIS + 1;
     static const unsigned PROTO_GETTER            = THROWTYPEERROR + 1;
 
     /*
      * Instances of the internal createArrayFromBuffer function used by the
      * typed array code, one per typed array element type.
      */
@@ -117,33 +125,16 @@ class GlobalObject : public JSObject
     ::js_InitObjectClass(JSContext *cx, js::HandleObject);
     friend JSObject *
     ::js_InitFunctionClass(JSContext *cx, js::HandleObject);
 
     /* Initialize the Function and Object classes.  Must only be called once! */
     JSObject *
     initFunctionAndObjectClasses(JSContext *cx);
 
-    void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
-        JS_ASSERT(getSlotRef(key).isUndefined());
-        JS_ASSERT(getSlotRef(JSProto_LIMIT + key).isUndefined());
-        JS_ASSERT(getSlotRef(2 * JSProto_LIMIT + key).isUndefined());
-        setSlot(key, ObjectValue(*ctor));
-        setSlot(JSProto_LIMIT + key, ObjectValue(*proto));
-        setSlot(2 * JSProto_LIMIT + key, ObjectValue(*ctor));
-    }
-
-    void setObjectClassDetails(JSFunction *ctor, JSObject *proto) {
-        setDetailsForKey(JSProto_Object, ctor, proto);
-    }
-
-    void setFunctionClassDetails(JSFunction *ctor, JSObject *proto) {
-        setDetailsForKey(JSProto_Function, ctor, proto);
-    }
-
     void setThrowTypeError(JSFunction *fun) {
         JS_ASSERT(getSlotRef(THROWTYPEERROR).isUndefined());
         setSlot(THROWTYPEERROR, ObjectValue(*fun));
     }
 
     void setOriginalEval(JSObject *evalobj) {
         JS_ASSERT(getSlotRef(EVAL).isUndefined());
         setSlot(EVAL, ObjectValue(*evalobj));
@@ -154,38 +145,106 @@ class GlobalObject : public JSObject
         setSlot(PROTO_GETTER, ObjectValue(*protoGetter));
     }
 
     void setIntrinsicsHolder(JSObject *obj) {
         JS_ASSERT(getSlotRef(INTRINSICS).isUndefined());
         setSlot(INTRINSICS, ObjectValue(*obj));
     }
 
+  public:
     Value getConstructor(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
-        return getSlot(key);
+        return getSlot(APPLICATION_SLOTS + key);
+    }
+
+    void setConstructor(JSProtoKey key, const Value &v) {
+        JS_ASSERT(key <= JSProto_LIMIT);
+        setSlot(APPLICATION_SLOTS + key, v);
     }
 
     Value getPrototype(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
-        return getSlot(JSProto_LIMIT + key);
+        return getSlot(APPLICATION_SLOTS + JSProto_LIMIT + key);
+    }
+
+    void setPrototype(JSProtoKey key, const Value &value) {
+        JS_ASSERT(key <= JSProto_LIMIT);
+        setSlot(APPLICATION_SLOTS + JSProto_LIMIT + key, value);
+    }
+
+    static uint32_t constructorPropertySlot(JSProtoKey key) {
+        JS_ASSERT(key <= JSProto_LIMIT);
+        return APPLICATION_SLOTS + JSProto_LIMIT * 2 + key;
+    }
+
+    Value getConstructorPropertySlot(JSProtoKey key) {
+        return getSlot(constructorPropertySlot(key));
+    }
+
+    void setConstructorPropertySlot(JSProtoKey key, const Value &ctor) {
+        setSlot(constructorPropertySlot(key), ctor);
     }
 
     bool classIsInitialized(JSProtoKey key) const {
         bool inited = !getConstructor(key).isUndefined();
         JS_ASSERT(inited == !getPrototype(key).isUndefined());
         return inited;
     }
 
     bool functionObjectClassesInitialized() const {
         bool inited = classIsInitialized(JSProto_Function);
         JS_ASSERT(inited == classIsInitialized(JSProto_Object));
         return inited;
     }
 
+    /*
+     * Lazy standard classes need a way to indicate they have been initialized.
+     * Otherwise, when we delete them, we might accidentally recreate them via
+     * a lazy initialization. We use the presence of a ctor or proto in the
+     * global object's slot to indicate that they've been constructed, but this
+     * only works for classes which have a proto and ctor. Classes which don't
+     * have one can call markStandardClassInitializedNoProto(), and we can
+     * always check whether a class is initialized by calling
+     * isStandardClassResolved().
+     */
+    bool isStandardClassResolved(const js::Class *clasp) const {
+        JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
+
+        // If the constructor is undefined, then it hasn't been initialized.
+        return !getConstructor(key).isUndefined();
+    }
+
+    void markStandardClassInitializedNoProto(const js::Class *clasp) {
+        JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
+
+        // We use true so that it's obvious what we're doing (instead of, say,
+        // null, which might be miscontrued as an error in setting Undefined).
+        if (getConstructor(key).isUndefined())
+            setConstructor(key, BooleanValue(true));
+    }
+
+  private:
+    void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
+        JS_ASSERT(getConstructor(key).isUndefined());
+        JS_ASSERT(getPrototype(key).isUndefined());
+        JS_ASSERT(getConstructorPropertySlot(key).isUndefined());
+        setConstructor(key, ObjectValue(*ctor));
+        setPrototype(key, ObjectValue(*proto));
+        setConstructorPropertySlot(key, ObjectValue(*ctor));
+    }
+
+    void setObjectClassDetails(JSFunction *ctor, JSObject *proto) {
+        setDetailsForKey(JSProto_Object, ctor, proto);
+    }
+
+    void setFunctionClassDetails(JSFunction *ctor, JSObject *proto) {
+        setDetailsForKey(JSProto_Function, ctor, proto);
+    }
+
     bool arrayClassInitialized() const {
         return classIsInitialized(JSProto_Array);
     }
 
     bool booleanClassInitialized() const {
         return classIsInitialized(JSProto_Boolean);
     }
     bool numberClassInitialized() const {
@@ -282,16 +341,22 @@ class GlobalObject : public JSObject
         if (arrayClassInitialized())
             return &getPrototype(JSProto_Array).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitArrayClass(cx, self))
             return nullptr;
         return &self->getPrototype(JSProto_Array).toObject();
     }
 
+    JSObject *maybeGetArrayPrototype() {
+        if (arrayClassInitialized())
+            return &getPrototype(JSProto_Array).toObject();
+        return NULL;
+    }
+
     JSObject *getOrCreateBooleanPrototype(JSContext *cx) {
         if (booleanClassInitialized())
             return &getPrototype(JSProto_Boolean).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitBooleanClass(cx, self))
             return nullptr;
         return &self->getPrototype(JSProto_Boolean).toObject();
     }
@@ -338,84 +403,87 @@ class GlobalObject : public JSObject
             return &getPrototype(key).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitExceptionClasses(cx, self))
             return nullptr;
         return &self->getPrototype(key).toObject();
     }
 
     JSObject *getOrCreateIntlObject(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_Intl, initIntlObject);
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Intl, initIntlObject);
+    }
+
+    JSObject *getIteratorPrototype() {
+        return &getPrototype(JSProto_Iterator).toObject();
+    }
+
+    JSObject *getOrCreateDataObject(JSContext *cx) {
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Data, initDataObject);
+    }
+
+    JSObject *getOrCreateTypeObject(JSContext *cx) {
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Type, initTypeObject);
+    }
+
+    JSObject *getOrCreateArrayTypeObject(JSContext *cx) {
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_ArrayTypeObject,
+                                 initArrayTypeObject);
     }
 
     JSObject *getOrCreateCollatorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, COLLATOR_PROTO, initCollatorProto);
     }
 
     JSObject *getOrCreateNumberFormatPrototype(JSContext *cx) {
         return getOrCreateObject(cx, NUMBER_FORMAT_PROTO, initNumberFormatProto);
     }
 
     JSObject *getOrCreateDateTimeFormatPrototype(JSContext *cx) {
         return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initDateTimeFormatProto);
     }
 
-    JSObject *getIteratorPrototype() {
-        return &getPrototype(JSProto_Iterator).toObject();
-    }
-
-    JSObject *getOrCreateDataObject(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_Data, initDataObject);
-    }
-
-    JSObject *getOrCreateTypeObject(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_Type, initTypeObject);
-    }
-
-    JSObject *getOrCreateArrayTypeObject(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_ArrayTypeObject, initArrayTypeObject);
-    }
-
   private:
     typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
 
     JSObject *getOrCreateObject(JSContext *cx, unsigned slot, ObjectInitOp init) {
         Value v = getSlotRef(slot);
         if (v.isObject())
             return &v.toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!init(cx, self))
             return nullptr;
         return &self->getSlot(slot).toObject();
     }
 
   public:
     JSObject *getOrCreateIteratorPrototype(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_LIMIT + JSProto_Iterator, initIteratorClasses);
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_LIMIT + JSProto_Iterator,
+                                 initIteratorClasses);
     }
 
     JSObject *getOrCreateElementIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, ELEMENT_ITERATOR_PROTO, initIteratorClasses);
     }
 
     JSObject *getOrCreateLegacyGeneratorObjectPrototype(JSContext *cx) {
         return getOrCreateObject(cx, LEGACY_GENERATOR_OBJECT_PROTO, initIteratorClasses);
     }
 
     JSObject *getOrCreateStarGeneratorObjectPrototype(JSContext *cx) {
         return getOrCreateObject(cx, STAR_GENERATOR_OBJECT_PROTO, initIteratorClasses);
     }
 
     JSObject *getOrCreateStarGeneratorFunctionPrototype(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_LIMIT + JSProto_GeneratorFunction,
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_LIMIT + JSProto_GeneratorFunction,
                                  initIteratorClasses);
     }
 
     JSObject *getOrCreateStarGeneratorFunction(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_GeneratorFunction, initIteratorClasses);
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_GeneratorFunction,
+                                 initIteratorClasses);
     }
 
     JSObject *getOrCreateMapIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, MAP_ITERATOR_PROTO, initMapIteratorProto);
     }
 
     JSObject *getOrCreateSetIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, SET_ITERATOR_PROTO, initSetIteratorProto);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -601,20 +601,20 @@ js::ExecuteKernel(JSContext *cx, HandleS
     if (script->isEmpty()) {
         if (result)
             result->setUndefined();
         return true;
     }
 
     TypeScript::SetThis(cx, script, thisv);
 
-    Probes::startExecution(script);
+    probes::StartExecution(script);
     ExecuteState state(cx, script, thisv, scopeChainArg, type, evalInFrame, result);
     bool ok = RunScript(cx, state);
-    Probes::stopExecution(script);
+    probes::StopExecution(script);
 
     return ok;
 }
 
 bool
 js::Execute(JSContext *cx, HandleScript script, JSObject &scopeChainArg, Value *rval)
 {
     /* The scope chain could be anything, so innerize just in case. */
@@ -1330,29 +1330,29 @@ Interpret(JSContext *cx, RunState &state
         JS_ASSERT(size_t(regs.pc - script->code) <= script->length);
         JS_ASSERT(regs.stackDepth() <= script->nslots);
 
         /*
          * To support generator_throw and to catch ignored exceptions,
          * fail if cx->isExceptionPending() is true.
          */
         if (cx->isExceptionPending()) {
-            Probes::enterScript(cx, script, script->function(), regs.fp());
+            probes::EnterScript(cx, script, script->function(), regs.fp());
             goto error;
         }
     }
 
     /* State communicated between non-local jumps: */
     bool interpReturnOK;
 
     if (!entryFrame->isGeneratorFrame()) {
         if (!entryFrame->prologue(cx))
             goto error;
     } else {
-        Probes::enterScript(cx, script, script->function(), entryFrame);
+        probes::EnterScript(cx, script, script->function(), entryFrame);
     }
     if (cx->compartment()->debugMode()) {
         JSTrapStatus status = ScriptDebugPrologue(cx, entryFrame);
         switch (status) {
           case JSTRAP_CONTINUE:
             break;
           case JSTRAP_RETURN:
             interpReturnOK = true;
@@ -1618,17 +1618,17 @@ BEGIN_CASE(JSOP_STOP)
 #endif
 
         if (cx->compartment()->debugMode())
             interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
 
         if (!regs.fp()->isYielding())
             regs.fp()->epilogue(cx);
         else
-            Probes::exitScript(cx, script, script->function(), regs.fp());
+            probes::ExitScript(cx, script, script->function(), regs.fp());
 
 #if defined(JS_ION)
   jit_return_pop_frame:
 #endif
 
         activation.popInlineFrame(regs.fp());
         SET_SCRIPT(regs.fp()->script());
 
@@ -3396,17 +3396,17 @@ default:
         goto inline_return;
 
   exit:
     if (cx->compartment()->debugMode())
         interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
     if (!regs.fp()->isYielding())
         regs.fp()->epilogue(cx);
     else
-        Probes::exitScript(cx, script, script->function(), regs.fp());
+        probes::ExitScript(cx, script, script->function(), regs.fp());
 
     gc::MaybeVerifyBarriers(cx, true);
 
 #if JS_TRACE_LOGGING
         TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_STOP);
 #endif
 
 #ifdef JS_ION
--- a/js/src/vm/Probes-inl.h
+++ b/js/src/vm/Probes-inl.h
@@ -14,38 +14,38 @@
 namespace js {
 
 /*
  * Many probe handlers are implemented inline for minimal performance impact,
  * especially important when no backends are enabled.
  */
 
 inline bool
-Probes::callTrackingActive(JSContext *cx)
+probes::CallTrackingActive(JSContext *cx)
 {
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED() || JAVASCRIPT_FUNCTION_RETURN_ENABLED())
         return true;
 #endif
 #ifdef MOZ_TRACE_JSCALLS
     if (cx->functionCallback)
         return true;
 #endif
     return false;
 }
 
 inline bool
-Probes::wantNativeAddressInfo(JSContext *cx)
+probes::WantNativeAddressInfo(JSContext *cx)
 {
     return (cx->reportGranularity >= JITREPORT_GRANULARITY_FUNCTION &&
             JITGranularityRequested(cx) >= JITREPORT_GRANULARITY_FUNCTION);
 }
 
 inline bool
-Probes::enterScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
+probes::EnterScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
                     StackFrame *fp)
 {
     bool ok = true;
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
         DTraceEnterJSFun(cx, maybeFun, script);
 #endif
 #ifdef MOZ_TRACE_JSCALLS
@@ -58,17 +58,17 @@ Probes::enterScript(JSContext *cx, JSScr
         JS_ASSERT_IF(!fp->isGeneratorFrame(), !fp->hasPushedSPSFrame());
         fp->setPushedSPSFrame();
     }
 
     return ok;
 }
 
 inline bool
-Probes::exitScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
+probes::ExitScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
                    AbstractFramePtr fp)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
         DTraceExitJSFun(cx, maybeFun, script);
 #endif
@@ -83,38 +83,38 @@ Probes::exitScript(JSContext *cx, JSScri
      * guaranteed that fp->hasPushedSPSFrame() would have been true
      */
     if ((!fp && rt->spsProfiler.enabled()) || (fp && fp.hasPushedSPSFrame()))
         rt->spsProfiler.exit(cx, script, maybeFun);
     return ok;
 }
 
 inline bool
-Probes::exitScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
+probes::ExitScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
                    StackFrame *fp)
 {
-    return Probes::exitScript(cx, script, maybeFun, fp ? AbstractFramePtr(fp) : AbstractFramePtr());
+    return probes::ExitScript(cx, script, maybeFun, fp ? AbstractFramePtr(fp) : AbstractFramePtr());
 }
 
 inline bool
-Probes::startExecution(JSScript *script)
+probes::StartExecution(JSScript *script)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_EXECUTE_START_ENABLED())
         JAVASCRIPT_EXECUTE_START((script->filename() ? (char *)script->filename() : nullName),
                                  script->lineno);
 #endif
 
     return ok;
 }
 
 inline bool
-Probes::stopExecution(JSScript *script)
+probes::StopExecution(JSScript *script)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
         JAVASCRIPT_EXECUTE_DONE((script->filename() ? (char *)script->filename() : nullName),
                                 script->lineno);
 #endif
--- a/js/src/vm/Probes.cpp
+++ b/js/src/vm/Probes.cpp
@@ -11,75 +11,75 @@
 #ifdef INCLUDE_MOZILLA_DTRACE
 #include "jsscriptinlines.h" 
 #endif
 
 #define TYPEOF(cx,v)    (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
 
 using namespace js;
 
-const char Probes::nullName[] = "(null)";
-const char Probes::anonymousName[] = "(anonymous)";
+const char probes::nullName[] = "(null)";
+const char probes::anonymousName[] = "(anonymous)";
 
-bool Probes::ProfilingActive = true;
+bool probes::ProfilingActive = true;
 
-Probes::JITReportGranularity
-Probes::JITGranularityRequested(JSContext *cx)
+probes::JITReportGranularity
+probes::JITGranularityRequested(JSContext *cx)
 {
     if (cx->runtime()->spsProfiler.enabled())
         return JITREPORT_GRANULARITY_LINE;
     return JITREPORT_GRANULARITY_NONE;
 }
 
 /* ICs are unregistered in a batch */
 void
-Probes::discardExecutableRegion(void *start, size_t size)
+probes::DiscardExecutableRegion(void *start, size_t size)
 {
     /*
      * Not needed for SPS because ICs are disposed of when the normal JITChunk
      * is disposed of
      */
 }
 
 #ifdef INCLUDE_MOZILLA_DTRACE
 static const char *
 ScriptFilename(const JSScript *script)
 {
     if (!script)
-        return Probes::nullName;
+        return probes::nullName;
     if (!script->filename())
-        return Probes::anonymousName;
+        return probes::anonymousName;
     return script->filename();
 }
 
 static const char *
 FunctionName(JSContext *cx, JSFunction *fun, JSAutoByteString* bytes)
 {
     if (!fun)
-        return Probes::nullName;
+        return probes::nullName;
     if (!fun->displayAtom())
-        return Probes::anonymousName;
-    return bytes->encodeLatin1(cx, fun->displayAtom()) ? bytes->ptr() : Probes::nullName;
+        return probes::anonymousName;
+    return bytes->encodeLatin1(cx, fun->displayAtom()) ? bytes->ptr() : probes::nullName;
 }
 
 /*
  * These functions call the DTrace macros for the JavaScript USDT probes.
  * Originally this code was inlined in the JavaScript code; however since
  * a number of operations are called, these have been placed into functions
  * to reduce any negative compiler optimization effect that the addition of
  * a number of usually unused lines of code would cause.
  */
 void
-Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
+probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
 {
     JSAutoByteString funNameBytes;
-    JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), Probes::nullName,
+    JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), probes::nullName,
                               FunctionName(cx, fun, &funNameBytes));
 }
 
 void
-Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
+probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
 {
     JSAutoByteString funNameBytes;
-    JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), Probes::nullName,
+    JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), probes::nullName,
                                FunctionName(cx, fun, &funNameBytes));
 }
 #endif
--- a/js/src/vm/Probes.h
+++ b/js/src/vm/Probes.h
@@ -10,23 +10,23 @@
 #ifdef INCLUDE_MOZILLA_DTRACE
 #include "javascript-trace.h"
 #endif
 
 #include "vm/Stack.h"
 
 namespace js {
 
-namespace Probes {
+namespace probes {
 
 /*
  * Static probes
  *
  * The probe points defined in this file are scattered around the SpiderMonkey
- * source tree. The presence of Probes::someEvent() means that someEvent is
+ * source tree. The presence of probes::SomeEvent() means that someEvent is
  * about to happen or has happened. To the extent possible, probes should be
  * inserted in all paths associated with a given event, regardless of the
  * active runmode (interpreter/traceJIT/methodJIT/ionJIT).
  *
  * When a probe fires, it is handled by any probe handling backends that have
  * been compiled in. By default, most probes do nothing or at least do nothing
  * expensive, so the presence of the probe should have negligible effect on
  * running time. (Probes in slow paths may do something by default, as long as
@@ -52,47 +52,47 @@ extern bool ProfilingActive;
 extern const char nullName[];
 extern const char anonymousName[];
 
 /*
  * Test whether we are tracking JS function call enter/exit. The JITs use this
  * to decide whether they can optimize in a way that would prevent probes from
  * firing.
  */
-bool callTrackingActive(JSContext *);
+bool CallTrackingActive(JSContext *);
 
 /*
  * Test whether anything is looking for JIT native code registration events.
  * This information will not be collected otherwise.
  */
-bool wantNativeAddressInfo(JSContext *);
+bool WantNativeAddressInfo(JSContext *);
 
 /* Entering a JS function */
-bool enterScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
+bool EnterScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
 
 /* About to leave a JS function */
-bool exitScript(JSContext *, JSScript *, JSFunction *, AbstractFramePtr);
-bool exitScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
+bool ExitScript(JSContext *, JSScript *, JSFunction *, AbstractFramePtr);
+bool ExitScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
 
 /* Executing a script */
-bool startExecution(JSScript *script);
+bool StartExecution(JSScript *script);
 
 /* Script has completed execution */
-bool stopExecution(JSScript *script);
+bool StopExecution(JSScript *script);
 
 /*
  * Object has been created. |obj| must exist (its class and size are read)
  */
-bool createObject(ExclusiveContext *cx, JSObject *obj);
+bool CreateObject(ExclusiveContext *cx, JSObject *obj);
 
 /*
  * Object is about to be finalized. |obj| must still exist (its class is
  * read)
  */
-bool finalizeObject(JSObject *obj);
+bool FinalizeObject(JSObject *obj);
 
 /* JIT code observation */
 
 enum JITReportGranularity {
     JITREPORT_GRANULARITY_NONE = 0,
     JITREPORT_GRANULARITY_FUNCTION = 1,
     JITREPORT_GRANULARITY_LINE = 2,
     JITREPORT_GRANULARITY_OP = 3
@@ -104,21 +104,21 @@ enum JITReportGranularity {
 JITReportGranularity
 JITGranularityRequested(JSContext *cx);
 
 /*
  * A whole region of code has been deallocated, containing any number of ICs.
  * (ICs are unregistered in a batch, so individual ICs are not registered.)
  */
 void
-discardExecutableRegion(void *start, size_t size);
+DiscardExecutableRegion(void *start, size_t size);
 
 /*
- * Internal: DTrace-specific functions to be called during Probes::enterScript
- * and Probes::exitScript. These will not be inlined, but the argument
+ * Internal: DTrace-specific functions to be called during probes::EnterScript
+ * and probes::ExitScript. These will not be inlined, but the argument
  * marshalling required for these probe points is expensive enough that it
  * shouldn't really matter.
  */
 void DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script);
 void DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script);
 
 } /* namespace Probes */
 
@@ -132,30 +132,30 @@ static const char *ObjectClassname(JSObj
     const char *class_name = clasp->name;
     if (!class_name)
         return "(null class name)";
     return class_name;
 }
 #endif
 
 inline bool
-Probes::createObject(ExclusiveContext *cx, JSObject *obj)
+probes::CreateObject(ExclusiveContext *cx, JSObject *obj)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_OBJECT_CREATE_ENABLED())
         JAVASCRIPT_OBJECT_CREATE(ObjectClassname(obj), (uintptr_t)obj);
 #endif
 
     return ok;
 }
 
 inline bool
-Probes::finalizeObject(JSObject *obj)
+probes::FinalizeObject(JSObject *obj)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED()) {
         const Class *clasp = obj->getClass();
 
         /* the first arg is nullptr - reserved for future use (filename?) */
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -48,17 +48,17 @@ NewObjectCache::newObjectFromHit(JSConte
 
     JSObject *templateObj = reinterpret_cast<JSObject *>(&entry->templateObject);
     if (templateObj->type()->isLongLivedForCachedAlloc())
         heap = gc::TenuredHeap;
 
     JSObject *obj = js_NewGCObject<NoGC>(cx, entry->kind, heap);
     if (obj) {
         copyCachedToObject(obj, templateObj, entry->kind);
-        Probes::createObject(cx, obj);
+        probes::CreateObject(cx, obj);
         return obj;
     }
 
     return nullptr;
 }
 
 }  /* namespace js */
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -137,17 +137,17 @@ intrinsic_MakeConstructible(JSContext *c
     JS_ASSERT(args.length() == 2);
     JS_ASSERT(args[0].isObject());
     JS_ASSERT(args[0].toObject().is<JSFunction>());
     JS_ASSERT(args[1].isObject());
 
     // Normal .prototype properties aren't enumerable.  But for this to clone
     // correctly, it must be enumerable.
     RootedObject ctor(cx, &args[0].toObject());
-    if (!JSObject::defineProperty(cx, ctor, cx->names().classPrototype, args[1],
+    if (!JSObject::defineProperty(cx, ctor, cx->names().prototype, args[1],
                                   JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT))
     {
         return false;
     }
 
     ctor->as<JSFunction>().setIsSelfHostedConstructor();
     args.rval().setUndefined();
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -259,22 +259,22 @@ StackFrame::prologue(JSContext *cx)
     if (isEvalFrame()) {
         if (script->strict) {
             CallObject *callobj = CallObject::createForStrictEval(cx, this);
             if (!callobj)
                 return false;
             pushOnScopeChain(*callobj);
             flags_ |= HAS_CALL_OBJ;
         }
-        Probes::enterScript(cx, script, nullptr, this);
+        probes::EnterScript(cx, script, nullptr, this);
         return true;
     }
 
     if (isGlobalFrame()) {
-        Probes::enterScript(cx, script, nullptr, this);
+        probes::EnterScript(cx, script, nullptr, this);
         return true;
     }
 
     JS_ASSERT(isNonEvalFunctionFrame());
     AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain());
 
     if (fun()->isHeavyweight() && !initFunctionScopeObjects(cx))
         return false;
@@ -282,28 +282,28 @@ StackFrame::prologue(JSContext *cx)
     if (isConstructing()) {
         RootedObject callee(cx, &this->callee());
         JSObject *obj = CreateThisForFunction(cx, callee, useNewType());
         if (!obj)
             return false;
         functionThis() = ObjectValue(*obj);
     }
 
-    Probes::enterScript(cx, script, script->function(), this);
+    probes::EnterScript(cx, script, script->function(), this);
     return true;
 }
 
 void
 StackFrame::epilogue(JSContext *cx)
 {
     JS_ASSERT(!isYielding());
     JS_ASSERT(!hasBlockChain());
 
     RootedScript script(cx, this->script());
-    Probes::exitScript(cx, script, script->function(), this);
+    probes::ExitScript(cx, script, script->function(), this);
 
     if (isEvalFrame()) {
         if (isStrictEvalFrame()) {
             JS_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval());
             if (cx->compartment()->debugMode())
                 DebugScopes::onPopStrictEvalScope(this);
         } else if (isDirectEvalFrame()) {
             if (isDebuggerFrame())
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
@@ -206,18 +206,18 @@ int nr_ice_peer_peer_rflx_candidate_crea
     cand->state=NR_ICE_CAND_STATE_INITIALIZED;
     cand->ctx=ctx;
     cand->type=ctype;
     cand->component_id=comp->component_id;
     cand->component=comp;
     cand->stream=comp->stream;
 
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): creating candidate %s with type %d",
-      ctx->label,label,ctype);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): creating candidate with type %s",
+      ctx->label,label,nr_ctype_name(ctype));
 
     if(r=nr_transport_addr_copy(&cand->base,addr))
       ABORT(r);
     if(r=nr_transport_addr_copy(&cand->addr,addr))
       ABORT(r);
     /* Bogus foundation */
     if(!(cand->foundation=r_strdup(cand->addr.as_string)))
       ABORT(r);
@@ -658,17 +658,17 @@ static int nr_ice_start_relay_turn(nr_ic
   }
 #endif /* USE_TURN */
 
 static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg)
   {
     int _status;
     nr_ice_candidate *cand=cb_arg;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): %s for %s",cand->ctx->label,__FUNCTION__,cand->label);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): %s",cand->ctx->label,cand->label,__FUNCTION__);
 
     /* Deregister to suppress duplicates */
     if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */
       nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle);
       cand->u.srvrflx.stun_handle=0;
     }
 
     switch(cand->u.srvrflx.stun->state){
@@ -711,27 +711,27 @@ static void nr_ice_turn_allocated_cb(NR_
       case NR_TURN_CLIENT_STATE_ALLOCATED:
         if (r=nr_turn_client_get_relayed_address(turn, &relay_addr))
           ABORT(r);
 
         if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|",
                                relay_addr.as_string,")",NULL))
           ABORT(r);
 
-        r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Switching from TURN (%s) to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label);
+        r_log(LOG_ICE,LOG_DEBUG,"TURN-CLIENT(%s)/CAND(%s): Switching from TURN to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label);
 
         /* Copy the relayed address into the candidate addr and
            into the candidate base. Note that we need to keep the
            ifname in the base. */
         if (r=nr_transport_addr_copy(&cand->addr, &relay_addr))
           ABORT(r);
         if (r=nr_transport_addr_copy_keep_ifname(&cand->base, &relay_addr))  /* Need to keep interface for priority calculation */
           ABORT(r);
 
-        r_log(LOG_ICE,LOG_DEBUG,"ICE-CANDIDATE(%s): base=%s, candidate=%s", cand->label, cand->base.as_string, cand->addr.as_string);
+        r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): new relay base=%s addr=%s", cand->ctx->label, cand->label, cand->base.as_string, cand->addr.as_string);
 
         RFREE(cand->label);
         cand->label=label;
         cand->state=NR_ICE_CAND_STATE_INITIALIZED;
 
         /* Execute the ready callback */
         cand->done_cb(0,0,cand->cb_arg);
 
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
@@ -83,17 +83,22 @@ int nr_ice_candidate_pair_create(nr_ice_
     }
     else{
       o_priority=rcand->priority;
       a_priority=lcand->priority;
     }
     pair->priority=(MIN(o_priority, a_priority))<<32 |
       (MAX(o_priority, a_priority))<<1 | (o_priority > a_priority?0:1);
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx) codeword=%s",pctx->ctx->label,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority,pair->codeword);
+    /*
+       TODO(bcampen@mozilla.com): Would be nice to log why this candidate was
+       created (initial pair generation, triggered check, and new trickle
+       candidate seem to be the possibilities here).
+    */
+    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND-PAIR(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx)",pctx->ctx->label,pair->codeword,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority);
 
     /* Foundation */
     if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|",
       rcand->foundation,NULL))
       ABORT(r);
 
     /* Compute the RTO per S 16 */
     RTO = MAX(100, (pctx->ctx->Ta * pctx->waiting_pairs));
@@ -179,30 +184,30 @@ static void nr_ice_candidate_pair_stun_c
     nr_transport_addr *request_src;
     nr_transport_addr *request_dst;
     nr_transport_addr *response_src;
     nr_transport_addr response_dst;
     nr_stun_message_attribute *attr;
 
     pair->stun_cb_timer=0;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s): STUN cb on pair %s",
-      pair->pctx->label,pair->local->stream->label,pair->as_string);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb on pair addr = %s",
+      pair->pctx->label,pair->local->stream->label,pair->codeword,pair->as_string);
 
     /* This ordinarily shouldn't happen, but can if we're
        doing the second check to confirm nomination.
        Just bail out */
     if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED)
       goto done;
 
     switch(pair->stun_client->state){
       case NR_STUN_CLIENT_STATE_FAILED:
         sres=pair->stun_client->response;
         if(sres && nr_stun_message_has_attribute(sres,NR_STUN_ATTR_ERROR_CODE,&attr)&&attr->u.error_code.number==487){
-          r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): detected role conflict. Switching to controlled",pair->pctx->label);
+          r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): detected role conflict. Switching to controlled",pair->pctx->label,pair->codeword);
 
           pair->pctx->controlling=0;
 
           /* Only restart if we haven't tried this already */
           if(!pair->restart_controlled_cb_timer)
             NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_controlled_cb,pair,&pair->restart_controlled_cb_timer);
 
           return;
@@ -211,37 +216,37 @@ static void nr_ice_candidate_pair_stun_c
       case NR_STUN_CLIENT_STATE_TIMED_OUT:
         nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
         break;
       case NR_STUN_CLIENT_STATE_DONE:
         /* make sure the addresses match up S 7.1.2.2 */
         response_src=&pair->stun_client->peer_addr;
         request_dst=&pair->remote->addr;
         if (nr_transport_addr_cmp(response_src,request_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
-          r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Peer address mismatch %s/%s",pair->pctx->label, response_src->as_string,request_dst->as_string);
+          r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND-PAIR(%s): Peer address mismatch %s != %s",pair->pctx->label,pair->codeword,response_src->as_string,request_dst->as_string);
           nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
           break;
         }
         request_src=&pair->stun_client->my_addr;
         nr_socket_getaddr(pair->local->osock,&response_dst);
         if (nr_transport_addr_cmp(request_src,&response_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
-          r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Address mismatch %s/%s",pair->pctx->label, request_src->as_string,response_dst.as_string);
+          r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND-PAIR(%s): Local address mismatch %s != %s",pair->pctx->label,pair->codeword,request_src->as_string,response_dst.as_string);
           nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
           break;
         }
 
         if(strlen(pair->stun_client->results.ice_binding_response.mapped_addr.as_string)==0){
           /* we're using the mapped_addr returned by the server to lookup our
            * candidate, but if the server fails to do that we can't perform
            * the lookup -- this may be a BUG because if we've gotten here
            * then the transaction ID check succeeded, and perhaps we should
            * just assume that it's the server we're talking to and that our
            * peer is ok, but I'm not sure how that'll interact with the
            * peer reflexive logic below */
-          r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): server failed to return mapped address on pair %s", pair->pctx->label,pair->as_string);
+          r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): server failed to return mapped address on pair %s", pair->pctx->label,pair->codeword,pair->as_string);
           nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
           break;
         }
         else if(!nr_transport_addr_cmp(&pair->local->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
           nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED);
         }
         else{
           /* OK, this didn't correspond to a pair on the check list, but
@@ -373,17 +378,17 @@ int nr_ice_candidate_pair_start(nr_ice_p
     return(_status);
   }
 
 
 int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
   {
     int r,_status;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): triggered check on %s",pctx->label,pair->as_string);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND-PAIR(%s): triggered check on %s",pctx->label,pair->codeword,pair->as_string);
 
     switch(pair->state){
       case NR_ICE_PAIR_STATE_FROZEN:
         nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
         /* Fall through */
       case NR_ICE_PAIR_STATE_WAITING:
         /* Start the checks */
         if(r=nr_ice_candidate_pair_start(pctx,pair))
@@ -426,17 +431,17 @@ int nr_ice_candidate_pair_select(nr_ice_
     int r,_status;
 
     if(!pair){
       r_log(LOG_ICE,LOG_ERR,"ICE-PAIR: No pair chosen");
       ABORT(R_BAD_ARGS);
     }
 
     if(pair->state!=NR_ICE_PAIR_STATE_SUCCEEDED){
-      r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): tried to install non-succeeded pair %s, ignoring",pair->pctx->label,pair->as_string);
+      r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): tried to install non-succeeded pair, ignoring: %s",pair->pctx->label,pair->codeword,pair->as_string);
     }
     else{
       /* Ok, they chose one */
       /* 1. Send a new request with nominated. Do it as a scheduled
             event to avoid reentrancy issues. Only do this if it hasn't
             happened already (though this shouldn't happen.)
       */
       if(!pair->restart_nominated_cb_timer)
@@ -451,18 +456,18 @@ int nr_ice_candidate_pair_select(nr_ice_
   abort:
     return(_status);
  }
 
 int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state)
   {
     int r,_status;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): setting pair %s to %s",
-      pctx->label,pair->as_string,nr_ice_cand_pair_states[state]);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND-PAIR(%s): setting pair to state %s: %s",
+      pctx->label,pair->codeword,nr_ice_cand_pair_states[state],pair->as_string);
 
     /* NOTE: This function used to reference pctx->state instead of
        pair->state and the assignment to pair->state was at the top
        of this function. Because pctx->state was never changed, this seems to have
        been a typo. The natural logic is "if the state changed
        decrement the counter" so this implies we should be checking
        the pair state rather than the pctx->state.
 
@@ -492,17 +497,17 @@ int nr_ice_candidate_pair_set_state(nr_i
 
     _status=0;
   abort:
     return(_status);
   }
 
 int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out)
   {
-    //r_log(LOG_ICE,LOG_DEBUG,"pair %s: state=%s, priority=0x%llx\n",pair->as_string,nr_ice_cand_pair_states[pair->state],pair->priority);
+    /*r_log(LOG_ICE,LOG_DEBUG,"CAND-PAIR(%s): pair %s: state=%s, priority=0x%llx\n",pair->codeword,pair->as_string,nr_ice_cand_pair_states[pair->state],pair->priority);*/
 
     return(0);
   }
 
 
 int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
   {
     nr_ice_cand_pair *c1;
@@ -523,17 +528,17 @@ int nr_ice_candidate_pair_insert(nr_ice_
 
 void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg)
   {
     nr_ice_cand_pair *pair=cb_arg;
     int r,_status;
 
     pair->restart_nominated_cb_timer=0;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as nominated",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s)/COMP(%d): Restarting pair as nominated: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->as_string);
 
     nr_stun_client_reset(pair->stun_client);
 
     if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair))
       ABORT(r);
 
     if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))
       ABORT(r);
@@ -545,17 +550,17 @@ void nr_ice_candidate_pair_restart_stun_
 
 static void nr_ice_candidate_pair_restart_stun_controlled_cb(NR_SOCKET s, int how, void *cb_arg)
   {
     nr_ice_cand_pair *pair=cb_arg;
     int r,_status;
 
     pair->restart_controlled_cb_timer=0;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as CONTROLLED",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s):COMP(%d): Restarting pair as CONTROLLED: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->as_string);
 
     nr_stun_client_reset(pair->stun_client);
     pair->stun_client->params.ice_binding_request.control=NR_ICE_CONTROLLED;
 
     if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_BINDING_REQUEST,nr_ice_candidate_pair_stun_cb,pair))
       ABORT(r);
 
     if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -181,20 +181,20 @@ int nr_ice_component_initialize(struct n
     char *lufrag;
     char *lpwd;
     Data pwd;
     int addr_ct=ctx->local_addr_ct;
     int i;
     int j;
     char label[256];
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/COMP(%d): initializing component",ctx->label,component->component_id);
 
     if(addr_ct==0){
-      r_log(LOG_ICE,LOG_ERR,"ICE(%s): no local addresses available",ctx->label);
+      r_log(LOG_ICE,LOG_ERR,"ICE(%s)/COMP(%d): no local addresses available",ctx->label, component->component_id);
       ABORT(R_NOT_FOUND);
     }
 
     /* Now one ice_socket for each address */
     for(i=0;i<addr_ct;i++){
       char suppress;
 
       if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
@@ -372,17 +372,17 @@ int nr_ice_component_maybe_prune_candida
           break;
         }
       }
 
       c2=TAILQ_NEXT(c2,entry_comp);
     }
 
     if (tmp) {
-      r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Removing redundant candidate %s",
+      r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): Removing redundant candidate",
             ctx->label,tmp->label);
 
       TAILQ_REMOVE(&comp->candidates,tmp,entry_comp);
       comp->candidate_ct--;
       TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock);
 
       nr_ice_candidate_destroy(&tmp);
     }
@@ -398,17 +398,17 @@ static int nr_ice_component_process_inco
     nr_stun_message *sreq=req->request;
     nr_stun_message_attribute *attr;
     int component_id_matched;
     int local_addr_matched;
     int remote_addr_matched;
     nr_ice_cand_pair *found_invalid=0;
     int r=0,_status;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string);
 
     if (comp->state == NR_ICE_COMPONENT_DISABLED)
       ABORT(R_REJECTED);
 
     /* Check for role conficts (7.2.1.1) */
     if(comp->stream->pctx->controlling){
       if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLING,&attr)){
         /* OK, there is a conflict. Who's right? */
@@ -472,17 +472,17 @@ static int nr_ice_component_process_inco
       remote_addr_matched = 1;
 
       if(pair->state==NR_ICE_PAIR_STATE_FAILED){
         found_invalid=pair;
         goto next_pair;
       }
 
       if (local_addr_matched && remote_addr_matched){
-        r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Found a matching pair: %s",comp->stream->pctx->label,pair->as_string);
+        r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Found a matching pair for received check: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
         break; /* OK, this is a known pair */
       }
 
     next_pair:
       pair=TAILQ_NEXT(pair,entry);
     }
 
     if(!pair){
@@ -538,48 +538,48 @@ static int nr_ice_component_process_inco
       }
       else{
         /* OK, there was a pair, it's just invalid: According to Section
            7.2.1.4, we need to resurrect it
         */
         if(found_invalid->state == NR_ICE_PAIR_STATE_FAILED){
           pair=found_invalid;
 
-          r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): received STUN check on invalid pair %s: resurrecting",comp->stream->pctx->label,pair->as_string);
+          r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on invalid pair, resurrecting: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
           nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_WAITING);
         }
         else{
           /* This shouldn't happen */
-          r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): received STUN check on invalid pair %s but not in FAILED state",comp->stream->pctx->label,pair->as_string);
+          r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on invalid pair that was not in state FAILED; this should not happen: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
           *error=500;
           ABORT(R_BAD_DATA);
         }
       }
     }
 
     /* OK, we've got a pair to work with. Turn it on */
+    assert(pair);
     if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){
       if(comp->stream->pctx->controlling){
-        r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label);
+        r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label, pair->codeword);
       }
       else{
         /* If this is the first time we've noticed this is nominated...*/
         pair->peer_nominated=1;
 
         if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){
           pair->nominated=1;
 
           if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) {
             *error=(r==R_NO_MEMORY)?500:400;
             ABORT(r);
           }
         }
       }
     }
-    assert(pair != 0);
 
     if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) {
       *error=(r==R_NO_MEMORY)?500:400;
       ABORT(r);
     }
 
     _status=0;
   abort:
@@ -615,26 +615,26 @@ int nr_ice_component_service_pre_answer_
   {
     nr_ice_pre_answer_request *r1,*r2;
     nr_ice_component *comp = pcomp->local_component;
     int r,_status;
 
     if (serviced)
       *serviced = 0;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): looking for pre-answer requests",pctx->label,comp->stream->label,comp->component_id);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): looking for pre-answer requests",pctx->label,comp->stream->label,comp->component_id);
 
     STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) {
       if (!strcmp(r1->username, username)) {
         int error = 0;
 
-        r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): found pre-answer request",pctx->label,comp->stream->label,comp->component_id);
+        r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): found pre-answer request",pctx->label,comp->stream->label,comp->component_id);
         r = nr_ice_component_process_incoming_check(pcomp, &r1->local_addr, &r1->req, &error);
         if (r) {
-          r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/comp(%d): error processing pre-answer request. Would have returned %d",pctx->label,comp->stream->label,comp->component_id, error);
+          r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): error processing pre-answer request. Would have returned %d",pctx->label,comp->stream->label,comp->component_id, error);
         }
         (*serviced)++;
         STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
         nr_ice_pre_answer_request_destroy(&r1);
       }
     }
 
     _status=0;
@@ -645,17 +645,17 @@ int nr_ice_component_service_pre_answer_
 int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote)
   {
     int r, _status;
     nr_ice_candidate *pcand;
     nr_ice_cand_pair *pair=0;
     char codeword[5];
 
     nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword);
-    r_log(LOG_ICE,LOG_DEBUG,"Pairing local candidate %s:%s",codeword,lcand->label);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing local candidate %s",pctx->label,codeword,lcand->label);
 
     switch(lcand->type){
       case HOST:
         break;
       case SERVER_REFLEXIVE:
       case PEER_REFLEXIVE:
         /* Don't actually pair these candidates */
         goto done;
@@ -681,17 +681,17 @@ int nr_ice_component_pair_candidate(nr_i
       */
       if (pair_all_remote || (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED)) {
         /* If we are pairing our own trickle candidates, the remote candidate should
            all be paired */
         if (pair_all_remote)
           assert (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_PAIRED);
 
         nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword);
-        r_log(LOG_ICE,LOG_DEBUG,"Pairing with peer candidate %s:%s",codeword,pcand->label);
+        r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing with peer candidate %s", pctx->label, codeword, pcand->label);
 
         if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair))
           ABORT(r);
 
         if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list,
                                           pair))
           ABORT(r);
       }
@@ -760,17 +760,17 @@ int nr_ice_component_pair_candidates(nr_
    component for use when we see the actual answer, at which point we need
    to do the procedures from S 7.2.1 in nr_ice_component_stun_server_cb.
  */
 static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error)
   {
     int r, _status;
     nr_ice_component *comp = (nr_ice_component *)cb_arg;
     nr_ice_pre_answer_request *par = 0;
-    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/comp(%d): Received STUN request pre-answer from %s",
+    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Received STUN request pre-answer from %s",
           comp->ctx->label, comp->stream->label, comp->component_id, req->src_addr.as_string);
 
     if (r=nr_ice_pre_answer_request_create(sock, req, &par))
       ABORT(r);
 
     *dont_free = 1;
     STAILQ_INSERT_TAIL(&comp->pre_answer_reqs, par, entry);
 
@@ -787,43 +787,43 @@ int nr_ice_component_nominated_pair(nr_i
 
     if(!comp->nominated)
       fire_cb=1;
 
     /* Are we changing what the nominated pair is? */
     if(comp->nominated){
       if(comp->nominated->priority > pair->priority)
         return(0);
-      r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): replacing pair %s with pair %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->as_string,pair->as_string);
+      r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): replacing pair %s with CAND-PAIR(%s)",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->codeword,comp->nominated->as_string,pair->codeword);
     }
 
     /* Set the new nominated pair */
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): nominated pair is %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,pair);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): nominated pair is %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string);
     comp->state=NR_ICE_COMPONENT_NOMINATED;
     comp->nominated=pair;
     comp->active=pair;
 
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling all pairs but %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,pair);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling all pairs but %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string);
 
     /* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */
     p2=TAILQ_FIRST(&comp->stream->check_list);
     while(p2){
       if((p2 != pair) &&
          (p2->remote->component->component_id == comp->component_id) &&
          ((p2->state == NR_ICE_PAIR_STATE_FROZEN) ||
 	  (p2->state == NR_ICE_PAIR_STATE_WAITING))) {
-        r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling pair %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->as_string,p2);
+        r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword);
 
         if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2))
           ABORT(r);
       }
 
       p2=TAILQ_NEXT(p2,entry);
     }
-    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id);
+    r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id);
 
     if(r=nr_ice_media_stream_component_nominated(comp->stream,comp))
       ABORT(r);
 
     _status=0;
   abort:
     return(_status);
   }
--- a/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
@@ -280,17 +280,17 @@ int nr_stun_client_force_retransmit(nr_s
 static int nr_stun_client_send_request(nr_stun_client_ctx *ctx)
   {
     int r,_status;
     char string[256];
 
     if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING)
         ABORT(R_NOT_PERMITTED);
 
-    r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Sending(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,ctx->peer_addr.as_string);
+    r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Sending check request (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,ctx->peer_addr.as_string);
 
     if (ctx->request == 0) {
         switch (ctx->mode) {
         case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH:
             ctx->params.stun_binding_request.nonce = ctx->nonce;
             ctx->params.stun_binding_request.realm = ctx->realm;
             assert(0);
             ABORT(R_INTERNAL);
@@ -430,17 +430,17 @@ int nr_stun_client_process_response(nr_s
 
     ATTACH_DATA(hmac_key, hmac_key_d);
 
     if(ctx->state==NR_STUN_CLIENT_STATE_CANCELLED)
       ABORT(R_REJECTED);
     if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING)
       ABORT(R_REJECTED);
 
-    r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
+    r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received check response (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
 
     snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Received ", ctx->label);
     r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len);
 
     /* determine password */
     switch (ctx->mode) {
     case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH:
       compute_lt_key = 1;
@@ -513,29 +513,29 @@ int nr_stun_client_process_response(nr_s
       }
       password = &hmac_key;
     }
 
     if ((r=nr_stun_message_create2(&ctx->response, msg, len)))
         ABORT(r);
 
     if ((r=nr_stun_decode_message(ctx->response, nr_stun_client_get_password, password))) {
-        r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): error decoding message",ctx->label);
+        r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): error decoding response",ctx->label);
         ABORT(r);
     }
 
     /* This will return an error if request and response don't match,
        which is how we reject responses that match other contexts. */
     if ((r=nr_stun_receive_message(ctx->request, ctx->response))) {
-        r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): error receiving message",ctx->label);
+        r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): error receiving response",ctx->label);
         ABORT(r);
     }
 
     r_log(NR_LOG_STUN,LOG_DEBUG,
-          "STUN-CLIENT(%s): successfully received message; processing",ctx->label);
+          "STUN-CLIENT(%s): successfully received response; processing",ctx->label);
 
 /* TODO: !nn! currently using password!=0 to mean that auth is required,
  * TODO: !nn! but we should probably pass that in explicitly via the
  * TODO: !nn! usage (ctx->mode?) */
     if (password) {
         if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_NONCE, 0)) {
             if ((r=nr_stun_receive_response_long_term_auth(ctx->response, ctx)))
                 ABORT(r);
--- a/media/webrtc/signaling/signaling.gyp
+++ b/media/webrtc/signaling/signaling.gyp
@@ -194,17 +194,18 @@
           ],
         }],
         ['build_for_test!=0', {
           'include_dirs': [
             './test'
           ],
           'defines' : [
             'NO_CHROMIUM_LOGGING',
-            'USE_FAKE_MEDIA_STREAMS'
+            'USE_FAKE_MEDIA_STREAMS',
+            'USE_FAKE_PCOBSERVER'
           ],
         }],
         ['(OS=="linux") or (OS=="android")', {
           'include_dirs': [
           ],
 
           'defines': [
             'SIP_OS_LINUX',
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ -305,16 +305,17 @@ WebrtcAudioConduit::ConfigureSendMediaCo
     if(error ==  VE_CANNOT_SET_SEND_CODEC || error == VE_CODEC_ERROR)
     {
       return kMediaConduitInvalidSendCodec;
     }
 
     return kMediaConduitUnknownError;
   }
 
+#ifdef MOZILLA_INTERNAL_API
   // TEMPORARY - see bug 694814 comment 2
   nsresult rv;
   nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
 
     if (branch) {
       int32_t aec = 0; // 0 == unchanged
@@ -326,16 +327,17 @@ WebrtcAudioConduit::ConfigureSendMediaCo
       CSFLogDebug(logTag,"Audio config: aec: %d", aec_on ? aec : -1);
       mEchoOn = aec_on;
       if (static_cast<webrtc::EcModes>(aec) != webrtc::kEcUnchanged)
         mEchoCancel = static_cast<webrtc::EcModes>(aec);
 
       branch->GetIntPref("media.peerconnection.capture_delay", &mCaptureDelay);
     }
   }
+#endif
 
   if (0 != (error = mPtrVoEProcessing->SetEcStatus(mEchoOn, mEchoCancel))) {
     CSFLogError(logTag,"%s Error setting EVStatus: %d ",__FUNCTION__, error);
     return kMediaConduitUnknownError;
   }
 
   //Let's Send Transport State-machine on the Engine
   if(mPtrVoEBase->StartSend(mChannel) == -1)
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -10,16 +10,17 @@
 #include "CSFAudioTermination.h"
 #include "CSFVideoTermination.h"
 #include "MediaConduitErrors.h"
 #include "MediaConduitInterface.h"
 #include "MediaPipeline.h"
 #include "VcmSIPCCBinding.h"
 #include "csf_common.h"
 #include "PeerConnectionImpl.h"
+#include "PeerConnectionMedia.h"
 #include "nsThreadUtils.h"
 #include "transportflow.h"
 #include "transportlayer.h"
 #include "transportlayerdtls.h"
 #include "transportlayerice.h"
 #include "runnable_utils.h"
 #include "cpr_stdlib.h"
 #include "cpr_string.h"
@@ -62,16 +63,17 @@ typedef enum {
     CC_VIDEO_1,
     CC_DATACHANNEL_1
 } cc_media_cap_name;
 
 #define SIPSDP_ILBC_MODE20 20
 
 /* static */
 
+using namespace mozilla;
 using namespace CSF;
 
 VcmSIPCCBinding * VcmSIPCCBinding::gSelf = NULL;
 int VcmSIPCCBinding::gAudioCodecMask = 0;
 int VcmSIPCCBinding::gVideoCodecMask = 0;
 nsIThread *VcmSIPCCBinding::gMainThread = NULL;
 nsIEventTarget *VcmSIPCCBinding::gSTSThread = NULL;
 nsCOMPtr<nsIPrefBranch> VcmSIPCCBinding::gBranch = NULL;
--- a/media/webrtc/signaling/src/peerconnection/MediaStreamList.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaStreamList.cpp
@@ -5,16 +5,17 @@
 #include "CSFLog.h"
 #include "base/basictypes.h"
 #include "MediaStreamList.h"
 #ifdef MOZILLA_INTERNAL_API
 #include "mozilla/dom/MediaStreamListBinding.h"
 #endif
 #include "nsIScriptGlobalObject.h"
 #include "PeerConnectionImpl.h"
+#include "PeerConnectionMedia.h"
 
 namespace mozilla {
 namespace dom {
 
 MediaStreamList::MediaStreamList(sipcc::PeerConnectionImpl* peerConnection,
                                  StreamType type)
   : mPeerConnection(peerConnection),
     mType(type)
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -15,16 +15,17 @@
 #include "CC_SIPCCDeviceInfo.h"
 #include "vcm.h"
 #include "VcmSIPCCBinding.h"
 #include "PeerConnectionImpl.h"
 #include "PeerConnectionCtx.h"
 #include "runnable_utils.h"
 #include "cpr_socket.h"
 #include "debug-psipcc-types.h"
+#include "prcvar.h"
 
 #include "mozilla/Telemetry.h"
 
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "mozilla/Services.h"
 #include "StaticPtr.h"
 extern "C" {
@@ -225,17 +226,17 @@ nsresult PeerConnectionCtx::Initialize()
   }
 
   if (!mCCM->startSDPMode())
     return NS_ERROR_FAILURE;
 
   mDevice = mCCM->getActiveDevice();
   mCCM->addCCObserver(this);
   NS_ENSURE_TRUE(mDevice.get(), NS_ERROR_FAILURE);
-  ChangeSipccState(PeerConnectionImpl::kStarting);
+  ChangeSipccState(mozilla::dom::PCImplSipccState::Starting);
 
   // Now that everything is set up, we let the CCApp thread
   // know that it's okay to start processing messages.
   PR_Lock(ccAppReadyToStartLock);
   ccAppReadyToStart = 1;
   PR_NotifyAllCondVar(ccAppReadyToStartCond);
   PR_Unlock(ccAppReadyToStartLock);
 
@@ -260,27 +261,27 @@ CSF::CC_CallPtr PeerConnectionCtx::creat
 }
 
 void PeerConnectionCtx::onDeviceEvent(ccapi_device_event_e aDeviceEvent,
                                       CSF::CC_DevicePtr aDevice,
                                       CSF::CC_DeviceInfoPtr aInfo ) {
   cc_service_state_t state = aInfo->getServiceState();
   // We are keeping this in a local var to avoid a data race
   // with ChangeSipccState in the debug message and compound if below
-  PeerConnectionImpl::SipccState currentSipccState = mSipccState;
+  mozilla::dom::PCImplSipccState currentSipccState = mSipccState;
 
   switch (aDeviceEvent) {
     case CCAPI_DEVICE_EV_STATE:
       CSFLogDebug(logTag, "%s - %d : %d", __FUNCTION__, state, currentSipccState);
 
       if (CC_STATE_INS == state) {
         // SIPCC is up
-        if (PeerConnectionImpl::kStarting == currentSipccState ||
-            PeerConnectionImpl::kIdle == currentSipccState) {
-          ChangeSipccState(PeerConnectionImpl::kStarted);
+        if (mozilla::dom::PCImplSipccState::Starting == currentSipccState ||
+            mozilla::dom::PCImplSipccState::Idle == currentSipccState) {
+          ChangeSipccState(mozilla::dom::PCImplSipccState::Started);
         } else {
           CSFLogError(logTag, "%s PeerConnection already started", __FUNCTION__);
         }
       } else {
         NS_NOTREACHED("Unsupported Signaling State Transition");
       }
       break;
     default:
@@ -317,12 +318,12 @@ void PeerConnectionCtx::onCallEvent(ccap
 static void onCallEvent_m(nsAutoPtr<std::string> peerconnection,
                           ccapi_call_event_e aCallEvent,
                           CSF::CC_CallInfoPtr aInfo) {
   CSFLogDebug(logTag, "onCallEvent()");
   PeerConnectionWrapper pc(peerconnection->c_str());
   if (!pc.impl())  // This must be an event on a dead PC. Ignore
     return;
   CSFLogDebug(logTag, "Calling PC");
-  pc.impl()->onCallEvent(aCallEvent, aInfo);
+  pc.impl()->onCallEvent(OnCallEventArgs(aCallEvent, aInfo));
 }
 
 }  // namespace sipcc
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
@@ -22,16 +22,36 @@
 #include "PeerConnectionImpl.h"
 
 namespace mozilla {
 class PeerConnectionCtxShutdown;
 }
 
 namespace sipcc {
 
+using namespace mozilla;
+
+// Unit-test helper, because cc_media_constraints_t is hard to forward-declare
+
+class MediaConstraintsExternal {
+public:
+  MediaConstraintsExternal(cc_media_constraints_t *aConstraints)
+  : mConstraints(aConstraints) {}
+  cc_media_constraints_t *mConstraints;
+};
+
+class OnCallEventArgs {
+public:
+  OnCallEventArgs(ccapi_call_event_e aCallEvent, CSF::CC_CallInfoPtr aInfo)
+  : mCallEvent(aCallEvent), mInfo(aInfo) {}
+
+  ccapi_call_event_e mCallEvent;
+  CSF::CC_CallInfoPtr mInfo;
+};
+
 // A class to hold some of the singleton objects we need:
 // * The global PeerConnectionImpl table and its associated lock.
 // * Currently SIPCC only allows a single stack instance to exist in a process
 //   at once. This class implements a singleton object that wraps that.
 // * The observer class that demuxes events onto individual PCs.
 class PeerConnectionCtx : public CSF::CC_Observer {
  public:
   static nsresult InitializeGlobal(nsIThread *mainThread, nsIEventTarget *stsThread);
@@ -43,45 +63,45 @@ class PeerConnectionCtx : public CSF::CC
   virtual void onDeviceEvent(ccapi_device_event_e deviceEvent, CSF::CC_DevicePtr device, CSF::CC_DeviceInfoPtr info);
   virtual void onFeatureEvent(ccapi_device_event_e deviceEvent, CSF::CC_DevicePtr device, CSF::CC_FeatureInfoPtr feature_info) {}
   virtual void onLineEvent(ccapi_line_event_e lineEvent, CSF::CC_LinePtr line, CSF::CC_LineInfoPtr info) {}
   virtual void onCallEvent(ccapi_call_event_e callEvent, CSF::CC_CallPtr call, CSF::CC_CallInfoPtr info);
 
   // Create a SIPCC Call
   CSF::CC_CallPtr createCall();
 
-  PeerConnectionImpl::SipccState sipcc_state() { return mSipccState; }
+  mozilla::dom::PCImplSipccState sipcc_state() { return mSipccState; }
 
   // Make these classes friend so that they can access mPeerconnections.
   friend class PeerConnectionImpl;
   friend class PeerConnectionWrapper;
 
  private:
   // We could make these available only via accessors but it's too much trouble.
   std::map<const std::string, PeerConnectionImpl *> mPeerConnections;
 
-  PeerConnectionCtx() :  mSipccState(PeerConnectionImpl::kIdle),
+  PeerConnectionCtx() :  mSipccState(mozilla::dom::PCImplSipccState::Idle),
                          mCCM(NULL), mDevice(NULL) {}
   // This is a singleton, so don't copy construct it, etc.
   PeerConnectionCtx(const PeerConnectionCtx& other) MOZ_DELETE;
   void operator=(const PeerConnectionCtx& other) MOZ_DELETE;
   virtual ~PeerConnectionCtx() {};
 
   nsresult Initialize();
   nsresult Cleanup();
 
-  void ChangeSipccState(PeerConnectionImpl::SipccState aState) {
+  void ChangeSipccState(mozilla::dom::PCImplSipccState aState) {
     mSipccState = aState;
   }
 
   // Telemetry Peer conection counter
   int mConnectionCounter;
 
   // SIPCC objects
-  PeerConnectionImpl::SipccState mSipccState;  // TODO(ekr@rtfm.com): refactor this out? What does it do?
+  mozilla::dom::PCImplSipccState mSipccState;  // TODO(ekr@rtfm.com): refactor this out? What does it do?
   CSF::CallControlManagerPtr mCCM;
   CSF::CC_DevicePtr mDevice;
 
   static PeerConnectionCtx *gInstance;
 public:
   static nsIThread *gMainThread;
   static StaticRefPtr<mozilla::PeerConnectionCtxShutdown> gPeerConnectionCtxShutdown;
 };
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -31,47 +31,59 @@
 #include "nsISocketTransportService.h"
 #include "nsIConsoleService.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 
 #include "runnable_utils.h"
 #include "PeerConnectionCtx.h"
 #include "PeerConnectionImpl.h"
+#include "PeerConnectionMedia.h"
 #include "nsPIDOMWindow.h"
 #include "nsDOMDataChannelDeclarations.h"
+#include "dtlsidentity.h"
 
 #ifdef MOZILLA_INTERNAL_API
+#include "nsDOMDataChannel.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "nsDOMJSUtils.h"
 #include "nsIDocument.h"
 #include "nsIScriptError.h"
 #include "nsPrintfCString.h"
 #include "nsURLHelper.h"
 #include "nsNetUtil.h"
+#include "nsIDOMDataChannel.h"
 #include "mozilla/dom/RTCConfigurationBinding.h"
+#include "mozilla/dom/RTCPeerConnectionBinding.h"
+#include "mozilla/dom/PeerConnectionImplBinding.h"
+#include "mozilla/dom/DataChannelBinding.h"
 #include "MediaStreamList.h"
 #include "nsIScriptGlobalObject.h"
 #include "jsapi.h"
 #include "DOMMediaStream.h"
 #endif
 
 #ifndef USE_FAKE_MEDIA_STREAMS
 #include "MediaSegment.h"
 #endif
 
+#ifdef USE_FAKE_PCOBSERVER
+#include "FakePCObserver.h"
+#else
+#include "mozilla/dom/PeerConnectionObserverBinding.h"
+#endif
+#include "mozilla/dom/PeerConnectionObserverEnumsBinding.h"
+
 #define ICE_PARSING "In RTCConfiguration passed to RTCPeerConnection constructor"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-namespace mozilla {
-  class DataChannel;
-}
+typedef PCObserverString ObString;
 
 class nsIDOMDataChannel;
 
 static const char* logTag = "PeerConnectionImpl";
 static const int DTLS_FINGERPRINT_LENGTH = 64;
 static const int MEDIA_STREAM_MUTE = 0x80;
 
 PRLogModuleInfo *signalingLogInfo() {
@@ -80,67 +92,106 @@ PRLogModuleInfo *signalingLogInfo() {
     logModuleInfo = PR_NewLogModule("signaling");
   }
   return logModuleInfo;
 }
 
 
 namespace sipcc {
 
-void MediaConstraints::setBooleanConstraint(const std::string& constraint, bool enabled, bool mandatory) {
+// Convert constraints to C structures
 
-  ConstraintInfo booleanconstraint;
-  booleanconstraint.mandatory = mandatory;
+#ifdef MOZILLA_INTERNAL_API
+static void
+Apply(const Optional<bool> &aSrc, cc_boolean_constraint_t *aDst,
+      bool mandatory = false) {
+  if (aSrc.WasPassed() && (mandatory || !aDst->was_passed)) {
+    aDst->was_passed = true;
+    aDst->value = aSrc.Value();
+    aDst->mandatory = mandatory;
+  }
+}
+#endif
 
-  if (enabled)
-    booleanconstraint.value = "TRUE";
-  else
-    booleanconstraint.value = "FALSE";
-
-  mConstraints[constraint] = booleanconstraint;
+static cc_media_constraints_t*
+ConvertConstraints(const MediaConstraintsInternal& aSrc) {
+  cc_media_constraints_t* c = (cc_media_constraints_t*)
+      cpr_malloc(sizeof(cc_media_constraints_t));
+  NS_ENSURE_TRUE(c,c);
+  memset(c, 0, sizeof(cc_media_constraints_t));
+#ifdef MOZILLA_INTERNAL_API
+  Apply(aSrc.mMandatory.mOfferToReceiveAudio, &c->offer_to_receive_audio, true);
+  Apply(aSrc.mMandatory.mOfferToReceiveVideo, &c->offer_to_receive_video, true);
+  Apply(aSrc.mMandatory.mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel,
+        true);
+  if (aSrc.mOptional.WasPassed()) {
+    const Sequence<MediaConstraintSet> &array = aSrc.mOptional.Value();
+    for (uint32_t i = 0; i < array.Length(); i++) {
+      Apply(array[i].mOfferToReceiveAudio, &c->offer_to_receive_audio);
+      Apply(array[i].mOfferToReceiveVideo, &c->offer_to_receive_video);
+      Apply(array[i].mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel);
+    }
+  }
+#endif
+  return c;
 }
 
-void MediaConstraints::buildArray(cc_media_constraints_t** constraintarray) {
-
-  if (0 == mConstraints.size())
-    return;
-
-  short i = 0;
-  std::string tmpStr;
-  *constraintarray = (cc_media_constraints_t*) cpr_malloc(sizeof(cc_media_constraints_t));
-  int tmpStrAllocLength;
-
-  (*constraintarray)->constraints = (cc_media_constraint_t**) cpr_malloc(mConstraints.size() * sizeof(cc_media_constraint_t));
+// Getting exceptions back down from PCObserver is generally not harmful.
+namespace {
+class JSErrorResult : public ErrorResult
+{
+public:
+  ~JSErrorResult()
+  {
+#ifdef MOZILLA_INTERNAL_API
+    WouldReportJSException();
+    if (IsJSException()) {
+      MOZ_ASSERT(NS_IsMainThread());
+      AutoJSContext cx;
+      Optional<JS::Handle<JS::Value> > value(cx);
+      StealJSException(cx, &value.Value());
+    }
+#endif
+  }
+};
 
-  for (constraints_map::iterator it = mConstraints.begin();
-          it != mConstraints.end(); ++it) {
-    (*constraintarray)->constraints[i] = (cc_media_constraint_t*) cpr_malloc(sizeof(cc_media_constraint_t));
-
-    tmpStr = it->first;
-    tmpStrAllocLength = tmpStr.size() + 1;
-    (*constraintarray)->constraints[i]->name = (char*) cpr_malloc(tmpStrAllocLength);
-    sstrncpy((*constraintarray)->constraints[i]->name, tmpStr.c_str(), tmpStrAllocLength);
+// The WrapRunnable() macros copy passed-in args and passes them to the function
+// later on the other thread. ErrorResult cannot be passed like this because it
+// disallows copy-semantics.
+//
+// This WrappableJSErrorResult hack solves this by not actually copying the
+// ErrorResult, but creating a new one instead, which works because we don't
+// care about the result.
+//
+// Since this is for JS-calls, these can only be dispatched to the main thread.
 
-    tmpStr = it->second.value;
-    tmpStrAllocLength = tmpStr.size() + 1;
-    (*constraintarray)->constraints[i]->value = (char*) cpr_malloc(tmpStrAllocLength);
-    sstrncpy((*constraintarray)->constraints[i]->value, tmpStr.c_str(), tmpStrAllocLength);
-
-    (*constraintarray)->constraints[i]->mandatory = it->second.mandatory;
-    i++;
+class WrappableJSErrorResult {
+public:
+  WrappableJSErrorResult() : isCopy(false) {}
+  WrappableJSErrorResult(WrappableJSErrorResult &other) : mRv(), isCopy(true) {}
+  ~WrappableJSErrorResult() {
+    if (isCopy) {
+#ifdef MOZILLA_INTERNAL_API
+      MOZ_ASSERT(NS_IsMainThread());
+#endif
+    }
   }
-  (*constraintarray)->constraint_count = i;
+  operator JSErrorResult &() { return mRv; }
+private:
+  JSErrorResult mRv;
+  bool isCopy;
+};
 }
 
 class PeerConnectionObserverDispatch : public nsRunnable {
 
 public:
   PeerConnectionObserverDispatch(CSF::CC_CallInfoPtr aInfo,
                                  nsRefPtr<PeerConnectionImpl> aPC,
-                                 IPeerConnectionObserver* aObserver)
+                                 PeerConnectionObserver* aObserver)
       : mPC(aPC),
         mObserver(aObserver),
         mCode(static_cast<PeerConnectionImpl::Error>(aInfo->getStatusCode())),
         mReason(aInfo->getStatus()),
         mSdpStr(),
 	mCandidateStr(),
         mCallState(aInfo->getCallState()),
         mFsmState(aInfo->getFsmState()),
@@ -161,35 +212,39 @@ public:
 
   ~PeerConnectionObserverDispatch(){}
 
 #ifdef MOZILLA_INTERNAL_API
   class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
   {
   public:
     TracksAvailableCallback(DOMMediaStream::TrackTypeHints aTrackTypeHints,
-                            nsCOMPtr<IPeerConnectionObserver> aObserver)
-      : DOMMediaStream::OnTracksAvailableCallback(aTrackTypeHints),
-        mObserver(aObserver) {}
+                            nsRefPtr<PeerConnectionObserver> aObserver)
+    : DOMMediaStream::OnTracksAvailableCallback(aTrackTypeHints)
+    , mObserver(aObserver) {}
 
     virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE
     {
       MOZ_ASSERT(NS_IsMainThread());
 
       // Start currentTime from the point where this stream was successfully
       // returned.
       aStream->SetLogicalStreamStartTime(aStream->GetStream()->GetCurrentTime());
 
       CSFLogInfo(logTag, "Returning success for OnAddStream()");
       // We are running on main thread here so we shouldn't have a race
       // on this callback
-      mObserver->OnAddStream(aStream);
+      JSErrorResult rv;
+      mObserver->OnAddStream(*aStream, rv);
+      if (rv.Failed()) {
+        CSFLogError(logTag, ": OnAddStream() failed! Error: %d", rv.ErrorCode());
+      }
     }
-
-    nsCOMPtr<IPeerConnectionObserver> mObserver;
+  private:
+    nsRefPtr<PeerConnectionObserver> mObserver;
   };
 #endif
 
   NS_IMETHOD Run() {
 
     CSFLogInfo(logTag, "PeerConnectionObserverDispatch processing "
                "mCallState = %d (%s), mFsmState = %d (%s)",
                mCallState, mStateStr.c_str(), mFsmState, mFsmStateStr.c_str());
@@ -215,78 +270,81 @@ public:
      * While the fsm_states_t (FSM_DEF_*) constants are a proper superset
      * of SignalingState, and the order in which the SignalingState values
      * appear matches the order they appear in fsm_states_t, their underlying
      * numeric representation is different. Hence, we need to perform an
      * offset calculation to map from one to the other.
      */
 
     if (mFsmState >= FSMDEF_S_STABLE && mFsmState <= FSMDEF_S_CLOSED) {
-      int offset = FSMDEF_S_STABLE - PeerConnectionImpl::kSignalingStable;
-      mPC->SetSignalingState_m(
-        static_cast<PeerConnectionImpl::SignalingState>(mFsmState - offset));
+      int offset = FSMDEF_S_STABLE - int(PCImplSignalingState::SignalingStable);
+      mPC->SetSignalingState_m(static_cast<PCImplSignalingState>(mFsmState - offset));
     } else {
       CSFLogError(logTag, ": **** UNHANDLED SIGNALING STATE : %d (%s)",
                   mFsmState, mFsmStateStr.c_str());
     }
 
+    JSErrorResult rv;
+
     switch (mCallState) {
       case CREATEOFFERSUCCESS:
-        mObserver->OnCreateOfferSuccess(mSdpStr.c_str());
+        mObserver->OnCreateOfferSuccess(ObString(mSdpStr.c_str()), rv);
         break;
 
       case CREATEANSWERSUCCESS:
-        mObserver->OnCreateAnswerSuccess(mSdpStr.c_str());
+        mObserver->OnCreateAnswerSuccess(ObString(mSdpStr.c_str()), rv);
         break;
 
       case CREATEOFFERERROR:
-        mObserver->OnCreateOfferError(mCode, mReason.c_str());
+        mObserver->OnCreateOfferError(mCode, ObString(mReason.c_str()), rv);
         break;
 
       case CREATEANSWERERROR:
-        mObserver->OnCreateAnswerError(mCode, mReason.c_str());
+        mObserver->OnCreateAnswerError(mCode, ObString(mReason.c_str()), rv);
         break;
 
       case SETLOCALDESCSUCCESS:
         // TODO: The SDP Parse error list should be copied out and sent up
         // to the Javascript layer before being cleared here. Even though
         // there was not a failure, it is possible that the SDP parse generated
         // warnings. The WebRTC spec does not currently have a mechanism for
         // providing non-fatal warnings.
         mPC->ClearSdpParseErrorMessages();
-        mObserver->OnSetLocalDescriptionSuccess();
+        mObserver->OnSetLocalDescriptionSuccess(rv);
         break;
 
       case SETREMOTEDESCSUCCESS:
         // TODO: The SDP Parse error list should be copied out and sent up
         // to the Javascript layer before being cleared here. Even though
         // there was not a failure, it is possible that the SDP parse generated
         // warnings. The WebRTC spec does not currently have a mechanism for
         // providing non-fatal warnings.
         mPC->ClearSdpParseErrorMessages();
-        mObserver->OnSetRemoteDescriptionSuccess();
+        mObserver->OnSetRemoteDescriptionSuccess(rv);
 #ifdef MOZILLA_INTERNAL_API
         mPC->startCallTelem();
 #endif
         break;
 
       case SETLOCALDESCERROR:
-        mObserver->OnSetLocalDescriptionError(mCode, mReason.c_str());
+        mObserver->OnSetLocalDescriptionError(mCode,
+                                              ObString(mReason.c_str()), rv);
         break;
 
       case SETREMOTEDESCERROR:
-        mObserver->OnSetRemoteDescriptionError(mCode, mReason.c_str());
+        mObserver->OnSetRemoteDescriptionError(mCode,
+                                               ObString(mReason.c_str()), rv);
         break;
 
       case ADDICECANDIDATE:
-        mObserver->OnAddIceCandidateSuccess();
+        mObserver->OnAddIceCandidateSuccess(rv);
         break;
 
       case ADDICECANDIDATEERROR:
-        mObserver->OnAddIceCandidateError(mCode, mReason.c_str());
+        mObserver->OnAddIceCandidateError(mCode, ObString(mReason.c_str()), rv);
         break;
 
       case FOUNDICECANDIDATE:
         {
             size_t end_of_level = mCandidateStr.find('\t');
             if (end_of_level == std::string::npos) {
                 MOZ_ASSERT(false);
                 return NS_OK;
@@ -311,21 +369,19 @@ public:
                 return NS_OK;
             }
 
             std::string mid = mCandidateStr.substr(end_of_level + 1,
                                                    end_of_mid - (end_of_level + 1));
 
             std::string candidate = mCandidateStr.substr(end_of_mid + 1);
 
-
-            mObserver->OnIceCandidate(
-                level_long & 0xffff,
-                mid.c_str(),
-                candidate.c_str());
+            mObserver->OnIceCandidate(level_long & 0xffff,
+                                      ObString(mid.c_str()),
+                                      ObString(candidate.c_str()), rv);
         }
         break;
       case REMOTESTREAMADD:
         {
           DOMMediaStream* stream = nullptr;
 
           if (!mRemoteStream) {
             CSFLogError(logTag, "%s: GetRemoteStream returned NULL", __FUNCTION__);
@@ -337,71 +393,85 @@ public:
             CSFLogError(logTag, "%s: GetMediaStream returned NULL", __FUNCTION__);
           } else {
 #ifdef MOZILLA_INTERNAL_API
             TracksAvailableCallback* tracksAvailableCallback =
               new TracksAvailableCallback(mRemoteStream->mTrackTypeHints, mObserver);
 
             stream->OnTracksAvailable(tracksAvailableCallback);
 #else
-            mObserver->OnAddStream(stream);
+            mObserver->OnAddStream(stream, rv);
 #endif
           }
           break;
         }
 
       case UPDATELOCALDESC:
         /* No action necessary */
         break;
 
       default:
         CSFLogError(logTag, ": **** UNHANDLED CALL STATE : %d (%s)",
                     mCallState, mStateStr.c_str());
         break;
     }
-
     return NS_OK;
   }
 
 private:
   nsRefPtr<PeerConnectionImpl> mPC;
-  nsCOMPtr<IPeerConnectionObserver> mObserver;
+  nsRefPtr<PeerConnectionObserver> mObserver;
   PeerConnectionImpl::Error mCode;
   std::string mReason;
   std::string mSdpStr;
   std::string mCandidateStr;
   cc_call_state_t mCallState;
   fsmdef_states_t mFsmState;
   std::string mStateStr;
   std::string mFsmStateStr;
   nsRefPtr<RemoteSourceStreamInfo> mRemoteStream;
 };
 
-NS_IMPL_ISUPPORTS1(PeerConnectionImpl, IPeerConnection)
+NS_IMPL_ISUPPORTS0(PeerConnectionImpl)
 
-PeerConnectionImpl::PeerConnectionImpl()
+#ifdef MOZILLA_INTERNAL_API
+JSObject*
+PeerConnectionImpl::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return PeerConnectionImplBinding::Wrap(aCx, aScope, this);
+}
+#endif
+
+struct PeerConnectionImpl::Internal {
+  CSF::CC_CallPtr mCall;
+};
+
+PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
 : mTimeCard(PR_LOG_TEST(signalingLogInfo(),PR_LOG_ERROR) ?
             create_timecard() : nullptr)
-  , mCall(NULL)
-  , mReadyState(kNew)
-  , mSignalingState(kSignalingStable)
-  , mIceState(kIceGathering)
-  , mPCObserver(NULL)
-  , mWindow(NULL)
+  , mInternal(new Internal())
+  , mReadyState(PCImplReadyState::New)
+  , mSignalingState(PCImplSignalingState::SignalingStable)
+  , mIceState(PCImplIceState::IceGathering)
+  , mWindow(nullptr)
   , mIdentity(NULL)
   , mSTSThread(NULL)
   , mMedia(NULL)
   , mNumAudioStreams(0)
   , mNumVideoStreams(0)
   , mHaveDataStream(false)
   , mTrickle(true) // TODO(ekr@rtfm.com): Use pref
 {
 #ifdef MOZILLA_INTERNAL_API
   MOZ_ASSERT(NS_IsMainThread());
+  if (aGlobal) {
+    mWindow = do_QueryInterface(aGlobal->GetAsSupports());
+  }
 #endif
+  MOZ_ASSERT(mInternal);
   CSFLogInfo(logTag, "%s: PeerConnectionImpl constructor for %s",
              __FUNCTION__, mHandle.c_str());
   STAMP_TIMECARD(mTimeCard, "Constructor Completed");
 }
 
 PeerConnectionImpl::~PeerConnectionImpl()
 {
   if (mTimeCard) {
@@ -429,22 +499,16 @@ PeerConnectionImpl::~PeerConnectionImpl(
 
   // Since this and Initialize() occur on MainThread, they can't both be
   // running at once
 
   // Right now, we delete PeerConnectionCtx at XPCOM shutdown only, but we
   // probably want to shut it down more aggressively to save memory.  We
   // could shut down here when there are no uses.  It might be more optimal
   // to release off a timer (and XPCOM Shutdown) to avoid churn
-
-  /* We should release mPCObserver on the main thread, but also prevent a double free.
-  nsCOMPtr<nsIThread> mainThread;
-  NS_GetMainThread(getter_AddRefs(mainThread));
-  NS_ProxyRelease(mainThread, mPCObserver);
-  */
 }
 
 already_AddRefed<DOMMediaStream>
 PeerConnectionImpl::MakeMediaStream(nsPIDOMWindow* aWindow,
                                     uint32_t aHint)
 {
   nsRefPtr<DOMMediaStream> stream =
     DOMMediaStream::CreateSourceStream(aWindow, aHint);
@@ -489,42 +553,31 @@ PeerConnectionImpl::CreateRemoteSourceSt
 
 /**
  * In JS, an RTCConfiguration looks like this:
  *
  * { "iceServers": [ { url:"stun:23.21.150.121" },
  *                   { url:"turn:turn.example.org?transport=udp",
  *                     username: "jib", credential:"mypass"} ] }
  *
- * This function converts an already-validated jsval that looks like the above
- * into an IceConfiguration object.
+ * This function converts that into an internal IceConfiguration object.
  */
 nsresult
-PeerConnectionImpl::ConvertRTCConfiguration(const JS::Value& aSrc,
-                                            IceConfiguration *aDst,
-                                            JSContext* aCx)
+PeerConnectionImpl::ConvertRTCConfiguration(const RTCConfiguration& aSrc,
+                                            IceConfiguration *aDst)
 {
 #ifdef MOZILLA_INTERNAL_API
-  if (!aSrc.isObject()) {
-    return NS_ERROR_FAILURE;
-  }
-  JSAutoCompartment ac(aCx, &aSrc.toObject());
-  RTCConfiguration config;
-  JS::Rooted<JS::Value> src(aCx, aSrc);
-  if (!(config.Init(aCx, src) && config.mIceServers.WasPassed())) {
-    return NS_ERROR_FAILURE;
+  if (!aSrc.mIceServers.WasPassed()) {
+    return NS_OK;
   }
-  for (uint32_t i = 0; i < config.mIceServers.Value().Length(); i++) {
-    RTCIceServer& server = config.mIceServers.Value()[i];
-    if (!server.mUrl.WasPassed()) {
-      return NS_ERROR_FAILURE;
-    }
+  for (uint32_t i = 0; i < aSrc.mIceServers.Value().Length(); i++) {
+    const RTCIceServer& server = aSrc.mIceServers.Value()[i];
+    NS_ENSURE_TRUE(server.mUrl.WasPassed(), NS_ERROR_UNEXPECTED);
     nsRefPtr<nsIURI> url;
-    nsresult rv;
-    rv = NS_NewURI(getter_AddRefs(url), server.mUrl.Value());
+    nsresult rv = NS_NewURI(getter_AddRefs(url), server.mUrl.Value());
     NS_ENSURE_SUCCESS(rv, rv);
     bool isStun = false, isStuns = false, isTurn = false, isTurns = false;
     url->SchemeIs("stun", &isStun);
     url->SchemeIs("stuns", &isStuns);
     url->SchemeIs("turn", &isTurn);
     url->SchemeIs("turns", &isTurns);
     if (!(isStun || isStuns || isTurn || isTurns)) {
       return NS_ERROR_FAILURE;
@@ -579,45 +632,33 @@ PeerConnectionImpl::ConvertRTCConfigurat
       }
     }
   }
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::Initialize(IPeerConnectionObserver* aObserver,
-                               nsIDOMWindow* aWindow,
-                               const JS::Value &aRTCConfiguration,
-                               nsIThread* aThread,
-                               JSContext* aCx)
-{
-  return Initialize(aObserver, aWindow, nullptr, &aRTCConfiguration, aThread, aCx);
-}
-
-nsresult
-PeerConnectionImpl::Initialize(IPeerConnectionObserver* aObserver,
+PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
                                nsIDOMWindow* aWindow,
                                const IceConfiguration* aConfiguration,
-                               const JS::Value* aRTCConfiguration,
-                               nsIThread* aThread,
-                               JSContext* aCx)
+                               const RTCConfiguration* aRTCConfiguration,
+                               nsISupports* aThread)
 {
   nsresult res;
 
   // Invariant: we receive configuration one way or the other but not both (XOR)
   MOZ_ASSERT(!aConfiguration != !aRTCConfiguration);
 #ifdef MOZILLA_INTERNAL_API
   MOZ_ASSERT(NS_IsMainThread());
 #endif
-  MOZ_ASSERT(aObserver);
   MOZ_ASSERT(aThread);
-  mThread = aThread;
+  mThread = do_QueryInterface(aThread);
 
-  mPCObserver = do_GetWeakReference(aObserver);
+  mPCObserver.Init(&aObserver);
 
   // Find the STS thread
 
   mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
   MOZ_ASSERT(mSTSThread);
 
 #ifdef MOZILLA_INTERNAL_API
   // This code interferes with the C++ unit test startup code.
@@ -655,25 +696,25 @@ PeerConnectionImpl::Initialize(IPeerConn
   STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx");
   res = PeerConnectionCtx::InitializeGlobal(mThread, mSTSThread);
   NS_ENSURE_SUCCESS(res, res);
 
   PeerConnectionCtx *pcctx = PeerConnectionCtx::GetInstance();
   MOZ_ASSERT(pcctx);
   STAMP_TIMECARD(mTimeCard, "Done Initializing PC Ctx");
 
-  mCall = pcctx->createCall();
-  if(!mCall.get()) {
+  mInternal->mCall = pcctx->createCall();
+  if (!mInternal->mCall.get()) {
     CSFLogError(logTag, "%s: Couldn't Create Call Object", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
   IceConfiguration converted;
   if (aRTCConfiguration) {
-    res = ConvertRTCConfiguration(*aRTCConfiguration, &converted, aCx);
+    res = ConvertRTCConfiguration(*aRTCConfiguration, &converted);
     if (NS_FAILED(res)) {
       CSFLogError(logTag, "%s: Invalid RTCConfiguration", __FUNCTION__);
       return res;
     }
     aConfiguration = &converted;
   }
 
   mMedia = new PeerConnectionMedia(this);
@@ -687,17 +728,17 @@ PeerConnectionImpl::Initialize(IPeerConn
   res = mMedia->Init(aConfiguration->getStunServers(),
                      aConfiguration->getTurnServers());
   if (NS_FAILED(res)) {
     CSFLogError(logTag, "%s: Couldn't initialize media object", __FUNCTION__);
     return res;
   }
 
   // Store under mHandle
-  mCall->setPeerConnection(mHandle);
+  mInternal->mCall->setPeerConnection(mHandle);
   PeerConnectionCtx::GetInstance()->mPeerConnections[mHandle] = this;
 
   STAMP_TIMECARD(mTimeCard, "Generating DTLS Identity");
   // Create the DTLS Identity
   mIdentity = DtlsIdentity::Generate();
   STAMP_TIMECARD(mTimeCard, "Done Generating DTLS Identity");
 
   if (!mIdentity) {
@@ -726,16 +767,22 @@ PeerConnectionImpl::Initialize(IPeerConn
     CSFLogError(logTag, "%s: do_GetService failed: %u",
       __FUNCTION__, static_cast<uint32_t>(res));
     return res;
   }
 
   return NS_OK;
 }
 
+mozilla::RefPtr<DtlsIdentity> const
+PeerConnectionImpl::GetIdentity() {
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+  return mIdentity;
+}
+
 nsresult
 PeerConnectionImpl::CreateFakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aRetval)
 {
   MOZ_ASSERT(aRetval);
   PC_AUTO_ENTER_API_CALL(false);
 
   bool mute = false;
 
@@ -824,107 +871,138 @@ PeerConnectionImpl::InitializeDataChanne
     // If we inited the DataConnection, call Destroy() before releasing it
     mDataConnection->Destroy();
   }
   mDataConnection = nullptr;
 #endif
   return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
-PeerConnectionImpl::CreateDataChannel(const nsACString& aLabel,
-                                      const nsACString& aProtocol,
+already_AddRefed<nsDOMDataChannel>
+PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel,
+                                      const nsAString& aProtocol,
                                       uint16_t aType,
                                       bool outOfOrderAllowed,
                                       uint16_t aMaxTime,
                                       uint16_t aMaxNum,
                                       bool aExternalNegotiated,
                                       uint16_t aStream,
-                                      nsIDOMDataChannel** aRetval)
+                                      ErrorResult &rv)
+{
+#ifdef MOZILLA_INTERNAL_API
+  nsRefPtr<nsDOMDataChannel> result;
+  rv = CreateDataChannel(aLabel, aProtocol, aType, outOfOrderAllowed,
+                         aMaxTime, aMaxNum, aExternalNegotiated,
+                         aStream, getter_AddRefs(result));
+  return result.forget();
+#else
+  return nullptr;
+#endif
+}
+
+NS_IMETHODIMP
+PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel,
+                                      const nsAString& aProtocol,
+                                      uint16_t aType,
+                                      bool outOfOrderAllowed,
+                                      uint16_t aMaxTime,
+                                      uint16_t aMaxNum,
+                                      bool aExternalNegotiated,
+                                      uint16_t aStream,
+                                      nsDOMDataChannel** aRetval)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aRetval);
 
 #ifdef MOZILLA_INTERNAL_API
   nsRefPtr<mozilla::DataChannel> dataChannel;
   mozilla::DataChannelConnection::Type theType =
     static_cast<mozilla::DataChannelConnection::Type>(aType);
 
   nsresult rv = EnsureDataConnection(WEBRTC_DATACHANNEL_STREAMS_DEFAULT);
   if (NS_FAILED(rv)) {
     return rv;
   }
   dataChannel = mDataConnection->Open(
-    aLabel, aProtocol, theType, !outOfOrderAllowed,
+    NS_ConvertUTF16toUTF8(aLabel), NS_ConvertUTF16toUTF8(aProtocol), theType,
+    !outOfOrderAllowed,
     aType == mozilla::DataChannelConnection::PARTIAL_RELIABLE_REXMIT ? aMaxNum :
     (aType == mozilla::DataChannelConnection::PARTIAL_RELIABLE_TIMED ? aMaxTime : 0),
     nullptr, nullptr, aExternalNegotiated, aStream
   );
   NS_ENSURE_TRUE(dataChannel,NS_ERROR_FAILURE);
 
   CSFLogDebug(logTag, "%s: making DOMDataChannel", __FUNCTION__);
 
   if (!mHaveDataStream) {
     // XXX stream_id of 0 might confuse things...
-    mCall->addStream(0, 2, DATA);
+    mInternal->mCall->addStream(0, 2, DATA);
     mHaveDataStream = true;
   }
-
-  return NS_NewDOMDataChannel(dataChannel.forget(), mWindow, aRetval);
-#else
+  nsIDOMDataChannel *retval;
+  rv = NS_NewDOMDataChannel(dataChannel.forget(), mWindow, &retval);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  *aRetval = static_cast<nsDOMDataChannel*>(retval);
+#endif
   return NS_OK;
-#endif
 }
 
 void
 PeerConnectionImpl::NotifyConnection()
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 
   CSFLogDebug(logTag, "%s", __FUNCTION__);
 
 #ifdef MOZILLA_INTERNAL_API
-  nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
+  nsRefPtr<PeerConnectionObserver> pco = mPCObserver.MayGet();
   if (!pco) {
     return;
   }
+  WrappableJSErrorResult rv;
   RUN_ON_THREAD(mThread,
                 WrapRunnable(pco,
-                             &IPeerConnectionObserver::NotifyConnection),
+                             &PeerConnectionObserver::NotifyConnection,
+                             rv, static_cast<JSCompartment*>(nullptr)),
                 NS_DISPATCH_NORMAL);
 #endif
 }
 
 void
 PeerConnectionImpl::NotifyClosedConnection()
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 
   CSFLogDebug(logTag, "%s", __FUNCTION__);
 
 #ifdef MOZILLA_INTERNAL_API
-  nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
+  nsRefPtr<PeerConnectionObserver> pco = mPCObserver.MayGet();
   if (!pco) {
     return;
   }
+  WrappableJSErrorResult rv;
   RUN_ON_THREAD(mThread,
-    WrapRunnable(pco, &IPeerConnectionObserver::NotifyClosedConnection),
+    WrapRunnable(pco, &PeerConnectionObserver::NotifyClosedConnection,
+                 rv, static_cast<JSCompartment*>(nullptr)),
     NS_DISPATCH_NORMAL);
 #endif
 }
 
 
 #ifdef MOZILLA_INTERNAL_API
 // Not a member function so that we don't need to keep the PC live.
 static void NotifyDataChannel_m(nsRefPtr<nsIDOMDataChannel> aChannel,
-                                nsCOMPtr<IPeerConnectionObserver> aObserver)
+                                nsRefPtr<PeerConnectionObserver> aObserver)
 {
   MOZ_ASSERT(NS_IsMainThread());
-
-  aObserver->NotifyDataChannel(aChannel);
+  JSErrorResult rv;
+  nsRefPtr<nsDOMDataChannel> channel = static_cast<nsDOMDataChannel*>(&*aChannel);
+  aObserver->NotifyDataChannel(*channel, rv);
   NS_DataChannelAppReady(aChannel);
 }
 #endif
 
 void
 PeerConnectionImpl::NotifyDataChannel(already_AddRefed<mozilla::DataChannel> aChannel)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
@@ -933,180 +1011,67 @@ PeerConnectionImpl::NotifyDataChannel(al
   CSFLogDebug(logTag, "%s: channel: %p", __FUNCTION__, aChannel.get());
 
 #ifdef MOZILLA_INTERNAL_API
   nsCOMPtr<nsIDOMDataChannel> domchannel;
   nsresult rv = NS_NewDOMDataChannel(aChannel, mWindow,
                                      getter_AddRefs(domchannel));
   NS_ENSURE_SUCCESS_VOID(rv);
 
-  nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
+  nsRefPtr<PeerConnectionObserver> pco = mPCObserver.MayGet();
   if (!pco) {
     return;
   }
 
   RUN_ON_THREAD(mThread,
                 WrapRunnableNM(NotifyDataChannel_m,
                                domchannel.get(),
                                pco),
                 NS_DISPATCH_NORMAL);
 #endif
 }
 
-/**
- * Constraints look like this:
- *
- * {
- *   mandatory: {"OfferToReceiveAudio": true, "OfferToReceiveVideo": true },
- *   optional: [{"VoiceActivityDetection": true}, {"FooBar": 10}]
- * }
- *
- * Optional constraints are ordered, and hence in an array. This function
- * converts a jsval that looks like the above into a MediaConstraints object.
- */
-nsresult
-PeerConnectionImpl::ConvertConstraints(
-  const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx)
+NS_IMETHODIMP
+PeerConnectionImpl::CreateOffer(const MediaConstraintsInternal& aConstraints)
 {
-  JS::Rooted<JS::Value> mandatory(aCx), optional(aCx);
-  JS::Rooted<JSObject*> constraints(aCx, &aConstraints.toObject());
-
-  // Mandatory constraints.  Note that we only care if the constraint array exists
-  if (!JS_GetProperty(aCx, constraints, "mandatory", &mandatory)) {
-    return NS_ERROR_FAILURE;
-  }
-  if (!mandatory.isNullOrUndefined()) {
-    if (!mandatory.isObject()) {
-      return NS_ERROR_FAILURE;
-    }
-
-    JS::Rooted<JSObject*> opts(aCx, &mandatory.toObject());
-    JS::AutoIdArray mandatoryOpts(aCx, JS_Enumerate(aCx, opts));
-
-    // Iterate over each property.
-    for (size_t i = 0; i < mandatoryOpts.length(); i++) {
-      JS::Rooted<JS::Value> option(aCx), optionName(aCx);
-      if (!JS_GetPropertyById(aCx, opts, mandatoryOpts[i], &option) ||
-          !JS_IdToValue(aCx, mandatoryOpts[i], optionName.address()) ||
-          // We only support boolean constraints for now.
-          !option.isBoolean()) {
-        return NS_ERROR_FAILURE;
-      }
-      JSString* optionNameString = JS_ValueToString(aCx, optionName);
-      if (!optionNameString) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-      NS_ConvertUTF16toUTF8 stringVal(JS_GetStringCharsZ(aCx, optionNameString));
-      aObj->setBooleanConstraint(stringVal.get(), JSVAL_TO_BOOLEAN(option), true);
-    }
-  }
-
-  // Optional constraints.
-  if (!JS_GetProperty(aCx, constraints, "optional", &optional)) {
-    return NS_ERROR_FAILURE;
-  }
-  if (!optional.isNullOrUndefined()) {
-    if (!optional.isObject()) {
-      return NS_ERROR_FAILURE;
-    }
-
-    JS::Rooted<JSObject*> array(aCx, &optional.toObject());
-    uint32_t length;
-    if (!JS_GetArrayLength(aCx, array, &length)) {
-      return NS_ERROR_FAILURE;
-    }
-    for (uint32_t i = 0; i < length; i++) {
-      JS::Rooted<JS::Value> element(aCx);
-      if (!JS_GetElement(aCx, array, i, &element) ||
-          !element.isObject()) {
-        return NS_ERROR_FAILURE;
-      }
-      JS::Rooted<JSObject*> opts(aCx, &element.toObject());
-      JS::AutoIdArray optionalOpts(aCx, JS_Enumerate(aCx, opts));
-      // Expect one property per entry.
-      if (optionalOpts.length() != 1) {
-        return NS_ERROR_FAILURE;
-      }
-      JS::Rooted<JS::Value> option(aCx), optionName(aCx);
-      if (!JS_GetPropertyById(aCx, opts, optionalOpts[0], &option) ||
-          !JS_IdToValue(aCx, optionalOpts[0], optionName.address())) {
-        return NS_ERROR_FAILURE;
-      }
-      // Ignore constraints other than boolean, as that's all we support.
-      if (option.isBoolean()) {
-        JSString* optionNameString = JS_ValueToString(aCx, optionName);
-        if (!optionNameString) {
-          return NS_ERROR_OUT_OF_MEMORY;
-        }
-        NS_ConvertUTF16toUTF8 stringVal(JS_GetStringCharsZ(aCx, optionNameString));
-        aObj->setBooleanConstraint(stringVal.get(), JSVAL_TO_BOOLEAN(option), false);
-      }
-    }
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PeerConnectionImpl::CreateOffer(const JS::Value& aConstraints, JSContext* aCx)
-{
-  PC_AUTO_ENTER_API_CALL(true);
-
-  MediaConstraints cs;
-  nsresult rv = ConvertConstraints(aConstraints, &cs, aCx);
-  if (rv != NS_OK) {
-    return rv;
-  }
-
-  return CreateOffer(cs);
+  return CreateOffer(MediaConstraintsExternal(ConvertConstraints(aConstraints)));
 }
 
 // Used by unit tests and the IDL CreateOffer.
 NS_IMETHODIMP
-PeerConnectionImpl::CreateOffer(MediaConstraints& constraints)
+PeerConnectionImpl::CreateOffer(const MediaConstraintsExternal& aConstraints)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Create Offer");
 
-  cc_media_constraints_t* cc_constraints = nullptr;
-  constraints.buildArray(&cc_constraints);
-
-  mCall->createOffer(cc_constraints, tc);
+  NS_ENSURE_TRUE(aConstraints.mConstraints, NS_ERROR_UNEXPECTED);
+  mInternal->mCall->createOffer(aConstraints.mConstraints, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::CreateAnswer(const JS::Value& aConstraints, JSContext* aCx)
+PeerConnectionImpl::CreateAnswer(const MediaConstraintsInternal& aConstraints)
 {
-  PC_AUTO_ENTER_API_CALL(true);
-
-  MediaConstraints cs;
-  nsresult rv = ConvertConstraints(aConstraints, &cs, aCx);
-  if (rv != NS_OK) {
-    return rv;
-  }
-
-  return CreateAnswer(cs);
+  return CreateAnswer(MediaConstraintsExternal(ConvertConstraints(aConstraints)));
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::CreateAnswer(MediaConstraints& constraints)
+PeerConnectionImpl::CreateAnswer(const MediaConstraintsExternal& aConstraints)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Create Answer");
 
-  cc_media_constraints_t* cc_constraints = nullptr;
-  constraints.buildArray(&cc_constraints);
-
-  mCall->createAnswer(cc_constraints, tc);
+  NS_ENSURE_TRUE(aConstraints.mConstraints, NS_ERROR_UNEXPECTED);
+  mInternal->mCall->createAnswer(aConstraints.mConstraints, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
@@ -1115,18 +1080,18 @@ PeerConnectionImpl::SetLocalDescription(
     return NS_ERROR_FAILURE;
   }
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Set Local Description");
 
   mLocalRequestedSDP = aSDP;
-  mCall->setLocalDescription((cc_jsep_action_t)aAction,
-                             mLocalRequestedSDP, tc);
+  mInternal->mCall->setLocalDescription((cc_jsep_action_t)aAction,
+                                        mLocalRequestedSDP, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
@@ -1135,58 +1100,52 @@ PeerConnectionImpl::SetRemoteDescription
     return NS_ERROR_FAILURE;
   }
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Set Remote Description");
 
   mRemoteRequestedSDP = aSDP;
-  mCall->setRemoteDescription((cc_jsep_action_t)action,
-                              mRemoteRequestedSDP, tc);
+  mInternal->mCall->setRemoteDescription((cc_jsep_action_t)action,
+                                         mRemoteRequestedSDP, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) {
   PC_AUTO_ENTER_API_CALL(true);
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Add Ice Candidate");
 
-  mCall->addICECandidate(aCandidate, aMid, aLevel, tc);
+  mInternal->mCall->addICECandidate(aCandidate, aMid, aLevel, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::CloseStreams() {
   PC_AUTO_ENTER_API_CALL(false);
 
-  if (mReadyState != PeerConnectionImpl::kClosed)  {
-    ChangeReadyState(PeerConnectionImpl::kClosing);
+  if (mReadyState != PCImplReadyState::Closed)  {
+    ChangeReadyState(PCImplReadyState::Closing);
   }
 
   CSFLogInfo(logTag, "%s: Ending associated call", __FUNCTION__);
 
-  mCall->endCall();
+  mInternal->mCall->endCall();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::AddStream(nsIDOMMediaStream* aMediaStream) {
+PeerConnectionImpl::AddStream(DOMMediaStream& aMediaStream) {
   PC_AUTO_ENTER_API_CALL(true);
 
-  if (!aMediaStream) {
-    CSFLogError(logTag, "%s: Attempt to add null stream",__FUNCTION__);
-    return NS_ERROR_FAILURE;
-  }
-
-  DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream);
-  uint32_t hints = stream->GetHintContents();
+  uint32_t hints = aMediaStream.GetHintContents();
 
   // XXX Remove this check once addStream has an error callback
   // available and/or we have plumbing to handle multiple
   // local audio streams.
   if ((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) &&
       mNumAudioStreams > 0) {
     CSFLogError(logTag, "%s: Only one local audio stream is supported for now",
                 __FUNCTION__);
@@ -1199,55 +1158,54 @@ PeerConnectionImpl::AddStream(nsIDOMMedi
   if ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) &&
       mNumVideoStreams > 0) {
     CSFLogError(logTag, "%s: Only one local video stream is supported for now",
                 __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
   uint32_t stream_id;
-  nsresult res = mMedia->AddStream(aMediaStream, &stream_id);
+  nsresult res = mMedia->AddStream(&aMediaStream, &stream_id);
   if (NS_FAILED(res))
     return res;
 
   // TODO(ekr@rtfm.com): these integers should be the track IDs
   if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
-    mCall->addStream(stream_id, 0, AUDIO);
+    mInternal->mCall->addStream(stream_id, 0, AUDIO);
     mNumAudioStreams++;
   }
 
   if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
-    mCall->addStream(stream_id, 1, VIDEO);
+    mInternal->mCall->addStream(stream_id, 1, VIDEO);
     mNumVideoStreams++;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::RemoveStream(nsIDOMMediaStream* aMediaStream) {
+PeerConnectionImpl::RemoveStream(DOMMediaStream& aMediaStream) {
   PC_AUTO_ENTER_API_CALL(true);
 
   uint32_t stream_id;
-  nsresult res = mMedia->RemoveStream(aMediaStream, &stream_id);
+  nsresult res = mMedia->RemoveStream(&aMediaStream, &stream_id);
 
   if (NS_FAILED(res))
     return res;
 
-  DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream);
-  uint32_t hints = stream->GetHintContents();
+  uint32_t hints = aMediaStream.GetHintContents();
 
   if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
-    mCall->removeStream(stream_id, 0, AUDIO);
+    mInternal->mCall->removeStream(stream_id, 0, AUDIO);
     MOZ_ASSERT(mNumAudioStreams > 0);
     mNumAudioStreams--;
   }
 
   if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
-    mCall->removeStream(stream_id, 1, VIDEO);
+    mInternal->mCall->removeStream(stream_id, 1, VIDEO);
     MOZ_ASSERT(mNumVideoStreams > 0);
     mNumVideoStreams--;
   }
 
   return NS_OK;
 }
 
 /*
@@ -1309,63 +1267,70 @@ PeerConnectionImpl::GetRemoteDescription
   std::copy(mRemoteSDP.begin(), mRemoteSDP.end(), tmp);
   tmp[mRemoteSDP.size()] = '\0';
 
   *aSDP = tmp;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetReadyState(uint32_t* aState)
+PeerConnectionImpl::ReadyState(PCImplReadyState* aState)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aState);
 
   *aState = mReadyState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetSignalingState(uint32_t* aState)
+PeerConnectionImpl::SignalingState(PCImplSignalingState* aState)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aState);
 
   *aState = mSignalingState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetSipccState(uint32_t* aState)
+PeerConnectionImpl::SipccState(PCImplSipccState* aState)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aState);
 
   PeerConnectionCtx* pcctx = PeerConnectionCtx::GetInstance();
-  *aState = pcctx ? pcctx->sipcc_state() : kIdle;
+  // Avoid B2G error: operands to ?: have different types
+  // 'mozilla::dom::PCImplSipccState' and 'mozilla::dom::PCImplSipccState::Enum'
+  if (pcctx) {
+    *aState = pcctx->sipcc_state();
+  } else {
+    *aState = PCImplSipccState::Idle;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetIceState(uint32_t* aState)
+PeerConnectionImpl::IceState(PCImplIceState* aState)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aState);
 
   *aState = mIceState;
   return NS_OK;
 }
 
 nsresult
 PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
-  MOZ_ASSERT(mTrickle || !assert_ice_ready || (mIceState != kIceGathering));
+  MOZ_ASSERT(mTrickle || !assert_ice_ready ||
+             (mIceState != PCImplIceState::IceGathering));
 
-  if (mReadyState == kClosed)
+  if (mReadyState == PCImplReadyState::Closed)
     return NS_ERROR_FAILURE;
   if (!mMedia)
     return NS_ERROR_FAILURE;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::Close()
@@ -1377,20 +1342,26 @@ PeerConnectionImpl::Close()
 }
 
 
 nsresult
 PeerConnectionImpl::CloseInt()
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 
-  if (mCall) {
+  // Clear raw pointer to observer since PeerConnection.js does not guarantee
+  // the observer's existence past Close().
+  //
+  // Any outstanding runnables hold RefPtr<> references to observer.
+  mPCObserver.Close();
+
+  if (mInternal->mCall) {
     CSFLogInfo(logTag, "%s: Closing PeerConnectionImpl %s; "
                "ending call", __FUNCTION__, mHandle.c_str());
-    mCall->endCall();
+    mInternal->mCall->endCall();
   }
 #ifdef MOZILLA_INTERNAL_API
   if (mDataConnection) {
     CSFLogInfo(logTag, "%s: Destroying DataChannelConnection %p for %s",
                __FUNCTION__, (void *) mDataConnection.get(), mHandle.c_str());
     mDataConnection->Destroy();
     mDataConnection = nullptr; // it may not go away until the runnables are dead
   }
@@ -1433,19 +1404,21 @@ PeerConnectionImpl::virtualDestroyNSSRef
 {
   MOZ_ASSERT(NS_IsMainThread());
   CSFLogDebug(logTag, "%s: NSS shutting down; freeing our DtlsIdentity.", __FUNCTION__);
   mIdentity = nullptr;
 }
 #endif
 
 void
-PeerConnectionImpl::onCallEvent(ccapi_call_event_e aCallEvent,
-                                CSF::CC_CallInfoPtr aInfo)
+PeerConnectionImpl::onCallEvent(const OnCallEventArgs& args)
 {
+  const ccapi_call_event_e &aCallEvent = args.mCallEvent;
+  const CSF::CC_CallInfoPtr &aInfo = args.mInfo;
+
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aInfo.get());
 
   cc_call_state_t event = aInfo->getCallState();
   std::string statestr = aInfo->callStateToString(event);
   Timecard *timecard = aInfo->takeTimecard();
 
   if (timecard) {
@@ -1467,71 +1440,74 @@ PeerConnectionImpl::onCallEvent(ccapi_ca
 
     case SETREMOTEDESCSUCCESS:
     case ADDICECANDIDATE:
       mRemoteSDP = aInfo->getSDP();
       break;
 
     case CONNECTED:
       CSFLogDebug(logTag, "Setting PeerConnnection state to kActive");
-      ChangeReadyState(kActive);
+      ChangeReadyState(PCImplReadyState::Active);
       break;
     default:
       break;
   }
 
-  nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
+  nsRefPtr<PeerConnectionObserver> pco = mPCObserver.MayGet();
   if (!pco) {
     return;
   }
 
   PeerConnectionObserverDispatch* runnable =
       new PeerConnectionObserverDispatch(aInfo, this, pco);
 
   if (mThread) {
     mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
     return;
   }
   runnable->Run();
   delete runnable;
 }
 
 void
-PeerConnectionImpl::ChangeReadyState(PeerConnectionImpl::ReadyState aReadyState)
+PeerConnectionImpl::ChangeReadyState(PCImplReadyState aReadyState)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   mReadyState = aReadyState;
 
-  // Note that we are passing an nsRefPtr<IPeerConnectionObserver> which
-  // keeps the observer live.
-  nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
+  // Note that we are passing an nsRefPtr which keeps the observer live.
+  nsRefPtr<PeerConnectionObserver> pco = mPCObserver.MayGet();
   if (!pco) {
     return;
   }
-  RUN_ON_THREAD(mThread, WrapRunnable(pco,
-                                      &IPeerConnectionObserver::OnStateChange,
-                                      // static_cast needed to work around old Android NDK r5c compiler
-                                      static_cast<int>(IPeerConnectionObserver::kReadyState)),
-    NS_DISPATCH_NORMAL);
+  WrappableJSErrorResult rv;
+  RUN_ON_THREAD(mThread,
+                WrapRunnable(pco,
+                             &PeerConnectionObserver::OnStateChange,
+                             PCObserverStateType::ReadyState,
+                             rv, static_cast<JSCompartment*>(nullptr)),
+                NS_DISPATCH_NORMAL);
 }
 
 void
-PeerConnectionImpl::SetSignalingState_m(SignalingState aSignalingState)
+PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   if (mSignalingState == aSignalingState) {
     return;
   }
 
   mSignalingState = aSignalingState;
-  nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
+  nsRefPtr<PeerConnectionObserver> pco = mPCObserver.MayGet();
   if (!pco) {
     return;
   }
-  pco->OnStateChange(IPeerConnectionObserver::kSignalingState);
+  JSErrorResult rv;
+  pco->OnStateChange(PCObserverStateType::SignalingState, rv);
+  MOZ_ASSERT(!rv.Failed());
 }
 
 PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle)
     : impl_(nullptr) {
   if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) ==
     PeerConnectionCtx::GetInstance()->mPeerConnections.end()) {
     return;
   }
@@ -1557,82 +1533,83 @@ void
 PeerConnectionImpl::IceGatheringCompleted(NrIceCtx *aCtx)
 {
   (void) aCtx;
   // Do an async call here to unwind the stack. refptr keeps the PC alive.
   nsRefPtr<PeerConnectionImpl> pc(this);
   RUN_ON_THREAD(mThread,
                 WrapRunnable(pc,
                              &PeerConnectionImpl::IceStateChange_m,
-                             kIceWaiting),
+                             PCImplIceState::IceWaiting),
                 NS_DISPATCH_NORMAL);
 }
 
 void
 PeerConnectionImpl::IceCompleted(NrIceCtx *aCtx)
 {
   (void) aCtx;
   // Do an async call here to unwind the stack. refptr keeps the PC alive.
   nsRefPtr<PeerConnectionImpl> pc(this);
   RUN_ON_THREAD(mThread,
                 WrapRunnable(pc,
                              &PeerConnectionImpl::IceStateChange_m,
-                             kIceConnected),
+                             PCImplIceState::IceConnected),
                 NS_DISPATCH_NORMAL);
 }
 
 void
 PeerConnectionImpl::IceFailed(NrIceCtx *aCtx)
 {
   (void) aCtx;
   // Do an async call here to unwind the stack. refptr keeps the PC alive.
   nsRefPtr<PeerConnectionImpl> pc(this);
   RUN_ON_THREAD(mThread,
                 WrapRunnable(pc,
                              &PeerConnectionImpl::IceStateChange_m,
-                             kIceFailed),
+                             PCImplIceState::IceFailed),
                 NS_DISPATCH_NORMAL);
 }
 
 nsresult
-PeerConnectionImpl::IceStateChange_m(IceState aState)
+PeerConnectionImpl::IceStateChange_m(PCImplIceState aState)
 {
   PC_AUTO_ENTER_API_CALL(false);
 
   CSFLogDebug(logTag, "%s", __FUNCTION__);
 
   mIceState = aState;
 
   switch (mIceState) {
-    case kIceGathering:
+    case PCImplIceState::IceGathering:
       STAMP_TIMECARD(mTimeCard, "Ice state: gathering");
       break;
-    case kIceWaiting:
+    case PCImplIceState::IceWaiting:
       STAMP_TIMECARD(mTimeCard, "Ice state: waiting");
       break;
-    case kIceChecking:
+    case PCImplIceState::IceChecking:
       STAMP_TIMECARD(mTimeCard, "Ice state: checking");
       break;
-    case kIceConnected:
+    case PCImplIceState::IceConnected:
       STAMP_TIMECARD(mTimeCard, "Ice state: connected");
       break;
-    case kIceFailed:
+    case PCImplIceState::IceFailed:
       STAMP_TIMECARD(mTimeCard, "Ice state: failed");
       break;
   }
 
-  nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
+  nsRefPtr<PeerConnectionObserver> pco = mPCObserver.MayGet();
   if (!pco) {
     return NS_OK;
   }
+  WrappableJSErrorResult rv;
   RUN_ON_THREAD(mThread,
                 WrapRunnable(pco,
-                             &IPeerConnectionObserver::OnStateChange,
-                             // static_cast required to work around old C++ compiler on Android NDK r5c
-                             static_cast<int>(IPeerConnectionObserver::kIceState)),
+                             &PeerConnectionObserver::OnStateChange,
+                             PCObserverStateType::IceState,
+                             rv, static_cast<JSCompartment*>(nullptr)),
                 NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 void
 PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
@@ -1670,59 +1647,42 @@ PeerConnectionImpl::startCallTelem() {
   int &cnt = PeerConnectionCtx::GetInstance()->mConnectionCounter;
   Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Subtract(cnt);
   cnt++;
   Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Add(cnt);
 #endif
 }
 #endif
 
-#ifdef MOZILLA_INTERNAL_API
-static nsresult
-GetStreams(JSContext* cx, PeerConnectionImpl* peerConnection,
-           MediaStreamList::StreamType type, JS::Value* streams)
-{
-  nsRefPtr<MediaStreamList> list(new MediaStreamList(peerConnection, type));
-
-  nsCOMPtr<nsIScriptGlobalObject> global =
-    do_QueryInterface(peerConnection->GetWindow());
-  JS::Rooted<JSObject*> scope(cx, global->GetGlobalJSObject());
-  if (!scope) {
-    streams->setNull();
-    return NS_ERROR_FAILURE;
-  }
-
-  JSAutoCompartment ac(cx, scope);
-  JSObject* obj = list->WrapObject(cx, scope);
-  if (!obj) {
-    streams->setNull();
-    return NS_ERROR_FAILURE;
-  }
-
-  streams->setObject(*obj);
-  return NS_OK;
-}
-#endif
-
 NS_IMETHODIMP
-PeerConnectionImpl::GetLocalStreams(JSContext* cx, JS::Value* streams)
+PeerConnectionImpl::GetLocalStreams(nsTArray<nsRefPtr<mozilla::DOMMediaStream > >& result)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 #ifdef MOZILLA_INTERNAL_API
-  return GetStreams(cx, this, MediaStreamList::Local, streams);
+  for(uint32_t i=0; i < media()->LocalStreamsLength(); i++) {
+    LocalSourceStreamInfo *info = media()->GetLocalStream(i);
+    NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED);
+    result.AppendElement(info->GetMediaStream());
+  }
+  return NS_OK;
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetRemoteStreams(JSContext* cx, JS::Value* streams)
+PeerConnectionImpl::GetRemoteStreams(nsTArray<nsRefPtr<mozilla::DOMMediaStream > >& result)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 #ifdef MOZILLA_INTERNAL_API
-  return GetStreams(cx, this, MediaStreamList::Remote, streams);
+  for(uint32_t i=0; i < media()->RemoteStreamsLength(); i++) {
+    RemoteSourceStreamInfo *info = media()->GetRemoteStream(i);
+    NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED);
+    result.AppendElement(info->GetMediaStream());
+  }
+  return NS_OK;
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
 
 }  // end sipcc namespace
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -7,73 +7,110 @@
 
 #include <string>
 #include <vector>
 #include <map>
 #include <cmath>
 
 #include "prlock.h"
 #include "mozilla/RefPtr.h"
-#include "nsWeakPtr.h"
-#include "nsIWeakReferenceUtils.h" // for the definition of nsWeakPtr
-#include "IPeerConnection.h"
+#include "sigslot.h"
+#include "nricectx.h"
+#include "nricemediastream.h"
 #include "nsComponentManagerUtils.h"
 #include "nsPIDOMWindow.h"
-
-#include "dtlsidentity.h"
+#include "nsIThread.h"
 
-#include "peer_connection_types.h"
-#include "CallControlManager.h"
-#include "CC_Device.h"
-#include "CC_Call.h"
-#include "CC_Observer.h"
-#include "MediaPipeline.h"
-#include "PeerConnectionMedia.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/PeerConnectionImplEnumsBinding.h"
 
 #ifdef MOZILLA_INTERNAL_API
 #include "mozilla/TimeStamp.h"
 #include "mozilla/net/DataChannel.h"
 #include "VideoUtils.h"
 #include "VideoSegment.h"
 #include "nsNSSShutDown.h"
-#else
+#endif
+
+namespace test {
+#ifdef USE_FAKE_PCOBSERVER
+class AFakePCObserver;
+#endif
+}
+
+#ifdef USE_FAKE_MEDIA_STREAMS
+class Fake_DOMMediaStream;
+#endif
+
+class nsIDOMMediaStream;
+class nsDOMDataChannel;
+
 namespace mozilla {
-  class DataChannel;
+class DataChannel;
+class DtlsIdentity;
+class NrIceCtx;
+class NrIceMediaStream;
+class NrIceStunServer;
+class NrIceTurnServer;
+
+#ifdef USE_FAKE_MEDIA_STREAMS
+typedef Fake_DOMMediaStream DOMMediaStream;
+#else
+class DOMMediaStream;
+#endif
+
+namespace dom {
+class RTCConfiguration;
+class MediaConstraintsInternal;
+
+#ifdef USE_FAKE_PCOBSERVER
+typedef test::AFakePCObserver PeerConnectionObserver;
+typedef const char *PCObserverString;
+#else
+class PeerConnectionObserver;
+typedef NS_ConvertUTF8toUTF16 PCObserverString;
+#endif
 }
-#endif
+}
 
 #if defined(__cplusplus) && __cplusplus >= 201103L
 typedef struct Timecard Timecard;
 #else
 #include "timecard.h"
 #endif
 
-using namespace mozilla;
+// To preserve blame, convert nsresult to ErrorResult with wrappers. These macros
+// help declare wrappers w/function being wrapped when there are no differences.
+
+#define NS_IMETHODIMP_TO_ERRORRESULT(func, rv, ...) \
+NS_IMETHODIMP func(__VA_ARGS__);                    \
+void func (__VA_ARGS__, rv)
+
+#define NS_IMETHODIMP_TO_ERRORRESULT_RETREF(resulttype, func, rv, ...) \
+NS_IMETHODIMP func(__VA_ARGS__, resulttype **result);                  \
+already_AddRefed<resulttype> func (__VA_ARGS__, rv)
 
 namespace sipcc {
 
-class PeerConnectionWrapper;
-
-struct ConstraintInfo
-{
-  std::string  value;
-  bool         mandatory;
-};
-typedef std::map<std::string, ConstraintInfo> constraints_map;
+using mozilla::dom::PeerConnectionObserver;
+using mozilla::dom::RTCConfiguration;
+using mozilla::dom::MediaConstraintsInternal;
+using mozilla::DOMMediaStream;
+using mozilla::NrIceCtx;
+using mozilla::NrIceMediaStream;
+using mozilla::DtlsIdentity;
+using mozilla::ErrorResult;
+using mozilla::NrIceStunServer;
+using mozilla::NrIceTurnServer;
 
-class MediaConstraints
-{
-public:
-  void setBooleanConstraint(const std::string& constraint, bool enabled, bool mandatory);
-
-  void buildArray(cc_media_constraints_t** constraintarray);
-
-private:
-  constraints_map  mConstraints;
-};
+class PeerConnectionWrapper;
+class PeerConnectionMedia;
+class RemoteSourceStreamInfo;
+class MediaConstraintsExternal;
+class OnCallEventArgs;
 
 class IceConfiguration
 {
 public:
   bool addStunServer(const std::string& addr, uint16_t port)
   {
     NrIceStunServer* server(NrIceStunServer::Create(addr, port));
     if (!server) {
@@ -101,106 +138,70 @@ public:
   void addTurnServer(const NrIceTurnServer& server) { mTurnServers.push_back (server); }
   const std::vector<NrIceStunServer>& getStunServers() const { return mStunServers; }
   const std::vector<NrIceTurnServer>& getTurnServers() const { return mTurnServers; }
 private:
   std::vector<NrIceStunServer> mStunServers;
   std::vector<NrIceTurnServer> mTurnServers;
 };
 
-class PeerConnectionWrapper;
-
 // Enter an API call and check that the state is OK,
 // the PC isn't closed, etc.
 #define PC_AUTO_ENTER_API_CALL(assert_ice_ready) \
     do { \
       /* do/while prevents res from conflicting with locals */    \
       nsresult res = CheckApiState(assert_ice_ready);             \
       if (NS_FAILED(res)) return res; \
     } while(0)
 #define PC_AUTO_ENTER_API_CALL_NO_CHECK() CheckThread()
 
-
-class PeerConnectionImpl MOZ_FINAL : public IPeerConnection,
+class PeerConnectionImpl MOZ_FINAL : public nsISupports,
 #ifdef MOZILLA_INTERNAL_API
                                      public mozilla::DataChannelConnection::DataConnectionListener,
                                      public nsNSSShutDownObject,
 #endif
                                      public sigslot::has_slots<>
 {
-public:
-  PeerConnectionImpl();
-  ~PeerConnectionImpl();
-
-  enum ReadyState {
-    kNew,
-    kNegotiating,
-    kActive,
-    kClosing,
-    kClosed
-  };
+  class Internal; // Avoid exposing c includes to bindings
 
-  /* Must match constants in IPeerConnection.idl */
-  /* Must also be int the same order as in fsmdef_states.h */
-  enum SignalingState {
-    kSignalingInvalid            = 0,
-    kSignalingStable             = 1,
-    kSignalingHaveLocalOffer     = 2,
-    kSignalingHaveRemoteOffer    = 3,
-    kSignalingHaveLocalPranswer  = 4,
-    kSignalingHaveRemotePranswer = 5,
-    kSignalingClosed             = 6
-  };
-
-  enum SipccState {
-    kIdle,
-    kStarting,
-    kStarted
-  };
-
-  // TODO(ekr@rtfm.com): make this conform to the specifications
-  enum IceState {
-    kIceGathering,
-    kIceWaiting,
-    kIceChecking,
-    kIceConnected,
-    kIceFailed
-  };
+public:
+  PeerConnectionImpl(const mozilla::dom::GlobalObject* aGlobal = nullptr);
+  virtual ~PeerConnectionImpl();
 
   enum Error {
     kNoError                          = 0,
     kInvalidConstraintsType           = 1,
     kInvalidCandidateType             = 2,
     kInvalidMediastreamTrack          = 3,
     kInvalidState                     = 4,
     kInvalidSessionDescription        = 5,
     kIncompatibleSessionDescription   = 6,
     kIncompatibleConstraints          = 7,
     kIncompatibleMediaStreamTrack     = 8,
     kInternalError                    = 9
   };
 
   NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_IPEERCONNECTION
 
+#ifdef MOZILLA_INTERNAL_API
+  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> scope);
+#endif
+
+  static already_AddRefed<PeerConnectionImpl>
+      Constructor(const mozilla::dom::GlobalObject& aGlobal, ErrorResult& rv);
   static PeerConnectionImpl* CreatePeerConnection();
-  static nsresult ConvertRTCConfiguration(const JS::Value& aSrc,
-    IceConfiguration *aDst, JSContext* aCx);
-  static nsresult ConvertConstraints(
-    const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx);
+  static nsresult ConvertRTCConfiguration(const RTCConfiguration& aSrc,
+                                          IceConfiguration *aDst);
   static already_AddRefed<DOMMediaStream> MakeMediaStream(nsPIDOMWindow* aWindow,
                                                           uint32_t aHint);
 
   nsresult CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo>* aInfo);
 
   // Implementation of the only observer we need
-  virtual void onCallEvent(
-    ccapi_call_event_e aCallEvent,
-    CSF::CC_CallInfoPtr aInfo
-  );
+  void onCallEvent(const OnCallEventArgs &args);
 
   // DataConnection observers
   void NotifyConnection();
   void NotifyClosedConnection();
   void NotifyDataChannel(already_AddRefed<mozilla::DataChannel> aChannel);
 
   // Get the media object
   const nsRefPtr<PeerConnectionMedia>& media() const {
@@ -228,80 +229,224 @@ public:
 
   // Get the STS thread
   nsCOMPtr<nsIEventTarget> GetSTSThread() {
     PC_AUTO_ENTER_API_CALL_NO_CHECK();
     return mSTSThread;
   }
 
   // Get the DTLS identity
-  mozilla::RefPtr<DtlsIdentity> const GetIdentity() {
-    PC_AUTO_ENTER_API_CALL_NO_CHECK();
-    return mIdentity;
-  }
+  mozilla::RefPtr<DtlsIdentity> const GetIdentity();
 
   // Create a fake media stream
   nsresult CreateFakeMediaStream(uint32_t hint, nsIDOMMediaStream** retval);
 
   nsPIDOMWindow* GetWindow() const {
     PC_AUTO_ENTER_API_CALL_NO_CHECK();
     return mWindow;
   }
 
-  // Initialize PeerConnection from an IceConfiguration object.
-  nsresult Initialize(IPeerConnectionObserver* aObserver,
+  // Initialize PeerConnection from an IceConfiguration object (unit-tests)
+  nsresult Initialize(PeerConnectionObserver& aObserver,
                       nsIDOMWindow* aWindow,
                       const IceConfiguration& aConfiguration,
                       nsIThread* aThread) {
-    return Initialize(aObserver, aWindow, &aConfiguration, nullptr, aThread, nullptr);
+    return Initialize(aObserver, aWindow, &aConfiguration, nullptr, aThread);
+  }
+
+  // Initialize PeerConnection from an RTCConfiguration object (JS entrypoint)
+  void Initialize(PeerConnectionObserver& aObserver,
+                  nsIDOMWindow* aWindow,
+                  const RTCConfiguration& aConfiguration,
+                  nsISupports* aThread,
+                  ErrorResult &rv)
+  {
+    nsresult r = Initialize(aObserver, aWindow, nullptr, &aConfiguration, aThread);
+    if (NS_FAILED(r)) {
+      rv.Throw(r);
+    }
+  }
+
+  NS_IMETHODIMP_TO_ERRORRESULT(CreateOffer, ErrorResult &rv,
+                               const MediaConstraintsInternal& aConstraints)
+  {
+    rv = CreateOffer(aConstraints);
+  }
+
+  NS_IMETHODIMP_TO_ERRORRESULT(CreateAnswer, ErrorResult &rv,
+                               const MediaConstraintsInternal& aConstraints)
+  {
+    rv = CreateAnswer(aConstraints);
+  }
+
+  NS_IMETHODIMP CreateOffer(const MediaConstraintsExternal& aConstraints);
+  NS_IMETHODIMP CreateAnswer(const MediaConstraintsExternal& aConstraints);
+
+  NS_IMETHODIMP SetLocalDescription (int32_t aAction, const char* aSDP);
+  void SetLocalDescription (int32_t aAction, const nsAString& aSDP, ErrorResult &rv)
+  {
+    rv = SetLocalDescription(aAction, NS_ConvertUTF16toUTF8(aSDP).get());
+  }
+
+  NS_IMETHODIMP SetRemoteDescription (int32_t aAction, const char* aSDP);
+  void SetRemoteDescription (int32_t aAction, const nsAString& aSDP, ErrorResult &rv)
+  {
+    rv = SetRemoteDescription(aAction, NS_ConvertUTF16toUTF8(aSDP).get());
+  }
+
+  NS_IMETHODIMP AddIceCandidate(const char* aCandidate, const char* aMid,
+                                unsigned short aLevel);
+  void AddIceCandidate(const nsAString& aCandidate, const nsAString& aMid,
+                       unsigned short aLevel, ErrorResult &rv)
+  {
+    rv = AddIceCandidate(NS_ConvertUTF16toUTF8(aCandidate).get(),
+                         NS_ConvertUTF16toUTF8(aMid).get(), aLevel);
+  }
+
+  NS_IMETHODIMP CloseStreams();
+  void CloseStreams(ErrorResult &rv)
+  {
+    rv = CloseStreams();
   }
 
-  // Validate constraints and construct a MediaConstraints object
-  // from a JS::Value.
-  NS_IMETHODIMP CreateOffer(MediaConstraints& aConstraints);
-  NS_IMETHODIMP CreateAnswer(MediaConstraints& aConstraints);
+  NS_IMETHODIMP_TO_ERRORRESULT(AddStream, ErrorResult &rv,
+                               DOMMediaStream& aMediaStream)
+  {
+    rv = AddStream(aMediaStream);
+  }
+
+  NS_IMETHODIMP_TO_ERRORRESULT(RemoveStream, ErrorResult &rv,
+                               DOMMediaStream& aMediaStream)
+  {
+    rv = RemoveStream(aMediaStream);
+  }
+
+  NS_IMETHODIMP GetLocalDescription(char** aSDP);
+  void GetLocalDescription(nsAString& aSDP)
+  {
+    char *tmp;
+    GetLocalDescription(&tmp);
+    aSDP.AssignASCII(tmp);
+    delete tmp;
+  }
+
+  NS_IMETHODIMP GetRemoteDescription(char** aSDP);
+  void GetRemoteDescription(nsAString& aSDP)
+  {
+    char *tmp;
+    GetRemoteDescription(&tmp);
+    aSDP.AssignASCII(tmp);
+    delete tmp;
+  }
+
+  NS_IMETHODIMP ReadyState(mozilla::dom::PCImplReadyState* aState);
+  mozilla::dom::PCImplReadyState ReadyState()
+  {
+    mozilla::dom::PCImplReadyState state;
+    ReadyState(&state);
+    return state;
+  }
+
+  NS_IMETHODIMP SignalingState(mozilla::dom::PCImplSignalingState* aState);
+  mozilla::dom::PCImplSignalingState SignalingState()
+  {
+    mozilla::dom::PCImplSignalingState state;
+    SignalingState(&state);
+    return state;
+  }
+
+  NS_IMETHODIMP SipccState(mozilla::dom::PCImplSipccState* aState);
+  mozilla::dom::PCImplSipccState SipccState()
+  {
+    mozilla::dom::PCImplSipccState state;
+    SipccState(&state);
+    return state;
+  }
+
+  NS_IMETHODIMP IceState(mozilla::dom::PCImplIceState* aState);
+  mozilla::dom::PCImplIceState IceState()
+  {
+    mozilla::dom::PCImplIceState state;
+    IceState(&state);
+    return state;
+  }
+
+  NS_IMETHODIMP Close();
+  void Close(ErrorResult &rv)
+  {
+    rv = Close();
+  }
 
   nsresult InitializeDataChannel(int track_id, uint16_t aLocalport,
                                  uint16_t aRemoteport, uint16_t aNumstreams);
 
+  NS_IMETHODIMP_TO_ERRORRESULT(ConnectDataConnection, ErrorResult &rv,
+                               uint16_t aLocalport,
+                               uint16_t aRemoteport,
+                               uint16_t aNumstreams)
+  {
+    rv = ConnectDataConnection(aLocalport, aRemoteport, aNumstreams);
+  }
+
+  NS_IMETHODIMP_TO_ERRORRESULT_RETREF(nsDOMDataChannel,
+                                      CreateDataChannel, ErrorResult &rv,
+                                      const nsAString& aLabel,
+                                      const nsAString& aProtocol,
+                                      uint16_t aType,
+                                      bool outOfOrderAllowed,
+                                      uint16_t aMaxTime,
+                                      uint16_t aMaxNum,
+                                      bool aExternalNegotiated,
+                                      uint16_t aStream);
+
+  NS_IMETHODIMP_TO_ERRORRESULT(GetLocalStreams, ErrorResult &rv,
+                               nsTArray<nsRefPtr<DOMMediaStream > >& result)
+  {
+    rv = GetLocalStreams(result);
+  }
+
+  NS_IMETHODIMP_TO_ERRORRESULT(GetRemoteStreams, ErrorResult &rv,
+                               nsTArray<nsRefPtr<DOMMediaStream > >& result)
+  {
+    rv = GetRemoteStreams(result);
+  }
+
   // Called whenever something is unrecognized by the parser
   // May be called more than once and does not necessarily mean
   // that parsing was stopped, only that something was unrecognized.
   void OnSdpParseError(const char* errorMessage);
 
   // Called when OnLocal/RemoteDescriptionSuccess/Error
   // is called to start the list over.
   void ClearSdpParseErrorMessages();
 
   // Called to retreive the list of parsing errors.
   const std::vector<std::string> &GetSdpParseErrors();
 
   // Sets the RTC Signaling State
-  void SetSignalingState_m(SignalingState aSignalingState);
+  void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState);
 
 #ifdef MOZILLA_INTERNAL_API
   // initialize telemetry for when calls start
   void startCallTelem();
 #endif
 
 private:
   PeerConnectionImpl(const PeerConnectionImpl&rhs);
   PeerConnectionImpl& operator=(PeerConnectionImpl);
-  nsresult Initialize(IPeerConnectionObserver* aObserver,
-                      nsIDOMWindow* aWindow,
-                      const IceConfiguration* aConfiguration,
-                      const JS::Value* aRTCConfiguration,
-                      nsIThread* aThread,
-                      JSContext* aCx);
-  NS_IMETHODIMP CreateOfferInt(MediaConstraints& constraints);
-  NS_IMETHODIMP CreateAnswerInt(MediaConstraints& constraints);
+  NS_IMETHODIMP Initialize(PeerConnectionObserver& aObserver,
+                           nsIDOMWindow* aWindow,
+                           const IceConfiguration* aConfiguration,
+                           const RTCConfiguration* aRTCConfiguration,
+                           nsISupports* aThread);
+
   NS_IMETHODIMP EnsureDataConnection(uint16_t aNumstreams);
 
   nsresult CloseInt();
-  void ChangeReadyState(ReadyState aReadyState);
+  void ChangeReadyState(mozilla::dom::PCImplReadyState aReadyState);
   nsresult CheckApiState(bool assert_ice_ready) const;
   void CheckThread() const {
     NS_ABORT_IF_FALSE(CheckThreadInt(), "Wrong thread");
   }
   bool CheckThreadInt() const {
 #ifdef MOZILLA_INTERNAL_API
     // Thread assertions are disabled in the C++ unit tests because those
     // make API calls off the main thread.
@@ -316,35 +461,56 @@ private:
 #ifdef MOZILLA_INTERNAL_API
   void virtualDestroyNSSReference() MOZ_FINAL;
 #endif
 
   // Shut down media - called on main thread only
   void ShutdownMedia();
 
   // ICE callbacks run on the right thread.
-  nsresult IceStateChange_m(IceState aState);
+  nsresult IceStateChange_m(mozilla::dom::PCImplIceState aState);
 
   // Timecard used to measure processing time. This should be the first class
   // attribute so that we accurately measure the time required to instantiate
   // any other attributes of this class.
   Timecard *mTimeCard;
 
   // The call
-  CSF::CC_CallPtr mCall;
-  ReadyState mReadyState;
-  SignalingState mSignalingState;
+  mozilla::ScopedDeletePtr<Internal> mInternal;
+  mozilla::dom::PCImplReadyState mReadyState;
+  mozilla::dom::PCImplSignalingState mSignalingState;
 
   // ICE State
-  IceState mIceState;
+  mozilla::dom::PCImplIceState mIceState;
 
   nsCOMPtr<nsIThread> mThread;
-  // Weak pointer to IPeerConnectionObserver
-  // This is only safe to use on the main thread
-  nsWeakPtr mPCObserver;
+  // We hold a raw pointer to PeerConnectionObserver (no WeakRefs to concretes!)
+  // which is an invariant guaranteed to exist between Initialize() and Close().
+  // We explicitly clear it in Close(). We wrap it in a helper, to encourage
+  // testing against nullptr before use. Use in Runnables requires wrapping
+  // access in RefPtr<> since they may execute after close. This is only safe
+  // to use on the main thread
+  //
+  // TODO: Remove if we ever properly wire PeerConnection for cycle-collection.
+  class WeakReminder
+  {
+  public:
+    WeakReminder() : mObserver(nullptr) {}
+    void Init(PeerConnectionObserver *aObserver) {
+      mObserver = aObserver;
+    }
+    void Close() {
+      mObserver = nullptr;
+    }
+    PeerConnectionObserver *MayGet() {
+      return mObserver;
+    }
+  private:
+    PeerConnectionObserver *mObserver;
+  } mPCObserver;
   nsCOMPtr<nsPIDOMWindow> mWindow;
 
   // The SDP sent in from JS - here for debugging.
   std::string mLocalRequestedSDP;
   std::string mRemoteRequestedSDP;
   // The SDP we are using.
   std::string mLocalSDP;
   std::string mRemoteSDP;
@@ -404,9 +570,11 @@ class PeerConnectionWrapper
   PeerConnectionImpl *impl() { return impl_; }
 
  private:
   nsRefPtr<PeerConnectionImpl> impl_;
 };
 
 }  // end sipcc namespace
 
+#undef NS_IMETHODIMP_TO_ERRORRESULT
+#undef NS_IMETHODIMP_TO_ERRORRESULT_RETREF
 #endif  // _PEER_CONNECTION_IMPL_H_
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -109,16 +109,26 @@ void RemoteSourceStreamInfo::DetachMedia
   for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it =
            mPipelines.begin(); it != mPipelines.end();
        ++it) {
     it->second->ShutdownMedia_m();
   }
   mMediaStream = nullptr;
 }
 
+already_AddRefed<PeerConnectionImpl>
+PeerConnectionImpl::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& rv)
+{
+  nsRefPtr<PeerConnectionImpl> pc = new PeerConnectionImpl(&aGlobal);
+
+  CSFLogDebug(logTag, "Created PeerConnection: %p", pc.get());
+
+  return pc.forget();
+}
+
 PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection()
 {
   PeerConnectionImpl *pc = new PeerConnectionImpl();
 
   CSFLogDebug(logTag, "Created PeerConnection: %p", pc);
 
   return pc;
 }
--- a/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
@@ -1102,27 +1102,19 @@ fsmdef_set_per_media_local_hold_sdp (fsm
 /**
  * This function deallocates a constraints structure
  *
  * @param[in]constraints - pointer to cc_media_constraints_t
  * @return None
  */
 void
 fsmdef_free_constraints(cc_media_constraints_t *constraints) {
-    int i;
-
     if (!constraints) {
        return;
     }
-
-    for (i = 0; i < constraints->constraint_count; i++) {
-        cpr_free(constraints->constraints[i]->name);
-        cpr_free(constraints->constraints[i]->value);
-    }
-    cpr_free(constraints->constraints);
     cpr_free(constraints);
 }
 
 void
 fsmdef_init_dcb (fsmdef_dcb_t *dcb, callid_t call_id,
                  fsmdef_call_types_t call_type,
                  string_t called_number, line_t line, fsm_fcb_t *fcb)
 {
--- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
@@ -206,55 +206,43 @@ static const cc_media_cap_table_t *gsmsd
 
     return (dcb_p->media_cap_tbl);
 }
 
 /*
  * Process a single constraint for one media capablity
  */
 void gsmsdp_process_cap_constraint(cc_media_cap_t *cap,
-                                   const char *constraint) {
-  /* Check constraint string for values "TRUE" or "FALSE"
-     (currently set in PeerConnectionImpl.cpp, with only
-     two possible hardcoded values).
-     TODO -- The values that constraints can take are
-     fairly narrow and enumerated; they should probably
-     use an enumeration rather than a string. See bug 811360.
-  */
-  if (constraint[0] == 'F') {
+                                   cc_boolean constraint) {
+  if (!constraint) {
     cap->support_direction &= ~SDP_DIRECTION_FLAG_RECV;
-  } else if (constraint[0] == 'T') {
+  } else {
     cap->support_direction |= SDP_DIRECTION_FLAG_RECV;
     cap->enabled = TRUE;
   }
 }
 
 /*
  * Process constraints only related to media capabilities., i.e
  * OfferToReceiveAudio, OfferToReceiveVideo
  */
 void gsmsdp_process_cap_constraints(fsmdef_dcb_t *dcb,
                                     cc_media_constraints_t* constraints) {
-  int i = 0;
-
-  for (i=0; i<constraints->constraint_count; i++) {
-    if (strcmp(constraints_table[OfferToReceiveAudio].name,
-               constraints->constraints[i]->name) == 0) {
-      gsmsdp_process_cap_constraint(&dcb->media_cap_tbl->cap[CC_AUDIO_1],
-                                    constraints->constraints[i]->value);
-    } else if (strcmp(constraints_table[OfferToReceiveVideo].name,
-               constraints->constraints[i]->name) == 0) {
-      gsmsdp_process_cap_constraint(&dcb->media_cap_tbl->cap[CC_VIDEO_1],
-                                    constraints->constraints[i]->value);
-    } else if (strcmp(constraints_table[MozDontOfferDataChannel].name,
-               constraints->constraints[i]->name) == 0) {
-      /* Hack to suppress data channel */
-      if (constraints->constraints[i]->value[0] == 'T') {
-        dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = FALSE;
-      }
+  if (constraints->offer_to_receive_audio.was_passed) {
+    gsmsdp_process_cap_constraint(&dcb->media_cap_tbl->cap[CC_AUDIO_1],
+                                  constraints->offer_to_receive_audio.value);
+  }
+  if (constraints->offer_to_receive_video.was_passed) {
+    gsmsdp_process_cap_constraint(&dcb->media_cap_tbl->cap[CC_VIDEO_1],
+                                  constraints->offer_to_receive_video.value);
+  }
+  if (constraints->moz_dont_offer_datachannel.was_passed) {
+    /* Hack to suppress data channel */
+    if (constraints->moz_dont_offer_datachannel.value) {
+      dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = FALSE;
     }
   }
 }
 
 /**
  * Copy an fsmdef_media_t's payload list to its previous_sdp's payload list
  *
  * @param[in]media   - pointer to the fsmdef_media_t to update
--- a/media/webrtc/signaling/src/sipcc/include/cc_constants.h
+++ b/media/webrtc/signaling/src/sipcc/include/cc_constants.h
@@ -570,22 +570,22 @@ typedef unsigned int cc_media_track_id_t
 typedef enum {
   NO_STREAM = -1,
   AUDIO,
   VIDEO,
   DATA,
   TYPE_MAX
 } cc_media_type_t;
 
+typedef struct {
+  cc_boolean was_passed;
+  cc_boolean value;
+  cc_boolean mandatory;
+} cc_boolean_constraint_t;
 
 typedef struct {
-  char        *name;
-  char        *value;
-  cc_boolean   mandatory;
-} cc_media_constraint_t;
-
-typedef struct {
-  cc_media_constraint_t**  constraints;
-  cc_uint16_t              constraint_count;
+  cc_boolean_constraint_t offer_to_receive_audio;
+  cc_boolean_constraint_t offer_to_receive_video;
+  cc_boolean_constraint_t moz_dont_offer_datachannel;
 } cc_media_constraints_t;
 
 #endif /* _CC_CONSTANTS_H_ */
 
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/test/FakePCObserver.h
@@ -0,0 +1,105 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TEST_PCOBSERVER_H_
+#define TEST_PCOBSERVER_H_
+
+#include "nsNetCID.h"
+#include "nsITimer.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+
+#include "mozilla/Mutex.h"
+#include "AudioSegment.h"
+#include "MediaSegment.h"
+#include "StreamBuffer.h"
+#include "nsTArray.h"
+#include "nsIRunnable.h"
+#include "nsISupportsImpl.h"
+#include "nsIDOMMediaStream.h"
+#include "mozilla/dom/PeerConnectionObserverEnumsBinding.h"
+#include "PeerConnectionImpl.h"
+
+namespace sipcc {
+class PeerConnectionImpl;
+}
+
+class nsIDOMWindow;
+class nsIDOMDataChannel;
+
+namespace test {
+using namespace mozilla::dom;
+
+class AFakePCObserver : public nsISupports
+{
+protected:
+  typedef mozilla::ErrorResult ER;
+public:
+  enum Action {
+    OFFER,
+    ANSWER
+  };
+
+  enum ResponseState {
+    stateNoResponse,
+    stateSuccess,
+    stateError
+  };
+
+  AFakePCObserver(sipcc::PeerConnectionImpl *peerConnection,
+                  const std::string &aName) :
+    state(stateNoResponse), addIceSuccessCount(0),
+    onAddStreamCalled(false),
+    name(aName),
+    pc(peerConnection) {
+  }
+
+  virtual ~AFakePCObserver() {}
+
+  std::vector<mozilla::DOMMediaStream *> GetStreams() { return streams; }
+
+  ResponseState state;
+  char *lastString;
+  sipcc::PeerConnectionImpl::Error lastStatusCode;
+  mozilla::dom::PCObserverStateType lastStateType;
+  int addIceSuccessCount;
+  bool onAddStreamCalled;
+  std::string name;
+  std::vector<std::string> candidates;
+
+  virtual NS_IMETHODIMP OnCreateOfferSuccess(const char* offer, ER&) = 0;
+  virtual NS_IMETHODIMP OnCreateOfferError(uint32_t code, const char *msg, ER&) = 0;
+  virtual NS_IMETHODIMP OnCreateAnswerSuccess(const char* answer, ER&) = 0;
+  virtual NS_IMETHODIMP OnCreateAnswerError(uint32_t code, const char *msg, ER&) = 0;
+  virtual NS_IMETHODIMP OnSetLocalDescriptionSuccess(ER&) = 0;
+  virtual NS_IMETHODIMP OnSetRemoteDescriptionSuccess(ER&) = 0;
+  virtual NS_IMETHODIMP OnSetLocalDescriptionError(uint32_t code, const char *msg, ER&) = 0;
+  virtual NS_IMETHODIMP OnSetRemoteDescriptionError(uint32_t code, const char *msg, ER&) = 0;
+  virtual NS_IMETHODIMP NotifyConnection(ER&) = 0;
+  virtual NS_IMETHODIMP NotifyClosedConnection(ER&) = 0;
+  virtual NS_IMETHODIMP NotifyDataChannel(nsIDOMDataChannel *channel, ER&) = 0;
+  virtual NS_IMETHODIMP OnStateChange(PCObserverStateType state_type, ER&,
+                                      void* = nullptr) = 0;
+  virtual NS_IMETHODIMP OnAddStream(nsIDOMMediaStream *stream, ER&) = 0;
+  virtual NS_IMETHODIMP OnRemoveStream(ER&) = 0;
+  virtual NS_IMETHODIMP OnAddTrack(ER&) = 0;
+  virtual NS_IMETHODIMP OnRemoveTrack(ER&) = 0;
+  virtual NS_IMETHODIMP OnAddIceCandidateSuccess(ER&) = 0;
+  virtual NS_IMETHODIMP OnAddIceCandidateError(uint32_t code, const char *msg, ER&) = 0;
+  virtual NS_IMETHODIMP OnIceCandidate(uint16_t level, const char *mid,
+                                       const char *candidate, ER&) = 0;
+protected:
+  sipcc::PeerConnectionImpl *pc;
+  std::vector<mozilla::DOMMediaStream *> streams;
+};
+}
+
+namespace mozilla {
+namespace dom {
+typedef test::AFakePCObserver PeerConnectionObserver;
+}
+}
+
+#endif
--- a/media/webrtc/signaling/test/Makefile.in
+++ b/media/webrtc/signaling/test/Makefile.in
@@ -110,16 +110,17 @@ endif
 ifeq ($(OS_TARGET),WINNT)
 LIBS += \
   $(DEPTH)/staticlib/components/$(LIB_PREFIX)windowsproxy.$(LIB_SUFFIX) \
   $(NULL)
 endif
 
 DEFINES += \
   -DUSE_FAKE_MEDIA_STREAMS \
+  -DUSE_FAKE_PCOBSERVER \
   -DNR_SOCKET_IS_VOID_PTR \
   -DHAVE_STRDUP \
   $(NULL)
 
 ifeq ($(OS_TARGET),Darwin)
 DEFINES += \
   -DGTEST_USE_OWN_TR1_TUPLE=1 \
   $(NULL)
@@ -150,16 +151,17 @@ LOCAL_INCLUDES += \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/share \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/util/libekr \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/log \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/registry \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/stats \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/plugin \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/event \
  -I$(topsrcdir)/xpcom/base/ \
+ -I$(DEPTH)/dom/bindings/ \
  $(NULL)
 
 ifneq (,$(filter Darwin DragonFly FreeBSD NetBSD OpenBSD,$(OS_TARGET)))
 LOCAL_INCLUDES +=  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/darwin/include
 ifneq (,$(filter DragonFly FreeBSD NetBSD OpenBSD,$(OS_TARGET)))
 LOCAL_INCLUDES +=  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/generic/include
 endif
 endif
--- a/media/webrtc/signaling/test/sdp_unittests.cpp
+++ b/media/webrtc/signaling/test/sdp_unittests.cpp
@@ -9,17 +9,19 @@
 #include <string>
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
 
 #include "nspr.h"
 #include "nss.h"
+#include "ssl.h"
 
+#include "nsThreadUtils.h"
 #include "FakeMediaStreams.h"
 #include "FakeMediaStreamsImpl.h"
 #include "PeerConnectionImpl.h"
 #include "PeerConnectionCtx.h"
 
 #include "mtransport_test_utils.h"
 MtransportTestUtils *test_utils;
 nsCOMPtr<nsIThread> gThread;
--- a/media/webrtc/signaling/test/signaling_unittests.cpp
+++ b/media/webrtc/signaling/test/signaling_unittests.cpp
@@ -15,55 +15,105 @@
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
 
 #include "nspr.h"
 #include "nss.h"
 #include "ssl.h"
 #include "prthread.h"
 
+#include "cpr_stdlib.h"
+#include "FakePCObserver.h"
 #include "FakeMediaStreams.h"
 #include "FakeMediaStreamsImpl.h"
 #include "PeerConnectionImpl.h"
 #include "PeerConnectionCtx.h"
+#include "PeerConnectionMedia.h"
+#include "MediaPipeline.h"
 #include "runnable_utils.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/Services.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsNetUtil.h"
 #include "nsIIOService.h"
 #include "nsIDNSService.h"
 #include "nsWeakReference.h"
 #include "nricectx.h"
 #include "mozilla/SyncRunnable.h"
 #include "logging.h"
 #include "stunserver.h"
 #include "stunserver.cpp"
+#include "PeerConnectionImplEnumsBinding.cpp"
 
 #include "mtransport_test_utils.h"
 MtransportTestUtils *test_utils;
 nsCOMPtr<nsIThread> gThread;
 
+#ifndef USE_FAKE_MEDIA_STREAMS
+#error USE_FAKE_MEDIA_STREAMS undefined
+#endif
+#ifndef USE_FAKE_PCOBSERVER
+#error USE_FAKE_PCOBSERVER undefined
+#endif
+
 static int kDefaultTimeout = 5000;
 static bool fRtcpMux = true;
 
 static std::string callerName = "caller";
 static std::string calleeName = "callee";
 
 #define ARRAY_TO_STL(container, type, array) \
         (container<type>((array), (array) + PR_ARRAY_SIZE(array)))
 
 #define ARRAY_TO_SET(type, array) ARRAY_TO_STL(std::set, type, array)
 
 std::string g_stun_server_address((char *)"23.21.150.121");
 uint16_t g_stun_server_port(3478);
 std::string kBogusSrflxAddress((char *)"192.0.2.1");
 uint16_t kBogusSrflxPort(1001);
 
+namespace sipcc {
+
+// We can't use mozilla/dom/MediaConstraintsBinding.h here because it uses
+// nsString, so we pass constraints in using MediaConstraintsExternal instead
+
+class MediaConstraints : public MediaConstraintsExternal {
+public:
+  MediaConstraints()
+  : MediaConstraintsExternal((cc_media_constraints_t*)
+                             cpr_malloc(sizeof(*mConstraints))) {
+    MOZ_ASSERT(mConstraints);
+    memset(mConstraints, 0, sizeof(*mConstraints));
+  }
+
+  void setBooleanConstraint(const char *namePtr, bool value, bool mandatory) {
+    cc_boolean_constraint_t &member (getMember(namePtr));
+    member.was_passed = true;
+    member.value = value;
+    member.mandatory = mandatory;
+  }
+private:
+  cc_boolean_constraint_t &getMember(const char *namePtr) {
+    MOZ_ASSERT(mConstraints);
+    if (strcmp(namePtr, "OfferToReceiveAudio") == 0) {
+        return mConstraints->offer_to_receive_audio;
+    }
+    if (strcmp(namePtr, "OfferToReceiveVideo") == 0) {
+        return mConstraints->offer_to_receive_video;
+    }
+    MOZ_ASSERT(false);
+    return mConstraints->moz_dont_offer_datachannel;
+  }
+};
+}
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
 namespace test {
 
 std::string indent(const std::string &s, int width = 4) {
   std::string prefix;
   std::string out;
   char previous = '\n';
   prefix.assign(width, ' ');
   for (std::string::const_iterator i = s.begin(); i != s.end(); i++) {
@@ -178,247 +228,214 @@ static bool SetupGlobalThread() {
     if (NS_FAILED(rv))
       return false;
 
     gThread = thread;
   }
   return true;
 }
 
-class TestObserver : public IPeerConnectionObserver,
-                     public nsSupportsWeakReference
+class TestObserver : public AFakePCObserver
 {
 public:
-  enum Action {
-    OFFER,
-    ANSWER
-  };
-
-  enum ResponseState {
-    stateNoResponse,
-    stateSuccess,
-    stateError
-  };
-
   TestObserver(sipcc::PeerConnectionImpl *peerConnection,
                const std::string &aName) :
-    state(stateNoResponse), addIceSuccessCount(0),
-    onAddStreamCalled(false),
-    name(aName),
-    candidates(),
-    pc(peerConnection) {
-  }
-
-  virtual ~TestObserver() {}
-
-  std::vector<DOMMediaStream *> GetStreams() { return streams; }
+    AFakePCObserver(peerConnection, aName) {}
 
   size_t MatchingCandidates(const std::string& cand) {
     size_t count = 0;
 
     for (size_t i=0; i<candidates.size(); ++i) {
       if (candidates[i].find(cand) != std::string::npos)
         ++count;
     }
 
     return count;
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_IPEERCONNECTIONOBSERVER
-
-  ResponseState state;
-  char *lastString;
-  sipcc::PeerConnectionImpl::Error lastStatusCode;
-  uint32_t lastStateType;
-  int addIceSuccessCount;
-  bool onAddStreamCalled;
-  std::string name;
-  std::vector<std::string> candidates;
-
-private:
-  sipcc::PeerConnectionImpl *pc;
-  std::vector<DOMMediaStream *> streams;
+  NS_IMETHODIMP OnCreateOfferSuccess(const char* offer, ER&);
+  NS_IMETHODIMP OnCreateOfferError(uint32_t code, const char *msg, ER&);
+  NS_IMETHODIMP OnCreateAnswerSuccess(const char* answer, ER&);
+  NS_IMETHODIMP OnCreateAnswerError(uint32_t code, const char *msg, ER&);
+  NS_IMETHODIMP OnSetLocalDescriptionSuccess(ER&);
+  NS_IMETHODIMP OnSetRemoteDescriptionSuccess(ER&);
+  NS_IMETHODIMP OnSetLocalDescriptionError(uint32_t code, const char *msg, ER&);
+  NS_IMETHODIMP OnSetRemoteDescriptionError(uint32_t code, const char *msg, ER&);
+  NS_IMETHODIMP NotifyConnection(ER&);
+  NS_IMETHODIMP NotifyClosedConnection(ER&);
+  NS_IMETHODIMP NotifyDataChannel(nsIDOMDataChannel *channel, ER&);
+  NS_IMETHODIMP OnStateChange(PCObserverStateType state_type, ER&, void*);
+  NS_IMETHODIMP OnAddStream(nsIDOMMediaStream *stream, ER&);
+  NS_IMETHODIMP OnRemoveStream(ER&);
+  NS_IMETHODIMP OnAddTrack(ER&);
+  NS_IMETHODIMP OnRemoveTrack(ER&);
+  NS_IMETHODIMP OnAddIceCandidateSuccess(ER&);
+  NS_IMETHODIMP OnAddIceCandidateError(uint32_t code, const char *msg, ER&);
+  NS_IMETHODIMP OnIceCandidate(uint16_t level, const char *mid, const char *cand, ER&);
 };
 
-NS_IMPL_ISUPPORTS2(TestObserver,
-                   IPeerConnectionObserver,
-                   nsISupportsWeakReference)
+NS_IMPL_ISUPPORTS0(TestObserver)
 
 NS_IMETHODIMP
-TestObserver::OnCreateOfferSuccess(const char* offer)
+TestObserver::OnCreateOfferSuccess(const char* offer, ER&)
 {
   lastString = strdup(offer);
   state = stateSuccess;
   std::cout << name << ": onCreateOfferSuccess = " << std::endl << indent(offer)
             << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnCreateOfferError(uint32_t code, const char *message)
+TestObserver::OnCreateOfferError(uint32_t code, const char *message, ER&)
 {
   lastStatusCode = static_cast<sipcc::PeerConnectionImpl::Error>(code);
   state = stateError;
   std::cout << name << ": onCreateOfferError = " << code
             << " (" << message << ")" << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnCreateAnswerSuccess(const char* answer)
+TestObserver::OnCreateAnswerSuccess(const char* answer, ER&)
 {
   lastString = strdup(answer);
   state = stateSuccess;
   std::cout << name << ": onCreateAnswerSuccess =" << std::endl
             << indent(answer) << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnCreateAnswerError(uint32_t code, const char *message)
+TestObserver::OnCreateAnswerError(uint32_t code, const char *message, ER&)
 {
   lastStatusCode = static_cast<sipcc::PeerConnectionImpl::Error>(code);
   std::cout << name << ": onCreateAnswerError = " << code
             << " (" << message << ")" << std::endl;
   state = stateError;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnSetLocalDescriptionSuccess()
+TestObserver::OnSetLocalDescriptionSuccess(ER&)
 {
   lastStatusCode = sipcc::PeerConnectionImpl::kNoError;
   state = stateSuccess;
   std::cout << name << ": onSetLocalDescriptionSuccess" << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnSetRemoteDescriptionSuccess()
+TestObserver::OnSetRemoteDescriptionSuccess(ER&)
 {
   lastStatusCode = sipcc::PeerConnectionImpl::kNoError;
   state = stateSuccess;
   std::cout << name << ": onSetRemoteDescriptionSuccess" << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnSetLocalDescriptionError(uint32_t code, const char *message)
+TestObserver::OnSetLocalDescriptionError(uint32_t code, const char *message, ER&)
 {
   lastStatusCode = static_cast<sipcc::PeerConnectionImpl::Error>(code);
   state = stateError;
   std::cout << name << ": onSetLocalDescriptionError = " << code
             << " (" << message << ")" << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnSetRemoteDescriptionError(uint32_t code, const char *message)
+TestObserver::OnSetRemoteDescriptionError(uint32_t code, const char *message, ER&)
 {
   lastStatusCode = static_cast<sipcc::PeerConnectionImpl::Error>(code);
   state = stateError;
   std::cout << name << ": onSetRemoteDescriptionError = " << code
             << " (" << message << ")" << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::NotifyConnection()
+TestObserver::NotifyConnection(ER&)
 {
   std::cout << name << ": NotifyConnection" << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::NotifyClosedConnection()
+TestObserver::NotifyClosedConnection(ER&)
 {
   std::cout << name << ": NotifyClosedConnection" << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::NotifyDataChannel(nsIDOMDataChannel *channel)
+TestObserver::NotifyDataChannel(nsIDOMDataChannel *channel, ER&)
 {
   std::cout << name << ": NotifyDataChannel" << std::endl;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnStateChange(uint32_t state_type)
+TestObserver::OnStateChange(PCObserverStateType state_type, ER&, void*)
 {
   nsresult rv;
-  uint32_t gotstate;
+  PCImplReadyState gotready;
+  PCImplIceState gotice;
+  PCImplSipccState gotsipcc;
+  PCImplSignalingState gotsignaling;
 
   std::cout << name << ": ";
 
   switch (state_type)
   {
-  case IPeerConnectionObserver::kReadyState:
-    rv = pc->GetReadyState(&gotstate);
+  case PCObserverStateType::ReadyState:
+    rv = pc->ReadyState(&gotready);
     NS_ENSURE_SUCCESS(rv, rv);
-    std::cout << "Ready State: " << gotstate << std::endl;
+    std::cout << "Ready State: "
+              << PCImplReadyStateValues::strings[int(gotready)].value
+              << std::endl;
     break;
-  case IPeerConnectionObserver::kIceState:
-    rv = pc->GetIceState(&gotstate);
+  case PCObserverStateType::IceState:
+    rv = pc->IceState(&gotice);
     NS_ENSURE_SUCCESS(rv, rv);
-    std::cout << "ICE State: " << gotstate << std::endl;
+    std::cout << "ICE State: "
+              << PCImplIceStateValues::strings[int(gotice)].value
+              << std::endl;
     break;
-  case IPeerConnectionObserver::kSdpState:
+  case PCObserverStateType::SdpState:
     std::cout << "SDP State: " << std::endl;
     // NS_ENSURE_SUCCESS(rv, rv);
     break;
-  case IPeerConnectionObserver::kSipccState:
-    rv = pc->GetSipccState(&gotstate);
-    NS_ENSURE_SUCCESS(rv, rv);
-    std::cout << "SIPCC State: " << gotstate << std::endl;
-    break;
-  case IPeerConnectionObserver::kSignalingState:
-    rv = pc->GetSignalingState(&gotstate);
+  case PCObserverStateType::SipccState:
+    rv = pc->SipccState(&gotsipcc);
     NS_ENSURE_SUCCESS(rv, rv);
-    std::cout << "Signaling State: " << gotstate << " (";
-    switch (gotstate) {
-      case sipcc::PeerConnectionImpl::kSignalingInvalid:
-        std::cout << "INVALID";
-        break;
-      case sipcc::PeerConnectionImpl::kSignalingStable:
-        std::cout << "stable";
-        break;
-      case sipcc::PeerConnectionImpl::kSignalingHaveLocalOffer:
-        std::cout << "have-local-offer";
-        break;
-      case sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer:
-        std::cout << "have-remote-offer";
-        break;
-      case sipcc::PeerConnectionImpl::kSignalingHaveLocalPranswer:
-        std::cout << "have-local-pranswer";
-        break;
-      case sipcc::PeerConnectionImpl::kSignalingHaveRemotePranswer:
-        std::cout << "have-remote-pranswer";
-        break;
-      case sipcc::PeerConnectionImpl::kSignalingClosed:
-        std::cout << "closed";
-        break;
-      default:
-        std::cout << "UNKNOWN";
-    }
-    std::cout << ")" << std::endl;
+    std::cout << "SIPCC State: "
+              << PCImplSipccStateValues::strings[int(gotsipcc)].value
+              << std::endl;
+    break;
+  case PCObserverStateType::SignalingState:
+    rv = pc->SignalingState(&gotsignaling);
+    NS_ENSURE_SUCCESS(rv, rv);
+    std::cout << "Signaling State: "
+              << PCImplSignalingStateValues::strings[int(gotsignaling)].value
+              << std::endl;
     break;
   default:
     // Unknown State
     break;
   }
 
   lastStateType = state_type;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-TestObserver::OnAddStream(nsIDOMMediaStream *stream)
+TestObserver::OnAddStream(nsIDOMMediaStream *stream, ER&)
 {
   PR_ASSERT(stream);
 
   DOMMediaStream *ms = static_cast<DOMMediaStream *>(stream);
 
   std::cout << name << ": OnAddStream called hints=" << ms->GetHintContents()
             << " thread=" << PR_GetCurrentThread() << std::endl ;
 
@@ -433,59 +450,59 @@ TestObserver::OnAddStream(nsIDOMMediaStr
   test_utils->sts_target()->Dispatch(
     WrapRunnable(fs, &Fake_SourceMediaStream::Start),
     NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnRemoveStream()
+TestObserver::OnRemoveStream(ER&)
 {
   state = stateSuccess;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnAddTrack()
+TestObserver::OnAddTrack(ER&)
 {
   state = stateSuccess;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnRemoveTrack()
+TestObserver::OnRemoveTrack(ER&)
 {
   state = stateSuccess;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TestObserver::OnIceCandidate(uint16_t level,
                              const char * mid,
-                             const char * candidate)
+                             const char * candidate, ER&)
 {
   std::cout << name << ": onIceCandidate [" << level << "/"
             << mid << "] " << candidate << std::endl;
   candidates.push_back(candidate);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnAddIceCandidateSuccess()
+TestObserver::OnAddIceCandidateSuccess(ER&)
 {
   lastStatusCode = sipcc::PeerConnectionImpl::kNoError;
   state = stateSuccess;
   std::cout << name << ": onAddIceCandidateSuccess" << std::endl;
   addIceSuccessCount++;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TestObserver::OnAddIceCandidateError(uint32_t code, const char *message)
+TestObserver::OnAddIceCandidateError(uint32_t code, const char *message, ER&)
 {
   lastStatusCode = static_cast<sipcc::PeerConnectionImpl::Error>(code);
   state = stateError;
   std::cout << name << ": onAddIceCandidateError = " << code
             << " (" << message << ")" << std::endl;
   return NS_OK;
 }
 
@@ -687,69 +704,60 @@ class SignalingAgent {
       WrapRunnable(this, &SignalingAgent::Close));
   }
 
   void Init_m(nsCOMPtr<nsIThread> thread)
   {
     pObserver = new TestObserver(pc, name);
     ASSERT_TRUE(pObserver);
 
-    ASSERT_EQ(pc->Initialize(pObserver, nullptr, cfg_, thread), NS_OK);
+    ASSERT_EQ(pc->Initialize(*pObserver, nullptr, cfg_, thread), NS_OK);
   }
 
   void Init(nsCOMPtr<nsIThread> thread)
   {
     mozilla::SyncRunnable::DispatchToThread(thread,
       WrapRunnable(this, &SignalingAgent::Init_m, thread));
 
-    ASSERT_TRUE_WAIT(sipcc_state() == sipcc::PeerConnectionImpl::kStarted,
+    ASSERT_TRUE_WAIT(sipcc_state() == PCImplSipccState::Started,
                      kDefaultTimeout);
   }
 
   void WaitForGather() {
-    ASSERT_TRUE_WAIT(ice_state() == sipcc::PeerConnectionImpl::kIceWaiting, 5000);
+    ASSERT_TRUE_WAIT(ice_state() == PCImplIceState::IceWaiting, 5000);
 
     std::cout << name << ": Init Complete" << std::endl;
   }
 
   bool WaitForGatherAllowFail() {
-    EXPECT_TRUE_WAIT(ice_state() == sipcc::PeerConnectionImpl::kIceWaiting ||
-                     ice_state() == sipcc::PeerConnectionImpl::kIceFailed, 5000);
-
-    if (ice_state() == sipcc::PeerConnectionImpl::kIceFailed) {
+    EXPECT_TRUE_WAIT(ice_state() == PCImplIceState::IceWaiting ||
+                     ice_state() == PCImplIceState::IceFailed, 5000);
+
+    if (ice_state() == PCImplIceState::IceFailed) {
       std::cout << name << ": Init Failed" << std::endl;
       return false;
     }
 
     std::cout << name << "Init Complete" << std::endl;
     return true;
   }
 
-  uint32_t sipcc_state()
+  PCImplSipccState sipcc_state()
   {
-    uint32_t res;
-
-    pc->GetSipccState(&res);
-    return res;
+    return pc->SipccState();
   }
 
-  uint32_t ice_state()
+  PCImplIceState ice_state()
   {
-    uint32_t res;
-
-    pc->GetIceState(&res);
-    return res;
+    return pc->IceState();
   }
 
-  sipcc::PeerConnectionImpl::SignalingState signaling_state()
+  PCImplSignalingState signaling_state()
   {
-    uint32_t res;
-
-   pc->GetSignalingState(&res);
-    return static_cast<sipcc::PeerConnectionImpl::SignalingState>(res);
+    return pc->SignalingState();
   }
 
   void Close()
   {
     if (pc) {
       std::cout << name << ": Close" << std::endl;
 
       pc->Close();
@@ -807,32 +815,32 @@ class SignalingAgent {
     nsRefPtr<DOMMediaStream> domMediaStream;
     if (stream) {
       domMediaStream = new DOMMediaStream(stream);
     } else {
       domMediaStream = new DOMMediaStream();
     }
 
     domMediaStream->SetHintContents(hint);
-    ASSERT_EQ(pc->AddStream(domMediaStream), NS_OK);
+    ASSERT_EQ(pc->AddStream(*domMediaStream), NS_OK);
     domMediaStream_ = domMediaStream;
   }
 
 
   // Removes a stream from the PeerConnection. If the stream
   // parameter is absent, removes the stream that was most
   // recently added to the PeerConnection.
   void RemoveLastStreamAdded() {
-    ASSERT_EQ(pc->RemoveStream(domMediaStream_), NS_OK);
+    ASSERT_EQ(pc->RemoveStream(*domMediaStream_), NS_OK);
   }
 
   void CreateOffer(sipcc::MediaConstraints& constraints,
                    uint32_t offerFlags, uint32_t sdpCheck,
-                   sipcc::PeerConnectionImpl::SignalingState endState =
-                     sipcc::PeerConnectionImpl::kSignalingStable) {
+                   PCImplSignalingState endState =
+                     PCImplSignalingState::SignalingStable) {
 
     // Create a media stream as if it came from GUM
     Fake_AudioStreamSource *audio_stream =
       new Fake_AudioStreamSource();
 
     nsresult ret;
     mozilla::SyncRunnable::DispatchToThread(
       test_utils->sts_target(),
@@ -860,18 +868,18 @@ class SignalingAgent {
     offer_ = pObserver->lastString;
   }
 
 void CreateAnswer(sipcc::MediaConstraints& constraints, std::string offer,
                     uint32_t offerAnswerFlags,
                     uint32_t sdpCheck = DONT_CHECK_AUDIO|
                                         DONT_CHECK_VIDEO|
                                         DONT_CHECK_DATA,
-                    sipcc::PeerConnectionImpl::SignalingState endState =
-                    sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer) {
+                    PCImplSignalingState endState =
+                    PCImplSignalingState::SignalingHaveRemoteOffer) {
 
     uint32_t aHintContents = 0;
     if (offerAnswerFlags & ANSWER_AUDIO) {
       aHintContents |= DOMMediaStream::HINT_CONTENTS_AUDIO;
     }
     if (offerAnswerFlags & ANSWER_VIDEO) {
       aHintContents |= DOMMediaStream::HINT_CONTENTS_VIDEO;
     }
@@ -899,58 +907,58 @@ void CreateAnswer(sipcc::MediaConstraint
                                uint32_t hints, uint32_t sdpCheck) {
 
     domMediaStream_->SetHintContents(hints);
 
     // This currently "removes" a stream that has the same audio/video
     // hints as were passed in.
     // When complete RemoveStream will remove and entire stream and its tracks
     // not just disable a track as this is currently doing
-    ASSERT_EQ(pc->RemoveStream(domMediaStream_), NS_OK);
+    ASSERT_EQ(pc->RemoveStream(*domMediaStream_), NS_OK);
 
     // Now call CreateOffer as JS would
     pObserver->state = TestObserver::stateNoResponse;
     ASSERT_EQ(pc->CreateOffer(constraints), NS_OK);
     ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse,
                      kDefaultTimeout);
     ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess);
     SDPSanityCheck(pObserver->lastString, sdpCheck, true);
     offer_ = pObserver->lastString;
   }
 
   void SetRemote(TestObserver::Action action, std::string remote,
                  bool ignoreError = false,
-                 sipcc::PeerConnectionImpl::SignalingState endState =
-                   sipcc::PeerConnectionImpl::kSignalingInvalid) {
-
-    if (endState == sipcc::PeerConnectionImpl::kSignalingInvalid) {
+                 PCImplSignalingState endState =
+                   PCImplSignalingState::SignalingInvalid) {
+
+    if (endState == PCImplSignalingState::SignalingInvalid) {
       endState = (action == TestObserver::OFFER ?
-                  sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer :
-                  sipcc::PeerConnectionImpl::kSignalingStable);
+                  PCImplSignalingState::SignalingHaveRemoteOffer :
+                  PCImplSignalingState::SignalingStable);
     }
 
     pObserver->state = TestObserver::stateNoResponse;
     ASSERT_EQ(pc->SetRemoteDescription(action, remote.c_str()), NS_OK);
     ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse,
                      kDefaultTimeout);
     ASSERT_EQ(signaling_state(), endState);
     if (!ignoreError) {
       ASSERT_EQ(pObserver->state, TestObserver::stateSuccess);
     }
   }
 
   void SetLocal(TestObserver::Action action, std::string local,
                 bool ignoreError = false,
-                sipcc::PeerConnectionImpl::SignalingState endState =
-                  sipcc::PeerConnectionImpl::kSignalingInvalid) {
-
-    if (endState == sipcc::PeerConnectionImpl::kSignalingInvalid) {
+                PCImplSignalingState endState =
+                  PCImplSignalingState::SignalingInvalid) {
+
+    if (endState == PCImplSignalingState::SignalingInvalid) {
       endState = (action == TestObserver::OFFER ?
-                  sipcc::PeerConnectionImpl::kSignalingHaveLocalOffer :
-                  sipcc::PeerConnectionImpl::kSignalingStable);
+                  PCImplSignalingState::SignalingHaveLocalOffer :
+                  PCImplSignalingState::SignalingStable);
     }
 
     pObserver->state = TestObserver::stateNoResponse;
     ASSERT_EQ(pc->SetLocalDescription(action, local.c_str()), NS_OK);
     ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse,
                      kDefaultTimeout);
     ASSERT_EQ(signaling_state(), endState);
     if (!ignoreError) {
@@ -991,24 +999,22 @@ void CreateAnswer(sipcc::MediaConstraint
       }
     }
     ASSERT_TRUE_WAIT(pObserver->addIceSuccessCount == expectAddIce,
                      kDefaultTimeout);
   }
 
 
   bool IceCompleted() {
-    uint32_t state;
-    pc->GetIceState(&state);
-    return state == sipcc::PeerConnectionImpl::kIceConnected;
+    return pc->IceState() == PCImplIceState::IceConnected;
   }
 
   void AddIceCandidate(const char* candidate, const char* mid, unsigned short level,
                        bool expectSuccess) {
-    sipcc::PeerConnectionImpl::SignalingState endState = signaling_state();
+    PCImplSignalingState endState = signaling_state();
     pObserver->state = TestObserver::stateNoResponse;
     pc->AddIceCandidate(candidate, mid, level);
     ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse,
                      kDefaultTimeout);
     ASSERT_TRUE(pObserver->state ==
                 expectSuccess ? TestObserver::stateSuccess :
                                 TestObserver::stateError
                );
@@ -1673,17 +1679,16 @@ TEST_F(SignalingTest, CreateOfferNoAudio
               SHOULD_OMIT_AUDIO | SHOULD_SENDRECV_VIDEO);
 }
 
 TEST_F(SignalingTest, CreateOfferDontReceiveAudio)
 {
   sipcc::MediaConstraints constraints;
   constraints.setBooleanConstraint("OfferToReceiveAudio", false, false);
   constraints.setBooleanConstraint("OfferToReceiveVideo", true, false);
-  constraints.setBooleanConstraint("VoiceActivityDetection", true, true);
   CreateOffer(constraints, OFFER_AV,
               SHOULD_SEND_AUDIO | SHOULD_SENDRECV_VIDEO);
 }
 
 TEST_F(SignalingTest, CreateOfferDontReceiveVideo)
 {
   sipcc::MediaConstraints constraints;
   constraints.setBooleanConstraint("OfferToReceiveAudio", true, false);
@@ -2492,117 +2497,117 @@ TEST_F(SignalingTest, BigOValuesExtraCha
 
   std::string offer;
 
   CreateSDPForBigOTests(offer, "12345678901234567FOOBAR");
 
   // The signaling state will remain "stable" because the unparsable
   // SDP leads to a failure in SetRemoteDescription.
   a2_->SetRemote(TestObserver::OFFER, offer, true,
-                sipcc::PeerConnectionImpl::kSignalingStable);
+                 PCImplSignalingState::SignalingStable);
   ASSERT_TRUE(a2_->pObserver->state == TestObserver::stateError);
 }
 
 TEST_F(SignalingTest, BigOValuesTooBig)
 {
   EnsureInit();
 
   std::string offer;
 
   CreateSDPForBigOTests(offer, "18446744073709551615");
 
   // The signaling state will remain "stable" because the unparsable
   // SDP leads to a failure in SetRemoteDescription.
   a2_->SetRemote(TestObserver::OFFER, offer, true,
-                sipcc::PeerConnectionImpl::kSignalingStable);
+                 PCImplSignalingState::SignalingStable);
   ASSERT_TRUE(a2_->pObserver->state == TestObserver::stateError);
 }
 
 TEST_F(SignalingTest, SetLocalAnswerInStable)
 {
   EnsureInit();
 
   sipcc::MediaConstraints constraints;
   CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO);
 
   // The signaling state will remain "stable" because the
   // SetLocalDescription call fails.
   a1_->SetLocal(TestObserver::ANSWER, a1_->offer(), true,
-               sipcc::PeerConnectionImpl::kSignalingStable);
+                PCImplSignalingState::SignalingStable);
   ASSERT_EQ(a1_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kInvalidState);
 }
 
 TEST_F(SignalingTest, SetRemoteAnswerInStable) {
   EnsureInit();
 
   // The signaling state will remain "stable" because the
   // SetRemoteDescription call fails.
   a1_->SetRemote(TestObserver::ANSWER, strSampleSdpAudioVideoNoIce, true,
-                sipcc::PeerConnectionImpl::kSignalingStable);
+                PCImplSignalingState::SignalingStable);
   ASSERT_EQ(a1_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kInvalidState);
 }
 
 TEST_F(SignalingTest, SetLocalAnswerInHaveLocalOffer) {
   sipcc::MediaConstraints constraints;
   CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO);
   a1_->SetLocal(TestObserver::OFFER, a1_->offer());
   ASSERT_EQ(a1_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kNoError);
 
   // The signaling state will remain "have-local-offer" because the
   // SetLocalDescription call fails.
   a1_->SetLocal(TestObserver::ANSWER, a1_->offer(), true,
-               sipcc::PeerConnectionImpl::kSignalingHaveLocalOffer);
+                PCImplSignalingState::SignalingHaveLocalOffer);
   ASSERT_EQ(a1_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kInvalidState);
 }
 
 TEST_F(SignalingTest, SetRemoteOfferInHaveLocalOffer) {
   sipcc::MediaConstraints constraints;
   CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO);
   a1_->SetLocal(TestObserver::OFFER, a1_->offer());
   ASSERT_EQ(a1_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kNoError);
 
   // The signaling state will remain "have-local-offer" because the
   // SetRemoteDescription call fails.
   a1_->SetRemote(TestObserver::OFFER, a1_->offer(), true,
-                sipcc::PeerConnectionImpl::kSignalingHaveLocalOffer);
+                 PCImplSignalingState::SignalingHaveLocalOffer);
   ASSERT_EQ(a1_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kInvalidState);
 }
 
 TEST_F(SignalingTest, SetLocalOfferInHaveRemoteOffer) {
   sipcc::MediaConstraints constraints;
   CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO);
   a2_->SetRemote(TestObserver::OFFER, a1_->offer());
   ASSERT_EQ(a2_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kNoError);
 
   // The signaling state will remain "have-remote-offer" because the
   // SetLocalDescription call fails.
   a2_->SetLocal(TestObserver::OFFER, a1_->offer(), true,
-               sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer);
+                PCImplSignalingState::SignalingHaveRemoteOffer);
   ASSERT_EQ(a2_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kInvalidState);
 }
 
 TEST_F(SignalingTest, SetRemoteAnswerInHaveRemoteOffer) {
   sipcc::MediaConstraints constraints;
   CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO);
   a2_->SetRemote(TestObserver::OFFER, a1_->offer());
   ASSERT_EQ(a2_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kNoError);
 
   // The signaling state will remain "have-remote-offer" because the
   // SetRemoteDescription call fails.
   a2_->SetRemote(TestObserver::ANSWER, a1_->offer(), true,
-               sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer);
+               PCImplSignalingState::SignalingHaveRemoteOffer);
   ASSERT_EQ(a2_->pObserver->lastStatusCode,
             sipcc::PeerConnectionImpl::kInvalidState);
 }
 
 // Disabled until the spec adds a failure callback to addStream
 TEST_F(SignalingTest, DISABLED_AddStreamInHaveLocalOffer) {
   sipcc::MediaConstraints constraints;
   CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO);
@@ -2777,17 +2782,17 @@ TEST_F(SignalingTest, missingUfrag)
     "a=sendrecv\r\n";
 
   // Need to create an offer, since that's currently required by our
   // FSM. This may change in the future.
   a1_->CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
   a1_->SetLocal(TestObserver::OFFER, offer, true);
   // We now detect the missing ICE parameters at SetRemoteDescription
   a2_->SetRemote(TestObserver::OFFER, offer, true,
-    sipcc::PeerConnectionImpl::kSignalingStable);
+                 PCImplSignalingState::SignalingStable);
   ASSERT_TRUE(a2_->pObserver->state == TestObserver::stateError);
 }
 
 TEST_F(SignalingTest, AudioOnlyCalleeNoRtcpMux)
 {
   EnsureInit();
 
   sipcc::MediaConstraints constraints;
--- a/netwerk/base/public/security-prefs.js
+++ b/netwerk/base/public/security-prefs.js
@@ -47,18 +47,18 @@ pref("security.ssl3.ecdh_rsa_aes_128_sha
 pref("security.ssl3.ecdh_rsa_des_ede3_sha", true);
 pref("security.ssl3.ecdh_rsa_rc4_128_sha", true);
 pref("security.ssl3.dhe_rsa_aes_128_sha", true);
 pref("security.ssl3.dhe_dss_aes_128_sha", true);
 pref("security.ssl3.rsa_aes_128_sha", true);
 pref("security.ssl3.dhe_rsa_des_ede3_sha", true);
 pref("security.ssl3.dhe_dss_des_ede3_sha", true);
 pref("security.ssl3.rsa_seed_sha", true);
-pref("security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256", true);
-pref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", true);
+pref("security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256", false);
+pref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", false);
 
 pref("security.default_personal_cert",   "Ask Every Time");
 pref("security.remember_cert_checkbox_default_setting", true);
 pref("security.ask_for_password",        0);
 pref("security.password_lifetime",       30);
 
 pref("security.OCSP.enabled", 1);
 pref("security.OCSP.require", false);
--- a/netwerk/cache2/CacheEntry.cpp
+++ b/netwerk/cache2/CacheEntry.cpp
@@ -95,17 +95,17 @@ CacheEntry::CacheEntry(const nsACString&
 CacheEntry::~CacheEntry()
 {
   ProxyReleaseMainThread(mURI);
 
   LOG(("CacheEntry::~CacheEntry [this=%p]", this));
   MOZ_COUNT_DTOR(CacheEntry);
 }
 
-#ifdef MOZ_LOGGING
+#ifdef PR_LOG
 
 char const * CacheEntry::StateString(uint32_t aState)
 {
   switch (aState) {
   case NOTLOADED:     return "NOTLOADED";
   case LOADING:       return "LOADING";
   case EMPTY:         return "EMPTY";
   case WRITING:       return "WRITING";
--- a/netwerk/cache2/CacheEntry.h
+++ b/netwerk/cache2/CacheEntry.h
@@ -237,17 +237,17 @@ private:
   // true: after load and an existing file, or after output stream has been opened.
   //       note - when opening an input stream, and this flag is false, output stream
   //       is open along ; this makes input streams on new entries behave correctly
   //       when EOF is reached (WOULD_BLOCK is returned).
   // false: after load and a new file, or dropped to back to false when a writer
   //        fails to open an output stream.
   bool mHasData : 1;
 
-#ifdef MOZ_LOGGING
+#ifdef PR_LOG
   static char const * StateString(uint32_t aState);
 #endif
 
   enum EState {      // transiting to:
     NOTLOADED = 0,   // -> LOADING | EMPTY
     LOADING = 1,     // -> EMPTY | READY
     EMPTY = 2,       // -> WRITING
     WRITING = 3,     // -> EMPTY | READY
--- a/testing/marionette/client/marionette/__init__.py
+++ b/testing/marionette/client/marionette/__init__.py
@@ -6,8 +6,9 @@ from gestures import *
 from by import By
 from marionette import Marionette, HTMLElement, Actions, MultiActions
 from marionette_test import MarionetteTestCase, CommonTestCase, expectedFailure, skip, SkipTest
 from emulator import Emulator
 from runtests import MarionetteTestResult
 from runtests import MarionetteTestRunner
 from runtests import MarionetteTestOptions
 from runtests import MarionetteTextTestRunner
+from errors import *
--- a/testing/marionette/client/marionette/tests/unit/test_clearing.py
+++ b/testing/marionette/client/marionette/tests/unit/test_clearing.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import InvalidElementStateException
+from marionette import InvalidElementStateException
 
 class TestClear(MarionetteTestCase):
     def testWriteableTextInputShouldClear(self):
         test_html = self.marionette.absolute_url("test_clearing.html")
         self.marionette.navigate(test_html)
         element = self.marionette.find_element("id", "writableTextInput")
         element.clear()
         self.assertEqual("", element.get_attribute("value"))
--- a/testing/marionette/client/marionette/tests/unit/test_element_touch.py
+++ b/testing/marionette/client/marionette/tests/unit/test_element_touch.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import MarionetteException
+from marionette import MarionetteException
 
 class testElementTouch(MarionetteTestCase):
     def test_touch(self):
       testAction = self.marionette.absolute_url("testAction.html")
       self.marionette.navigate(testAction)
       button = self.marionette.find_element("id", "button1")
       button.tap()
       expected = "button1-touchstart-touchend-mousemove-mousedown-mouseup-click"
--- a/testing/marionette/client/marionette/tests/unit/test_emulator.py
+++ b/testing/marionette/client/marionette/tests/unit/test_emulator.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import JavascriptException, MarionetteException
+from marionette import JavascriptException, MarionetteException
 
 
 class TestEmulatorContent(MarionetteTestCase):
 
     def test_emulator_cmd(self):
         self.marionette.set_script_timeout(10000)
         expected = ["<build>",
                     "OK"]
--- a/testing/marionette/client/marionette/tests/unit/test_execute_async_script.py
+++ b/testing/marionette/client/marionette/tests/unit/test_execute_async_script.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import JavascriptException, MarionetteException, ScriptTimeoutException
+from marionette import JavascriptException, MarionetteException, ScriptTimeoutException
 import time
 
 
 class TestExecuteAsyncContent(MarionetteTestCase):
     def setUp(self):
         super(TestExecuteAsyncContent, self).setUp()
         self.marionette.set_script_timeout(1000)
 
--- a/testing/marionette/client/marionette/tests/unit/test_execute_isolate.py
+++ b/testing/marionette/client/marionette/tests/unit/test_execute_isolate.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase, skip_if_b2g
-from errors import JavascriptException, MarionetteException, ScriptTimeoutException
+from marionette import JavascriptException, MarionetteException, ScriptTimeoutException
 
 class TestExecuteIsolationContent(MarionetteTestCase):
     def setUp(self):
         super(TestExecuteIsolationContent, self).setUp()
         self.content = True
 
     def test_execute_async_isolate(self):
         # Results from one execute call that has timed out should not
--- a/testing/marionette/client/marionette/tests/unit/test_execute_script.py
+++ b/testing/marionette/client/marionette/tests/unit/test_execute_script.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import JavascriptException, MarionetteException
+from marionette import JavascriptException, MarionetteException
 
 class TestExecuteContent(MarionetteTestCase):
     def test_stack_trace(self):
         try:
             self.marionette.execute_script("""
                 let a = 1;
                 return b;
                 """)
--- a/testing/marionette/client/marionette/tests/unit/test_findelement.py
+++ b/testing/marionette/client/marionette/tests/unit/test_findelement.py
@@ -1,16 +1,16 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
 from marionette import HTMLElement
 from by import By
-from errors import NoSuchElementException
+from marionette import NoSuchElementException
 
 
 class TestElements(MarionetteTestCase):
     def test_id(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementById('mozLink');")
         found_el = self.marionette.find_element(By.ID, "mozLink")
--- a/testing/marionette/client/marionette/tests/unit/test_findelement_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_findelement_chrome.py
@@ -1,16 +1,16 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
 from marionette import HTMLElement
 from by import By
-from errors import NoSuchElementException
+from marionette import NoSuchElementException
 
 
 class TestElementsChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
         self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
--- a/testing/marionette/client/marionette/tests/unit/test_implicit_waits.py
+++ b/testing/marionette/client/marionette/tests/unit/test_implicit_waits.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import NoSuchElementException
+from marionette import NoSuchElementException
 
 class TestImplicitWaits(MarionetteTestCase):
     def testShouldImplicitlyWaitForASingleElement(self):
         test_html = self.marionette.absolute_url("test_dynamic.html")
         self.marionette.navigate(test_html)
         add = self.marionette.find_element("id", "adder")
         self.marionette.set_search_timeout("3000")
         add.click()
--- a/testing/marionette/client/marionette/tests/unit/test_navigation.py
+++ b/testing/marionette/client/marionette/tests/unit/test_navigation.py
@@ -1,15 +1,15 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import MarionetteException
-from errors import TimeoutException
+from marionette import MarionetteException
+from marionette import TimeoutException
 
 class TestNavigate(MarionetteTestCase):
     def test_navigate(self):
         self.assertTrue(self.marionette.execute_script("window.location.href = 'about:blank'; return true;"))
         self.assertEqual("about:blank", self.marionette.execute_script("return window.location.href;"))
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         self.assertNotEqual("about:blank", self.marionette.execute_script("return window.location.href;"))
--- a/testing/marionette/client/marionette/tests/unit/test_simpletest_sanity.py
+++ b/testing/marionette/client/marionette/tests/unit/test_simpletest_sanity.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import JavascriptException, MarionetteException, ScriptTimeoutException
+from marionette import JavascriptException, MarionetteException, ScriptTimeoutException
 
 class SimpletestSanityTest(MarionetteTestCase):
 
     callFinish = "return finish();"
 
     def test_is(self):
         def runtests():
             sentFail1 = "is(true, false, 'isTest1', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish
--- a/testing/marionette/client/marionette/tests/unit/test_single_finger.py
+++ b/testing/marionette/client/marionette/tests/unit/test_single_finger.py
@@ -1,15 +1,15 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
 from marionette import Actions
-from errors import MarionetteException
+from marionette import MarionetteException
 #add this directory to the path
 import os
 import sys
 sys.path.append(os.path.dirname(__file__))
 from single_finger_functions import *
 
 class testSingleFinger(MarionetteTestCase):
     def test_press_release(self):
--- a/testing/marionette/client/marionette/tests/unit/test_single_finger_desktop.py
+++ b/testing/marionette/client/marionette/tests/unit/test_single_finger_desktop.py
@@ -1,11 +1,11 @@
 from marionette_test import MarionetteTestCase
 from marionette import Actions
-from errors import MarionetteException
+from marionette import MarionetteException
 #add this directory to the path
 import os
 import sys
 sys.path.append(os.path.dirname(__file__))
 from single_finger_functions import *
 
 class testSingleFingerMouse(MarionetteTestCase):
     def setUp(self):
--- a/testing/marionette/client/marionette/tests/unit/test_specialpowers.py
+++ b/testing/marionette/client/marionette/tests/unit/test_specialpowers.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import JavascriptException, MarionetteException
+from marionette import JavascriptException, MarionetteException
 
 class TestSpecialPowersContent(MarionetteTestCase):
 
     testpref = "testing.marionette.contentcharpref"
     testvalue = "blabla"
 
     def test_prefs(self):
         result = self.marionette.execute_script("""
--- a/testing/marionette/client/marionette/tests/unit/test_switch_frame.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_frame.py
@@ -1,15 +1,15 @@
 # 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/.
 
 import os
 from marionette_test import MarionetteTestCase
-from errors import JavascriptException
+from marionette import JavascriptException
 
 # boiler plate for the initial navigation and frame switch
 def switch_to_window_verify(test, start_url, frame, verify_title, verify_url):
     test.assertTrue(test.marionette.execute_script("window.location.href = 'about:blank'; return true;"))
     test.assertEqual("about:blank", test.marionette.execute_script("return window.location.href;"))
     test_html = test.marionette.absolute_url(start_url)
     test.marionette.navigate(test_html)
     test.assertEqual(test.marionette.get_active_frame(), None)
--- a/testing/marionette/client/marionette/tests/unit/test_switch_frame_b2g.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_frame_b2g.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import *
+from marionette import *
 
 class TestGaiaLaunch(MarionetteTestCase):
     """Trivial example of launching a Gaia app, entering its context and performing some test on it.
     """
 
     def test_launch_app(self):
         # Launch a Gaia app; see CommonTestCase.launch_gaia_app in 
         # marionette.py for implementation.  This returns an HTMLElement
--- a/testing/marionette/client/marionette/tests/unit/test_switch_frame_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_frame_chrome.py
@@ -1,14 +1,14 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
-from errors import JavascriptException
+from marionette import JavascriptException
 
 class TestSwitchFrameChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
         self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
         self.marionette.switch_to_window('foo')
--- a/testing/marionette/client/marionette/tests/unit/test_timeouts.py
+++ b/testing/marionette/client/marionette/tests/unit/test_timeouts.py
@@ -1,16 +1,16 @@
 # 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/.
 
 import os
 from marionette_test import MarionetteTestCase
 from marionette import HTMLElement
-from errors import NoSuchElementException, JavascriptException, MarionetteException, ScriptTimeoutException
+from marionette import NoSuchElementException, JavascriptException, MarionetteException, ScriptTimeoutException
 
 class TestTimeouts(MarionetteTestCase):
     def test_pagetimeout_notdefinetimeout_pass(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
 
     def test_pagetimeout_fail(self):
         self.marionette.timeouts("page load", 0)
--- a/testing/marionette/client/marionette/tests/unit/test_typing.py
+++ b/testing/marionette/client/marionette/tests/unit/test_typing.py
@@ -1,15 +1,15 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
 from keys import Keys
-from errors import ElementNotVisibleException
+from marionette import ElementNotVisibleException
 
 
 class TestTyping(MarionetteTestCase):
 
     def testShouldFireKeyPressEvents(self):
         test_html = self.marionette.absolute_url("javascriptPage.html")
         self.marionette.navigate(test_html)
         keyReporter = self.marionette.find_element("id", "keyReporter")
--- a/testing/mozbase/mozcrash/mozcrash/mozcrash.py
+++ b/testing/mozbase/mozcrash/mozcrash/mozcrash.py
@@ -1,28 +1,30 @@
 # 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/.
 
-__all__ = ['check_for_crashes']
+__all__ = ['check_for_crashes',
+           'check_for_java_exception']
 
 import glob
 import mozlog
 import os
 import re
 import shutil
 import subprocess
 import sys
 import tempfile
 import urllib2
 import zipfile
 
 from mozfile import extract_zip
 from mozfile import is_url
 
+
 def check_for_crashes(dump_directory, symbols_path,
                       stackwalk_binary=None,
                       dump_save_path=None,
                       test_name=None,
                       quiet=False):
     """
     Print a stack trace for minidump files left behind by a crashing program.
 
@@ -147,8 +149,49 @@ def check_for_crashes(dump_directory, sy
             extra = os.path.splitext(d)[0] + ".extra"
             if os.path.exists(extra):
                 os.remove(extra)
     finally:
         if remove_symbols:
             shutil.rmtree(symbols_path)
 
     return True
+
+
+def check_for_java_exception(logcat):
+    """
+    Print a summary of a fatal Java exception, if present in the provided
+    logcat output.
+
+    Example:
+    PROCESS-CRASH | java-exception | java.lang.NullPointerException at org.mozilla.gecko.GeckoApp$21.run(GeckoApp.java:1833)
+
+    `logcat` should be a list of strings.
+
+    Returns True if a fatal Java exception was found, False otherwise.
+    """
+    found_exception = False
+
+    for i, line in enumerate(logcat):
+        # Logs will be of form:
+        #
+        # 01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> REPORTING UNCAUGHT EXCEPTION FROM THREAD 9 ("GeckoBackgroundThread")
+        # 01-30 20:15:41.937 E/GeckoAppShell( 1703): java.lang.NullPointerException
+        # 01-30 20:15:41.937 E/GeckoAppShell( 1703): 	at org.mozilla.gecko.GeckoApp$21.run(GeckoApp.java:1833)
+        # 01-30 20:15:41.937 E/GeckoAppShell( 1703): 	at android.os.Handler.handleCallback(Handler.java:587)
+        if "REPORTING UNCAUGHT EXCEPTION" in line or "FATAL EXCEPTION" in line:
+            # Strip away the date, time, logcat tag and pid from the next two lines and
+            # concatenate the remainder to form a concise summary of the exception.
+            found_exception = True
+            if len(logcat) >= i + 3:
+                logre = re.compile(r".*\): \t?(.*)")
+                m = logre.search(logcat[i+1])
+                if m and m.group(1):
+                    exception_type = m.group(1)
+                m = logre.search(logcat[i+2])
+                if m and m.group(1):
+                    exception_location = m.group(1)
+                print "PROCESS-CRASH | java-exception | %s %s" % (exception_type, exception_location)
+            else:
+                print "Automation Error: Logcat is truncated!"
+            break
+
+    return found_exception
--- a/testing/mozbase/mozcrash/setup.py
+++ b/testing/mozbase/mozcrash/setup.py
@@ -1,15 +1,15 @@
 # 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/.
 
 from setuptools import setup
 
-PACKAGE_VERSION = '0.8'
+PACKAGE_VERSION = '0.9'
 
 # dependencies
 deps = ['mozfile >= 0.3',
         'mozlog']
 
 setup(name='mozcrash',
       version=PACKAGE_VERSION,
       description="Library for printing stack traces from minidumps left behind by crashed processes",
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -327,16 +327,18 @@ endif
 GECKO_APP_AP_PATH = $(call core_abspath,$(DEPTH)/mobile/android/base)
 
 ifdef ENABLE_TESTS
 INNER_ROBOCOP_PACKAGE=echo
 INNER_BACKGROUND_TESTS_PACKAGE=echo
 ifeq ($(MOZ_BUILD_APP),mobile/android)
 UPLOAD_EXTRA_FILES += robocop.apk
 UPLOAD_EXTRA_FILES += fennec_ids.txt
+UPLOAD_EXTRA_FILES += geckoview_library/geckoview_library.zip
+UPLOAD_EXTRA_FILES += geckoview_library/geckoview_assets.zip
 
 # Robocop/Robotium tests, Android Background tests, and Fennec need to
 # be signed with the same key, which means release signing them all.
 
 # $(1) is the full path to input:  foo-debug-unsigned-unaligned.apk.
 # $(2) is the full path to output: foo.apk.
 RELEASE_SIGN_ANDROID_APK = \
   cp $(1) $(2)-unaligned.apk && \