Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 13 Mar 2015 15:52:19 -0400
changeset 233576 977add19414a6d3885eaab96f50d8d21a5239b23
parent 233575 76aab45b330046bb50e23914e0238690f95673d8 (diff)
parent 233519 d252d310ca5c498e1143dd4b2417afdeab4135d1 (current diff)
child 233582 3532d6c1dd8e6e0ba72d32df591080bfe0de2dda
push id28417
push userryanvm@gmail.com
push dateFri, 13 Mar 2015 19:52:44 +0000
treeherdermozilla-central@977add19414a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.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=merge
testing/mozharness/mozharness.json
--- a/browser/base/content/aboutDialog.css
+++ b/browser/base/content/aboutDialog.css
@@ -18,26 +18,26 @@
   background-position: 100% 0;
 }
 
 #bottomBox {
   padding: 15px 10px 0;
 }
 
 #version {
+  font-weight: bold;
   margin-top: 10px;
   -moz-margin-start: 0;
   -moz-user-select: text;
   -moz-user-focus: normal;
   cursor: text;
 }
 
 #distribution,
 #distributionId {
-  font-weight: bold;
   display: none;
   margin-top: 0;
   margin-bottom: 0;
 }
 
 .text-blurb {
   margin-bottom: 10px;
   -moz-margin-start: 0;
--- a/browser/devtools/debugger/test/browser_dbg_parser-09.js
+++ b/browser/devtools/debugger/test/browser_dbg_parser-09.js
@@ -24,267 +24,267 @@ function test() {
     is(info.loc.start.toSource(), { line: sline, column: scol }.toSource(),
       "The start location was correct for the identifier in: '" + source + "'.");
     is(info.loc.end.toSource(), { line: eline, column: ecol }.toSource(),
       "The end location was correct for the identifier in: '" + source + "'.");
   }
 
   // VariableDeclarator
 
-  verify("var foo=()=>{}", e => e.type == "ArrowExpression", {
+  verify("var foo=()=>{}", e => e.type == "ArrowFunctionExpression", {
     name: "foo",
     chain: null,
     loc: [[1, 4], [1, 7]]
   });
-  verify("\nvar\nfoo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression", {
+  verify("\nvar\nfoo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression", {
     name: "foo",
     chain: null,
     loc: [[3, 0], [3, 3]]
   });
 
   // AssignmentExpression
 
-  verify("foo=()=>{}", e => e.type == "ArrowExpression",
+  verify("foo=()=>{}", e => e.type == "ArrowFunctionExpression",
     { name: "foo", chain: [], loc: [[1, 0], [1, 3]] });
 
-  verify("\nfoo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
+  verify("\nfoo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "foo", chain: [], loc: [[2, 0], [2, 3]] });
 
-  verify("foo.bar=()=>{}", e => e.type == "ArrowExpression",
+  verify("foo.bar=()=>{}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo"], loc: [[1, 0], [1, 7]] });
 
-  verify("\nfoo.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
+  verify("\nfoo.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo"], loc: [[2, 0], [2, 7]] });
 
-  verify("this.foo=()=>{}", e => e.type == "ArrowExpression",
+  verify("this.foo=()=>{}", e => e.type == "ArrowFunctionExpression",
     { name: "foo", chain: ["this"], loc: [[1, 0], [1, 8]] });
 
-  verify("\nthis.foo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
+  verify("\nthis.foo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "foo", chain: ["this"], loc: [[2, 0], [2, 8]] });
 
-  verify("this.foo.bar=()=>{}", e => e.type == "ArrowExpression",
+  verify("this.foo.bar=()=>{}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["this", "foo"], loc: [[1, 0], [1, 12]] });
 
-  verify("\nthis.foo.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
+  verify("\nthis.foo.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["this", "foo"], loc: [[2, 0], [2, 12]] });
 
-  verify("foo.this.bar=()=>{}", e => e.type == "ArrowExpression",
+  verify("foo.this.bar=()=>{}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo", "this"], loc: [[1, 0], [1, 12]] });
 
-  verify("\nfoo.this.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
+  verify("\nfoo.this.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo", "this"], loc: [[2, 0], [2, 12]] });
 
   // ObjectExpression
 
-  verify("({foo:()=>{}})", e => e.type == "ArrowExpression",
+  verify("({foo:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "foo", chain: [], loc: [[1, 2], [1, 5]] });
 
-  verify("(\n{\nfoo\n:\n(\n)\n=>\n{\n}\n}\n)", e => e.type == "ArrowExpression",
+  verify("(\n{\nfoo\n:\n(\n)\n=>\n{\n}\n}\n)", e => e.type == "ArrowFunctionExpression",
     { name: "foo", chain: [], loc: [[3, 0], [3, 3]] });
 
-  verify("({foo:{bar:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("({foo:{bar:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo"], loc: [[1, 7], [1, 10]] });
 
-  verify("(\n{\nfoo\n:\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n}\n)", e => e.type == "ArrowExpression",
+  verify("(\n{\nfoo\n:\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n}\n)", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] });
 
   // AssignmentExpression + ObjectExpression
 
-  verify("foo={bar:()=>{}}", e => e.type == "ArrowExpression",
+  verify("foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo"], loc: [[1, 5], [1, 8]] });
 
-  verify("\nfoo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nfoo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo"], loc: [[5, 0], [5, 3]] });
 
-  verify("foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
+  verify("foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["foo", "bar"], loc: [[1, 10], [1, 13]] });
 
-  verify("\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["foo", "bar"], loc: [[8, 0], [8, 3]] });
 
-  verify("nested.foo={bar:()=>{}}", e => e.type == "ArrowExpression",
+  verify("nested.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["nested", "foo"], loc: [[1, 12], [1, 15]] });
 
-  verify("\nnested.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nnested.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["nested", "foo"], loc: [[5, 0], [5, 3]] });
 
-  verify("nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
+  verify("nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["nested", "foo", "bar"], loc: [[1, 17], [1, 20]] });
 
-  verify("\nnested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nnested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["nested", "foo", "bar"], loc: [[8, 0], [8, 3]] });
 
-  verify("this.foo={bar:()=>{}}", e => e.type == "ArrowExpression",
+  verify("this.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["this", "foo"], loc: [[1, 10], [1, 13]] });
 
-  verify("\nthis.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nthis.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["this", "foo"], loc: [[5, 0], [5, 3]] });
 
-  verify("this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
+  verify("this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["this", "foo", "bar"], loc: [[1, 15], [1, 18]] });
 
-  verify("\nthis.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nthis.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["this", "foo", "bar"], loc: [[8, 0], [8, 3]] });
 
-  verify("this.nested.foo={bar:()=>{}}", e => e.type == "ArrowExpression",
+  verify("this.nested.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["this", "nested", "foo"], loc: [[1, 17], [1, 20]] });
 
-  verify("\nthis.nested.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nthis.nested.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["this", "nested", "foo"], loc: [[5, 0], [5, 3]] });
 
-  verify("this.nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
+  verify("this.nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[1, 22], [1, 25]] });
 
-  verify("\nthis.nested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nthis.nested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[8, 0], [8, 3]] });
 
-  verify("nested.this.foo={bar:()=>{}}", e => e.type == "ArrowExpression",
+  verify("nested.this.foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["nested", "this", "foo"], loc: [[1, 17], [1, 20]] });
 
-  verify("\nnested.this.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nnested.this.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["nested", "this", "foo"], loc: [[5, 0], [5, 3]] });
 
-  verify("nested.this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
+  verify("nested.this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[1, 22], [1, 25]] });
 
-  verify("\nnested.this.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nnested.this.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[8, 0], [8, 3]] });
 
   // VariableDeclarator + AssignmentExpression + ObjectExpression
 
-  verify("let foo={bar:()=>{}}", e => e.type == "ArrowExpression",
+  verify("let foo={bar:()=>{}}", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo"], loc: [[1, 9], [1, 12]] });
 
-  verify("\nlet\nfoo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nlet\nfoo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] });
 
-  verify("let foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
+  verify("let foo={bar:{baz:()=>{}}}", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["foo", "bar"], loc: [[1, 14], [1, 17]] });
 
-  verify("\nlet\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
+  verify("\nlet\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["foo", "bar"], loc: [[9, 0], [9, 3]] });
 
   // New/CallExpression + AssignmentExpression + ObjectExpression
 
-  verify("foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[1, 5], [1, 8]] });
 
-  verify("\nfoo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nfoo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
 
-  verify("foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[1, 10], [1, 13]] });
 
-  verify("\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
 
-  verify("nested.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[1, 12], [1, 15]] });
 
-  verify("\nnested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nnested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
 
-  verify("nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[1, 17], [1, 20]] });
 
-  verify("\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
 
-  verify("this.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[1, 10], [1, 13]] });
 
-  verify("\nthis.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nthis.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
 
-  verify("this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[1, 15], [1, 18]] });
 
-  verify("\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
 
-  verify("this.nested.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("this.nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[1, 17], [1, 20]] });
 
-  verify("\nthis.nested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nthis.nested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
 
-  verify("this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] });
 
-  verify("\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
 
-  verify("nested.this.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("nested.this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[1, 17], [1, 20]] });
 
-  verify("\nnested.this.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nnested.this.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
 
-  verify("nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] });
 
-  verify("\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
 
   // New/CallExpression + VariableDeclarator + AssignmentExpression + ObjectExpression
 
-  verify("let target=foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("let target=foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[1, 16], [1, 19]] });
 
-  verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
 
-  verify("let target=foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("let target=foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[1, 21], [1, 24]] });
 
-  verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
 
-  verify("let target=nested.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("let target=nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[1, 23], [1, 26]] });
 
-  verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
 
-  verify("let target=nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("let target=nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[1, 28], [1, 31]] });
 
-  verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
 
-  verify("let target=this.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("let target=this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[1, 21], [1, 24]] });
 
-  verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
 
-  verify("let target=this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("let target=this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[1, 26], [1, 29]] });
 
-  verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
 
-  verify("let target=this.nested.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("let target=this.nested.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] });
 
-  verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
 
-  verify("let target=this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("let target=this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] });
 
-  verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
 
-  verify("let target=nested.this.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
+  verify("let target=nested.this.foo({bar:()=>{}})", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] });
 
-  verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
 
-  verify("let target=nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
+  verify("let target=nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] });
 
-  verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
+  verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowFunctionExpression",
     { name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
 
   finish();
 }
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -738,17 +738,17 @@ var Scratchpad = {
         break;
 
       case "VariableDeclaration":
         for (let decl of statement.declarations) {
           if (!decl.init) {
             continue;
           }
           if ((decl.init.type == "FunctionExpression"
-               || decl.init.type == "ArrowExpression")
+               || decl.init.type == "ArrowFunctionExpression")
               && this._containsCursor(decl.loc, aCursorPos)) {
             return decl;
           }
         }
         break;
       }
     }
 
--- a/browser/devtools/shared/Parser.jsm
+++ b/browser/devtools/shared/Parser.jsm
@@ -371,17 +371,17 @@ SyntaxTree.prototype = {
           });
         }
       },
 
       /**
        * Callback invoked for each arrow expression node.
        * @param Node aNode
        */
-      onArrowExpression: function(aNode) {
+      onArrowFunctionExpression: function(aNode) {
         // Infer the function's name from an enclosing syntax tree node.
         let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(aNode);
         let inferredName = inferredInfo.name;
         let inferredChain = inferredInfo.chain;
         let inferredLocation = inferredInfo.loc;
 
         // Current node may be part of a larger assignment expression stack.
         if (aNode._parent.type == "AssignmentExpression") {
@@ -1617,39 +1617,39 @@ let SyntaxTreeVisitor = {
       this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
     }
     this[aNode.body.type](aNode.body, aNode, aCallbacks);
   },
 
   /**
    * An arrow expression.
    *
-   * interface ArrowExpression <: Function, Expression {
-   *   type: "ArrowExpression";
+   * interface ArrowFunctionExpression <: Function, Expression {
+   *   type: "ArrowFunctionExpression";
    *   params: [ Pattern ];
    *   defaults: [ Expression ];
    *   rest: Identifier | null;
    *   body: BlockStatement | Expression;
    *   generator: boolean;
    *   expression: boolean;
    * }
    */
-  ArrowExpression: function(aNode, aParent, aCallbacks) {
+  ArrowFunctionExpression: function(aNode, aParent, aCallbacks) {
     aNode._parent = aParent;
 
     if (this.break) {
       return;
     }
     if (aCallbacks.onNode) {
       if (aCallbacks.onNode(aNode, aParent) === false) {
         return;
       }
     }
-    if (aCallbacks.onArrowExpression) {
-      aCallbacks.onArrowExpression(aNode);
+    if (aCallbacks.onArrowFunctionExpression) {
+      aCallbacks.onArrowFunctionExpression(aNode);
     }
     for (let param of aNode.params) {
       this[param.type](param, aNode, aCallbacks);
     }
     for (let _default of aNode.defaults) {
       this[_default.type](_default, aNode, aCallbacks);
     }
     if (aNode.rest) {
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -8,16 +8,18 @@ let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 this.EXPORTED_SYMBOLS = [ "AboutHomeUtils", "AboutHome" ];
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+  "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
   "resource://gre/modules/FxAccounts.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 
 // Url to fetch snippets, in the urlFormatter service format.
@@ -45,20 +47,20 @@ this.AboutHomeUtils = {
     try {
       return !Services.prefs.getBoolPref("browser.rights.override");
     } catch (e) { }
     // Ditto, for the legacy EULA pref.
     try {
       return !Services.prefs.getBoolPref("browser.EULA.override");
     } catch (e) { }
 
-#ifndef MOZILLA_OFFICIAL
-    // Non-official builds shouldn't show the notification.
-    return false;
-#endif
+    if (!AppConstants.MOZILLA_OFFICIAL) {
+      // Non-official builds shouldn't show the notification.
+      return false;
+    }
 
     // Look to see if the user has seen the current version or not.
     var currentVersion = Services.prefs.getIntPref("browser.rights.version");
     try {
       return !Services.prefs.getBoolPref("browser.rights." + currentVersion + ".shown");
     } catch (e) { }
 
     // Legacy: If the user accepted a EULA, we won't annoy them with the
@@ -188,19 +190,20 @@ let AboutHome = {
         }
 
         Services.search.init(function(status) {
           if (!Components.isSuccessCode(status)) {
             return;
           }
 
           let engine = Services.search.currentEngine;
-#ifdef MOZ_SERVICES_HEALTHREPORT
-          window.BrowserSearch.recordSearchInHealthReport(engine, "abouthome", data.selection);
-#endif
+          if (AppConstants.MOZ_SERVICES_HEALTHREPORT) {
+            window.BrowserSearch.recordSearchInHealthReport(engine, "abouthome", data.selection);
+          }
+
           // Trigger a search through nsISearchEngine.getSubmission()
           let submission = engine.getSubmission(data.searchTerms, null, "homepage");
           let where = data.useNewTab ? "tab" : "current";
           window.openUILinkIn(submission.uri.spec, where, false,
                               submission.postData);
 
           // Used for testing
           let mm = aMessage.target.messageManager;
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -1,12 +1,11 @@
-# -*- indent-tabs-mode: nil; js-indent-level: 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 Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 this.EXPORTED_SYMBOLS = [ "PluginContent" ];
@@ -16,16 +15,19 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/BrowserUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
   const url = "chrome://browser/locale/browser.properties";
   return Services.strings.createBundle(url);
 });
 
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+  "resource://gre/modules/AppConstants.jsm");
+
 this.PluginContent = function (global) {
   this.init(global);
 }
 
 PluginContent.prototype = {
   init: function (global) {
     this.global = global;
     // Need to hold onto the content window or else it'll get destroyed
@@ -512,34 +514,35 @@ PluginContent.prototype = {
       objLoadingContent.cancelPlayPreview();
   },
 
   // Forward a link click callback to the chrome process.
   forwardCallback: function (name) {
     this.global.sendAsyncMessage("PluginContent:LinkClickCallback", { name: name });
   },
 
-#ifdef MOZ_CRASHREPORTER
   submitReport: function submitReport(pluginDumpID, browserDumpID, plugin) {
+    if (!AppConstants.MOZ_CRASHREPORTER) {
+      return;
+    }
     let keyVals = {};
     if (plugin) {
       let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
       if (userComment)
         keyVals.PluginUserComment = userComment;
       if (this.getPluginUI(plugin, "submitURLOptIn").checked)
         keyVals.PluginContentURL = plugin.ownerDocument.URL;
     }
 
     this.global.sendAsyncMessage("PluginContent:SubmitReport", {
       pluginDumpID: pluginDumpID,
       browserDumpID: browserDumpID,
       keyVals: keyVals,
     });
   },
-#endif
 
   reloadPage: function () {
     this.global.content.location.reload();
   },
 
   // Event listener for click-to-play plugins.
   _handleClickToPlayEvent: function (plugin) {
     let doc = plugin.ownerDocument;
@@ -880,17 +883,16 @@ PluginContent.prototype = {
         return;
       }
       // doPrompt is specific to the crashed plugin overlay, and
       // therefore is not applicable for window-global plugins.
       doPrompt = false;
     }
 
     let status;
-#ifdef MOZ_CRASHREPORTER
     // Determine which message to show regarding crash reports.
     if (submittedReport) { // submitReports && !doPrompt, handled in observer
       status = "submitted";
     }
     else if (!submitReports && !doPrompt) {
       status = "noSubmit";
     }
     else if (!pluginDumpID) {
@@ -906,17 +908,17 @@ PluginContent.prototype = {
     // This can happen if the plugin is killed from the task manager.
     if (!pluginDumpID) {
         status = "noReport";
     }
 
     // If we're showing the link to manually trigger report submission, we'll
     // want to be able to update all the instances of the UI for this crash to
     // show an updated message when a report is submitted.
-    if (doPrompt) {
+    if (AppConstants.MOZ_CRASHREPORTER && doPrompt) {
       let observer = {
         QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                                Ci.nsISupportsWeakReference]),
         observe : (subject, topic, data) => {
           let propertyBag = subject;
           if (!(propertyBag instanceof Ci.nsIPropertyBag2))
             return;
           // Ignore notifications for other crashes.
@@ -935,17 +937,16 @@ PluginContent.prototype = {
       Services.obs.addObserver(observer, "crash-report-status", true);
       // ...alas, now we need something to hold a strong reference to prevent
       // it from being GC. But I don't want to manually manage the reference's
       // lifetime (which should be no greater than the page).
       // Clever solution? Use a closue with an event listener on the document.
       // When the doc goes away, so do the listener references and the closure.
       doc.addEventListener("mozCleverClosureHack", observer, false);
     }
-#endif
 
     let isShowing = false;
 
     if (plugin) {
       // If there's no plugin (an <object> or <embed> element), this call is
       // for a window-global plugin. In this case, there's no overlay to show.
       isShowing = _setUpPluginOverlay.call(this, plugin, doPrompt);
     }
--- a/browser/modules/RecentWindow.jsm
+++ b/browser/modules/RecentWindow.jsm
@@ -3,25 +3,20 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["RecentWindow"];
 
 const Cu = Components.utils;
 
+Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-#define BROKEN_WM_Z_ORDER
-#endif
-#endif
-
 this.RecentWindow = {
   /*
    * Get the most recent browser window.
    *
    * @param aOptions an object accepting the arguments for the search.
    *        * private: true to restrict the search to private windows
    *            only, false to restrict the search to non-private only.
    *            Omit the property to search in both groups.
@@ -36,35 +31,38 @@ this.RecentWindow = {
     function isSuitableBrowserWindow(win) {
       return (!win.closed &&
               (allowPopups || win.toolbar.visible) &&
               (!checkPrivacy ||
                PrivateBrowsingUtils.permanentPrivateBrowsing ||
                PrivateBrowsingUtils.isWindowPrivate(win) == aOptions.private));
     }
 
-#ifdef BROKEN_WM_Z_ORDER
-    let win = Services.wm.getMostRecentWindow("navigator:browser");
+    let broken_wm_z_order =
+      AppConstants.platform != "macosx" && AppConstants.platform != "win";
+
+    if (broken_wm_z_order) {
+      let win = Services.wm.getMostRecentWindow("navigator:browser");
 
-    // if we're lucky, this isn't a popup, and we can just return this
-    if (win && !isSuitableBrowserWindow(win)) {
-      win = null;
-      let windowList = Services.wm.getEnumerator("navigator:browser");
-      // this is oldest to newest, so this gets a bit ugly
+      // if we're lucky, this isn't a popup, and we can just return this
+      if (win && !isSuitableBrowserWindow(win)) {
+        win = null;
+        let windowList = Services.wm.getEnumerator("navigator:browser");
+        // this is oldest to newest, so this gets a bit ugly
+        while (windowList.hasMoreElements()) {
+          let nextWin = windowList.getNext();
+          if (isSuitableBrowserWindow(nextWin))
+            win = nextWin;
+        }
+      }
+      return win;
+    } else {
+      let windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
       while (windowList.hasMoreElements()) {
-        let nextWin = windowList.getNext();
-        if (isSuitableBrowserWindow(nextWin))
-          win = nextWin;
+        let win = windowList.getNext();
+        if (isSuitableBrowserWindow(win))
+          return win;
       }
+      return null;
     }
-    return win;
-#else
-    let windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
-    while (windowList.hasMoreElements()) {
-      let win = windowList.getNext();
-      if (isSuitableBrowserWindow(win))
-        return win;
-    }
-    return null;
-#endif
   }
 };
 
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -6,16 +6,17 @@
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 XPCSHELL_TESTS_MANIFESTS += [
     'test/unit/social/xpcshell.ini',
     'test/xpcshell/xpcshell.ini',
 ]
 
 EXTRA_JS_MODULES += [
+    'AboutHome.jsm',
     'BrowserUITelemetry.jsm',
     'CastingApps.jsm',
     'Chat.jsm',
     'ContentClick.jsm',
     'ContentLinkHandler.jsm',
     'ContentObservers.jsm',
     'ContentSearch.jsm',
     'ContentWebRTC.jsm',
@@ -24,43 +25,39 @@ EXTRA_JS_MODULES += [
     'E10SUtils.jsm',
     'Feeds.jsm',
     'FormSubmitObserver.jsm',
     'FormValidationHandler.jsm',
     'HiddenFrame.jsm',
     'NetworkPrioritizer.jsm',
     'offlineAppCache.jsm',
     'PanelFrame.jsm',
+    'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
+    'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'SelfSupportBackend.jsm',
     'SitePermissions.jsm',
     'Social.jsm',
     'TabCrashReporter.jsm',
     'WebappManager.jsm',
+    'webrtcUI.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'Windows8WindowFrameColor.jsm',
         'WindowsJumpLists.jsm',
         'WindowsPreviewPerTab.jsm',
     ]
 
 if CONFIG['NIGHTLY_BUILD']:
     EXTRA_JS_MODULES += [
         'SignInToWebsite.jsm',
     ]
 
-EXTRA_PP_JS_MODULES += [
-    'AboutHome.jsm',
-    'PluginContent.jsm',
-    'RecentWindow.jsm',
-    'webrtcUI.jsm',
-]
-
 EXTRA_PP_COMPONENTS += [
     'browsermodules.manifest',
 ]
 
 if CONFIG['MOZILLA_OFFICIAL']:
     DEFINES['MOZILLA_OFFICIAL'] = 1
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -8,16 +8,18 @@ this.EXPORTED_SYMBOLS = ["webrtcUI"];
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
 this.webrtcUI = {
   init: function () {
     Services.obs.addObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished", false);
 
     let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
@@ -85,29 +87,27 @@ this.webrtcUI = {
       browserWindow.gBrowser.selectedTab = aActiveStream.tab;
     } else {
       aActiveStream.browser.focus();
     }
     browserWindow.focus();
     let PopupNotifications = browserWindow.PopupNotifications;
     let notif = PopupNotifications.getNotification("webRTC-sharing" + aType,
                                                    aActiveStream.browser);
-#ifdef XP_MACOSX
-    if (!Services.focus.activeWindow) {
+    if (AppConstants.platform == "macosx" && !Services.focus.activeWindow) {
       browserWindow.addEventListener("activate", function onActivate() {
         browserWindow.removeEventListener("activate", onActivate);
         Services.tm.mainThread.dispatch(function() {
           notif.reshow();
         }, Ci.nsIThread.DISPATCH_NORMAL);
       });
       Cc["@mozilla.org/widget/macdocksupport;1"].getService(Ci.nsIMacDockSupport)
         .activateApplication(true);
       return;
     }
-#endif
     notif.reshow();
   },
 
   updateMainActionLabel: function(aMenuList) {
     let type = aMenuList.selectedItem.getAttribute("devicetype");
     let document = aMenuList.ownerDocument;
     document.getElementById("webRTC-all-windows-shared").hidden = type != "Screen";
 
@@ -456,22 +456,23 @@ function removePrompt(aBrowser, aCallId)
   let chromeWin = aBrowser.ownerDocument.defaultView;
   let notification =
     chromeWin.PopupNotifications.getNotification("webRTC-shareDevices", aBrowser);
   if (notification && notification.callID == aCallId)
     notification.remove();
 }
 
 function getGlobalIndicator() {
-#ifndef XP_MACOSX
-  const INDICATOR_CHROME_URI = "chrome://browser/content/webrtcIndicator.xul";
-  const features = "chrome,dialog=yes,titlebar=no,popup=yes";
+  if (AppConstants.platform != "macosx") {
+    const INDICATOR_CHROME_URI = "chrome://browser/content/webrtcIndicator.xul";
+    const features = "chrome,dialog=yes,titlebar=no,popup=yes";
 
-  return Services.ww.openWindow(null, INDICATOR_CHROME_URI, "_blank", features, []);
-#else
+    return Services.ww.openWindow(null, INDICATOR_CHROME_URI, "_blank", features, []);
+  }
+
   let indicator = {
     _camera: null,
     _microphone: null,
     _screen: null,
 
     _hiddenDoc: Cc["@mozilla.org/appshell/appShellService;1"]
                   .getService(Ci.nsIAppShellService)
                   .hiddenDOMWindow.document,
@@ -588,17 +589,16 @@ function getGlobalIndicator() {
       this._setIndicatorState("Camera", false);
       this._setIndicatorState("Microphone", false);
       this._setIndicatorState("Screen", false);
     }
   };
 
   indicator.updateIndicatorState();
   return indicator;
-#endif
 }
 
 function onTabSharingMenuPopupShowing(e) {
   let streams = webrtcUI.getActiveStreams(true, true, true);
   for (let streamInfo of streams) {
     let stringName = "getUserMedia.sharingMenu";
     let types = streamInfo.types;
     if (types.camera)
@@ -645,39 +645,41 @@ function showOrCreateMenuForWindow(aWind
   let document = aWindow.document;
   let menu = document.getElementById("tabSharingMenu");
   if (!menu) {
     let stringBundle = aWindow.gNavigatorBundle;
     menu = document.createElement("menu");
     menu.id = "tabSharingMenu";
     let labelStringId = "getUserMedia.sharingMenu.label";
     menu.setAttribute("label", stringBundle.getString(labelStringId));
-#ifdef XP_MACOSX
-    let container = document.getElementById("windowPopup");
-    let insertionPoint = document.getElementById("sep-window-list");
-    let separator = document.createElement("menuseparator");
-    separator.id = "tabSharingSeparator";
-    container.insertBefore(separator, insertionPoint);
-#else
-    let accesskeyStringId = "getUserMedia.sharingMenu.accesskey";
-    menu.setAttribute("accesskey", stringBundle.getString(accesskeyStringId));
-    let container = document.getElementById("main-menubar");
-    let insertionPoint = document.getElementById("helpMenu");
-#endif
+
+    let container, insertionPoint;
+    if (AppConstants.platform == "macosx") {
+      container = document.getElementById("windowPopup");
+      insertionPoint = document.getElementById("sep-window-list");
+      let separator = document.createElement("menuseparator");
+      separator.id = "tabSharingSeparator";
+      container.insertBefore(separator, insertionPoint);
+    } else {
+      let accesskeyStringId = "getUserMedia.sharingMenu.accesskey";
+      menu.setAttribute("accesskey", stringBundle.getString(accesskeyStringId));
+      container = document.getElementById("main-menubar");
+      insertionPoint = document.getElementById("helpMenu");
+    }
     let popup = document.createElement("menupopup");
     popup.id = "tabSharingMenuPopup";
     popup.addEventListener("popupshowing", onTabSharingMenuPopupShowing);
     popup.addEventListener("popuphiding", onTabSharingMenuPopupHiding);
     menu.appendChild(popup);
     container.insertBefore(menu, insertionPoint);
   } else {
     menu.hidden = false;
-#ifdef XP_MACOSX
-    document.getElementById("tabSharingSeparator").hidden = false;
-#endif
+    if (AppConstants.platform == "macosx") {
+      document.getElementById("tabSharingSeparator").hidden = false;
+    }
   }
 }
 
 function maybeAddMenuIndicator(window) {
   if (webrtcUI.showGlobalIndicator) {
     showOrCreateMenuForWindow(window);
   }
 }
@@ -696,22 +698,22 @@ function updateIndicators(data) {
     if (webrtcUI.showGlobalIndicator) {
       showOrCreateMenuForWindow(chromeWin);
     } else {
       let doc = chromeWin.document;
       let existingMenu = doc.getElementById("tabSharingMenu");
       if (existingMenu) {
         existingMenu.hidden = true;
       }
-#ifdef XP_MACOSX
-      let separator = doc.getElementById("tabSharingSeparator");
-      if (separator) {
-        separator.hidden = true;
+      if (AppConstants.platform == "macosx") {
+        let separator = doc.getElementById("tabSharingSeparator");
+        if (separator) {
+          separator.hidden = true;
+        }
       }
-#endif
     }
   }
 
   if (webrtcUI.showGlobalIndicator) {
     if (!gIndicatorWindow)
       gIndicatorWindow = getGlobalIndicator();
     else
       gIndicatorWindow.updateIndicatorState();
--- a/config/config.mk
+++ b/config/config.mk
@@ -381,34 +381,26 @@ else
 HOST_CFLAGS	+= $(MOZ_OPTIMIZE_FLAGS)
 endif # MOZ_OPTIMIZE == 1
 endif # MOZ_OPTIMIZE
 endif # CROSS_COMPILE
 
 CFLAGS += $(MOZ_FRAMEPTR_FLAGS)
 CXXFLAGS += $(MOZ_FRAMEPTR_FLAGS)
 
-# Check for FAIL_ON_WARNINGS & FAIL_ON_WARNINGS_DEBUG (Shorthand for Makefiles
-# to request that we use the 'warnings as errors' compile flags)
+# Check for FAIL_ON_WARNINGS (Shorthand for Makefiles to request that we use
+# the 'warnings as errors' compile flags)
 
 # NOTE: First, we clear FAIL_ON_WARNINGS[_DEBUG] if we're doing a Windows PGO
 # build, since WARNINGS_AS_ERRORS has been suspected of causing isuses in that
 # situation. (See bug 437002.)
 ifeq (WINNT_1,$(OS_ARCH)_$(MOZ_PROFILE_GENERATE)$(MOZ_PROFILE_USE))
-FAIL_ON_WARNINGS_DEBUG=
 FAIL_ON_WARNINGS=
 endif # WINNT && (MOS_PROFILE_GENERATE ^ MOZ_PROFILE_USE)
 
-# Now, check for debug version of flag; it turns on normal flag in debug builds.
-ifdef FAIL_ON_WARNINGS_DEBUG
-ifdef MOZ_DEBUG
-FAIL_ON_WARNINGS = 1
-endif # MOZ_DEBUG
-endif # FAIL_ON_WARNINGS_DEBUG
-
 # Check for normal version of flag, and add WARNINGS_AS_ERRORS if it's set to 1.
 ifdef FAIL_ON_WARNINGS
 # Never treat warnings as errors in clang-cl, because it warns about many more
 # things than MSVC does.
 ifndef CLANG_CL
 CXXFLAGS += $(WARNINGS_AS_ERRORS)
 CFLAGS   += $(WARNINGS_AS_ERRORS)
 endif # CLANG_CL
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6241,18 +6241,17 @@ nsDocument::RegisterElement(JSContext* a
       // This check will go through a wrapper, but as we checked above
       // it should be transparent or an xray. This should be fine for now,
       // until the spec is sorted out.
       if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) {
         rv.Throw(NS_ERROR_UNEXPECTED);
         return;
       }
 
-      // Check if non-configurable
-      if (desc.isPermanent()) {
+      if (!desc.configurable()) {
         rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
         return;
       }
 
       JS::Handle<JSObject*> svgProto(
         SVGElementBinding::GetProtoObjectHandle(aCx, global));
       if (!svgProto) {
         rv.Throw(NS_ERROR_OUT_OF_MEMORY);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -792,17 +792,17 @@ nsOuterWindowProxy::defineProperty(JSCon
     // to the caller to decide whether to throw a TypeError.
     return result.failCantDefineWindowElement();
   }
 
   // For now, allow chrome code to define non-configurable properties
   // on windows, until we sort out what exactly the addon SDK is
   // doing.  In the meantime, this still allows us to test web compat
   // behavior.
-  if (false && desc.isPermanent() && !nsContentUtils::IsCallerChrome()) {
+  if (false && !desc.configurable() && !nsContentUtils::IsCallerChrome()) {
     return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW);
   }
 
   return js::Wrapper::defineProperty(cx, proxy, id, desc, result);
 }
 
 bool
 nsOuterWindowProxy::ownPropertyKeys(JSContext *cx,
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3201,18 +3201,19 @@ def CopyUnforgeablePropertiesToInstance(
 
 def AssertInheritanceChain(descriptor):
     asserts = ""
     iface = descriptor.interface
     while iface:
         desc = descriptor.getDescriptor(iface.identifier.name)
         asserts += (
             "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
-            "           reinterpret_cast<%s*>(aObject));\n" %
-            (desc.nativeType, desc.nativeType))
+            "           reinterpret_cast<%s*>(aObject),\n"
+            "           \"Multiple inheritance for %s is broken.\");\n" %
+            (desc.nativeType, desc.nativeType, desc.nativeType))
         iface = iface.parent
     asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
     return asserts
 
 
 def InitMemberSlots(descriptor, wrapperCache):
     """
     Initialize member slots on our JS object if we're supposed to have some.
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -954,27 +954,64 @@ Event::GetClientCoords(nsPresContext* aP
       !aEvent->AsGUIEvent()->widget) {
     return aDefaultPoint;
   }
 
   nsIPresShell* shell = aPresContext->GetPresShell();
   if (!shell) {
     return CSSIntPoint(0, 0);
   }
-
   nsIFrame* rootFrame = shell->GetRootFrame();
   if (!rootFrame) {
     return CSSIntPoint(0, 0);
   }
   nsPoint pt =
     nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, aPoint, rootFrame);
 
   return CSSIntPoint::FromAppUnitsRounded(pt);
 }
 
+// static
+CSSIntPoint
+Event::GetOffsetCoords(nsPresContext* aPresContext,
+                       WidgetEvent* aEvent,
+                       LayoutDeviceIntPoint aPoint,
+                       CSSIntPoint aDefaultPoint)
+{
+  if (!aEvent->mFlags.mIsBeingDispatched) {
+    return GetPageCoords(aPresContext, aEvent, aPoint, aDefaultPoint);
+  }
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->target);
+  if (!content) {
+    return CSSIntPoint(0, 0);
+  }
+  nsCOMPtr<nsIPresShell> shell = aPresContext->GetPresShell();
+  if (!shell) {
+    return CSSIntPoint(0, 0);
+  }
+  shell->FlushPendingNotifications(Flush_Layout);
+  nsIFrame* frame = content->GetPrimaryFrame();
+  if (!frame) {
+    return CSSIntPoint(0, 0);
+  }
+  nsIFrame* rootFrame = shell->GetRootFrame();
+  if (!rootFrame) {
+    return CSSIntPoint(0, 0);
+  }
+  CSSIntPoint clientCoords =
+    GetClientCoords(aPresContext, aEvent, aPoint, aDefaultPoint);
+  nsPoint pt = CSSPixel::ToAppUnits(clientCoords);
+  if (nsLayoutUtils::TransformPoint(rootFrame, frame, pt) ==
+      nsLayoutUtils::TRANSFORM_SUCCEEDED) {
+    pt -= frame->GetPaddingRectRelativeToSelf().TopLeft();
+    return CSSPixel::FromAppUnitsRounded(pt);
+  }
+  return CSSIntPoint(0, 0);
+}
+
 // To be called ONLY by Event::GetType (which has the additional
 // logic for handling user-defined events).
 // static
 const char*
 Event::GetEventName(uint32_t aEventType)
 {
   switch(aEventType) {
 #define ID_TO_EVENT(name_, _id, _type, _struct) \
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -136,16 +136,20 @@ public:
                                      CSSIntPoint aDefaultPoint);
   static CSSIntPoint GetPageCoords(nsPresContext* aPresContext,
                                    WidgetEvent* aEvent,
                                    LayoutDeviceIntPoint aPoint,
                                    CSSIntPoint aDefaultPoint);
   static LayoutDeviceIntPoint GetScreenCoords(nsPresContext* aPresContext,
                                               WidgetEvent* aEvent,
                                               LayoutDeviceIntPoint aPoint);
+  static CSSIntPoint GetOffsetCoords(nsPresContext* aPresContext,
+                                     WidgetEvent* aEvent,
+                                     LayoutDeviceIntPoint aPoint,
+                                     CSSIntPoint aDefaultPoint);
 
   static already_AddRefed<Event> Constructor(const GlobalObject& aGlobal,
                                              const nsAString& aType,
                                              const EventInit& aParam,
                                              ErrorResult& aRv);
 
   // Implemented as xpidl method
   // void GetType(nsString& aRetval) {}
--- a/dom/events/MouseEvent.cpp
+++ b/dom/events/MouseEvent.cpp
@@ -381,16 +381,30 @@ MouseEvent::GetClientY(int32_t* aClientY
 
 int32_t
 MouseEvent::ClientY()
 {
   return Event::GetClientCoords(mPresContext, mEvent, mEvent->refPoint,
                                 mClientPoint).y;
 }
 
+int32_t
+MouseEvent::OffsetX()
+{
+  return Event::GetOffsetCoords(mPresContext, mEvent, mEvent->refPoint,
+                                mClientPoint).x;
+}
+
+int32_t
+MouseEvent::OffsetY()
+{
+  return Event::GetOffsetCoords(mPresContext, mEvent, mEvent->refPoint,
+                                mClientPoint).y;
+}
+
 bool
 MouseEvent::AltKey()
 {
   return mEvent->AsInputEvent()->IsAlt();
 }
 
 NS_IMETHODIMP
 MouseEvent::GetAltKey(bool* aIsDown)
--- a/dom/events/MouseEvent.h
+++ b/dom/events/MouseEvent.h
@@ -40,16 +40,18 @@ public:
   {
     return Button() + 1;
   }
 
   int32_t ScreenX();
   int32_t ScreenY();
   int32_t ClientX();
   int32_t ClientY();
+  int32_t OffsetX();
+  int32_t OffsetY();
   bool CtrlKey();
   bool ShiftKey();
   bool AltKey();
   bool MetaKey();
   int16_t Button();
   uint16_t Buttons();
   already_AddRefed<EventTarget> GetRelatedTarget();
   void GetRegion(nsAString& aRegion);
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -177,8 +177,9 @@ support-files =
 [test_dom_before_after_keyboard_event_remote.html]
 support-files =
   bug989198_embedded.html
   bug989198_helper.js
 skip-if = buildapp == 'b2g' || e10s
 [test_bug1096146.html]
 support-files =
   bug1096146_embedded.html
+[test_offsetxy.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_offsetxy.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for DOM MouseEvent offsetX/Y</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<div id="d" style="position:absolute; top:100px; left:100px; width:100px; border:5px dotted black; height:100px"></div>
+<div id="d2" style="position:absolute; top:100px; left:100px; width:100px; border:5px dotted black; height:100px; transform:translateX(100px)"></div>
+<div id="d3" style="display:none; position:absolute; top:100px; left:100px; width:100px; border:5px dotted black; height:100px"></div>
+<div id="d4" style="transform:scale(0); position:absolute; top:100px; left:100px; width:100px; border:5px dotted black; height:100px"></div>
+
+<pre id="test">
+<script type="application/javascript">
+
+var offsetX = -1, offsetY = -1;
+var ev = new MouseEvent("click", {clientX:110, clientY:110});
+d.addEventListener("click", function (event) {
+  is(ev, event, "Event objects must match");
+  offsetX = event.offsetX;
+  offsetY = event.offsetY;
+});
+d.dispatchEvent(ev);
+is(offsetX, 5);
+is(offsetY, 5);
+is(ev.offsetX, ev.pageX);
+is(ev.offsetY, ev.pageY);
+
+var ev2 = new MouseEvent("click", {clientX:220, clientY:130});
+d2.addEventListener("click", function (event) {
+  is(ev2, event, "Event objects must match");
+  offsetX = event.offsetX;
+  offsetY = event.offsetY;
+});
+d2.dispatchEvent(ev2);
+is(offsetX, 15);
+is(offsetY, 25);
+is(ev2.offsetX, ev2.pageX);
+is(ev2.offsetY, ev2.pageY);
+
+var ev3 = new MouseEvent("click", {clientX:110, clientY:110});
+d3.addEventListener("click", function (event) {
+  is(ev3, event, "Event objects must match");
+  offsetX = event.offsetX;
+  offsetY = event.offsetY;
+});
+d3.dispatchEvent(ev3);
+is(offsetX, 0);
+is(offsetY, 0);
+is(ev3.offsetX, ev3.pageX);
+is(ev3.offsetY, ev3.pageY);
+
+var ev4 = new MouseEvent("click", {clientX:110, clientY:110});
+d4.addEventListener("click", function (event) {
+  is(ev4, event, "Event objects must match");
+  offsetX = event.offsetX;
+  offsetY = event.offsetY;
+});
+d4.dispatchEvent(ev4);
+is(offsetX, 0);
+is(offsetY, 0);
+is(ev4.offsetX, ev4.pageX);
+is(ev4.offsetY, ev4.pageY);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
@@ -111,20 +111,26 @@ MediaEngineCameraVideoSource::FitnessDis
     return FitnessDistance(n, aConstraint.GetAsConstrainDoubleRange());
   }
 }
 
 /*static*/ uint32_t
 MediaEngineCameraVideoSource::GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
                                                  const MediaTrackConstraintSet &aConstraints)
 {
+  // Treat width|height|frameRate == 0 on capability as "can do any".
+  // This allows for orthogonal capabilities that are not in discrete steps.
+
   uint64_t distance =
-    uint64_t(FitnessDistance(int32_t(aCandidate.width), aConstraints.mWidth)) +
-    uint64_t(FitnessDistance(int32_t(aCandidate.height), aConstraints.mHeight)) +
-    uint64_t(FitnessDistance(double(aCandidate.maxFPS), aConstraints.mFrameRate));
+    uint64_t(aCandidate.width? FitnessDistance(int32_t(aCandidate.width),
+                                               aConstraints.mWidth) : 0) +
+    uint64_t(aCandidate.height? FitnessDistance(int32_t(aCandidate.height),
+                                                aConstraints.mHeight) : 0) +
+    uint64_t(aCandidate.maxFPS? FitnessDistance(double(aCandidate.maxFPS),
+                                                aConstraints.mFrameRate) : 0);
   return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
 }
 
 // Find best capability by removing inferiors. May leave >1 of equal distance
 
 /* static */ void
 MediaEngineCameraVideoSource::TrimLessFitCandidates(CapabilitySet& set) {
   uint32_t best = UINT32_MAX;
--- a/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -157,37 +157,53 @@ size_t
 MediaEngineWebRTCVideoSource::NumCapabilities()
 {
   NS_ConvertUTF16toUTF8 uniqueId(mUniqueId); // TODO: optimize this?
 
   int num = mViECapture->NumberOfCapabilities(uniqueId.get(), kMaxUniqueIdLength);
   if (num > 0) {
     return num;
   }
-  // Mac doesn't support capabilities.
-  //
-  // Hardcode generic desktop capabilities modeled on OSX camera.
-  // Note: Values are empirically picked to be OSX friendly, as on OSX, values
-  // other than these cause the source to not produce.
+
+  switch (mMediaSource) {
+    case dom::MediaSourceEnum::Camera:
+#ifdef XP_MACOSX
+      // Mac doesn't support capabilities.
+      //
+      // Hardcode generic desktop capabilities modeled on OSX camera.
+      // Note: Values are empirically picked to be OSX friendly, as on OSX,
+      // values other than these cause the source to not produce.
 
-  if (mHardcodedCapabilities.IsEmpty()) {
-    for (int i = 0; i < 9; i++) {
+      if (mHardcodedCapabilities.IsEmpty()) {
+        for (int i = 0; i < 9; i++) {
+          webrtc::CaptureCapability c;
+          c.width = 1920 - i*128;
+          c.height = 1080 - i*72;
+          c.maxFPS = 30;
+          mHardcodedCapabilities.AppendElement(c);
+        }
+        for (int i = 0; i < 16; i++) {
+          webrtc::CaptureCapability c;
+          c.width = 640 - i*40;
+          c.height = 480 - i*30;
+          c.maxFPS = 30;
+          mHardcodedCapabilities.AppendElement(c);
+        }
+      }
+      break;
+#endif
+    default:
+      // The default for devices that don't return discrete capabilities: treat
+      // them as supporting all capabilities orthogonally. E.g. screensharing.
       webrtc::CaptureCapability c;
-      c.width = 1920 - i*128;
-      c.height = 1080 - i*72;
-      c.maxFPS = 30;
+      c.width = 0; // 0 = accept any value
+      c.height = 0;
+      c.maxFPS = 0;
       mHardcodedCapabilities.AppendElement(c);
-    }
-    for (int i = 0; i < 16; i++) {
-      webrtc::CaptureCapability c;
-      c.width = 640 - i*40;
-      c.height = 480 - i*30;
-      c.maxFPS = 30;
-      mHardcodedCapabilities.AppendElement(c);
-    }
+      break;
   }
   return mHardcodedCapabilities.Length();
 }
 
 void
 MediaEngineWebRTCVideoSource::GetCapability(size_t aIndex,
                                             webrtc::CaptureCapability& aOut)
 {
--- a/dom/messages/SystemMessageCache.js
+++ b/dom/messages/SystemMessageCache.js
@@ -20,17 +20,16 @@ function debug(aMsg) {
 
 const kMessages = ["SystemMessageCache:RefreshCache"];
 
 function SystemMessageCache() {
   debug("init");
 
   this._pagesCache = [];
 
-  dump("SystemMessageCache: init");
   Services.obs.addObserver(this, "xpcom-shutdown", false);
   kMessages.forEach(function(aMessage) {
     cpmm.addMessageListener(aMessage, this);
   }, this);
 }
 
 SystemMessageCache.prototype = {
 
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -245,33 +245,23 @@ SystemMessageInternal.prototype = {
       let result = this._sendMessageCommon(aType,
                                            aMessage,
                                            messageID,
                                            aPageURL,
                                            manifestURL,
                                            aExtra);
       debug("Returned status of sending message: " + result);
 
-      // Don't need to open the pages and queue the system message
-      // which was not allowed to be sent.
       if (result === MSG_SENT_FAILURE_PERM_DENIED) {
         return;
       }
 
       // For each page we must receive a confirm.
       ++pendingPromise.counter;
 
-      let page = this._findPage(aType, aPageURL, manifestURL);
-      if (page) {
-        // Queue this message in the corresponding pages.
-        this._queueMessage(page, aMessage, messageID);
-
-        this._openAppPage(page, aMessage, aExtra, result);
-      }
-
     }, this);
 
     if (pendingPromise.counter) {
       this._pendingPromises.set(messageID, pendingPromise);
     }
   },
 
   broadcastMessage: function(aType, aMessage, aExtra) {
@@ -303,28 +293,16 @@ SystemMessageInternal.prototype = {
       let doDispatch = () => {
         let result = this._sendMessageCommon(aType,
                                              aMessage,
                                              messageID,
                                              aPage.pageURL,
                                              aPage.manifestURL,
                                              aExtra);
         debug("Returned status of sending message: " + result);
-
-
-        // Don't need to open the pages and queue the system message
-        // which was not allowed to be sent.
-        if (result === MSG_SENT_FAILURE_PERM_DENIED) {
-          return;
-        }
-
-        // Queue this message in the corresponding pages.
-        this._queueMessage(aPage, aMessage, messageID);
-
-        this._openAppPage(aPage, aMessage, aExtra, result);
       };
 
       if ('function' !== typeof shouldDispatchFunc) {
         // If the configurator has no 'shouldDispatch' defined,
         // always dispatch this message.
         doDispatch();
         return;
       }
@@ -733,25 +711,42 @@ SystemMessageInternal.prototype = {
     // Don't send the system message not granted by the app's permissions.
     if (!SystemMessagePermissionsChecker
           .isSystemMessagePermittedToSend(aType,
                                           aPageURL,
                                           aManifestURL)) {
       return MSG_SENT_FAILURE_PERM_DENIED;
     }
 
+    // Queue this message in the corresponding pages.
+    let page = this._findPage(aType, aPageURL, aManifestURL);
+    if (!page) {
+      debug("Message " + aType + " is not registered for " +
+            aPageURL + " @ " + aManifestURL);
+      // FIXME bug 1140275 should only send message to page registered in manifest
+      // return MSG_SENT_FAILURE_PERM_DENIED;
+    }
+    if (page)
+      this._queueMessage(page, aMessage, aMessageID);
+
     let appPageIsRunning = false;
     let pageKey = this._createKeyForPage({ type: aType,
                                            manifestURL: aManifestURL,
                                            pageURL: aPageURL });
 
+    let cache = this._findCacheForApp(aManifestURL);
     let targets = this._listeners[aManifestURL];
     if (targets) {
       for (let index = 0; index < targets.length; ++index) {
         let target = targets[index];
+        let manager = target.target;
+
+        // Ensure hasPendingMessage cache is refreshed before we open app
+        manager.sendAsyncMessage("SystemMessageCache:RefreshCache", cache);
+
         // We only need to send the system message to the targets (processes)
         // which contain the window page that matches the manifest/page URL of
         // the destination of system message.
         if (target.winCounts[aPageURL] === undefined) {
           continue;
         }
 
         appPageIsRunning = true;
@@ -760,39 +755,39 @@ SystemMessageInternal.prototype = {
         // "SystemMessageManager:HandleMessageDone"  message when the page
         // finishes handling the system message. At that point, we'll release
         // the lock we acquired.
         this._acquireCpuWakeLock(pageKey);
 
         // Multiple windows can share the same target (process), the content
         // window needs to check if the manifest/page URL is matched. Only
         // *one* window should handle the system message.
-        let manager = target.target;
         manager.sendAsyncMessage("SystemMessageManager:Message",
                                  { type: aType,
                                    msg: aMessage,
                                    manifestURL: aManifestURL,
                                    pageURL: aPageURL,
                                    msgID: aMessageID });
       }
     }
 
+    let result = MSG_SENT_SUCCESS;
     if (!appPageIsRunning) {
       // The app page isn't running and relies on the 'open-app' chrome event to
       // wake it up. We still need to acquire a CPU wake lock for that page and
       // expect that we will receive a "SystemMessageManager:HandleMessagesDone"
       // or a "SystemMessageManager:HandleMessageDone" message when the page
       // finishes handling the system message with other pending messages. At
       // that point, we'll release the lock we acquired.
+      result = MSG_SENT_FAILURE_APP_NOT_RUNNING;
       this._acquireCpuWakeLock(pageKey);
-      return MSG_SENT_FAILURE_APP_NOT_RUNNING;
-    } else {
-      return MSG_SENT_SUCCESS;
     }
-
+    if (page)
+      this._openAppPage(page, aMessage, aExtra, result);
+    return result;
   },
 
   _resolvePendingPromises: function(aMessageID) {
     if (!this._pendingPromises.has(aMessageID)) {
       debug("Unknown pendingPromise messageID. This seems a bug!!");
       return;
     }
 
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -28,16 +28,20 @@ Cu.importGlobalProperties(["indexedDB"])
 
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
                                    "@mozilla.org/network/dns-service;1",
                                    "nsIDNSService");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
                                   "resource://gre/modules/AlarmService.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
+                                   "@mozilla.org/power/powermanagerservice;1",
+                                   "nsIPowerManagerService");
+
 var threadManager = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
 
 this.EXPORTED_SYMBOLS = ["PushService"];
 
 const prefs = new Preferences("services.push.");
 // Set debug first so that all debugging actually works.
 gDebuggingEnabled = prefs.get("debug");
 
@@ -868,18 +872,28 @@ this.PushService = {
     else {
       debug("Unsupported websocket scheme " + uri.scheme);
       return;
     }
 
     debug("serverURL: " + uri.spec);
     this._wsListener = new PushWebSocketListener(this);
     this._ws.protocol = "push-notification";
-    this._ws.asyncOpen(uri, serverURL, this._wsListener, null);
-    this._currentState = STATE_WAITING_FOR_WS_START;
+
+    try {
+      // Grab a wakelock before we open the socket to ensure we don't go to sleep
+      // before connection the is opened.
+      this._ws.asyncOpen(uri, serverURL, this._wsListener, null);
+      this._acquireWakeLock();
+      this._currentState = STATE_WAITING_FOR_WS_START;
+    } catch(e) {
+      debug("Error opening websocket. asyncOpen failed!");
+      this._shutdownWS();
+      this._reconnectAfterBackoff();
+    }
   },
 
   _startListeningIfChannelsPresent: function() {
     // Check to see if we need to do anything.
     this._db.getAllChannelIDs(function(channelIDs) {
       if (channelIDs.length > 0) {
         this._beginWSSetup();
       }
@@ -992,16 +1006,48 @@ this.PushService = {
       // called between _reconnectAfterBackoff() setting the alarm and the
       // alarm firing.
 
       // Websocket is shut down. Backoff interval expired, try to connect.
       this._beginWSSetup();
     }
   },
 
+  _acquireWakeLock: function() {
+    if (!this._socketWakeLock) {
+      debug("Acquiring Socket Wakelock");
+      this._socketWakeLock = gPowerManagerService.newWakeLock("cpu");
+    }
+    if (!this._socketWakeLockTimer) {
+      debug("Creating Socket WakeLock Timer");
+      this._socketWakeLockTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    }
+
+    debug("Setting Socket WakeLock Timer");
+    this._socketWakeLockTimer
+      .initWithCallback(this._releaseWakeLock.bind(this),
+                        // Allow the same time for socket setup as we do for
+                        // requests after the setup. Fudge it a bit since
+                        // timers can be a little off and we don't want to go
+                        // to sleep just as the socket connected.
+                        this._requestTimeout + 1000,
+                        Ci.nsITimer.ONE_SHOT);
+  },
+
+  _releaseWakeLock: function() {
+    debug("Releasing Socket WakeLock");
+    if (this._socketWakeLockTimer) {
+      this._socketWakeLockTimer.cancel();
+    }
+    if (this._socketWakeLock) {
+      this._socketWakeLock.unlock();
+      this._socketWakeLock = null;
+    }
+  },
+
   /**
    * Protocol handler invoked by server message.
    */
   _handleHelloReply: function(reply) {
     debug("handleHelloReply()");
     if (this._currentState != STATE_WAITING_FOR_HELLO) {
       debug("Unexpected state " + this._currentState +
             "(expected STATE_WAITING_FOR_HELLO)");
@@ -1507,16 +1553,18 @@ this.PushService = {
       requestID: aPageRecord.requestID,
       error: "Database error"
     });
   },
 
   // begin Push protocol handshake
   _wsOnStart: function(context) {
     debug("wsOnStart()");
+    this._releaseWakeLock();
+
     if (this._currentState != STATE_WAITING_FOR_WS_START) {
       debug("NOT in STATE_WAITING_FOR_WS_START. Current state " +
             this._currentState + ". Skipping");
       return;
     }
 
     // Since we've had a successful connection reset the retry fail count.
     this._retryFailCount = 0;
@@ -1563,16 +1611,17 @@ this.PushService = {
    * This statusCode is not the websocket protocol status code, but the TCP
    * connection close status code.
    *
    * If we do not explicitly call ws.close() then statusCode is always
    * NS_BASE_STREAM_CLOSED, even on a successful close.
    */
   _wsOnStop: function(context, statusCode) {
     debug("wsOnStop()");
+    this._releaseWakeLock();
 
     if (statusCode != Cr.NS_OK &&
         !(statusCode == Cr.NS_BASE_STREAM_CLOSED && this._willBeWokenUpByUDP)) {
       debug("Socket error " + statusCode);
       this._reconnectAfterBackoff();
     }
 
     // Bug 896919. We always shutdown the WebSocket, even if we need to
--- a/dom/webidl/MouseEvent.webidl
+++ b/dom/webidl/MouseEvent.webidl
@@ -10,16 +10,18 @@
  * liability, trademark and document use rules apply.
  */
 
 interface MouseEvent : UIEvent {
   readonly attribute long           screenX;
   readonly attribute long           screenY;
   readonly attribute long           clientX;
   readonly attribute long           clientY;
+  readonly attribute long           offsetX;
+  readonly attribute long           offsetY;
   readonly attribute boolean        ctrlKey;
   readonly attribute boolean        shiftKey;
   readonly attribute boolean        altKey;
   readonly attribute boolean        metaKey;
   readonly attribute short          button;
   readonly attribute unsigned short buttons;
   readonly attribute EventTarget?   relatedTarget;
   readonly attribute DOMString?     region;
--- a/editor/composer/nsComposerCommandsUpdater.cpp
+++ b/editor/composer/nsComposerCommandsUpdater.cpp
@@ -380,14 +380,12 @@ nsComposerCommandsUpdater::Notify(nsITim
 #if 0
 #pragma mark -
 #endif
 
 
 nsresult
 NS_NewComposerCommandsUpdater(nsISelectionListener** aInstancePtrResult)
 {
-  nsComposerCommandsUpdater* newThang = new nsComposerCommandsUpdater;
-  NS_ENSURE_TRUE(newThang, NS_ERROR_OUT_OF_MEMORY);
-
-  return newThang->QueryInterface(NS_GET_IID(nsISelectionListener),
-                                  (void **)aInstancePtrResult);
+  nsRefPtr<nsComposerCommandsUpdater> newThang = new nsComposerCommandsUpdater;
+  newThang.forget(aInstancePtrResult);
+  return NS_OK;
 }
--- a/editor/libeditor/nsHTMLURIRefObject.cpp
+++ b/editor/libeditor/nsHTMLURIRefObject.cpp
@@ -249,12 +249,12 @@ nsHTMLURIRefObject::SetNode(nsIDOMNode *
 nsresult NS_NewHTMLURIRefObject(nsIURIRefObject** aResult, nsIDOMNode* aNode)
 {
   nsRefPtr<nsHTMLURIRefObject> refObject = new nsHTMLURIRefObject();
   nsresult rv = refObject->SetNode(aNode);
   if (NS_FAILED(rv)) {
     *aResult = 0;
     return rv;
   }
-  return refObject->QueryInterface(NS_GET_IID(nsIURIRefObject),
-                                   (void**)aResult);
+  refObject.forget(aResult);
+  return NS_OK;
 }
 
--- a/editor/txtsvc/nsTextServicesDocument.cpp
+++ b/editor/txtsvc/nsTextServicesDocument.cpp
@@ -1995,31 +1995,31 @@ nsTextServicesDocument::GetDocumentConte
     nsCOMPtr<nsIDOMHTMLElement> bodyElement;
 
     result = htmlDoc->GetBody(getter_AddRefs(bodyElement));
 
     NS_ENSURE_SUCCESS(result, result);
 
     NS_ENSURE_TRUE(bodyElement, NS_ERROR_FAILURE);
 
-    result = bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
+    bodyElement.forget(aNode);
   }
   else
   {
     // For non-HTML documents, the content root node will be the document element.
 
     nsCOMPtr<nsIDOMElement> docElement;
 
     result = mDOMDocument->GetDocumentElement(getter_AddRefs(docElement));
 
     NS_ENSURE_SUCCESS(result, result);
 
     NS_ENSURE_TRUE(docElement, NS_ERROR_FAILURE);
 
-    result = docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
+    docElement.forget(aNode);
   }
 
   return result;
 }
 
 nsresult
 nsTextServicesDocument::CreateDocumentContentRange(nsRange** aRange)
 {
--- a/embedding/components/commandhandler/nsCommandGroup.cpp
+++ b/embedding/components/commandhandler/nsCommandGroup.cpp
@@ -283,30 +283,30 @@ nsControllerCommandGroup::IsCommandInGro
   }
   return NS_OK;
 }
 
 /* nsISimpleEnumerator getGroupsEnumerator (); */
 NS_IMETHODIMP
 nsControllerCommandGroup::GetGroupsEnumerator(nsISimpleEnumerator **_retval)
 {
-  nsGroupsEnumerator *groupsEnum = new nsGroupsEnumerator(mGroupsHash);
-  if (!groupsEnum) return NS_ERROR_OUT_OF_MEMORY;
+  nsRefPtr<nsGroupsEnumerator> groupsEnum = new nsGroupsEnumerator(mGroupsHash);
 
-  return groupsEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)_retval);
+  groupsEnum.forget(_retval);
+  return NS_OK;
 }
 
 /* nsISimpleEnumerator getEnumeratorForGroup (in DOMString aGroup); */
 NS_IMETHODIMP
 nsControllerCommandGroup::GetEnumeratorForGroup(const char *aGroup, nsISimpleEnumerator **_retval)
 {
   nsDependentCString groupKey(aGroup);
   nsTArray<nsCString> *commandList = mGroupsHash.Get(groupKey); // may be null
 
-  nsNamedGroupEnumerator *theGroupEnum = new nsNamedGroupEnumerator(commandList);
-  if (!theGroupEnum) return NS_ERROR_OUT_OF_MEMORY;
+  nsRefPtr<nsNamedGroupEnumerator> theGroupEnum = new nsNamedGroupEnumerator(commandList);
 
-  return theGroupEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)_retval);
+  theGroupEnum.forget(_retval);
+  return NS_OK;
 }
 
 #if 0
 #pragma mark -
 #endif
--- a/embedding/components/find/nsWebBrowserFind.cpp
+++ b/embedding/components/find/nsWebBrowserFind.cpp
@@ -404,26 +404,27 @@ nsresult nsWebBrowserFind::GetRootNode(n
   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDomDoc);
   if (htmlDoc)
   {
     // For HTML documents, the content root node is the body.
     nsCOMPtr<nsIDOMHTMLElement> bodyElement;
     rv = htmlDoc->GetBody(getter_AddRefs(bodyElement));
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_ARG_POINTER(bodyElement);
-    return bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode),
-                                       (void **)aNode);
+    bodyElement.forget(aNode);
+    return NS_OK;
   }
 
   // For non-HTML documents, the content root node will be the doc element.
   nsCOMPtr<nsIDOMElement> docElement;
   rv = aDomDoc->GetDocumentElement(getter_AddRefs(docElement));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_ARG_POINTER(docElement);
-  return docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
+  docElement.forget(aNode);
+  return NS_OK;
 }
 
 nsresult nsWebBrowserFind::SetRangeAroundDocument(nsIDOMRange* aSearchRange,
                                                   nsIDOMRange* aStartPt,
                                                   nsIDOMRange* aEndPt,
                                                   nsIDOMDocument* aDoc)
 {
     nsCOMPtr<nsIDOMNode> bodyNode;
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -44,19 +44,23 @@ ChromeProcessController::InitializeRoot(
   // in the chrome processes is covered by an APZC.
   // The displayport is zero-margin because this element is generally not
   // actually scrollable (if it is, APZC will set proper margins when it's
   // scrolled).
   nsIPresShell* presShell = GetPresShell();
   if (!presShell) {
     return;
   }
+
   MOZ_ASSERT(presShell->GetDocument());
   nsIContent* content = presShell->GetDocument()->GetDocumentElement();
-  MOZ_ASSERT(content);
+  if (!content) {
+    return;
+  }
+
   uint32_t presShellId;
   FrameMetrics::ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId, &viewId)) {
     nsLayoutUtils::SetDisplayPortMargins(content, presShell, ScreenMargin(), 0,
         nsLayoutUtils::RepaintMode::DoNotRepaint);
   }
 }
 
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -975,19 +975,21 @@ AsyncCompositionManager::TransformScroll
   if (metrics.IsScrollable()) {
     geckoScroll = metrics.GetScrollOffset() * userZoom;
   }
 
   LayerToParentLayerScale asyncZoom = userZoom / metrics.LayersPixelsPerCSSPixel();
   ParentLayerPoint translation = userScroll - geckoScroll;
   Matrix4x4 treeTransform = ViewTransform(asyncZoom, -translation);
 
-  SetShadowTransform(aLayer, oldTransform * treeTransform);
-  NS_ASSERTION(!aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation(),
-               "overwriting animated transform!");
+  // Apply the tree transform on top of GetLocalTransform() here (rather than
+  // GetTransform()) in case the OMTA code in SampleAnimations already set a
+  // shadow transform; in that case we want to apply ours on top of that one
+  // rather than clobber it.
+  SetShadowTransform(aLayer, aLayer->GetLocalTransform() * treeTransform);
 
   // Make sure that overscroll and under-zoom are represented in the old
   // transform so that fixed position content moves and scales accordingly.
   // These calculations will effectively scale and offset fixed position layers
   // in screen space when the compensatory transform is performed in
   // AlignFixedAndStickyLayers.
   ParentLayerRect contentScreenRect = mContentRect * userZoom;
   Point3D overscrollTranslation;
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=2 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/CompositorChild.h"
-#include "mozilla/layers/CompositorParent.h"
 #include <stddef.h>                     // for size_t
 #include "ClientLayerManager.h"         // for ClientLayerManager
 #include "base/message_loop.h"          // for MessageLoop
 #include "base/process_util.h"          // for OpenProcessHandle
 #include "base/task.h"                  // for NewRunnableMethod, etc
 #include "base/tracked.h"               // for FROM_HERE
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/layers/PLayerTransactionChild.h"
@@ -36,78 +35,38 @@ namespace mozilla {
 namespace layers {
 
 /*static*/ CompositorChild* CompositorChild::sCompositor;
 
 Atomic<int32_t> CompositableForwarder::sSerialCounter(0);
 
 CompositorChild::CompositorChild(ClientLayerManager *aLayerManager)
   : mLayerManager(aLayerManager)
-  , mCanSend(false)
+  , mCanSend(true)
 {
 }
 
 CompositorChild::~CompositorChild()
 {
-  if (mCanSend) {
-    gfxCriticalError() << "CompositorChild was not deinitialized";
-  }
-}
-
-static void DeferredDestroyCompositor(nsRefPtr<CompositorParent> aCompositorParent,
-                                      nsRefPtr<CompositorChild> aCompositorChild)
-{
-    // Bug 848949 needs to be fixed before
-    // we can close the channel properly
-    //aCompositorChild->Close();
 }
 
 void
 CompositorChild::Destroy()
 {
-  // This must not be called from the destructor!
-  MOZ_ASSERT(mRefCnt != 0);
-
-  if (!mCanSend) {
-    NS_WARNING("Trying to deinitialize a CompositorChild twice");
-    return;
-  }
-
-  SendWillStop();
-  // The call just made to SendWillStop can result in IPC from the
-  // CompositorParent to the CompositorChild (e.g. caused by the destruction
-  // of shared memory). We need to ensure this gets processed by the
-  // CompositorChild before it gets destroyed. It suffices to ensure that
-  // events already in the MessageLoop get processed before the
-  // CompositorChild is destroyed, so we add a task to the MessageLoop to
-  // handle compositor desctruction.
-
-  // From now on the only message we can send is Stop.
-  mCanSend = false;
-
-  if (mLayerManager) {
-    mLayerManager->Destroy();
-    mLayerManager = nullptr;
-  }
-
+  mLayerManager->Destroy();
+  mLayerManager = nullptr;
   // start from the end of the array because Destroy() can cause the
   // LayerTransactionChild to be removed from the array.
   for (int i = ManagedPLayerTransactionChild().Length() - 1; i >= 0; --i) {
     RefPtr<LayerTransactionChild> layers =
       static_cast<LayerTransactionChild*>(ManagedPLayerTransactionChild()[i]);
     layers->Destroy();
   }
-
+  MOZ_ASSERT(!mCanSend);
   SendStop();
-
-  // The DeferredDestroyCompositor task takes ownership of compositorParent and
-  // will release them when it runs.
-  nsRefPtr<CompositorChild> selfRef = this;
-  MessageLoop::current()->PostTask(FROM_HERE,
-             NewRunnableFunction(DeferredDestroyCompositor, mCompositorParent, selfRef));
 }
 
 bool
 CompositorChild::LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId,
                                               FrameMetrics& aFrame)
 {
   SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId);
   if (data) {
@@ -130,42 +89,28 @@ CompositorChild::Create(Transport* aTran
     NS_RUNTIMEABORT("Couldn't OpenProcessHandle() to parent process.");
     return nullptr;
   }
   if (!child->Open(aTransport, handle, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
     NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
     return nullptr;
   }
 
-  child->mCanSend = true;
-
   // We release this ref in ActorDestroy().
   sCompositor = child.forget().take();
 
   int32_t width;
   int32_t height;
   sCompositor->SendGetTileSize(&width, &height);
   gfxPlatform::GetPlatform()->SetTileSize(width, height);
 
   // We release this ref in ActorDestroy().
   return sCompositor;
 }
 
-bool
-CompositorChild::OpenSameProcess(CompositorParent* aParent)
-{
-  MOZ_ASSERT(aParent);
-
-  mCompositorParent = aParent;
-  mCanSend = Open(mCompositorParent->GetIPCChannel(),
-                  CompositorParent::CompositorLoop(),
-                  ipc::ChildSide);
-  return mCanSend;
-}
-
 /*static*/ CompositorChild*
 CompositorChild::Get()
 {
   // This is only expected to be used in child processes.
   MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
   return sCompositor;
 }
 
@@ -532,16 +477,18 @@ CompositorChild::CancelNotifyAfterRemote
     mWeakTabChild = nullptr;
   }
 }
 
 bool
 CompositorChild::SendWillStop()
 {
   MOZ_ASSERT(mCanSend);
+  // From now on the only two messages we can send are WillStop and Stop.
+  mCanSend = false;
   return PCompositorChild::SendWillStop();
 }
 
 bool
 CompositorChild::SendPause()
 {
   MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
--- a/gfx/layers/ipc/CompositorChild.h
+++ b/gfx/layers/ipc/CompositorChild.h
@@ -55,22 +55,16 @@ public:
   /**
    * We're asked to create a new Compositor in response to an Opens()
    * or Bridge() request from our parent process.  The Transport is to
    * the compositor's context.
    */
   static PCompositorChild*
   Create(Transport* aTransport, ProcessId aOtherProcess);
 
-  /**
-   * Initialize the CompositorChild and open the connection in the non-multi-process
-   * case.
-   */
-  bool OpenSameProcess(CompositorParent* aParent);
-
   static CompositorChild* Get();
 
   static bool ChildProcessHasCompositor() { return sCompositor != nullptr; }
 
   void AddOverfillObserver(ClientLayerManager* aLayerManager);
 
   virtual bool
   RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId) MOZ_OVERRIDE;
@@ -169,19 +163,16 @@ private:
     uint32_t mAPZCId;
   };
 
   static PLDHashOperator RemoveSharedMetricsForLayersId(const uint64_t& aKey,
                                                         nsAutoPtr<SharedFrameMetricsData>& aData,
                                                         void* aLayerTransactionChild);
 
   nsRefPtr<ClientLayerManager> mLayerManager;
-  // When not multi-process, hold a reference to the CompositorParent to keep it
-  // alive. This reference should be null in multi-process.
-  nsRefPtr<CompositorParent> mCompositorParent;
 
   // The ViewID of the FrameMetrics is used as the key for this hash table.
   // While this should be safe to use since the ViewID is unique
   nsClassHashtable<nsUint64HashKey, SharedFrameMetricsData> mFrameMetricsTable;
 
   // When we're in a child process, this is the process-global
   // compositor that we use to forward transactions directly to the
   // compositor context in another process.
--- a/intl/strres/nsStringBundle.cpp
+++ b/intl/strres/nsStringBundle.cpp
@@ -646,19 +646,18 @@ nsStringBundleService::CreateExtensibleB
 
   nsRefPtr<nsExtensibleStringBundle> bundle = new nsExtensibleStringBundle();
 
   nsresult res = bundle->Init(aCategory, this);
   if (NS_FAILED(res)) {
     return res;
   }
 
-  res = bundle->QueryInterface(NS_GET_IID(nsIStringBundle), (void**) aResult);
-
-  return res;
+  bundle.forget(aResult);
+  return NS_OK;
 }
 
 #define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties"
 
 nsresult
 nsStringBundleService::FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
                                         uint32_t argCount, char16_t** argArray,
                                         char16_t* *result)
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -402,16 +402,25 @@ void MessageLoop::ReloadWorkQueue() {
     if (incoming_queue_.empty())
       return;
     std::swap(incoming_queue_, work_queue_);
     DCHECK(incoming_queue_.empty());
   }
 }
 
 bool MessageLoop::DeletePendingTasks() {
+#ifdef DEBUG
+  if (!work_queue_.empty()) {
+    Task* task = work_queue_.front().task;
+    tracked_objects::Location loc = task->GetBirthPlace();
+    printf("Unexpected task! %s:%s:%d\n",
+	   loc.function_name(), loc.file_name(), loc.line_number());
+  }
+#endif
+
   MOZ_ASSERT(work_queue_.empty());
   bool did_work = !deferred_non_nestable_work_queue_.empty();
   while (!deferred_non_nestable_work_queue_.empty()) {
     Task* task = deferred_non_nestable_work_queue_.front().task;
     deferred_non_nestable_work_queue_.pop();
     delete task;
   }
   did_work |= !delayed_work_queue_.empty();
--- a/ipc/chromium/src/base/tracked.cc
+++ b/ipc/chromium/src/base/tracked.cc
@@ -76,16 +76,23 @@ void Tracked::SetBirthPlace(const Locati
     tracked_births_->ForgetBirth();
   ThreadData* current_thread_data = ThreadData::current();
   if (!current_thread_data)
     return;  // Shutdown started, and this thread wasn't registered.
   tracked_births_ = current_thread_data->FindLifetime(from_here);
   tracked_births_->RecordBirth();
 }
 
+Location Tracked::GetBirthPlace() const {
+  if (tracked_births_) {
+    return tracked_births_->location();
+  }
+  return Location();
+}
+
 void Tracked::ResetBirthTime() {
   tracked_birth_time_ = Time::Now();
 }
 
 bool Tracked::MissingBirthplace() const {
   return -1 == tracked_births_->location().line_number();
 }
 
--- a/ipc/chromium/src/base/tracked.h
+++ b/ipc/chromium/src/base/tracked.h
@@ -94,16 +94,17 @@ class Births;
 
 class Tracked {
  public:
   Tracked();
   virtual ~Tracked();
 
   // Used to record the FROM_HERE location of a caller.
   void SetBirthPlace(const Location& from_here);
+  Location GetBirthPlace() const;
 
   // When a task sits around a long time, such as in a timer, or object watcher,
   // this method should be called when the task becomes active, and its
   // significant lifetime begins (and its waiting to be woken up has passed).
   void ResetBirthTime();
 
   bool MissingBirthplace() const;
 
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -91,17 +91,17 @@ GetDataProperty(JSContext *cx, HandleVal
     Rooted<PropertyDescriptor> desc(cx);
     RootedId id(cx, NameToId(field));
     if (!GetPropertyDescriptor(cx, obj, id, &desc))
         return false;
 
     if (!desc.object())
         return LinkFail(cx, "property not present on object");
 
-    if (desc.hasGetterOrSetterObject())
+    if (!desc.isDataDescriptor())
         return LinkFail(cx, "property is not a data property");
 
     v.set(desc.value());
     return true;
 }
 
 static bool
 HasPureCoercion(JSContext *cx, HandleValue v)
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -91,17 +91,17 @@ obj_propertyIsEnumerable(JSContext *cx, 
         return false;
 
     /* Step 3. */
     Rooted<PropertyDescriptor> desc(cx);
     if (!GetOwnPropertyDescriptor(cx, obj, idRoot, &desc))
         return false;
 
     /* Steps 4-5. */
-    args.rval().setBoolean(desc.object() && desc.isEnumerable());
+    args.rval().setBoolean(desc.object() && desc.enumerable());
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 static bool
 obj_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -191,17 +191,17 @@ js::ObjectToSource(JSContext *cx, Handle
     for (size_t i = 0; i < idv.length(); ++i) {
         RootedId id(cx, idv[i]);
         Rooted<PropertyDescriptor> desc(cx);
         if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
             return nullptr;
 
         int valcnt = 0;
         if (desc.object()) {
-            if (desc.hasGetterOrSetterObject()) {
+            if (desc.isAccessorDescriptor()) {
                 if (desc.hasGetterObject() && desc.getterObject()) {
                     val[valcnt].setObject(*desc.getterObject());
                     gsop[valcnt].set(cx->names().get);
                     valcnt++;
                 }
                 if (desc.hasSetterObject() && desc.setterObject()) {
                     val[valcnt].setObject(*desc.setterObject());
                     gsop[valcnt].set(cx->names().set);
@@ -703,17 +703,17 @@ js::obj_getOwnPropertyDescriptor(JSConte
     // Steps 3-4.
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args.get(1), &id))
         return false;
 
     // Steps 5-7.
     Rooted<PropertyDescriptor> desc(cx);
     return GetOwnPropertyDescriptor(cx, obj, id, &desc) &&
-           NewPropertyDescriptorObject(cx, desc, args.rval());
+           FromPropertyDescriptor(cx, desc, args.rval());
 }
 
 // ES6 draft rev27 (2014/08/24) 19.1.2.14 Object.keys(O)
 static bool
 obj_keys(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY);
@@ -798,36 +798,38 @@ js::obj_getOwnPropertyNames(JSContext *c
 static bool
 obj_getOwnPropertySymbols(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return GetOwnPropertyKeys(cx, args,
                               JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY);
 }
 
-/* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
+/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.4: Object.defineProperty(O, P, Attributes) */
 bool
 js::obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
+
+    // Steps 1-3.
     RootedObject obj(cx);
     if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperty", &obj))
         return false;
-
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args.get(1), &id))
         return false;
 
-    Rooted<PropDesc> desc(cx);
-    if (!desc.initialize(cx, args.get(2)))
+    // Steps 4-5.
+    Rooted<PropertyDescriptor> desc(cx);
+    if (!ToPropertyDescriptor(cx, args.get(2), true, &desc))
         return false;
 
+    // Steps 6-8.
     if (!StandardDefineProperty(cx, obj, id, desc))
         return false;
-
     args.rval().setObject(*obj);
     return true;
 }
 
 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
 static bool
 obj_defineProperties(JSContext *cx, unsigned argc, Value *vp)
 {
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1482,17 +1482,18 @@ OutlineTypedObject::createUnattachedWith
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp,
                                                              TaggedProto(&descr->typedProto()),
                                                              descr));
     if (!group)
         return nullptr;
 
     NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
     OutlineTypedObject *obj = NewObjectWithGroup<OutlineTypedObject>(cx, group, cx->global(),
-                                                                     gc::FINALIZE_OBJECT0, newKind);
+                                                                     gc::AllocKind::OBJECT0,
+                                                                     newKind);
     if (!obj)
         return nullptr;
 
     obj->setOwnerAndData(nullptr, nullptr);
     return obj;
 }
 
 void
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -103,20 +103,20 @@ CheckAllocatorState(ExclusiveContext *cx
 
     if (!cx->isJSContext())
         return true;
 
     JSContext *ncx = cx->asJSContext();
     JSRuntime *rt = ncx->runtime();
 #if defined(JS_GC_ZEAL) || defined(DEBUG)
     MOZ_ASSERT_IF(rt->isAtomsCompartment(ncx->compartment()),
-                  kind == FINALIZE_STRING ||
-                  kind == FINALIZE_FAT_INLINE_STRING ||
-                  kind == FINALIZE_SYMBOL ||
-                  kind == FINALIZE_JITCODE);
+                  kind == AllocKind::STRING ||
+                  kind == AllocKind::FAT_INLINE_STRING ||
+                  kind == AllocKind::SYMBOL ||
+                  kind == AllocKind::JITCODE);
     MOZ_ASSERT(!rt->isHeapBusy());
     MOZ_ASSERT(rt->gc.isAllocAllowed());
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !rt->mainThread.suppressGC)
         JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
 
@@ -151,17 +151,17 @@ CheckIncrementalZoneState(ExclusiveConte
  */
 
 template <typename T, AllowGC allowGC /* = CanGC */>
 JSObject *
 js::Allocate(ExclusiveContext *cx, AllocKind kind, size_t nDynamicSlots, InitialHeap heap,
              const Class *clasp)
 {
     static_assert(mozilla::IsConvertible<T *, JSObject *>::value, "must be JSObject derived");
-    MOZ_ASSERT(kind >= FINALIZE_OBJECT0 && kind <= FINALIZE_OBJECT_LAST);
+    MOZ_ASSERT(kind <= AllocKind::OBJECT_LAST);
     size_t thingSize = Arena::thingSize(kind);
 
     MOZ_ASSERT(thingSize == Arena::thingSize(kind));
     MOZ_ASSERT(thingSize >= sizeof(JSObject_Slots0));
     static_assert(sizeof(JSObject_Slots0) >= CellSize,
                   "All allocations must be at least the allocator-imposed minimum size.");
 
     if (!CheckAllocatorState<allowGC>(cx, kind))
--- a/js/src/gc/GCTrace.cpp
+++ b/js/src/gc/GCTrace.cpp
@@ -13,18 +13,18 @@
 
 #include "gc/GCTraceFormat.h"
 
 #include "js/HashTable.h"
 
 using namespace js;
 using namespace js::gc;
 
-JS_STATIC_ASSERT(AllocKinds == FINALIZE_LIMIT);
-JS_STATIC_ASSERT(LastObjectAllocKind == FINALIZE_OBJECT_LAST);
+JS_STATIC_ASSERT(AllocKinds == unsigned(AllocKind::LIMIT));
+JS_STATIC_ASSERT(LastObjectAllocKind == unsigned(AllocKind::OBJECT_LAST));
 
 static FILE *gcTraceFile = nullptr;
 
 static HashSet<const Class *, DefaultHasher<const Class *>, SystemAllocPolicy> tracedClasses;
 static HashSet<const ObjectGroup *, DefaultHasher<const ObjectGroup *>, SystemAllocPolicy> tracedGroups;
 
 static inline void
 WriteWord(uint64_t data)
@@ -94,18 +94,18 @@ js::gc::InitTrace(GCRuntime &gc)
     if (!gcTraceFile) {
         FinishTrace();
         return false;
     }
 
     TraceEvent(TraceEventInit, 0, TraceFormatVersion);
 
     /* Trace information about thing sizes. */
-    for (unsigned kind = 0; kind < FINALIZE_LIMIT; ++kind)
-        TraceEvent(TraceEventThingSize, Arena::thingSize((AllocKind)kind));
+    for (ALL_ALLOC_KINDS(kind))
+        TraceEvent(TraceEventThingSize, Arena::thingSize(kind));
 
     return true;
 }
 
 void
 js::gc::FinishTrace()
 {
     if (gcTraceFile) {
@@ -224,17 +224,17 @@ js::gc::TraceMajorGCStart()
     TraceEvent(TraceEventMajorGCStart);
 }
 
 void
 js::gc::TraceTenuredFinalize(Cell *thing)
 {
     if (!gcTraceFile)
         return;
-    if (thing->tenuredGetAllocKind() == FINALIZE_OBJECT_GROUP)
+    if (thing->tenuredGetAllocKind() == AllocKind::OBJECT_GROUP)
         tracedGroups.remove(static_cast<const ObjectGroup *>(thing));
     TraceEvent(TraceEventTenuredFinalize, uint64_t(thing));
 }
 
 void
 js::gc::TraceMajorGCEnd()
 {
     TraceEvent(TraceEventMajorGCEnd);
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_Heap_h
 #define gc_Heap_h
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/EnumeratedArray.h"
 #include "mozilla/PodOperations.h"
 
 #include <stddef.h>
 #include <stdint.h>
 
 #include "jspubtd.h"
 #include "jstypes.h"
 #include "jsutil.h"
@@ -70,86 +71,102 @@ MarkKind(JSTracer *trc, void **thingp, J
  * site.
  */
 enum InitialHeap {
     DefaultHeap,
     TenuredHeap
 };
 
 /* The GC allocation kinds. */
-enum AllocKind {
-    FINALIZE_OBJECT0,
-    FINALIZE_OBJECT0_BACKGROUND,
-    FINALIZE_OBJECT2,
-    FINALIZE_OBJECT2_BACKGROUND,
-    FINALIZE_OBJECT4,
-    FINALIZE_OBJECT4_BACKGROUND,
-    FINALIZE_OBJECT8,
-    FINALIZE_OBJECT8_BACKGROUND,
-    FINALIZE_OBJECT12,
-    FINALIZE_OBJECT12_BACKGROUND,
-    FINALIZE_OBJECT16,
-    FINALIZE_OBJECT16_BACKGROUND,
-    FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
-    FINALIZE_SCRIPT,
-    FINALIZE_LAZY_SCRIPT,
-    FINALIZE_SHAPE,
-    FINALIZE_ACCESSOR_SHAPE,
-    FINALIZE_BASE_SHAPE,
-    FINALIZE_OBJECT_GROUP,
-    FINALIZE_FAT_INLINE_STRING,
-    FINALIZE_STRING,
-    FINALIZE_EXTERNAL_STRING,
-    FINALIZE_SYMBOL,
-    FINALIZE_JITCODE,
-    FINALIZE_LAST = FINALIZE_JITCODE
+enum class AllocKind : uint8_t {
+    FIRST,
+    OBJECT0 = FIRST,
+    OBJECT0_BACKGROUND,
+    OBJECT2,
+    OBJECT2_BACKGROUND,
+    OBJECT4,
+    OBJECT4_BACKGROUND,
+    OBJECT8,
+    OBJECT8_BACKGROUND,
+    OBJECT12,
+    OBJECT12_BACKGROUND,
+    OBJECT16,
+    OBJECT16_BACKGROUND,
+    OBJECT_LIMIT,
+    OBJECT_LAST = OBJECT_LIMIT - 1,
+    SCRIPT,
+    LAZY_SCRIPT,
+    SHAPE,
+    ACCESSOR_SHAPE,
+    BASE_SHAPE,
+    OBJECT_GROUP,
+    FAT_INLINE_STRING,
+    STRING,
+    EXTERNAL_STRING,
+    SYMBOL,
+    JITCODE,
+    LIMIT,
+    LAST = LIMIT - 1
 };
 
-static const unsigned FINALIZE_LIMIT = FINALIZE_LAST + 1;
-static const unsigned FINALIZE_OBJECT_LIMIT = FINALIZE_OBJECT_LAST + 1;
+static_assert(uint8_t(AllocKind::OBJECT0) == 0, "Please check AllocKind iterations and comparisons"
+    " of the form |kind <= AllocKind::OBJECT_LAST| to ensure their range is still valid!");
+
+#define ALL_ALLOC_KINDS(i) AllocKind i = AllocKind::FIRST;\
+    i < AllocKind::LIMIT; i = AllocKind(uint8_t(i) + 1)
+
+#define OBJECT_ALLOC_KINDS(i) AllocKind i = AllocKind::OBJECT0;\
+    i < AllocKind::OBJECT_LIMIT; i = AllocKind(uint8_t(i) + 1)
+
+template<typename ValueType> using AllAllocKindArray =
+    mozilla::EnumeratedArray<AllocKind, AllocKind::LIMIT, ValueType>;
+
+template<typename ValueType> using ObjectAllocKindArray =
+    mozilla::EnumeratedArray<AllocKind, AllocKind::OBJECT_LIMIT, ValueType>;
 
 static inline JSGCTraceKind
 MapAllocToTraceKind(AllocKind kind)
 {
     static const JSGCTraceKind map[] = {
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT0 */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT0_BACKGROUND */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT2 */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT2_BACKGROUND */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT4 */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT4_BACKGROUND */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT8 */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT8_BACKGROUND */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT12 */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT12_BACKGROUND */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT16 */
-        JSTRACE_OBJECT,       /* FINALIZE_OBJECT16_BACKGROUND */
-        JSTRACE_SCRIPT,       /* FINALIZE_SCRIPT */
-        JSTRACE_LAZY_SCRIPT,  /* FINALIZE_LAZY_SCRIPT */
-        JSTRACE_SHAPE,        /* FINALIZE_SHAPE */
-        JSTRACE_SHAPE,        /* FINALIZE_ACCESSOR_SHAPE */
-        JSTRACE_BASE_SHAPE,   /* FINALIZE_BASE_SHAPE */
-        JSTRACE_OBJECT_GROUP, /* FINALIZE_OBJECT_GROUP */
-        JSTRACE_STRING,       /* FINALIZE_FAT_INLINE_STRING */
-        JSTRACE_STRING,       /* FINALIZE_STRING */
-        JSTRACE_STRING,       /* FINALIZE_EXTERNAL_STRING */
-        JSTRACE_SYMBOL,       /* FINALIZE_SYMBOL */
-        JSTRACE_JITCODE,      /* FINALIZE_JITCODE */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT0 */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT0_BACKGROUND */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT2 */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT2_BACKGROUND */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT4 */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT4_BACKGROUND */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT8 */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT8_BACKGROUND */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT12 */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT12_BACKGROUND */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT16 */
+        JSTRACE_OBJECT,       /* AllocKind::OBJECT16_BACKGROUND */
+        JSTRACE_SCRIPT,       /* AllocKind::SCRIPT */
+        JSTRACE_LAZY_SCRIPT,  /* AllocKind::LAZY_SCRIPT */
+        JSTRACE_SHAPE,        /* AllocKind::SHAPE */
+        JSTRACE_SHAPE,        /* AllocKind::ACCESSOR_SHAPE */
+        JSTRACE_BASE_SHAPE,   /* AllocKind::BASE_SHAPE */
+        JSTRACE_OBJECT_GROUP, /* AllocKind::OBJECT_GROUP */
+        JSTRACE_STRING,       /* AllocKind::FAT_INLINE_STRING */
+        JSTRACE_STRING,       /* AllocKind::STRING */
+        JSTRACE_STRING,       /* AllocKind::EXTERNAL_STRING */
+        JSTRACE_SYMBOL,       /* AllocKind::SYMBOL */
+        JSTRACE_JITCODE,      /* AllocKind::JITCODE */
     };
 
-    static_assert(MOZ_ARRAY_LENGTH(map) == FINALIZE_LIMIT,
+    static_assert(MOZ_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT),
                   "AllocKind-to-TraceKind mapping must be in sync");
-    return map[kind];
+    return map[size_t(kind)];
 }
 
 /*
  * This must be an upper bound, but we do not need the least upper bound, so
  * we just exclude non-background objects.
  */
-static const size_t MAX_BACKGROUND_FINALIZE_KINDS = FINALIZE_LIMIT - FINALIZE_OBJECT_LIMIT / 2;
+static const size_t MAX_BACKGROUND_FINALIZE_KINDS =
+    size_t(AllocKind::LIMIT) - size_t(AllocKind::OBJECT_LIMIT) / 2;
 
 class TenuredCell;
 
 // A GC cell is the base class for all GC things.
 struct Cell
 {
   public:
     MOZ_ALWAYS_INLINE bool isTenured() const { return !IsInsideNursery(this); }
@@ -521,17 +538,17 @@ struct ArenaHeader
   private:
     /*
      * The first span of free things in the arena. We encode it as a
      * CompactFreeSpan rather than a FreeSpan to minimize the header size.
      */
     CompactFreeSpan firstFreeSpan;
 
     /*
-     * One of AllocKind constants or FINALIZE_LIMIT when the arena does not
+     * One of AllocKind constants or AllocKind::LIMIT when the arena does not
      * contain any GC things and is on the list of empty arenas in the GC
      * chunk.
      *
      * We use 8 bits for the allocKind so the compiler can use byte-level memory
      * instructions to access it.
      */
     size_t allocKind : 8;
 
@@ -566,39 +583,40 @@ struct ArenaHeader
     static_assert(ArenaShift >= 8 + 1 + 1 + 1,
                   "ArenaHeader::auxNextLink packing assumes that ArenaShift has enough bits to "
                   "cover allocKind and hasDelayedMarking.");
 
     inline uintptr_t address() const;
     inline Chunk *chunk() const;
 
     bool allocated() const {
-        MOZ_ASSERT(allocKind <= size_t(FINALIZE_LIMIT));
-        return allocKind < size_t(FINALIZE_LIMIT);
+        MOZ_ASSERT(allocKind <= size_t(AllocKind::LIMIT));
+        return allocKind < size_t(AllocKind::LIMIT);
     }
 
     void init(JS::Zone *zoneArg, AllocKind kind) {
         MOZ_ASSERT(!allocated());
         MOZ_ASSERT(!markOverflow);
         MOZ_ASSERT(!allocatedDuringIncremental);
         MOZ_ASSERT(!hasDelayedMarking);
         zone = zoneArg;
 
-        static_assert(FINALIZE_LIMIT <= 255, "We must be able to fit the allockind into uint8_t.");
+        static_assert(size_t(AllocKind::LIMIT) <= 255,
+            "We must be able to fit the allockind into uint8_t.");
         allocKind = size_t(kind);
 
         /*
          * The firstFreeSpan is initially marked as empty (and thus the arena
          * is marked as full). See allocateFromArenaInline().
          */
         firstFreeSpan.initAsEmpty();
     }
 
     void setAsNotAllocated() {
-        allocKind = size_t(FINALIZE_LIMIT);
+        allocKind = size_t(AllocKind::LIMIT);
         markOverflow = 0;
         allocatedDuringIncremental = 0;
         hasDelayedMarking = 0;
         auxNextLink = 0;
     }
 
     inline uintptr_t arenaAddress() const;
     inline Arena *getArena();
@@ -668,21 +686,21 @@ struct Arena
   private:
     static JS_FRIEND_DATA(const uint32_t) ThingSizes[];
     static JS_FRIEND_DATA(const uint32_t) FirstThingOffsets[];
 
   public:
     static void staticAsserts();
 
     static size_t thingSize(AllocKind kind) {
-        return ThingSizes[kind];
+        return ThingSizes[size_t(kind)];
     }
 
     static size_t firstThingOffset(AllocKind kind) {
-        return FirstThingOffsets[kind];
+        return FirstThingOffsets[size_t(kind)];
     }
 
     static size_t thingsPerArena(size_t thingSize) {
         MOZ_ASSERT(thingSize % CellSize == 0);
 
         /* We should be able to fit FreeSpan in any GC thing. */
         MOZ_ASSERT(thingSize >= sizeof(FreeSpan));
 
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -33,21 +33,21 @@ static void
 IterateCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
                                JSIterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback)
 {
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         (*compartmentCallback)(rt, data, comp);
 
-    for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
-        JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind));
-        size_t thingSize = Arena::thingSize(AllocKind(thingKind));
+    for (ALL_ALLOC_KINDS(thingKind)) {
+        JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind);
+        size_t thingSize = Arena::thingSize(thingKind);
 
-        for (ArenaIter aiter(zone, AllocKind(thingKind)); !aiter.done(); aiter.next()) {
+        for (ArenaIter aiter(zone, thingKind); !aiter.done(); aiter.next()) {
             ArenaHeader *aheader = aiter.get();
             (*arenaCallback)(rt, data, aheader->getArena(), traceKind, thingSize);
             for (ArenaCellIterUnderGC iter(aheader); !iter.done(); iter.next())
                 (*cellCallback)(rt, data, iter.getCell(), traceKind, thingSize);
         }
     }
 }
 
@@ -93,37 +93,37 @@ js::IterateChunks(JSRuntime *rt, void *d
 void
 js::IterateScripts(JSRuntime *rt, JSCompartment *compartment,
                    void *data, IterateScriptCallback scriptCallback)
 {
     rt->gc.evictNursery();
     AutoPrepareForTracing prep(rt, SkipAtoms);
 
     if (compartment) {
-        for (ZoneCellIterUnderGC i(compartment->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (ZoneCellIterUnderGC i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             if (script->compartment() == compartment)
                 scriptCallback(rt, data, script);
         }
     } else {
         for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-            for (ZoneCellIterUnderGC i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next())
+            for (ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next())
                 scriptCallback(rt, data, i.get<JSScript>());
         }
     }
 }
 
 void
 js::IterateGrayObjects(Zone *zone, GCThingCallback cellCallback, void *data)
 {
     zone->runtimeFromMainThread()->gc.evictNursery();
     AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms);
 
-    for (size_t finalizeKind = 0; finalizeKind <= FINALIZE_OBJECT_LAST; finalizeKind++) {
-        for (ZoneCellIterUnderGC i(zone, AllocKind(finalizeKind)); !i.done(); i.next()) {
+    for (OBJECT_ALLOC_KINDS(thingKind)) {
+        for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
             JSObject *obj = i.get<JSObject>();
             if (obj->asTenured().isMarked(GRAY))
                 cellCallback(data, JS::GCCellPtr(obj));
         }
     }
 }
 
 JS_PUBLIC_API(void)
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -414,17 +414,17 @@ static AllocKind
 GetObjectAllocKindForCopy(const Nursery &nursery, JSObject *obj)
 {
     if (obj->is<ArrayObject>()) {
         ArrayObject *aobj = &obj->as<ArrayObject>();
         MOZ_ASSERT(aobj->numFixedSlots() == 0);
 
         /* Use minimal size object if we are just going to copy the pointer. */
         if (!nursery.isInside(aobj->getElementsHeader()))
-            return FINALIZE_OBJECT0_BACKGROUND;
+            return AllocKind::OBJECT0_BACKGROUND;
 
         size_t nelements = aobj->getDenseCapacity();
         return GetBackgroundAllocKind(GetGCArrayKind(nelements));
     }
 
     if (obj->is<JSFunction>())
         return obj->as<JSFunction>().getAllocKind();
 
@@ -454,17 +454,17 @@ GetObjectAllocKindForCopy(const Nursery 
         // to check forwarding pointers.
         TypeDescr *descr = &obj->as<InlineTypedObject>().typeDescr();
         MOZ_ASSERT(!IsInsideNursery(descr));
         return InlineTypedObject::allocKindForTypeDescriptor(descr);
     }
 
     // Outline typed objects use the minimum allocation kind.
     if (obj->is<OutlineTypedObject>())
-        return FINALIZE_OBJECT0;
+        return AllocKind::OBJECT0;
 
     // All nursery allocatable non-native objects are handled above.
     MOZ_ASSERT(obj->isNative());
 
     AllocKind kind = GetGCObjectFixedSlotsKind(obj->as<NativeObject>().numFixedSlots());
     MOZ_ASSERT(!IsBackgroundFinalized(kind));
     if (!CanBeFinalizedInBackground(kind, obj->getClass()))
         return kind;
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -19,17 +19,16 @@
 #include "builtin/MapObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "jit/MacroAssembler.h"
 #include "js/HashTable.h"
 #include "vm/Debugger.h"
 #include "vm/JSONParser.h"
-#include "vm/PropDesc.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
@@ -51,22 +50,16 @@ MarkBindingsRoot(JSTracer *trc, Bindings
 }
 
 void
 MarkPropertyDescriptorRoot(JSTracer *trc, JSPropertyDescriptor *pd, const char *name)
 {
     pd->trace(trc);
 }
 
-void
-MarkPropDescRoot(JSTracer *trc, PropDesc *pd, const char *name)
-{
-    pd->trace(trc);
-}
-
 template <class T>
 static inline bool
 IgnoreExactRoot(T *thingp)
 {
     return false;
 }
 
 template <class T>
@@ -111,17 +104,16 @@ MarkExactStackRootsAcrossTypes(T context
     MarkExactStackRootList<JSScript *, MarkScriptRoot>(trc, context, "exact-script");
     MarkExactStackRootList<LazyScript *, MarkLazyScriptRoot>(trc, context, "exact-lazy-script");
     MarkExactStackRootList<jsid, MarkIdRoot>(trc, context, "exact-id");
     MarkExactStackRootList<Value, MarkValueRoot>(trc, context, "exact-value");
     MarkExactStackRootList<TypeSet::Type, TypeSet::MarkTypeRoot>(trc, context, "TypeSet::Type");
     MarkExactStackRootList<Bindings, MarkBindingsRoot>(trc, context, "Bindings");
     MarkExactStackRootList<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(
         trc, context, "JSPropertyDescriptor");
-    MarkExactStackRootList<PropDesc, MarkPropDescRoot>(trc, context, "PropDesc");
 }
 
 static void
 MarkExactStackRoots(JSRuntime* rt, JSTracer *trc)
 {
     for (ContextIter cx(rt); !cx.done(); cx.next())
         MarkExactStackRootsAcrossTypes<JSContext*>(cx.get(), trc);
     MarkExactStackRootsAcrossTypes<PerThreadData*>(&rt->mainThread, trc);
@@ -144,18 +136,18 @@ AutoGCRooter::trace(JSTracer *trc)
 
       case IDARRAY: {
         JSIdArray *ida = static_cast<AutoIdArray *>(this)->idArray;
         MarkIdRange(trc, ida->length, ida->vector, "JS::AutoIdArray.idArray");
         return;
       }
 
       case DESCVECTOR: {
-        AutoPropDescVector::VectorImpl &descriptors =
-            static_cast<AutoPropDescVector *>(this)->vector;
+        AutoPropertyDescriptorVector::VectorImpl &descriptors =
+            static_cast<AutoPropertyDescriptorVector *>(this)->vector;
         for (size_t i = 0, len = descriptors.length(); i < len; i++)
             descriptors[i].trace(trc);
         return;
       }
 
       case VALVECTOR: {
         AutoValueVector::VectorImpl &vector = static_cast<AutoValueVector *>(this)->vector;
         MarkValueRootRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
@@ -489,17 +481,17 @@ js::gc::GCRuntime::markRuntime(JSTracer 
         acx->mark(trc);
 
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         if (traceOrMark == MarkRuntime && !zone->isCollecting())
             continue;
 
         /* Do not discard scripts with counts while profiling. */
         if (rt->profilingScripts && !isHeapMinorCollecting()) {
-            for (ZoneCellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
+            for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->hasScriptCounts()) {
                     MarkScriptRoot(trc, &script, "profilingScripts");
                     MOZ_ASSERT(script == i.get<JSScript>());
                 }
             }
         }
     }
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -496,20 +496,20 @@ js::gc::GCRuntime::endVerifyPostBarriers
     if (!edges.init())
         goto oom;
     trc->edges = &edges;
     storeBuffer.markAll(trc);
 
     /* Walk the heap to find any edges not the the |edges| set. */
     trc->setTraceCallback(PostVerifierVisitEdge);
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
-        for (size_t kind = 0; kind < FINALIZE_LIMIT; ++kind) {
-            for (ZoneCellIterUnderGC cells(zone, AllocKind(kind)); !cells.done(); cells.next()) {
+        for (ALL_ALLOC_KINDS(kind)) {
+            for (ZoneCellIterUnderGC cells(zone, kind); !cells.done(); cells.next()) {
                 Cell *src = cells.getCell();
-                JS_TraceChildren(trc, src, MapAllocToTraceKind(AllocKind(kind)));
+                JS_TraceChildren(trc, src, MapAllocToTraceKind(kind));
             }
         }
     }
 
 oom:
     js_delete(trc);
     verifyPostData = nullptr;
     return true;
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -126,17 +126,17 @@ Zone::sweepBreakpoints(FreeOp *fop)
         return;
 
     /*
      * Sweep all compartments in a zone at the same time, since there is no way
      * to iterate over the scripts belonging to a single compartment in a zone.
      */
 
     MOZ_ASSERT(isGCSweepingOrCompacting());
-    for (ZoneCellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         MOZ_ASSERT_IF(isGCSweeping(), script->zone()->isGCSweeping());
         if (!script->hasAnyBreakpointsOrStepMode())
             continue;
 
         bool scriptGone = IsScriptAboutToBeFinalized(&script);
         MOZ_ASSERT(script == i.get<JSScript>());
         for (unsigned i = 0; i < script->length(); i++) {
@@ -166,29 +166,29 @@ Zone::discardJitCode(FreeOp *fop)
         return;
 
     if (isPreservingCode()) {
         PurgeJITCaches(this);
     } else {
 
 #ifdef DEBUG
         /* Assert no baseline scripts are marked as active. */
-        for (ZoneCellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active());
         }
 #endif
 
         /* Mark baseline scripts on the stack as active. */
         jit::MarkActiveBaselineScripts(this);
 
         /* Only mark OSI points if code is being discarded. */
         jit::InvalidateAll(fop, this);
 
-        for (ZoneCellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             jit::FinishInvalidation(fop, script);
 
             /*
              * Discard baseline script if it's not marked as active. Note that
              * this also resets the active flag.
              */
             jit::FinishDiscardBaselineScript(fop, script);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/recover-lambdas-bug1133389.js
@@ -0,0 +1,17 @@
+var o = {}
+Object.defineProperty(o, "p", {
+    get: function() {
+        return arguments.callee.caller.caller;
+    }
+});
+
+function f() {
+    function g() {
+        return o.p;
+    }
+    return g();
+}
+
+for (var k = 0; k < 2; k++) {
+    assertEq(f(), f);
+}
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1071,44 +1071,44 @@ jit::AddSizeOfBaselineData(JSScript *scr
     if (script->hasBaselineScript())
         script->baselineScript()->addSizeOfIncludingThis(mallocSizeOf, data, fallbackStubs);
 }
 
 void
 jit::ToggleBaselineProfiling(JSRuntime *runtime, bool enable)
 {
     for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
-        for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             if (!script->hasBaselineScript())
                 continue;
             script->baselineScript()->toggleProfilerInstrumentation(enable);
         }
     }
 }
 
 #ifdef JS_TRACE_LOGGING
 void
 jit::ToggleBaselineTraceLoggerScripts(JSRuntime *runtime, bool enable)
 {
     for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
-        for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             if (!script->hasBaselineScript())
                 continue;
             script->baselineScript()->toggleTraceLoggerScripts(runtime, script, enable);
         }
     }
 }
 
 void
 jit::ToggleBaselineTraceLoggerEngine(JSRuntime *runtime, bool enable)
 {
     for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
-        for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             if (!script->hasBaselineScript())
                 continue;
             script->baselineScript()->toggleTraceLoggerEngine(enable);
         }
     }
 }
 #endif
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4792,17 +4792,17 @@ CodeGenerator::visitCreateThisWithTempla
     const js::Class *clasp = templateObject->getClass();
     size_t ndynamic = 0;
     if (templateObject->isNative())
         ndynamic = templateObject->as<NativeObject>().numDynamicSlots();
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
 
     OutOfLineCode *ool = oolCallVM(NewGCObjectInfo, lir,
-                                   (ArgList(), Imm32(allocKind), Imm32(initialHeap),
+                                   (ArgList(), Imm32(int32_t(allocKind)), Imm32(initialHeap),
                                     Imm32(ndynamic), ImmPtr(clasp)),
                                    StoreRegisterTo(objReg));
 
     // Allocate. If the FreeList is empty, call to VM, which may GC.
     masm.newGCThing(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
 
     // Initialize based on the templateObject.
     masm.bind(ool->rejoin());
@@ -6138,42 +6138,47 @@ JitRuntime::generateFreeStub(JSContext *
 }
 
 
 JitCode *
 JitRuntime::generateLazyLinkStub(JSContext *cx)
 {
     MacroAssembler masm(cx);
 #ifdef JS_USE_LINK_REGISTER
-    masm.push(lr);
+    masm.pushReturnAddress();
 #endif
 
-    Label call;
     GeneralRegisterSet regs = GeneralRegisterSet::Volatile();
     Register temp0 = regs.takeAny();
 
-    masm.callWithExitFrame(&call);
-#ifdef JS_USE_LINK_REGISTER
-    // sigh, this should probably attempt to bypass the push lr that starts off the block
-    // but oh well.
-    masm.pop(lr);
-#endif
-    masm.jump(ReturnReg);
-
-    masm.bind(&call);
-#ifdef JS_USE_LINK_REGISTER
-        masm.push(lr);
-#endif
-    masm.enterExitFrame();
+    // The caller did not push an exit frame on the stack, it pushed a
+    // JitFrameLayout.  We modify the descriptor to be a valid exit frame and
+    // restore it once the lazy link is complete.
+    Address descriptor(StackPointer, CommonFrameLayout::offsetOfDescriptor());
+    size_t convertToExitFrame = JitFrameLayout::Size() - ExitFrameLayout::Size();
+    masm.addPtr(Imm32(convertToExitFrame << FRAMESIZE_SHIFT), descriptor);
+
+    masm.enterFakeExitFrame(LazyLinkExitFrameLayout::Token());
+    masm.PushStubCode();
+
     masm.setupUnalignedABICall(1, temp0);
     masm.loadJSContext(temp0);
     masm.passABIArg(temp0);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, LazyLinkTopActivation));
-    masm.leaveExitFrame();
-    masm.retn(Imm32(sizeof(ExitFrameLayout)));
+
+    masm.leaveExitFrame(/* stub code */ sizeof(JitCode*));
+
+    masm.addPtr(Imm32(- (convertToExitFrame << FRAMESIZE_SHIFT)), descriptor);
+
+#ifdef JS_USE_LINK_REGISTER
+    // Restore the return address such that the emitPrologue function of the
+    // CodeGenerator can push it back on the stack with pushReturnAddress.
+    masm.pop(lr);
+#endif
+    masm.jump(ReturnReg);
 
     Linker linker(masm);
     AutoFlushICache afc("LazyLinkStub");
     JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "LazyLinkStub");
 #endif
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -439,25 +439,22 @@ FinishAllOffThreadCompilations(JSCompart
 
 uint8_t *
 jit::LazyLinkTopActivation(JSContext *cx)
 {
     JitActivationIterator iter(cx->runtime());
 
     // First frame should be an exit frame.
     JitFrameIterator it(iter);
-    MOZ_ASSERT(it.type() == JitFrame_Exit);
-
-    // Second frame is the Ion frame.
-    ++it;
-    MOZ_ASSERT(it.type() == JitFrame_IonJS);
+    LazyLinkExitFrameLayout *ll = it.exitFrame()->as<LazyLinkExitFrameLayout>();
+    JSScript *calleeScript = ScriptFromCalleeToken(ll->jsFrame()->calleeToken());
 
     // Get the pending builder from the Ion frame.
-    IonBuilder *builder = it.script()->ionScript()->pendingBuilder();
-    it.script()->setPendingIonBuilder(cx, nullptr);
+    IonBuilder *builder = calleeScript->ionScript()->pendingBuilder();
+    calleeScript->setPendingIonBuilder(cx, nullptr);
 
     AutoEnterAnalysis enterTypes(cx);
     RootedScript script(cx, builder->script());
 
     // Remove from pending.
     builder->remove();
 
     if (CodeGenerator *codegen = builder->backgroundCodegen()) {
@@ -489,17 +486,17 @@ jit::LazyLinkTopActivation(JSContext *cx
     return script->baselineOrIonRawPointer();
 }
 
 /* static */ void
 JitRuntime::Mark(JSTracer *trc)
 {
     MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
     Zone *zone = trc->runtime()->atomsCompartment()->zone();
-    for (gc::ZoneCellIterUnderGC i(zone, gc::FINALIZE_JITCODE); !i.done(); i.next()) {
+    for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) {
         JitCode *code = i.get<JitCode>();
         MarkJitCodeRoot(trc, &code, "wrapper");
     }
 }
 
 /* static */ bool
 JitRuntime::MarkJitcodeGlobalTableIteratively(JSTracer *trc)
 {
@@ -1131,17 +1128,17 @@ IonScript::unlinkFromRuntime(FreeOp *fop
 
 void
 jit::ToggleBarriers(JS::Zone *zone, bool needs)
 {
     JSRuntime *rt = zone->runtimeFromMainThread();
     if (!rt->hasJitRuntime())
         return;
 
-    for (gc::ZoneCellIterUnderGC i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+    for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->hasIonScript())
             script->ionScript()->toggleBarriers(needs);
         if (script->hasBaselineScript())
             script->baselineScript()->toggleBarriers(needs);
     }
 
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -983,42 +983,47 @@ ReadAllocation(const JitFrameIterator &f
         Register reg = a->toGeneralReg()->reg();
         return frame.machineState().read(reg);
     }
     return *frame.jsFrame()->slotRef(SafepointSlotEntry(a));
 }
 #endif
 
 static void
-MarkThisAndArguments(JSTracer *trc, const JitFrameIterator &frame)
+MarkThisAndArguments(JSTracer *trc, JitFrameLayout *layout)
 {
     // Mark |this| and any extra actual arguments for an Ion frame. Marking of
     // formal arguments is taken care of by the frame's safepoint/snapshot,
     // except when the script might have lazy arguments, in which case we mark
     // them as well.
 
-    JitFrameLayout *layout = frame.jsFrame();
-
-    size_t nargs = frame.numActualArgs();
+    size_t nargs = layout->numActualArgs();
     size_t nformals = 0;
     if (CalleeTokenIsFunction(layout->calleeToken())) {
         JSFunction *fun = CalleeTokenToFunction(layout->calleeToken());
         nformals = fun->nonLazyScript()->argumentsHasVarBinding() ? 0 : fun->nargs();
     }
 
     Value *argv = layout->argv();
 
     // Trace |this|.
     gc::MarkValueRoot(trc, argv, "ion-thisv");
 
     // Trace actual arguments beyond the formals. Note + 1 for thisv.
     for (size_t i = nformals + 1; i < nargs + 1; i++)
         gc::MarkValueRoot(trc, &argv[i], "ion-argv");
 }
 
+static void
+MarkThisAndArguments(JSTracer *trc, const JitFrameIterator &frame)
+{
+    JitFrameLayout *layout = frame.jsFrame();
+    MarkThisAndArguments(trc, layout);
+}
+
 #ifdef JS_NUNBOX32
 static inline void
 WriteAllocation(const JitFrameIterator &frame, const LAllocation *a, uintptr_t value)
 {
     if (a->isGeneralReg()) {
         Register reg = a->toGeneralReg()->reg();
         frame.machineState().write(reg, value);
     } else {
@@ -1347,16 +1352,26 @@ MarkJitExitFrame(JSTracer *trc, const Ji
             Value *vp = method->vp();
             gc::MarkValueRootRange(trc, len, vp, "ion-dom-args");
         } else {
             gc::MarkValueRoot(trc, dom->vp(), "ion-dom-args");
         }
         return;
     }
 
+    if (frame.isExitFrameLayout<LazyLinkExitFrameLayout>()) {
+        LazyLinkExitFrameLayout *ll = frame.exitFrame()->as<LazyLinkExitFrameLayout>();
+        JitFrameLayout *layout = ll->jsFrame();
+
+        gc::MarkJitCodeRoot(trc, ll->stubCode(), "lazy-link-code");
+        layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken()));
+        MarkThisAndArguments(trc, layout);
+        return;
+    }
+
     if (frame.isBareExit()) {
         // Nothing to mark. Fake exit frame pushed for VM functions with
         // nothing to mark on the stack.
         return;
     }
 
     MarkJitCodeRoot(trc, footer->addressOfJitCode(), "ion-exit-code");
 
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -491,16 +491,17 @@ enum ExitFrameTokenValues
     NativeExitFrameLayoutToken            = 0x0,
     IonDOMExitFrameLayoutGetterToken      = 0x1,
     IonDOMExitFrameLayoutSetterToken      = 0x2,
     IonDOMMethodExitFrameLayoutToken      = 0x3,
     IonOOLNativeExitFrameLayoutToken      = 0x4,
     IonOOLPropertyOpExitFrameLayoutToken  = 0x5,
     IonOOLSetterOpExitFrameLayoutToken    = 0x6,
     IonOOLProxyExitFrameLayoutToken       = 0x7,
+    LazyLinkExitFrameLayoutToken          = 0xFE,
     ExitFrameLayoutBareToken              = 0xFF
 };
 
 // this is the frame layout when we are exiting ion code, and about to enter platform ABI code
 class ExitFrameLayout : public CommonFrameLayout
 {
     inline uint8_t *top() {
         return reinterpret_cast<uint8_t *>(this + 1);
@@ -869,16 +870,53 @@ ExitFrameLayout::as<IonDOMExitFrameLayou
 }
 
 struct IonDOMMethodExitFrameLayoutTraits {
     static const size_t offsetOfArgcFromArgv =
         offsetof(IonDOMMethodExitFrameLayout, argc_) -
         offsetof(IonDOMMethodExitFrameLayout, argv_);
 };
 
+// Cannot inherit implementation since we need to extend the top of
+// ExitFrameLayout.
+class LazyLinkExitFrameLayout
+{
+  protected: // silence clang warning about unused private fields
+    JitCode *stubCode_;
+    ExitFooterFrame footer_;
+    JitFrameLayout exit_;
+
+  public:
+    static JitCode *Token() { return (JitCode *) LazyLinkExitFrameLayoutToken; }
+
+    static inline size_t Size() {
+        return sizeof(LazyLinkExitFrameLayout);
+    }
+
+    inline JitCode **stubCode() {
+        return &stubCode_;
+    }
+    inline JitFrameLayout *jsFrame() {
+        return &exit_;
+    }
+    static size_t offsetOfExitFrame() {
+        return offsetof(LazyLinkExitFrameLayout, exit_);
+    }
+};
+
+template <>
+inline LazyLinkExitFrameLayout *
+ExitFrameLayout::as<LazyLinkExitFrameLayout>()
+{
+    MOZ_ASSERT(is<LazyLinkExitFrameLayout>());
+    uint8_t *sp = reinterpret_cast<uint8_t *>(this);
+    sp -= LazyLinkExitFrameLayout::offsetOfExitFrame();
+    return reinterpret_cast<LazyLinkExitFrameLayout *>(sp);
+}
+
 class ICStub;
 
 class BaselineStubFrameLayout : public CommonFrameLayout
 {
   public:
     static inline size_t Size() {
         return sizeof(BaselineStubFrameLayout);
     }
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -988,17 +988,17 @@ MacroAssembler::callFreeStub(Register sl
     pop(regSlots);
 }
 
 // Inlined equivalent of gc::AllocateObject, without failure case handling.
 void
 MacroAssembler::allocateObject(Register result, Register temp, gc::AllocKind allocKind,
                                uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail)
 {
-    MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
+    MOZ_ASSERT(allocKind <= gc::AllocKind::OBJECT_LAST);
 
     checkAllocatorState(fail);
 
     if (shouldNurseryAllocate(allocKind, initialHeap))
         return nurseryAllocate(result, temp, allocKind, nDynamicSlots, initialHeap, fail);
 
     if (!nDynamicSlots)
         return freeListAllocate(result, temp, allocKind, fail);
@@ -1024,40 +1024,40 @@ MacroAssembler::allocateObject(Register 
     bind(&success);
 }
 
 void
 MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObj,
                            gc::InitialHeap initialHeap, Label *fail)
 {
     gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
-    MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
+    MOZ_ASSERT(allocKind <= gc::AllocKind::OBJECT_LAST);
 
     size_t ndynamic = 0;
     if (templateObj->isNative())
         ndynamic = templateObj->as<NativeObject>().numDynamicSlots();
     allocateObject(result, temp, allocKind, ndynamic, initialHeap, fail);
 }
 
 void
 MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateObj,
                                gc::InitialHeap initialHeap, Label *fail, bool initContents)
 {
     gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
-    MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
+    MOZ_ASSERT(allocKind <= gc::AllocKind::OBJECT_LAST);
 
     uint32_t nDynamicSlots = 0;
     if (templateObj->isNative()) {
         nDynamicSlots = templateObj->as<NativeObject>().numDynamicSlots();
 
         // Arrays with copy on write elements do not need fixed space for an
         // elements header. The template object, which owns the original
         // elements, might have another allocation kind.
         if (templateObj->as<NativeObject>().denseElementsAreCopyOnWrite())
-            allocKind = gc::FINALIZE_OBJECT0_BACKGROUND;
+            allocKind = gc::AllocKind::OBJECT0_BACKGROUND;
     }
 
     allocateObject(obj, temp, allocKind, nDynamicSlots, initialHeap, fail);
     initGCThing(obj, temp, templateObj, initContents);
 }
 
 
 // Inlined equivalent of gc::AllocateNonObject, without failure case handling.
@@ -1068,23 +1068,23 @@ MacroAssembler::allocateNonObject(Regist
 {
     checkAllocatorState(fail);
     freeListAllocate(result, temp, allocKind, fail);
 }
 
 void
 MacroAssembler::newGCString(Register result, Register temp, Label *fail)
 {
-    allocateNonObject(result, temp, js::gc::FINALIZE_STRING, fail);
+    allocateNonObject(result, temp, js::gc::AllocKind::STRING, fail);
 }
 
 void
 MacroAssembler::newGCFatInlineString(Register result, Register temp, Label *fail)
 {
-    allocateNonObject(result, temp, js::gc::FINALIZE_FAT_INLINE_STRING, fail);
+    allocateNonObject(result, temp, js::gc::AllocKind::FAT_INLINE_STRING, fail);
 }
 
 void
 MacroAssembler::copySlotsFromTemplate(Register obj, const NativeObject *templateObj,
                                       uint32_t start, uint32_t end)
 {
     uint32_t nfixed = Min(templateObj->numFixedSlots(), end);
     for (unsigned i = start; i < nfixed; i++)
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -858,34 +858,38 @@ class MacroAssembler : public MacroAssem
     // This is a reference to a patch location where the JitCode* will be written.
   private:
     CodeOffsetLabel exitCodePatch_;
 
   private:
     void linkExitFrame();
 
   public:
+    void PushStubCode() {
+        exitCodePatch_ = PushWithPatch(ImmWord(-1));
+    }
+
     void enterExitFrame(const VMFunction *f = nullptr) {
         linkExitFrame();
         // Push the ioncode. (Bailout or VM wrapper)
-        exitCodePatch_ = PushWithPatch(ImmWord(-1));
+        PushStubCode();
         // Push VMFunction pointer, to mark arguments.
         Push(ImmPtr(f));
     }
 
     // The JitCode * argument here is one of the tokens defined in the various
     // exit frame layout classes, e.g. NativeExitFrameLayout::Token().
     void enterFakeExitFrame(JitCode *codeVal) {
         linkExitFrame();
         Push(ImmPtr(codeVal));
         Push(ImmPtr(nullptr));
     }
 
-    void leaveExitFrame() {
-        freeStack(ExitFooterFrame::Size());
+    void leaveExitFrame(size_t extraFrame = 0) {
+        freeStack(ExitFooterFrame::Size() + extraFrame);
     }
 
     bool hasEnteredExitFrame() const {
         return exitCodePatch_.offset() != 0;
     }
 
     // Generates code used to complete a bailout.
     void generateBailoutTail(Register scratch, Register bailoutInfo);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1145,17 +1145,17 @@ AssertValidObjectPtr(JSContext *cx, JSOb
     MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
 
     MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(),
                   obj->group()->clasp() == obj->maybeShape()->getObjectClass());
 
     if (obj->isTenured()) {
         MOZ_ASSERT(obj->isAligned());
         gc::AllocKind kind = obj->asTenured().getAllocKind();
-        MOZ_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
+        MOZ_ASSERT(kind <= js::gc::AllocKind::OBJECT_LAST);
         MOZ_ASSERT(obj->asTenured().zone() == cx->zone());
     }
 }
 
 void
 AssertValidObjectOrNullPtr(JSContext *cx, JSObject *obj)
 {
     if (obj)
@@ -1177,23 +1177,23 @@ AssertValidStringPtr(JSContext *cx, JSSt
         MOZ_ASSERT(str->zone() == cx->zone());
 
     MOZ_ASSERT(str->runtimeFromMainThread() == cx->runtime());
     MOZ_ASSERT(str->isAligned());
     MOZ_ASSERT(str->length() <= JSString::MAX_LENGTH);
 
     gc::AllocKind kind = str->getAllocKind();
     if (str->isFatInline())
-        MOZ_ASSERT(kind == gc::FINALIZE_FAT_INLINE_STRING);
+        MOZ_ASSERT(kind == gc::AllocKind::FAT_INLINE_STRING);
     else if (str->isExternal())
-        MOZ_ASSERT(kind == gc::FINALIZE_EXTERNAL_STRING);
+        MOZ_ASSERT(kind == gc::AllocKind::EXTERNAL_STRING);
     else if (str->isAtom() || str->isFlat())
-        MOZ_ASSERT(kind == gc::FINALIZE_STRING || kind == gc::FINALIZE_FAT_INLINE_STRING);
+        MOZ_ASSERT(kind == gc::AllocKind::STRING || kind == gc::AllocKind::FAT_INLINE_STRING);
     else
-        MOZ_ASSERT(kind == gc::FINALIZE_STRING);
+        MOZ_ASSERT(kind == gc::AllocKind::STRING);
 }
 
 void
 AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym)
 {
     // We can't closely inspect symbols from another runtime.
     if (sym->runtimeFromAnyThread() != cx->runtime())
         return;
@@ -1202,17 +1202,17 @@ AssertValidSymbolPtr(JSContext *cx, JS::
 
     MOZ_ASSERT(sym->runtimeFromMainThread() == cx->runtime());
     MOZ_ASSERT(sym->isAligned());
     if (JSString *desc = sym->description()) {
         MOZ_ASSERT(desc->isAtom());
         AssertValidStringPtr(cx, desc);
     }
 
-    MOZ_ASSERT(sym->getAllocKind() == gc::FINALIZE_SYMBOL);
+    MOZ_ASSERT(sym->getAllocKind() == gc::AllocKind::SYMBOL);
 }
 
 void
 AssertValidValue(JSContext *cx, Value *v)
 {
     if (v->isObject())
         AssertValidObjectPtr(cx, &v->toObject());
     else if (v->isString())
--- a/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
+++ b/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
@@ -45,14 +45,14 @@ BEGIN_TEST(testDefineGetterSetterNonEnum
                             JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funGetObj),
                             JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funSetObj)));
 
     JS::Rooted<JSPropertyDescriptor> desc(cx);
     CHECK(JS_GetOwnPropertyDescriptor(cx, vObject, PROPERTY_NAME, &desc));
     CHECK(desc.object());
     CHECK(desc.hasGetterObject());
     CHECK(desc.hasSetterObject());
-    CHECK(desc.isPermanent());
-    CHECK(!desc.isEnumerable());
+    CHECK(!desc.configurable());
+    CHECK(!desc.enumerable());
 
     return true;
 }
 END_TEST(testDefineGetterSetterNonEnumerable)
--- a/js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
+++ b/js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
@@ -19,27 +19,33 @@ static const unsigned ValueWithConfigura
 static bool
 Getter(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     JS::CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(true);
     return true;
 }
 
+enum PropertyDescriptorKind {
+    DataDescriptor, AccessorDescriptor
+};
+
 static bool
-CheckDescriptor(JS::Handle<JSPropertyDescriptor> desc, bool enumerable,
-                bool writable, bool configurable)
+CheckDescriptor(JS::Handle<JSPropertyDescriptor> desc, PropertyDescriptorKind kind,
+                bool enumerable, bool writable, bool configurable)
 {
     if (!desc.object())
         return false;
-    if (desc.isEnumerable() != enumerable)
+    if (!(kind == DataDescriptor ? desc.isDataDescriptor() : desc.isAccessorDescriptor()))
+        return false;
+    if (desc.enumerable() != enumerable)
         return false;
-    if (desc.isReadonly() == writable)
+    if (kind == DataDescriptor && desc.writable() != writable)
         return false;
-    if (desc.isPermanent() == configurable)
+    if (desc.configurable() != configurable)
         return false;
     return true;
 }
 
 BEGIN_TEST(testDefinePropertyIgnoredAttributes)
 {
     JS::RootedObject obj(cx, JS_NewPlainObject(cx));
     JS::Rooted<JSPropertyDescriptor> desc(cx);
@@ -47,50 +53,49 @@ BEGIN_TEST(testDefinePropertyIgnoredAttr
 
     // Try a getter. Allow it to fill in the defaults.
     CHECK(JS_DefineProperty(cx, obj, "foo", defineValue,
                             IgnoreAll | JSPROP_SHARED,
                             Getter));
 
     CHECK(JS_GetPropertyDescriptor(cx, obj, "foo", &desc));
 
-    // Note that since JSPROP_READONLY means nothing for accessor properties, we will actually
-    // claim to be writable, since the flag is not included in the mask.
-    CHECK(CheckDescriptor(desc, false, true, false));
+    // Note that JSPROP_READONLY is meaningless for accessor properties.
+    CHECK(CheckDescriptor(desc, AccessorDescriptor, false, true, false));
 
     // Install another configurable property, so we can futz with it.
     CHECK(JS_DefineProperty(cx, obj, "bar", defineValue,
                             AllowConfigure | JSPROP_SHARED,
                             Getter));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "bar", &desc));
-    CHECK(CheckDescriptor(desc, false, true, true));
+    CHECK(CheckDescriptor(desc, AccessorDescriptor, false, true, true));
 
     // Rewrite the descriptor to now be enumerable, ensuring that the lack of
     // configurablity stayed.
     CHECK(JS_DefineProperty(cx, obj, "bar", defineValue,
                             AllowEnumerate |
                             JSPROP_ENUMERATE |
                             JSPROP_SHARED,
                             Getter));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "bar", &desc));
-    CHECK(CheckDescriptor(desc, true, true, true));
+    CHECK(CheckDescriptor(desc, AccessorDescriptor, true, true, true));
 
     // Now try the same game with a value property
     defineValue.setObject(*obj);
     CHECK(JS_DefineProperty(cx, obj, "baz", defineValue, IgnoreWithValue));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "baz", &desc));
-    CHECK(CheckDescriptor(desc, false, false, false));
+    CHECK(CheckDescriptor(desc, DataDescriptor, false, false, false));
 
     // Now again with a configurable property
     CHECK(JS_DefineProperty(cx, obj, "quox", defineValue, ValueWithConfigurable));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "quox", &desc));
-    CHECK(CheckDescriptor(desc, false, false, true));
+    CHECK(CheckDescriptor(desc, DataDescriptor, false, false, true));
 
     // Just make it writable. Leave the old value and everythign else alone.
     defineValue.setUndefined();
     CHECK(JS_DefineProperty(cx, obj, "quox", defineValue, AllowWritable));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "quox", &desc));
-    CHECK(CheckDescriptor(desc, false, true, true));
+    CHECK(CheckDescriptor(desc, DataDescriptor, false, true, true));
     CHECK_SAME(JS::ObjectValue(*obj), desc.value());
 
     return true;
 }
 END_TEST(testDefinePropertyIgnoredAttributes)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2720,21 +2720,20 @@ JS_DefineProperties(JSContext *cx, Handl
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectToCompletePropertyDescriptor(JSContext *cx,
                                        HandleObject obj,
                                        HandleValue descObj,
                                        MutableHandle<JSPropertyDescriptor> desc)
 {
-    Rooted<PropDesc> d(cx);
-    if (!d.initialize(cx, descObj))
+    if (!ToPropertyDescriptor(cx, descObj, true, desc))
         return false;
-    d.complete();
-    d.populatePropertyDescriptor(obj, desc);
+    CompletePropertyDescriptor(desc);
+    desc.object().set(obj);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_GetOwnPropertyDescriptorById(JSContext *cx, HandleObject obj, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc)
 {
     AssertHeapIsIdle(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2529,88 +2529,125 @@ struct JSPropertyDescriptor {
 
 namespace JS {
 
 template <typename Outer>
 class PropertyDescriptorOperations
 {
     const JSPropertyDescriptor * desc() const { return static_cast<const Outer*>(this)->extract(); }
 
+    bool has(unsigned bit) const {
+        MOZ_ASSERT(bit != 0);
+        MOZ_ASSERT((bit & (bit - 1)) == 0);  // only a single bit
+        return (desc()->attrs & bit) != 0;
+    }
+
+    bool hasAny(unsigned bits) const {
+        return (desc()->attrs & bits) != 0;
+    }
+
   public:
-    bool isEnumerable() const { return desc()->attrs & JSPROP_ENUMERATE; }
-    bool isReadonly() const { return desc()->attrs & JSPROP_READONLY; }
-    bool isPermanent() const { return desc()->attrs & JSPROP_PERMANENT; }
-    bool hasGetterObject() const { return desc()->attrs & JSPROP_GETTER; }
-    bool hasSetterObject() const { return desc()->attrs & JSPROP_SETTER; }
-    bool hasGetterOrSetterObject() const { return desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER); }
-    bool hasGetterOrSetter() const { return desc()->getter || desc()->setter; }
-    bool isShared() const { return desc()->attrs & JSPROP_SHARED; }
-    bool isIndex() const { return desc()->attrs & JSPROP_INDEX; }
-    bool hasAttributes(unsigned attrs) const { return desc()->attrs & attrs; }
-
     // Descriptors with JSGetterOp/JSSetterOp are considered data
     // descriptors. It's complicated.
-    bool isAccessorDescriptor() const { return hasGetterOrSetterObject(); }
-    bool isDataDescriptor() const { return !isAccessorDescriptor(); }
-
-    bool isWritable() const { MOZ_ASSERT(isDataDescriptor()); return !isReadonly(); }
+    bool isAccessorDescriptor() const { return hasAny(JSPROP_GETTER | JSPROP_SETTER); }
+    bool isGenericDescriptor() const {
+        return (desc()->attrs &
+                (JSPROP_GETTER | JSPROP_SETTER | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
+               (JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
+    }
+    bool isDataDescriptor() const { return !isAccessorDescriptor() && !isGenericDescriptor(); }
+
+    bool hasConfigurable() const { return !has(JSPROP_IGNORE_PERMANENT); }
+    bool configurable() const { MOZ_ASSERT(hasConfigurable()); return !has(JSPROP_PERMANENT); }
+
+    bool hasEnumerable() const { return !has(JSPROP_IGNORE_ENUMERATE); }
+    bool enumerable() const { MOZ_ASSERT(hasEnumerable()); return has(JSPROP_ENUMERATE); }
+
+    bool hasValue() const { return !isAccessorDescriptor() && !has(JSPROP_IGNORE_VALUE); }
+    JS::HandleValue value() const {
+        return JS::HandleValue::fromMarkedLocation(&desc()->value);
+    }
+
+    bool hasWritable() const { return !isAccessorDescriptor() && !has(JSPROP_IGNORE_READONLY); }
+    bool writable() const { MOZ_ASSERT(hasWritable()); return !has(JSPROP_READONLY); }
+
+    bool hasGetterObject() const { return has(JSPROP_GETTER); }
+    JS::HandleObject getterObject() const {
+        MOZ_ASSERT(hasGetterObject());
+        return JS::HandleObject::fromMarkedLocation(
+                reinterpret_cast<JSObject *const *>(&desc()->getter));
+    }
+    bool hasSetterObject() const { return has(JSPROP_SETTER); }
+    JS::HandleObject setterObject() const {
+        MOZ_ASSERT(hasSetterObject());
+        return JS::HandleObject::fromMarkedLocation(
+                reinterpret_cast<JSObject *const *>(&desc()->setter));
+    }
+
+    bool hasGetterOrSetter() const { return desc()->getter || desc()->setter; }
+    bool isShared() const { return has(JSPROP_SHARED); }
 
     JS::HandleObject object() const {
         return JS::HandleObject::fromMarkedLocation(&desc()->obj);
     }
     unsigned attributes() const { return desc()->attrs; }
     JSGetterOp getter() const { return desc()->getter; }
     JSSetterOp setter() const { return desc()->setter; }
-    JS::HandleObject getterObject() const {
-        MOZ_ASSERT(hasGetterObject());
-        return JS::HandleObject::fromMarkedLocation(
-                reinterpret_cast<JSObject *const *>(&desc()->getter));
-    }
-    JS::HandleObject setterObject() const {
-        MOZ_ASSERT(hasSetterObject());
-        return JS::HandleObject::fromMarkedLocation(
-                reinterpret_cast<JSObject *const *>(&desc()->setter));
-    }
-    JS::HandleValue value() const {
-        return JS::HandleValue::fromMarkedLocation(&desc()->value);
-    }
 };
 
 template <typename Outer>
 class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations<Outer>
 {
     JSPropertyDescriptor * desc() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
-
     void clear() {
         object().set(nullptr);
         setAttributes(0);
         setGetter(nullptr);
         setSetter(nullptr);
         value().setUndefined();
     }
 
     void assign(JSPropertyDescriptor &other) {
         object().set(other.obj);
         setAttributes(other.attrs);
         setGetter(other.getter);
         setSetter(other.setter);
         value().set(other.value);
     }
 
+    void setDataDescriptor(HandleValue v, unsigned attrs) {
+        MOZ_ASSERT((attrs & ~(JSPROP_ENUMERATE |
+                              JSPROP_PERMANENT |
+                              JSPROP_READONLY |
+                              JSPROP_IGNORE_ENUMERATE |
+                              JSPROP_IGNORE_PERMANENT |
+                              JSPROP_IGNORE_READONLY)) == 0);
+        object().set(nullptr);
+        setAttributes(attrs);
+        setGetter(nullptr);
+        setSetter(nullptr);
+        value().set(v);
+    }
+
     JS::MutableHandleObject object() {
         return JS::MutableHandleObject::fromMarkedLocation(&desc()->obj);
     }
     unsigned &attributesRef() { return desc()->attrs; }
     JSGetterOp &getter() { return desc()->getter; }
     JSSetterOp &setter() { return desc()->setter; }
     JS::MutableHandleValue value() {
         return JS::MutableHandleValue::fromMarkedLocation(&desc()->value);
     }
+    void setValue(JS::HandleValue v) {
+        MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+        attributesRef() &= ~JSPROP_IGNORE_VALUE;
+        value().set(v);
+    }
 
     void setEnumerable() { desc()->attrs |= JSPROP_ENUMERATE; }
     void setAttributes(unsigned attrs) { desc()->attrs = attrs; }
 
     void setGetter(JSGetterOp op) {
         MOZ_ASSERT(op != JS_PropertyStub);
         desc()->getter = op;
     }
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3218,21 +3218,21 @@ CreateArrayPrototype(JSContext *cx, JSPr
 
     JSObject *metadata = nullptr;
     if (!NewObjectMetadata(cx, &metadata))
         return nullptr;
 
     proto->assertParentIs(cx->global());
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
                                                       cx->global(), metadata,
-                                                      gc::FINALIZE_OBJECT0));
+                                                      gc::AllocKind::OBJECT0));
     if (!shape)
         return nullptr;
 
-    RootedArrayObject arrayProto(cx, ArrayObject::createArray(cx, gc::FINALIZE_OBJECT4,
+    RootedArrayObject arrayProto(cx, ArrayObject::createArray(cx, gc::AllocKind::OBJECT4,
                                                               gc::TenuredHeap, shape, group, 0));
     if (!arrayProto ||
         !JSObject::setSingleton(cx, arrayProto) ||
         !AddLengthProperty(cx, arrayProto))
     {
         return nullptr;
     }
 
@@ -3347,17 +3347,17 @@ NewArray(ExclusiveContext *cxArg, uint32
         return nullptr;
 
     /*
      * Get a shape with zero fixed slots, regardless of the size class.
      * See JSObject::createArray.
      */
     RootedShape shape(cxArg, EmptyShape::getInitialShape(cxArg, &ArrayObject::class_,
                                                          TaggedProto(proto), cxArg->global(),
-                                                         metadata, gc::FINALIZE_OBJECT0));
+                                                         metadata, gc::AllocKind::OBJECT0));
     if (!shape)
         return nullptr;
 
     RootedArrayObject arr(cxArg, ArrayObject::createArray(cxArg, allocKind,
                                                           GetInitialHeap(newKind, &ArrayObject::class_),
                                                           shape, group, length));
     if (!arr)
         return nullptr;
--- a/js/src/jsast.tbl
+++ b/js/src/jsast.tbl
@@ -24,17 +24,17 @@ ASTDEF(AST_UNARY_EXPR,            "Unary
 ASTDEF(AST_BINARY_EXPR,           "BinaryExpression",               "binaryExpression")
 ASTDEF(AST_ASSIGN_EXPR,           "AssignmentExpression",           "assignmentExpression")
 ASTDEF(AST_LOGICAL_EXPR,          "LogicalExpression",              "logicalExpression")
 ASTDEF(AST_UPDATE_EXPR,           "UpdateExpression",               "updateExpression")
 ASTDEF(AST_NEW_EXPR,              "NewExpression",                  "newExpression")
 ASTDEF(AST_CALL_EXPR,             "CallExpression",                 "callExpression")
 ASTDEF(AST_MEMBER_EXPR,           "MemberExpression",               "memberExpression")
 ASTDEF(AST_FUNC_EXPR,             "FunctionExpression",             "functionExpression")
-ASTDEF(AST_ARROW_EXPR,            "ArrowExpression",                "arrowExpression")
+ASTDEF(AST_ARROW_EXPR,            "ArrowFunctionExpression",        "arrowFunctionExpression")
 ASTDEF(AST_ARRAY_EXPR,            "ArrayExpression",                "arrayExpression")
 ASTDEF(AST_SPREAD_EXPR,           "SpreadExpression",               "spreadExpression")
 ASTDEF(AST_OBJECT_EXPR,           "ObjectExpression",               "objectExpression")
 ASTDEF(AST_THIS_EXPR,             "ThisExpression",                 "thisExpression")
 ASTDEF(AST_COMP_EXPR,             "ComprehensionExpression",        "comprehensionExpression")
 ASTDEF(AST_GENERATOR_EXPR,        "GeneratorExpression",            "generatorExpression")
 ASTDEF(AST_YIELD_EXPR,            "YieldExpression",                "yieldExpression")
 ASTDEF(AST_LET_EXPR,              "LetExpression",                  "letExpression")
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -470,45 +470,16 @@ JSCompartment::wrap(JSContext *cx, Mutab
     if (desc.hasSetterObject()) {
         if (!wrap(cx, desc.setterObject()))
             return false;
     }
 
     return wrap(cx, desc.value());
 }
 
-bool
-JSCompartment::wrap(JSContext *cx, MutableHandle<PropDesc> desc)
-{
-    if (desc.isUndefined())
-        return true;
-
-    JSCompartment *comp = cx->compartment();
-
-    if (desc.hasValue()) {
-        RootedValue value(cx, desc.value());
-        if (!comp->wrap(cx, &value))
-            return false;
-        desc.setValue(value);
-    }
-    if (desc.hasGet()) {
-        RootedValue get(cx, desc.getterValue());
-        if (!comp->wrap(cx, &get))
-            return false;
-        desc.setGetter(get);
-    }
-    if (desc.hasSet()) {
-        RootedValue set(cx, desc.setterValue());
-        if (!comp->wrap(cx, &set))
-            return false;
-        desc.setSetter(set);
-    }
-    return true;
-}
-
 /*
  * This method marks pointers that cross compartment boundaries. It is called in
  * per-zone GCs (since full GCs naturally follow pointers across compartments)
  * and when compacting to update cross-compartment pointers.
  */
 void
 JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
 {
@@ -755,17 +726,17 @@ CreateLazyScriptsForCompartment(JSContex
     AutoObjectVector lazyFunctions(cx);
 
     // Find all live lazy scripts in the compartment, and via them all root
     // lazy functions in the compartment: those which have not been compiled,
     // which have a source object, indicating that they have a parent, and
     // which do not have an uncompiled enclosing script. The last condition is
     // so that we don't compile lazy scripts whose enclosing scripts failed to
     // compile, indicating that the lazy script did not escape the script.
-    for (gc::ZoneCellIter i(cx->zone(), gc::FINALIZE_LAZY_SCRIPT); !i.done(); i.next()) {
+    for (gc::ZoneCellIter i(cx->zone(), gc::AllocKind::LAZY_SCRIPT); !i.done(); i.next()) {
         LazyScript *lazy = i.get<LazyScript>();
         JSFunction *fun = lazy->functionNonDelazifying();
         if (fun->compartment() == cx->compartment() &&
             lazy->sourceObject() && !lazy->maybeScript() &&
             !lazy->hasUncompiledEnclosingScript())
         {
             MOZ_ASSERT(fun->isInterpretedLazy());
             MOZ_ASSERT(lazy == fun->lazyScriptOrNull());
@@ -834,17 +805,17 @@ JSCompartment::unsetIsDebuggee()
         debugModeBits &= ~DebuggerObservesMask;
         DebugScopes::onCompartmentUnsetIsDebuggee(this);
     }
 }
 
 void
 JSCompartment::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, HandleObject handler)
 {
-    for (gc::ZoneCellIter i(zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+    for (gc::ZoneCellIter i(zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
             script->clearBreakpointsIn(fop, dbg, handler);
     }
 }
 
 void
 JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -343,17 +343,16 @@ struct JSCompartment
 
     inline bool wrap(JSContext *cx, JS::MutableHandleValue vp,
                      JS::HandleObject existing = js::NullPtr());
 
     bool wrap(JSContext *cx, js::MutableHandleString strp);
     bool wrap(JSContext *cx, JS::MutableHandleObject obj,
               JS::HandleObject existingArg = js::NullPtr());
     bool wrap(JSContext *cx, JS::MutableHandle<js::PropertyDescriptor> desc);
-    bool wrap(JSContext *cx, JS::MutableHandle<js::PropDesc> desc);
 
     template<typename T> bool wrap(JSContext *cx, JS::AutoVectorRooter<T> &vec) {
         for (size_t i = 0; i < vec.length(); ++i) {
             if (!wrap(cx, vec[i]))
                 return false;
         }
         return true;
     };
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -426,21 +426,21 @@ class JSFunction : public js::NativeObje
         static_assert(offsetof(U, n.native) == offsetof(U, nativeOrScript),
                       "U::nativeOrScript must be at the same offset as "
                       "native");
 
         return offsetof(JSFunction, u.nativeOrScript);
     }
 
 #if JS_BITS_PER_WORD == 32
-    static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2_BACKGROUND;
-    static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
+    static const js::gc::AllocKind FinalizeKind = js::gc::AllocKind::OBJECT2_BACKGROUND;
+    static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::AllocKind::OBJECT4_BACKGROUND;
 #else
-    static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
-    static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8_BACKGROUND;
+    static const js::gc::AllocKind FinalizeKind = js::gc::AllocKind::OBJECT4_BACKGROUND;
+    static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::AllocKind::OBJECT8_BACKGROUND;
 #endif
 
     inline void trace(JSTracer *trc);
 
     /* Bound function accessors. */
 
     inline bool initBoundFunction(JSContext *cx, js::HandleObject target, js::HandleValue thisArg,
                                   const js::Value *args, unsigned argslen);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -247,88 +247,88 @@ using JS::AutoGCRooter;
 
 /* Perform a Full GC every 20 seconds if MaybeGC is called */
 static const uint64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
 
 /* Increase the IGC marking slice time if we are in highFrequencyGC mode. */
 static const int IGC_MARK_SLICE_MULTIPLIER = 2;
 
 const AllocKind gc::slotsToThingKind[] = {
-    /* 0 */  FINALIZE_OBJECT0,  FINALIZE_OBJECT2,  FINALIZE_OBJECT2,  FINALIZE_OBJECT4,
-    /* 4 */  FINALIZE_OBJECT4,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,
-    /* 8 */  FINALIZE_OBJECT8,  FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12,
-    /* 12 */ FINALIZE_OBJECT12, FINALIZE_OBJECT16, FINALIZE_OBJECT16, FINALIZE_OBJECT16,
-    /* 16 */ FINALIZE_OBJECT16
+    /*  0 */ AllocKind::OBJECT0,  AllocKind::OBJECT2,  AllocKind::OBJECT2,  AllocKind::OBJECT4,
+    /*  4 */ AllocKind::OBJECT4,  AllocKind::OBJECT8,  AllocKind::OBJECT8,  AllocKind::OBJECT8,
+    /*  8 */ AllocKind::OBJECT8,  AllocKind::OBJECT12, AllocKind::OBJECT12, AllocKind::OBJECT12,
+    /* 12 */ AllocKind::OBJECT12, AllocKind::OBJECT16, AllocKind::OBJECT16, AllocKind::OBJECT16,
+    /* 16 */ AllocKind::OBJECT16
 };
 
 static_assert(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT,
               "We have defined a slot count for each kind.");
 
 // Assert that SortedArenaList::MinThingSize is <= the real minimum thing size.
 #define CHECK_MIN_THING_SIZE_INNER(x_)                                         \
     static_assert(x_ >= SortedArenaList::MinThingSize,                         \
     #x_ " is less than SortedArenaList::MinThingSize!");
 #define CHECK_MIN_THING_SIZE(...) { __VA_ARGS__ }; /* Define the array. */     \
     MOZ_FOR_EACH(CHECK_MIN_THING_SIZE_INNER, (), (__VA_ARGS__ UINT32_MAX))
 
 const uint32_t Arena::ThingSizes[] = CHECK_MIN_THING_SIZE(
-    sizeof(JSObject_Slots0),    /* FINALIZE_OBJECT0             */
-    sizeof(JSObject_Slots0),    /* FINALIZE_OBJECT0_BACKGROUND  */
-    sizeof(JSObject_Slots2),    /* FINALIZE_OBJECT2             */
-    sizeof(JSObject_Slots2),    /* FINALIZE_OBJECT2_BACKGROUND  */
-    sizeof(JSObject_Slots4),    /* FINALIZE_OBJECT4             */
-    sizeof(JSObject_Slots4),    /* FINALIZE_OBJECT4_BACKGROUND  */
-    sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8             */
-    sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
-    sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
-    sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
-    sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
-    sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
-    sizeof(JSScript),           /* FINALIZE_SCRIPT              */
-    sizeof(LazyScript),         /* FINALIZE_LAZY_SCRIPT         */
-    sizeof(Shape),              /* FINALIZE_SHAPE               */
-    sizeof(AccessorShape),      /* FINALIZE_ACCESSOR_SHAPE      */
-    sizeof(BaseShape),          /* FINALIZE_BASE_SHAPE          */
-    sizeof(ObjectGroup),        /* FINALIZE_OBJECT_GROUP        */
-    sizeof(JSFatInlineString),  /* FINALIZE_FAT_INLINE_STRING   */
-    sizeof(JSString),           /* FINALIZE_STRING              */
-    sizeof(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
-    sizeof(JS::Symbol),         /* FINALIZE_SYMBOL              */
-    sizeof(jit::JitCode),       /* FINALIZE_JITCODE             */
+    sizeof(JSObject_Slots0),    /* AllocKind::OBJECT0             */
+    sizeof(JSObject_Slots0),    /* AllocKind::OBJECT0_BACKGROUND  */
+    sizeof(JSObject_Slots2),    /* AllocKind::OBJECT2             */
+    sizeof(JSObject_Slots2),    /* AllocKind::OBJECT2_BACKGROUND  */
+    sizeof(JSObject_Slots4),    /* AllocKind::OBJECT4             */
+    sizeof(JSObject_Slots4),    /* AllocKind::OBJECT4_BACKGROUND  */
+    sizeof(JSObject_Slots8),    /* AllocKind::OBJECT8             */
+    sizeof(JSObject_Slots8),    /* AllocKind::OBJECT8_BACKGROUND  */
+    sizeof(JSObject_Slots12),   /* AllocKind::OBJECT12            */
+    sizeof(JSObject_Slots12),   /* AllocKind::OBJECT12_BACKGROUND */
+    sizeof(JSObject_Slots16),   /* AllocKind::OBJECT16            */
+    sizeof(JSObject_Slots16),   /* AllocKind::OBJECT16_BACKGROUND */
+    sizeof(JSScript),           /* AllocKind::SCRIPT              */
+    sizeof(LazyScript),         /* AllocKind::LAZY_SCRIPT         */
+    sizeof(Shape),              /* AllocKind::SHAPE               */
+    sizeof(AccessorShape),      /* AllocKind::ACCESSOR_SHAPE      */
+    sizeof(BaseShape),          /* AllocKind::BASE_SHAPE          */
+    sizeof(ObjectGroup),        /* AllocKind::OBJECT_GROUP        */
+    sizeof(JSFatInlineString),  /* AllocKind::FAT_INLINE_STRING   */
+    sizeof(JSString),           /* AllocKind::STRING              */
+    sizeof(JSExternalString),   /* AllocKind::EXTERNAL_STRING     */
+    sizeof(JS::Symbol),         /* AllocKind::SYMBOL              */
+    sizeof(jit::JitCode),       /* AllocKind::JITCODE             */
 );
 
 #undef CHECK_MIN_THING_SIZE_INNER
 #undef CHECK_MIN_THING_SIZE
 
 #define OFFSET(type) uint32_t(sizeof(ArenaHeader) + (ArenaSize - sizeof(ArenaHeader)) % sizeof(type))
 
 const uint32_t Arena::FirstThingOffsets[] = {
-    OFFSET(JSObject_Slots0),    /* FINALIZE_OBJECT0             */
-    OFFSET(JSObject_Slots0),    /* FINALIZE_OBJECT0_BACKGROUND  */
-    OFFSET(JSObject_Slots2),    /* FINALIZE_OBJECT2             */
-    OFFSET(JSObject_Slots2),    /* FINALIZE_OBJECT2_BACKGROUND  */
-    OFFSET(JSObject_Slots4),    /* FINALIZE_OBJECT4             */
-    OFFSET(JSObject_Slots4),    /* FINALIZE_OBJECT4_BACKGROUND  */
-    OFFSET(JSObject_Slots8),    /* FINALIZE_OBJECT8             */
-    OFFSET(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
-    OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
-    OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
-    OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
-    OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
-    OFFSET(JSScript),           /* FINALIZE_SCRIPT              */
-    OFFSET(LazyScript),         /* FINALIZE_LAZY_SCRIPT         */
-    OFFSET(Shape),              /* FINALIZE_SHAPE               */
-    OFFSET(AccessorShape),      /* FINALIZE_ACCESSOR_SHAPE      */
-    OFFSET(BaseShape),          /* FINALIZE_BASE_SHAPE          */
-    OFFSET(ObjectGroup),        /* FINALIZE_OBJECT_GROUP        */
-    OFFSET(JSFatInlineString),  /* FINALIZE_FAT_INLINE_STRING   */
-    OFFSET(JSString),           /* FINALIZE_STRING              */
-    OFFSET(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
-    OFFSET(JS::Symbol),         /* FINALIZE_SYMBOL              */
-    OFFSET(jit::JitCode),       /* FINALIZE_JITCODE             */
+    OFFSET(JSObject_Slots0),    /* AllocKind::OBJECT0             */
+    OFFSET(JSObject_Slots0),    /* AllocKind::OBJECT0_BACKGROUND  */
+    OFFSET(JSObject_Slots2),    /* AllocKind::OBJECT2             */
+    OFFSET(JSObject_Slots2),    /* AllocKind::OBJECT2_BACKGROUND  */
+    OFFSET(JSObject_Slots4),    /* AllocKind::OBJECT4             */
+    OFFSET(JSObject_Slots4),    /* AllocKind::OBJECT4_BACKGROUND  */
+    OFFSET(JSObject_Slots8),    /* AllocKind::OBJECT8             */
+    OFFSET(JSObject_Slots8),    /* AllocKind::OBJECT8_BACKGROUND  */
+    OFFSET(JSObject_Slots12),   /* AllocKind::OBJECT12            */
+    OFFSET(JSObject_Slots12),   /* AllocKind::OBJECT12_BACKGROUND */
+    OFFSET(JSObject_Slots16),   /* AllocKind::OBJECT16            */
+    OFFSET(JSObject_Slots16),   /* AllocKind::OBJECT16_BACKGROUND */
+    OFFSET(JSScript),           /* AllocKind::SCRIPT              */
+    OFFSET(LazyScript),         /* AllocKind::LAZY_SCRIPT         */
+    OFFSET(Shape),              /* AllocKind::SHAPE               */
+    OFFSET(AccessorShape),      /* AllocKind::ACCESSOR_SHAPE      */
+    OFFSET(BaseShape),          /* AllocKind::BASE_SHAPE          */
+    OFFSET(ObjectGroup),        /* AllocKind::OBJECT_GROUP        */
+    OFFSET(JSFatInlineString),  /* AllocKind::FAT_INLINE_STRING   */
+    OFFSET(JSString),           /* AllocKind::STRING              */
+    OFFSET(JSExternalString),   /* AllocKind::EXTERNAL_STRING     */
+    OFFSET(JS::Symbol),         /* AllocKind::SYMBOL              */
+    OFFSET(jit::JitCode),       /* AllocKind::JITCODE             */
 };
 
 #undef OFFSET
 
 struct js::gc::FinalizePhase
 {
     size_t length;
     const AllocKind *kinds;
@@ -337,58 +337,58 @@ struct js::gc::FinalizePhase
 
 #define PHASE(x, p) { ArrayLength(x), x, p }
 
 /*
  * Finalization order for incrementally swept things.
  */
 
 static const AllocKind IncrementalPhaseStrings[] = {
-    FINALIZE_EXTERNAL_STRING
+    AllocKind::EXTERNAL_STRING
 };
 
 static const AllocKind IncrementalPhaseScripts[] = {
-    FINALIZE_SCRIPT,
-    FINALIZE_LAZY_SCRIPT
+    AllocKind::SCRIPT,
+    AllocKind::LAZY_SCRIPT
 };
 
 static const AllocKind IncrementalPhaseJitCode[] = {
-    FINALIZE_JITCODE
+    AllocKind::JITCODE
 };
 
 static const FinalizePhase IncrementalFinalizePhases[] = {
     PHASE(IncrementalPhaseStrings, gcstats::PHASE_SWEEP_STRING),
     PHASE(IncrementalPhaseScripts, gcstats::PHASE_SWEEP_SCRIPT),
     PHASE(IncrementalPhaseJitCode, gcstats::PHASE_SWEEP_JITCODE)
 };
 
 /*
  * Finalization order for things swept in the background.
  */
 
 static const AllocKind BackgroundPhaseObjects[] = {
-    FINALIZE_OBJECT0_BACKGROUND,
-    FINALIZE_OBJECT2_BACKGROUND,
-    FINALIZE_OBJECT4_BACKGROUND,
-    FINALIZE_OBJECT8_BACKGROUND,
-    FINALIZE_OBJECT12_BACKGROUND,
-    FINALIZE_OBJECT16_BACKGROUND
+    AllocKind::OBJECT0_BACKGROUND,
+    AllocKind::OBJECT2_BACKGROUND,
+    AllocKind::OBJECT4_BACKGROUND,
+    AllocKind::OBJECT8_BACKGROUND,
+    AllocKind::OBJECT12_BACKGROUND,
+    AllocKind::OBJECT16_BACKGROUND
 };
 
 static const AllocKind BackgroundPhaseStringsAndSymbols[] = {
-    FINALIZE_FAT_INLINE_STRING,
-    FINALIZE_STRING,
-    FINALIZE_SYMBOL
+    AllocKind::FAT_INLINE_STRING,
+    AllocKind::STRING,
+    AllocKind::SYMBOL
 };
 
 static const AllocKind BackgroundPhaseShapes[] = {
-    FINALIZE_SHAPE,
-    FINALIZE_ACCESSOR_SHAPE,
-    FINALIZE_BASE_SHAPE,
-    FINALIZE_OBJECT_GROUP
+    AllocKind::SHAPE,
+    AllocKind::ACCESSOR_SHAPE,
+    AllocKind::BASE_SHAPE,
+    AllocKind::OBJECT_GROUP
 };
 
 static const FinalizePhase BackgroundFinalizePhases[] = {
     PHASE(BackgroundPhaseObjects, gcstats::PHASE_SWEEP_OBJECT),
     PHASE(BackgroundPhaseStringsAndSymbols, gcstats::PHASE_SWEEP_STRING),
     PHASE(BackgroundPhaseShapes, gcstats::PHASE_SWEEP_SHAPE)
 };
 
@@ -440,18 +440,20 @@ ArenaHeader::unmarkAll()
 {
     uintptr_t *word = chunk()->bitmap.arenaBits(this);
     memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
 }
 
 /* static */ void
 Arena::staticAsserts()
 {
-    static_assert(JS_ARRAY_LENGTH(ThingSizes) == FINALIZE_LIMIT, "We have defined all thing sizes.");
-    static_assert(JS_ARRAY_LENGTH(FirstThingOffsets) == FINALIZE_LIMIT, "We have defined all offsets.");
+    static_assert(JS_ARRAY_LENGTH(ThingSizes) == size_t(AllocKind::LIMIT),
+        "We haven't defined all thing sizes.");
+    static_assert(JS_ARRAY_LENGTH(FirstThingOffsets) == size_t(AllocKind::LIMIT),
+        "We haven't defined all offsets.");
 }
 
 void
 Arena::setAsFullyUnused(AllocKind thingKind)
 {
     FreeSpan fullSpan;
     size_t thingSize = Arena::thingSize(thingKind);
     fullSpan.initFinal(thingsStart(thingKind), thingsEnd() - thingSize, thingSize);
@@ -581,50 +583,50 @@ static bool
 FinalizeArenas(FreeOp *fop,
                ArenaHeader **src,
                SortedArenaList &dest,
                AllocKind thingKind,
                SliceBudget &budget,
                ArenaLists::KeepArenasEnum keepArenas)
 {
     switch (thingKind) {
-      case FINALIZE_OBJECT0:
-      case FINALIZE_OBJECT0_BACKGROUND:
-      case FINALIZE_OBJECT2:
-      case FINALIZE_OBJECT2_BACKGROUND:
-      case FINALIZE_OBJECT4:
-      case FINALIZE_OBJECT4_BACKGROUND:
-      case FINALIZE_OBJECT8:
-      case FINALIZE_OBJECT8_BACKGROUND:
-      case FINALIZE_OBJECT12:
-      case FINALIZE_OBJECT12_BACKGROUND:
-      case FINALIZE_OBJECT16:
-      case FINALIZE_OBJECT16_BACKGROUND:
+      case AllocKind::OBJECT0:
+      case AllocKind::OBJECT0_BACKGROUND:
+      case AllocKind::OBJECT2:
+      case AllocKind::OBJECT2_BACKGROUND:
+      case AllocKind::OBJECT4:
+      case AllocKind::OBJECT4_BACKGROUND:
+      case AllocKind::OBJECT8:
+      case AllocKind::OBJECT8_BACKGROUND:
+      case AllocKind::OBJECT12:
+      case AllocKind::OBJECT12_BACKGROUND:
+      case AllocKind::OBJECT16:
+      case AllocKind::OBJECT16_BACKGROUND:
         return FinalizeTypedArenas<JSObject>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_SCRIPT:
+      case AllocKind::SCRIPT:
         return FinalizeTypedArenas<JSScript>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_LAZY_SCRIPT:
+      case AllocKind::LAZY_SCRIPT:
         return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_SHAPE:
+      case AllocKind::SHAPE:
         return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_ACCESSOR_SHAPE:
+      case AllocKind::ACCESSOR_SHAPE:
         return FinalizeTypedArenas<AccessorShape>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_BASE_SHAPE:
+      case AllocKind::BASE_SHAPE:
         return FinalizeTypedArenas<BaseShape>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_OBJECT_GROUP:
+      case AllocKind::OBJECT_GROUP:
         return FinalizeTypedArenas<ObjectGroup>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_STRING:
+      case AllocKind::STRING:
         return FinalizeTypedArenas<JSString>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_FAT_INLINE_STRING:
+      case AllocKind::FAT_INLINE_STRING:
         return FinalizeTypedArenas<JSFatInlineString>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_EXTERNAL_STRING:
+      case AllocKind::EXTERNAL_STRING:
         return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_SYMBOL:
+      case AllocKind::SYMBOL:
         return FinalizeTypedArenas<JS::Symbol>(fop, src, dest, thingKind, budget, keepArenas);
-      case FINALIZE_JITCODE:
+      case AllocKind::JITCODE:
         return FinalizeTypedArenas<jit::JitCode>(fop, src, dest, thingKind, budget, keepArenas);
       default:
         MOZ_CRASH("Invalid alloc kind");
     }
 }
 
 Chunk *
 ChunkPool::pop()
@@ -1782,17 +1784,17 @@ GCMarker::delayMarkingChildren(const voi
     const TenuredCell *cell = TenuredCell::fromPointer(thing);
     cell->arenaHeader()->markOverflow = 1;
     delayMarkingArena(cell->arenaHeader());
 }
 
 inline void
 ArenaLists::prepareForIncrementalGC(JSRuntime *rt)
 {
-    for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
+    for (ALL_ALLOC_KINDS(i)) {
         FreeList *freeList = &freeLists[i];
         if (!freeList->isEmpty()) {
             ArenaHeader *aheader = freeList->arenaHeader();
             aheader->allocatedDuringIncremental = true;
             rt->gc.marker.delayMarkingArena(aheader);
         }
     }
 }
@@ -1931,17 +1933,17 @@ static bool
 CanRelocateZone(JSRuntime *rt, Zone *zone)
 {
     return !rt->isAtomsZone(zone) && !rt->isSelfHostingZone(zone);
 }
 
 static bool
 CanRelocateAllocKind(AllocKind kind)
 {
-    return kind <= FINALIZE_OBJECT_LAST;
+    return kind <= AllocKind::OBJECT_LAST;
 }
 
 size_t ArenaHeader::countFreeCells()
 {
     size_t count = 0;
     size_t thingSize = getThingSize();
     FreeSpan firstSpan(getFirstFreeSpan());
     for (const FreeSpan *span = &firstSpan; !span->isEmpty(); span = span->nextSpan())
@@ -2057,17 +2059,17 @@ RelocateCell(Zone *zone, TenuredCell *sr
         dstAlloc = GCRuntime::refillFreeListInGC(zone, thingKind);
     if (!dstAlloc)
         return false;
     TenuredCell *dst = TenuredCell::fromPointer(dstAlloc);
 
     // Copy source cell contents to destination.
     memcpy(dst, src, thingSize);
 
-    if (thingKind <= FINALIZE_OBJECT_LAST) {
+    if (thingKind <= AllocKind::OBJECT_LAST) {
         JSObject *srcObj = static_cast<JSObject *>(static_cast<Cell *>(src));
         JSObject *dstObj = static_cast<JSObject *>(static_cast<Cell *>(dst));
 
         if (srcObj->isNative()) {
             NativeObject *srcNative = &srcObj->as<NativeObject>();
             NativeObject *dstNative = &dstObj->as<NativeObject>();
 
             // Fixup the pointer to inline object elements if necessary.
@@ -2175,38 +2177,39 @@ ArenaLists::relocateArenas(ArenaHeader *
     MOZ_ASSERT(runtime_->isHeapCompacting());
     MOZ_ASSERT(!runtime_->gc.isBackgroundSweeping());
 
     // Flush all the freeLists back into the arena headers
     purge();
     checkEmptyFreeLists();
 
     if (ShouldRelocateAllArenas(reason)) {
-        for (size_t i = 0; i < FINALIZE_LIMIT; i++) {
-            if (CanRelocateAllocKind(AllocKind(i))) {
+        for (ALL_ALLOC_KINDS(i)) {
+            if (CanRelocateAllocKind(i)) {
                 ArenaList &al = arenaLists[i];
                 ArenaHeader *allArenas = al.head();
                 al.clear();
                 relocatedListOut = al.relocateArenas(allArenas, relocatedListOut, stats);
             }
         }
     } else {
         size_t arenaCount = 0;
         size_t relocCount = 0;
-        ArenaHeader **toRelocate[FINALIZE_LIMIT] = {nullptr};
-
-        for (size_t i = 0; i < FINALIZE_LIMIT; i++) {
-            if (CanRelocateAllocKind(AllocKind(i)))
+        AllAllocKindArray<ArenaHeader **> toRelocate;
+
+        for (ALL_ALLOC_KINDS(i)) {
+            toRelocate[i] = nullptr;
+            if (CanRelocateAllocKind(i))
                 toRelocate[i] = arenaLists[i].pickArenasToRelocate(arenaCount, relocCount);
         }
 
         if (!ShouldRelocateZone(arenaCount, relocCount, reason))
             return false;
 
-        for (size_t i = 0; i < FINALIZE_LIMIT; i++) {
+        for (ALL_ALLOC_KINDS(i)) {
             if (toRelocate[i]) {
                 ArenaList &al = arenaLists[i];
                 ArenaHeader *arenas = al.removeRemainingArenas(toRelocate[i]);
                 relocatedListOut = al.relocateArenas(arenas, relocatedListOut, stats);
             }
         }
     }
 
@@ -2259,22 +2262,22 @@ MovingTracer::Visit(JSTracer *jstrc, voi
 void
 GCRuntime::sweepTypesAfterCompacting(Zone *zone)
 {
     FreeOp *fop = rt->defaultFreeOp();
     zone->beginSweepTypes(fop, rt->gc.releaseObservedTypes && !zone->isPreservingCode());
 
     AutoClearTypeInferenceStateOnOOM oom(zone);
 
-    for (ZoneCellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         script->maybeSweepTypes(&oom);
     }
 
-    for (ZoneCellIterUnderGC i(zone, FINALIZE_OBJECT_GROUP); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
         ObjectGroup *group = i.get<ObjectGroup>();
         group->maybeSweep(&oom);
     }
 
     zone->types.endSweep(rt);
 }
 
 void
@@ -2318,49 +2321,49 @@ UpdateCellPointersTyped(MovingTracer *tr
  */
 static void
 UpdateCellPointers(MovingTracer *trc, ArenaHeader *arena)
 {
     AllocKind kind = arena->getAllocKind();
     JSGCTraceKind traceKind = MapAllocToTraceKind(kind);
 
     switch (kind) {
-      case FINALIZE_OBJECT0:
-      case FINALIZE_OBJECT0_BACKGROUND:
-      case FINALIZE_OBJECT2:
-      case FINALIZE_OBJECT2_BACKGROUND:
-      case FINALIZE_OBJECT4:
-      case FINALIZE_OBJECT4_BACKGROUND:
-      case FINALIZE_OBJECT8:
-      case FINALIZE_OBJECT8_BACKGROUND:
-      case FINALIZE_OBJECT12:
-      case FINALIZE_OBJECT12_BACKGROUND:
-      case FINALIZE_OBJECT16:
-      case FINALIZE_OBJECT16_BACKGROUND:
+      case AllocKind::OBJECT0:
+      case AllocKind::OBJECT0_BACKGROUND:
+      case AllocKind::OBJECT2:
+      case AllocKind::OBJECT2_BACKGROUND:
+      case AllocKind::OBJECT4:
+      case AllocKind::OBJECT4_BACKGROUND:
+      case AllocKind::OBJECT8:
+      case AllocKind::OBJECT8_BACKGROUND:
+      case AllocKind::OBJECT12:
+      case AllocKind::OBJECT12_BACKGROUND:
+      case AllocKind::OBJECT16:
+      case AllocKind::OBJECT16_BACKGROUND:
         UpdateCellPointersTyped<JSObject>(trc, arena, traceKind);
         return;
-      case FINALIZE_SCRIPT:
+      case AllocKind::SCRIPT:
         UpdateCellPointersTyped<JSScript>(trc, arena, traceKind);
         return;
-      case FINALIZE_LAZY_SCRIPT:
+      case AllocKind::LAZY_SCRIPT:
         UpdateCellPointersTyped<LazyScript>(trc, arena, traceKind);
         return;
-      case FINALIZE_SHAPE:
+      case AllocKind::SHAPE:
         UpdateCellPointersTyped<Shape>(trc, arena, traceKind);
         return;
-      case FINALIZE_ACCESSOR_SHAPE:
+      case AllocKind::ACCESSOR_SHAPE:
         UpdateCellPointersTyped<AccessorShape>(trc, arena, traceKind);
         return;
-      case FINALIZE_BASE_SHAPE:
+      case AllocKind::BASE_SHAPE:
         UpdateCellPointersTyped<BaseShape>(trc, arena, traceKind);
         return;
-      case FINALIZE_OBJECT_GROUP:
+      case AllocKind::OBJECT_GROUP:
         UpdateCellPointersTyped<ObjectGroup>(trc, arena, traceKind);
         return;
-      case FINALIZE_JITCODE:
+      case AllocKind::JITCODE:
         UpdateCellPointersTyped<jit::JitCode>(trc, arena, traceKind);
         return;
       default:
         MOZ_CRASH("Invalid alloc kind for UpdateCellPointers");
     }
 }
 
 namespace js {
@@ -2380,31 +2383,31 @@ struct ArenasToUpdate
 
   private:
     bool initialized;
     KindsToUpdate kinds;
     GCZonesIter zone;    // Current zone to process, unless zone.done()
     unsigned kind;       // Current alloc kind to process
     ArenaHeader *arena;  // Next arena to process
 
-    bool shouldProcessKind(unsigned kind);
+    bool shouldProcessKind(AllocKind kind);
 };
 
-bool ArenasToUpdate::shouldProcessKind(unsigned kind)
-{
-    MOZ_ASSERT(kind < FINALIZE_LIMIT);
-    if (kind == FINALIZE_FAT_INLINE_STRING ||
-        kind == FINALIZE_STRING ||
-        kind == FINALIZE_EXTERNAL_STRING ||
-        kind == FINALIZE_SYMBOL)
+bool ArenasToUpdate::shouldProcessKind(AllocKind kind)
+{
+    MOZ_ASSERT(kind < AllocKind::LIMIT);
+    if (kind == AllocKind::FAT_INLINE_STRING ||
+        kind == AllocKind::STRING ||
+        kind == AllocKind::EXTERNAL_STRING ||
+        kind == AllocKind::SYMBOL)
     {
         return false;
     }
 
-    if (js::gc::IsBackgroundFinalized(AllocKind(kind)))
+    if (js::gc::IsBackgroundFinalized(kind))
         return (kinds & BACKGROUND) != 0;
     else
         return (kinds & FOREGROUND) != 0;
 }
 
 ArenasToUpdate::ArenasToUpdate(JSRuntime *rt, KindsToUpdate kinds)
   : initialized(false), kinds(kinds), zone(rt, SkipAtoms)
 {
@@ -2420,26 +2423,26 @@ ArenasToUpdate::next(AutoLockHelperThrea
     // called, |initialized| is false and the for-loops below are entered in the
     // normal way, returning the first arena found. In subsequent invocations we
     // jump directly into the body of the for loops just after the previous
     // return. All state is stored in class members and so preserved between
     // invocations.
 
     if (initialized) {
         MOZ_ASSERT(arena);
-        MOZ_ASSERT(shouldProcessKind(kind));
+        MOZ_ASSERT(shouldProcessKind(AllocKind(kind)));
         MOZ_ASSERT(!zone.done());
         goto resumePoint;
     }
 
     initialized = true;
     for (; !zone.done(); zone.next()) {
         if (zone->isGCCompacting()) {
-            for (kind = 0; kind < FINALIZE_LIMIT; ++kind) {
-                if (shouldProcessKind(kind)) {
+            for (kind = 0; kind < size_t(AllocKind::LIMIT); ++kind) {
+                if (shouldProcessKind(AllocKind(kind))) {
                     for (arena = zone.get()->arenas.getFirstArena(AllocKind(kind));
                          arena;
                          arena = arena->next)
                     {
                         return arena;
                       resumePoint:;
                     }
                 }
@@ -2752,27 +2755,27 @@ ReleaseArenaList(JSRuntime *rt, ArenaHea
         rt->gc.releaseArena(aheader, lock);
     }
 }
 
 ArenaLists::~ArenaLists()
 {
     AutoLockGC lock(runtime_);
 
-    for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
+    for (ALL_ALLOC_KINDS(i)) {
         /*
          * We can only call this during the shutdown after the last GC when
          * the background finalization is disabled.
          */
         MOZ_ASSERT(backgroundFinalizeState[i] == BFS_DONE);
         ReleaseArenaList(runtime_, arenaLists[i].head(), lock);
     }
     ReleaseArenaList(runtime_, incrementalSweptArenas.head(), lock);
 
-    for (size_t i = 0; i < FINALIZE_OBJECT_LIMIT; i++)
+    for (OBJECT_ALLOC_KINDS(i))
         ReleaseArenaList(runtime_, savedObjectArenas[i].head(), lock);
     ReleaseArenaList(runtime_, savedEmptyObjectArenas, lock);
 }
 
 void
 ArenaLists::finalizeNow(FreeOp *fop, const FinalizePhase& phase)
 {
     gcstats::AutoPhase ap(fop->runtime()->gc.stats, phase.statsPhase);
@@ -2904,75 +2907,75 @@ ArenaLists::backgroundFinalize(FreeOp *f
 }
 
 void
 ArenaLists::queueForegroundObjectsForSweep(FreeOp *fop)
 {
     gcstats::AutoPhase ap(fop->runtime()->gc.stats, gcstats::PHASE_SWEEP_OBJECT);
 
 #ifdef DEBUG
-    for (size_t i = 0; i < FINALIZE_OBJECT_LIMIT; i++)
+    for (OBJECT_ALLOC_KINDS(i))
         MOZ_ASSERT(savedObjectArenas[i].isEmpty());
     MOZ_ASSERT(savedEmptyObjectArenas == nullptr);
 #endif
 
     // Foreground finalized objects must be finalized at the beginning of the
     // sweep phase, before control can return to the mutator. Otherwise,
     // mutator behavior can resurrect certain objects whose references would
     // otherwise have been erased by the finalizer.
-    finalizeNow(fop, FINALIZE_OBJECT0, KEEP_ARENAS, &savedEmptyObjectArenas);
-    finalizeNow(fop, FINALIZE_OBJECT2, KEEP_ARENAS, &savedEmptyObjectArenas);
-    finalizeNow(fop, FINALIZE_OBJECT4, KEEP_ARENAS, &savedEmptyObjectArenas);
-    finalizeNow(fop, FINALIZE_OBJECT8, KEEP_ARENAS, &savedEmptyObjectArenas);
-    finalizeNow(fop, FINALIZE_OBJECT12, KEEP_ARENAS, &savedEmptyObjectArenas);
-    finalizeNow(fop, FINALIZE_OBJECT16, KEEP_ARENAS, &savedEmptyObjectArenas);
+    finalizeNow(fop, AllocKind::OBJECT0, KEEP_ARENAS, &savedEmptyObjectArenas);
+    finalizeNow(fop, AllocKind::OBJECT2, KEEP_ARENAS, &savedEmptyObjectArenas);
+    finalizeNow(fop, AllocKind::OBJECT4, KEEP_ARENAS, &savedEmptyObjectArenas);
+    finalizeNow(fop, AllocKind::OBJECT8, KEEP_ARENAS, &savedEmptyObjectArenas);
+    finalizeNow(fop, AllocKind::OBJECT12, KEEP_ARENAS, &savedEmptyObjectArenas);
+    finalizeNow(fop, AllocKind::OBJECT16, KEEP_ARENAS, &savedEmptyObjectArenas);
 
     // Prevent the arenas from having new objects allocated into them. We need
     // to know which objects are marked while we incrementally sweep dead
     // references from type information.
-    savedObjectArenas[FINALIZE_OBJECT0] = arenaLists[FINALIZE_OBJECT0].copyAndClear();
-    savedObjectArenas[FINALIZE_OBJECT2] = arenaLists[FINALIZE_OBJECT2].copyAndClear();
-    savedObjectArenas[FINALIZE_OBJECT4] = arenaLists[FINALIZE_OBJECT4].copyAndClear();
-    savedObjectArenas[FINALIZE_OBJECT8] = arenaLists[FINALIZE_OBJECT8].copyAndClear();
-    savedObjectArenas[FINALIZE_OBJECT12] = arenaLists[FINALIZE_OBJECT12].copyAndClear();
-    savedObjectArenas[FINALIZE_OBJECT16] = arenaLists[FINALIZE_OBJECT16].copyAndClear();
+    savedObjectArenas[AllocKind::OBJECT0] = arenaLists[AllocKind::OBJECT0].copyAndClear();
+    savedObjectArenas[AllocKind::OBJECT2] = arenaLists[AllocKind::OBJECT2].copyAndClear();
+    savedObjectArenas[AllocKind::OBJECT4] = arenaLists[AllocKind::OBJECT4].copyAndClear();
+    savedObjectArenas[AllocKind::OBJECT8] = arenaLists[AllocKind::OBJECT8].copyAndClear();
+    savedObjectArenas[AllocKind::OBJECT12] = arenaLists[AllocKind::OBJECT12].copyAndClear();
+    savedObjectArenas[AllocKind::OBJECT16] = arenaLists[AllocKind::OBJECT16].copyAndClear();
 }
 
 void
 ArenaLists::mergeForegroundSweptObjectArenas()
 {
     AutoLockGC lock(runtime_);
     ReleaseArenaList(runtime_, savedEmptyObjectArenas, lock);
     savedEmptyObjectArenas = nullptr;
 
-    mergeSweptArenas(FINALIZE_OBJECT0);
-    mergeSweptArenas(FINALIZE_OBJECT2);
-    mergeSweptArenas(FINALIZE_OBJECT4);
-    mergeSweptArenas(FINALIZE_OBJECT8);
-    mergeSweptArenas(FINALIZE_OBJECT12);
-    mergeSweptArenas(FINALIZE_OBJECT16);
+    mergeSweptArenas(AllocKind::OBJECT0);
+    mergeSweptArenas(AllocKind::OBJECT2);
+    mergeSweptArenas(AllocKind::OBJECT4);
+    mergeSweptArenas(AllocKind::OBJECT8);
+    mergeSweptArenas(AllocKind::OBJECT12);
+    mergeSweptArenas(AllocKind::OBJECT16);
 }
 
 inline void
 ArenaLists::mergeSweptArenas(AllocKind thingKind)
 {
     ArenaList *al = &arenaLists[thingKind];
     ArenaList *saved = &savedObjectArenas[thingKind];
 
     *al = saved->insertListWithCursorAtEnd(*al);
     saved->clear();
 }
 
 void
 ArenaLists::queueForegroundThingsForSweep(FreeOp *fop)
 {
-    gcShapeArenasToUpdate = arenaListsToSweep[FINALIZE_SHAPE];
-    gcAccessorShapeArenasToUpdate = arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE];
-    gcObjectGroupArenasToUpdate = arenaListsToSweep[FINALIZE_OBJECT_GROUP];
-    gcScriptArenasToUpdate = arenaListsToSweep[FINALIZE_SCRIPT];
+    gcShapeArenasToUpdate = arenaListsToSweep[AllocKind::SHAPE];
+    gcAccessorShapeArenasToUpdate = arenaListsToSweep[AllocKind::ACCESSOR_SHAPE];
+    gcObjectGroupArenasToUpdate = arenaListsToSweep[AllocKind::OBJECT_GROUP];
+    gcScriptArenasToUpdate = arenaListsToSweep[AllocKind::SCRIPT];
 }
 
 /* static */ void *
 GCRuntime::tryRefillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
 {
     ArenaLists *arenas = cx->arenas();
     Zone *zone = cx->zone();
 
@@ -3331,17 +3334,17 @@ GCRuntime::decommitArenas(AutoLockGC &lo
     // head and don't want to thrash with the mutator.
     for (size_t i = toDecommit.length(); i > 1; --i) {
         Chunk *chunk = toDecommit[i - 1];
         MOZ_ASSERT(chunk);
 
         // The arena list is not doubly-linked, so we have to work in the free
         // list order and not in the natural order.
         while (chunk->info.numArenasFreeCommitted) {
-            ArenaHeader *aheader = chunk->allocateArena(rt, nullptr, FINALIZE_OBJECT0, lock);
+            ArenaHeader *aheader = chunk->allocateArena(rt, nullptr, AllocKind::OBJECT0, lock);
             bool ok;
             {
                 AutoUnlockGC unlock(lock);
                 ok = MarkPagesUnused(aheader->getArena(), ArenaSize);
             }
             chunk->releaseArena(rt, aheader, lock, Chunk::ArenaDecommitState(ok));
 
             // FIXME Bug 1095620: add cancellation support when this becomes
@@ -3396,19 +3399,19 @@ GCRuntime::sweepBackgroundThings(ZoneLis
 
 void
 GCRuntime::assertBackgroundSweepingFinished()
 {
 #ifdef DEBUG
     MOZ_ASSERT(backgroundSweepZones.isEmpty());
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         MOZ_ASSERT(!zone->isOnList());
-        for (unsigned i = 0; i < FINALIZE_LIMIT; ++i) {
+        for (ALL_ALLOC_KINDS(i)) {
             MOZ_ASSERT(!zone->arenas.arenaListsToSweep[i]);
-            MOZ_ASSERT(zone->arenas.doneBackgroundFinalize(AllocKind(i)));
+            MOZ_ASSERT(zone->arenas.doneBackgroundFinalize(i));
         }
     }
     MOZ_ASSERT(freeLifoAlloc.computedSizeOfExcludingThis() == 0);
 #endif
 }
 
 unsigned
 js::GetCPUCount()
@@ -3864,20 +3867,20 @@ void
 GCRuntime::checkForCompartmentMismatches()
 {
     if (disableStrictProxyCheckingCount)
         return;
 
     CompartmentCheckTracer trc(rt, CheckCompartmentCallback);
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         trc.zone = zone;
-        for (size_t thingKind = 0; thingKind < FINALIZE_LAST; thingKind++) {
-            for (ZoneCellIterUnderGC i(zone, AllocKind(thingKind)); !i.done(); i.next()) {
+        for (ALL_ALLOC_KINDS(thingKind)) {
+            for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
                 trc.src = i.getCell();
-                trc.srcKind = MapAllocToTraceKind(AllocKind(thingKind));
+                trc.srcKind = MapAllocToTraceKind(thingKind);
                 trc.compartment = CompartmentOfCell(trc.src, trc.srcKind);
                 JS_TraceChildren(&trc, trc.src, trc.srcKind);
             }
         }
     }
 }
 #endif
 
@@ -3893,17 +3896,17 @@ GCRuntime::beginMarkPhase(JS::gcreason::
 
     isFull = true;
     bool any = false;
 
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         /* Assert that zone state is as we expect */
         MOZ_ASSERT(!zone->isCollecting());
         MOZ_ASSERT(!zone->compartments.empty());
-        for (unsigned i = 0; i < FINALIZE_LIMIT; ++i)
+        for (ALL_ALLOC_KINDS(i))
             MOZ_ASSERT(!zone->arenas.arenaListsToSweep[i]);
 
         /* Set up which zones will be collected. */
         if (zone->isGCScheduled()) {
             if (!rt->isAtomsZone(zone)) {
                 any = true;
                 zone->setGCState(Zone::Mark);
             }
@@ -5202,17 +5205,18 @@ ArenaLists::foregroundFinalize(FreeOp *f
         return false;
     }
 
     // Clear any previous incremental sweep state we may have saved.
     incrementalSweptArenas.clear();
 
     // Join |arenaLists[thingKind]| and |sweepList| into a single list.
     ArenaList finalized = sweepList.toArenaList();
-    arenaLists[thingKind] = finalized.insertListWithCursorAtEnd(arenaLists[thingKind]);
+    arenaLists[thingKind] =
+        finalized.insertListWithCursorAtEnd(arenaLists[thingKind]);
 
     return true;
 }
 
 GCRuntime::IncrementalProgress
 GCRuntime::drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase)
 {
     /* Run a marking slice and return whether the stack is now empty. */
@@ -5450,18 +5454,18 @@ GCRuntime::endSweepPhase(bool lastGC)
         if (lastGC)
             sweepZones(&fop, lastGC);
     }
 
     finishMarkingValidation();
 
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
-        for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i) {
-            MOZ_ASSERT_IF(!IsBackgroundFinalized(AllocKind(i)) ||
+        for (ALL_ALLOC_KINDS(i)) {
+            MOZ_ASSERT_IF(!IsBackgroundFinalized(i) ||
                           !sweepOnBackgroundThread,
                           !zone->arenas.arenaListsToSweep[i]);
         }
     }
 
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         MOZ_ASSERT(!c->gcIncomingGrayPointers);
 
@@ -5525,19 +5529,19 @@ GCRuntime::compactPhase(JS::gcreason::Re
     CheckHashTablesAfterMovingGC(rt);
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         if (zone->isGCCompacting()) {
             MOZ_ASSERT(!zone->isPreservingCode());
             zone->arenas.checkEmptyFreeLists();
 
             // Check that we did as much compaction as we should have. There
             // should always be less than one arena's worth of free cells.
-            for (size_t i = 0; i < FINALIZE_LIMIT; i++) {
-                size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(AllocKind(i)));
-                if (CanRelocateAllocKind(AllocKind(i))) {
+            for (ALL_ALLOC_KINDS(i)) {
+                size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(i));
+                if (CanRelocateAllocKind(i)) {
                     ArenaList &al = zone->arenas.arenaLists[i];
                     size_t freeCells = 0;
                     for (ArenaHeader *arena = al.arenaAfterCursor(); arena; arena = arena->next)
                         freeCells += arena->countFreeCells();
                     MOZ_ASSERT(freeCells < thingsPerArena);
                 }
             }
         }
@@ -5738,17 +5742,17 @@ GCRuntime::resetIncrementalGC(const char
         MOZ_CRASH("Invalid incremental GC state");
     }
 
     stats.reset(reason);
 
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         MOZ_ASSERT(!zone->needsIncrementalBarrier());
-        for (unsigned i = 0; i < FINALIZE_LIMIT; ++i)
+        for (ALL_ALLOC_KINDS(i))
             MOZ_ASSERT(!zone->arenas.arenaListsToSweep[i]);
     }
 #endif
 }
 
 namespace {
 
 class AutoGCSlice {
@@ -6589,39 +6593,39 @@ gc::MergeCompartments(JSCompartment *sou
 
     // Release any relocated arenas which we may be holding on to as they might
     // be in the source zone
     rt->gc.releaseHeldRelocatedArenas();
 
     // Fixup compartment pointers in source to refer to target, and make sure
     // type information generations are in sync.
 
-    for (ZoneCellIter iter(source->zone(), FINALIZE_SCRIPT); !iter.done(); iter.next()) {
+    for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) {
         JSScript *script = iter.get<JSScript>();
         MOZ_ASSERT(script->compartment() == source);
         script->compartment_ = target;
         script->setTypesGeneration(target->zone()->types.generation);
     }
 
-    for (ZoneCellIter iter(source->zone(), FINALIZE_BASE_SHAPE); !iter.done(); iter.next()) {
+    for (ZoneCellIter iter(source->zone(), AllocKind::BASE_SHAPE); !iter.done(); iter.next()) {
         BaseShape *base = iter.get<BaseShape>();
         MOZ_ASSERT(base->compartment() == source);
         base->compartment_ = target;
     }
 
-    for (ZoneCellIter iter(source->zone(), FINALIZE_OBJECT_GROUP); !iter.done(); iter.next()) {
+    for (ZoneCellIter iter(source->zone(), AllocKind::OBJECT_GROUP); !iter.done(); iter.next()) {
         ObjectGroup *group = iter.get<ObjectGroup>();
         group->setGeneration(target->zone()->types.generation);
         group->compartment_ = target;
     }
 
     // Fixup zone pointers in source's zone to refer to target's zone.
 
-    for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
-        for (ArenaIter aiter(source->zone(), AllocKind(thingKind)); !aiter.done(); aiter.next()) {
+    for (ALL_ALLOC_KINDS(thingKind)) {
+        for (ArenaIter aiter(source->zone(), thingKind); !aiter.done(); aiter.next()) {
             ArenaHeader *aheader = aiter.get();
             aheader->zone = target->zone();
         }
     }
 
     // The source should be the only compartment in its zone.
     for (CompartmentsInZoneIter c(source->zone()); !c.done(); c.next())
         MOZ_ASSERT(c.get() == source);
@@ -6748,28 +6752,28 @@ js::ReleaseAllJITCode(FreeOp *fop)
     fop->runtime()->gc.evictNursery();
 
     for (ZonesIter zone(fop->runtime(), SkipAtoms); !zone.done(); zone.next()) {
         if (!zone->jitZone())
             continue;
 
 #ifdef DEBUG
         /* Assert no baseline scripts are marked as active. */
-        for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active());
         }
 #endif
 
         /* Mark baseline scripts on the stack as active. */
         jit::MarkActiveBaselineScripts(zone);
 
         jit::InvalidateAll(fop, zone);
 
-        for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             jit::FinishInvalidation(fop, script);
 
             /*
              * Discard baseline script if it's not marked as active. Note that
              * this also resets the active flag.
              */
             jit::FinishDiscardBaselineScript(fop, script);
@@ -6777,17 +6781,17 @@ js::ReleaseAllJITCode(FreeOp *fop)
 
         zone->jitZone()->optimizedStubSpace()->free();
     }
 }
 
 void
 js::PurgeJITCaches(Zone *zone)
 {
-    for (ZoneCellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
 
         /* Discard Ion caches. */
         jit::PurgeCaches(script);
     }
 }
 
 void
@@ -6806,22 +6810,22 @@ ArenaLists::normalizeBackgroundFinalizeS
 void
 ArenaLists::adoptArenas(JSRuntime *rt, ArenaLists *fromArenaLists)
 {
     // GC should be inactive, but still take the lock as a kind of read fence.
     AutoLockGC lock(rt);
 
     fromArenaLists->purge();
 
-    for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
+    for (ALL_ALLOC_KINDS(thingKind)) {
         // When we enter a parallel section, we join the background
         // thread, and we do not run GC while in the parallel section,
         // so no finalizer should be active!
-        normalizeBackgroundFinalizeState(AllocKind(thingKind));
-        fromArenaLists->normalizeBackgroundFinalizeState(AllocKind(thingKind));
+        normalizeBackgroundFinalizeState(thingKind);
+        fromArenaLists->normalizeBackgroundFinalizeState(thingKind);
 
         ArenaList *fromList = &fromArenaLists->arenaLists[thingKind];
         ArenaList *toList = &arenaLists[thingKind];
         fromList->check();
         toList->check();
         ArenaHeader *next;
         for (ArenaHeader *fromHeader = fromList->head(); fromHeader; fromHeader = next) {
             // Copy fromHeader->next before releasing/reinserting.
@@ -6834,18 +6838,18 @@ ArenaLists::adoptArenas(JSRuntime *rt, A
         toList->check();
     }
 }
 
 bool
 ArenaLists::containsArena(JSRuntime *rt, ArenaHeader *needle)
 {
     AutoLockGC lock(rt);
-    size_t allocKind = needle->getAllocKind();
-    for (ArenaHeader *aheader = arenaLists[allocKind].head(); aheader; aheader = aheader->next) {
+    ArenaList &list = arenaLists[needle->getAllocKind()];
+    for (ArenaHeader *aheader = list.head(); aheader; aheader = aheader->next) {
         if (aheader == needle)
             return true;
     }
     return false;
 }
 
 
 AutoSuppressGC::AutoSuppressGC(ExclusiveContext *cx)
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -6,16 +6,17 @@
 
 /* JS Garbage Collector. */
 
 #ifndef jsgc_h
 #define jsgc_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/EnumeratedArray.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jslock.h"
 
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "js/Vector.h"
@@ -58,139 +59,139 @@ enum State {
     MARK_ROOTS,
     MARK,
     SWEEP,
     COMPACT
 };
 
 /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
 template <typename T> struct MapTypeToFinalizeKind {};
-template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = FINALIZE_SCRIPT; };
-template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; };
-template <> struct MapTypeToFinalizeKind<Shape>             { static const AllocKind kind = FINALIZE_SHAPE; };
-template <> struct MapTypeToFinalizeKind<AccessorShape>     { static const AllocKind kind = FINALIZE_ACCESSOR_SHAPE; };
-template <> struct MapTypeToFinalizeKind<BaseShape>         { static const AllocKind kind = FINALIZE_BASE_SHAPE; };
-template <> struct MapTypeToFinalizeKind<ObjectGroup>       { static const AllocKind kind = FINALIZE_OBJECT_GROUP; };
-template <> struct MapTypeToFinalizeKind<JSFatInlineString> { static const AllocKind kind = FINALIZE_FAT_INLINE_STRING; };
-template <> struct MapTypeToFinalizeKind<JSString>          { static const AllocKind kind = FINALIZE_STRING; };
-template <> struct MapTypeToFinalizeKind<JSExternalString>  { static const AllocKind kind = FINALIZE_EXTERNAL_STRING; };
-template <> struct MapTypeToFinalizeKind<JS::Symbol>        { static const AllocKind kind = FINALIZE_SYMBOL; };
-template <> struct MapTypeToFinalizeKind<jit::JitCode>      { static const AllocKind kind = FINALIZE_JITCODE; };
+template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = AllocKind::SCRIPT; };
+template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = AllocKind::LAZY_SCRIPT; };
+template <> struct MapTypeToFinalizeKind<Shape>             { static const AllocKind kind = AllocKind::SHAPE; };
+template <> struct MapTypeToFinalizeKind<AccessorShape>     { static const AllocKind kind = AllocKind::ACCESSOR_SHAPE; };
+template <> struct MapTypeToFinalizeKind<BaseShape>         { static const AllocKind kind = AllocKind::BASE_SHAPE; };
+template <> struct MapTypeToFinalizeKind<ObjectGroup>       { static const AllocKind kind = AllocKind::OBJECT_GROUP; };
+template <> struct MapTypeToFinalizeKind<JSFatInlineString> { static const AllocKind kind = AllocKind::FAT_INLINE_STRING; };
+template <> struct MapTypeToFinalizeKind<JSString>          { static const AllocKind kind = AllocKind::STRING; };
+template <> struct MapTypeToFinalizeKind<JSExternalString>  { static const AllocKind kind = AllocKind::EXTERNAL_STRING; };
+template <> struct MapTypeToFinalizeKind<JS::Symbol>        { static const AllocKind kind = AllocKind::SYMBOL; };
+template <> struct MapTypeToFinalizeKind<jit::JitCode>      { static const AllocKind kind = AllocKind::JITCODE; };
 
 static inline bool
 IsNurseryAllocable(AllocKind kind)
 {
-    MOZ_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT);
+    MOZ_ASSERT(kind < AllocKind::LIMIT);
     static const bool map[] = {
-        false,     /* FINALIZE_OBJECT0 */
-        true,      /* FINALIZE_OBJECT0_BACKGROUND */
-        false,     /* FINALIZE_OBJECT2 */
-        true,      /* FINALIZE_OBJECT2_BACKGROUND */
-        false,     /* FINALIZE_OBJECT4 */
-        true,      /* FINALIZE_OBJECT4_BACKGROUND */
-        false,     /* FINALIZE_OBJECT8 */
-        true,      /* FINALIZE_OBJECT8_BACKGROUND */
-        false,     /* FINALIZE_OBJECT12 */
-        true,      /* FINALIZE_OBJECT12_BACKGROUND */
-        false,     /* FINALIZE_OBJECT16 */
-        true,      /* FINALIZE_OBJECT16_BACKGROUND */
-        false,     /* FINALIZE_SCRIPT */
-        false,     /* FINALIZE_LAZY_SCRIPT */
-        false,     /* FINALIZE_SHAPE */
-        false,     /* FINALIZE_ACCESSOR_SHAPE */
-        false,     /* FINALIZE_BASE_SHAPE */
-        false,     /* FINALIZE_OBJECT_GROUP */
-        false,     /* FINALIZE_FAT_INLINE_STRING */
-        false,     /* FINALIZE_STRING */
-        false,     /* FINALIZE_EXTERNAL_STRING */
-        false,     /* FINALIZE_SYMBOL */
-        false,     /* FINALIZE_JITCODE */
+        false,     /* AllocKind::OBJECT0 */
+        true,      /* AllocKind::OBJECT0_BACKGROUND */
+        false,     /* AllocKind::OBJECT2 */
+        true,      /* AllocKind::OBJECT2_BACKGROUND */
+        false,     /* AllocKind::OBJECT4 */
+        true,      /* AllocKind::OBJECT4_BACKGROUND */
+        false,     /* AllocKind::OBJECT8 */
+        true,      /* AllocKind::OBJECT8_BACKGROUND */
+        false,     /* AllocKind::OBJECT12 */
+        true,      /* AllocKind::OBJECT12_BACKGROUND */
+        false,     /* AllocKind::OBJECT16 */
+        true,      /* AllocKind::OBJECT16_BACKGROUND */
+        false,     /* AllocKind::SCRIPT */
+        false,     /* AllocKind::LAZY_SCRIPT */
+        false,     /* AllocKind::SHAPE */
+        false,     /* AllocKind::ACCESSOR_SHAPE */
+        false,     /* AllocKind::BASE_SHAPE */
+        false,     /* AllocKind::OBJECT_GROUP */
+        false,     /* AllocKind::FAT_INLINE_STRING */
+        false,     /* AllocKind::STRING */
+        false,     /* AllocKind::EXTERNAL_STRING */
+        false,     /* AllocKind::SYMBOL */
+        false,     /* AllocKind::JITCODE */
     };
-    JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
-    return map[kind];
+    JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
+    return map[size_t(kind)];
 }
 
 static inline bool
 IsBackgroundFinalized(AllocKind kind)
 {
-    MOZ_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT);
+    MOZ_ASSERT(kind < AllocKind::LIMIT);
     static const bool map[] = {
-        false,     /* FINALIZE_OBJECT0 */
-        true,      /* FINALIZE_OBJECT0_BACKGROUND */
-        false,     /* FINALIZE_OBJECT2 */
-        true,      /* FINALIZE_OBJECT2_BACKGROUND */
-        false,     /* FINALIZE_OBJECT4 */
-        true,      /* FINALIZE_OBJECT4_BACKGROUND */
-        false,     /* FINALIZE_OBJECT8 */
-        true,      /* FINALIZE_OBJECT8_BACKGROUND */
-        false,     /* FINALIZE_OBJECT12 */
-        true,      /* FINALIZE_OBJECT12_BACKGROUND */
-        false,     /* FINALIZE_OBJECT16 */
-        true,      /* FINALIZE_OBJECT16_BACKGROUND */
-        false,     /* FINALIZE_SCRIPT */
-        false,     /* FINALIZE_LAZY_SCRIPT */
-        true,      /* FINALIZE_SHAPE */
-        true,      /* FINALIZE_ACCESSOR_SHAPE */
-        true,      /* FINALIZE_BASE_SHAPE */
-        true,      /* FINALIZE_OBJECT_GROUP */
-        true,      /* FINALIZE_FAT_INLINE_STRING */
-        true,      /* FINALIZE_STRING */
-        false,     /* FINALIZE_EXTERNAL_STRING */
-        true,      /* FINALIZE_SYMBOL */
-        false,     /* FINALIZE_JITCODE */
+        false,     /* AllocKind::OBJECT0 */
+        true,      /* AllocKind::OBJECT0_BACKGROUND */
+        false,     /* AllocKind::OBJECT2 */
+        true,      /* AllocKind::OBJECT2_BACKGROUND */
+        false,     /* AllocKind::OBJECT4 */
+        true,      /* AllocKind::OBJECT4_BACKGROUND */
+        false,     /* AllocKind::OBJECT8 */
+        true,      /* AllocKind::OBJECT8_BACKGROUND */
+        false,     /* AllocKind::OBJECT12 */
+        true,      /* AllocKind::OBJECT12_BACKGROUND */
+        false,     /* AllocKind::OBJECT16 */
+        true,      /* AllocKind::OBJECT16_BACKGROUND */
+        false,     /* AllocKind::SCRIPT */
+        false,     /* AllocKind::LAZY_SCRIPT */
+        true,      /* AllocKind::SHAPE */
+        true,      /* AllocKind::ACCESSOR_SHAPE */
+        true,      /* AllocKind::BASE_SHAPE */
+        true,      /* AllocKind::OBJECT_GROUP */
+        true,      /* AllocKind::FAT_INLINE_STRING */
+        true,      /* AllocKind::STRING */
+        false,     /* AllocKind::EXTERNAL_STRING */
+        true,      /* AllocKind::SYMBOL */
+        false,     /* AllocKind::JITCODE */
     };
-    JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
-    return map[kind];
+    JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
+    return map[size_t(kind)];
 }
 
 static inline bool
-CanBeFinalizedInBackground(gc::AllocKind kind, const Class *clasp)
+CanBeFinalizedInBackground(AllocKind kind, const Class *clasp)
 {
-    MOZ_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST);
+    MOZ_ASSERT(kind <= AllocKind::OBJECT_LAST);
     /* If the class has no finalizer or a finalizer that is safe to call on
-     * a different thread, we change the finalize kind. For example,
-     * FINALIZE_OBJECT0 calls the finalizer on the main thread,
-     * FINALIZE_OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
+     * a different thread, we change the alloc kind. For example,
+     * AllocKind::OBJECT0 calls the finalizer on the main thread,
+     * AllocKind::OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
      * IsBackgroundFinalized is called to prevent recursively incrementing
-     * the finalize kind; kind may already be a background finalize kind.
+     * the alloc kind; kind may already be a background finalize kind.
      */
-    return (!gc::IsBackgroundFinalized(kind) &&
+    return (!IsBackgroundFinalized(kind) &&
             (!clasp->finalize || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
 }
 
 inline JSGCTraceKind
 GetGCThingTraceKind(const void *thing);
 
 /* Capacity for slotsToThingKind */
 const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
 
 extern const AllocKind slotsToThingKind[];
 
 /* Get the best kind to use when making an object with the given slot count. */
 static inline AllocKind
 GetGCObjectKind(size_t numSlots)
 {
     if (numSlots >= SLOTS_TO_THING_KIND_LIMIT)
-        return FINALIZE_OBJECT16;
+        return AllocKind::OBJECT16;
     return slotsToThingKind[numSlots];
 }
 
 /* As for GetGCObjectKind, but for dense array allocation. */
 static inline AllocKind
 GetGCArrayKind(size_t numSlots)
 {
     /*
      * Dense arrays can use their fixed slots to hold their elements array
      * (less two Values worth of ObjectElements header), but if more than the
      * maximum number of fixed slots is needed then the fixed slots will be
      * unused.
      */
     JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2);
     if (numSlots > NativeObject::NELEMENTS_LIMIT || numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT)
-        return FINALIZE_OBJECT2;
+        return AllocKind::OBJECT2;
     return slotsToThingKind[numSlots + 2];
 }
 
 static inline AllocKind
 GetGCObjectFixedSlotsKind(size_t numFixedSlots)
 {
     MOZ_ASSERT(numFixedSlots < SLOTS_TO_THING_KIND_LIMIT);
     return slotsToThingKind[numFixedSlots];
@@ -199,74 +200,74 @@ GetGCObjectFixedSlotsKind(size_t numFixe
 // Get the best kind to use when allocating an object that needs a specific
 // number of bytes.
 static inline AllocKind
 GetGCObjectKindForBytes(size_t nbytes)
 {
     MOZ_ASSERT(nbytes <= JSObject::MAX_BYTE_SIZE);
 
     if (nbytes <= sizeof(NativeObject))
-        return FINALIZE_OBJECT0;
+        return AllocKind::OBJECT0;
     nbytes -= sizeof(NativeObject);
 
     size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value);
     MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
     return GetGCObjectKind(dataSlots);
 }
 
 static inline AllocKind
 GetBackgroundAllocKind(AllocKind kind)
 {
     MOZ_ASSERT(!IsBackgroundFinalized(kind));
-    MOZ_ASSERT(kind <= FINALIZE_OBJECT_LAST);
-    return (AllocKind) (kind + 1);
+    MOZ_ASSERT(kind < AllocKind::OBJECT_LAST);
+    return AllocKind(size_t(kind) + 1);
 }
 
 /* Get the number of fixed slots and initial capacity associated with a kind. */
 static inline size_t
 GetGCKindSlots(AllocKind thingKind)
 {
     /* Using a switch in hopes that thingKind will usually be a compile-time constant. */
     switch (thingKind) {
-      case FINALIZE_OBJECT0:
-      case FINALIZE_OBJECT0_BACKGROUND:
+      case AllocKind::OBJECT0:
+      case AllocKind::OBJECT0_BACKGROUND:
         return 0;
-      case FINALIZE_OBJECT2:
-      case FINALIZE_OBJECT2_BACKGROUND:
+      case AllocKind::OBJECT2:
+      case AllocKind::OBJECT2_BACKGROUND:
         return 2;
-      case FINALIZE_OBJECT4:
-      case FINALIZE_OBJECT4_BACKGROUND:
+      case AllocKind::OBJECT4:
+      case AllocKind::OBJECT4_BACKGROUND:
         return 4;
-      case FINALIZE_OBJECT8:
-      case FINALIZE_OBJECT8_BACKGROUND:
+      case AllocKind::OBJECT8:
+      case AllocKind::OBJECT8_BACKGROUND:
         return 8;
-      case FINALIZE_OBJECT12:
-      case FINALIZE_OBJECT12_BACKGROUND:
+      case AllocKind::OBJECT12:
+      case AllocKind::OBJECT12_BACKGROUND:
         return 12;
-      case FINALIZE_OBJECT16:
-      case FINALIZE_OBJECT16_BACKGROUND:
+      case AllocKind::OBJECT16:
+      case AllocKind::OBJECT16_BACKGROUND:
         return 16;
       default:
-        MOZ_CRASH("Bad object finalize kind");
+        MOZ_CRASH("Bad object alloc kind");
     }
 }
 
 static inline size_t
 GetGCKindSlots(AllocKind thingKind, const Class *clasp)
 {
     size_t nslots = GetGCKindSlots(thingKind);
 
     /* An object's private data uses the space taken by its last fixed slot. */
     if (clasp->flags & JSCLASS_HAS_PRIVATE) {
         MOZ_ASSERT(nslots > 0);
         nslots--;
     }
 
     /*
-     * Functions have a larger finalize kind than FINALIZE_OBJECT to reserve
+     * Functions have a larger alloc kind than AllocKind::OBJECT to reserve
      * space for the extra fields in JSFunction, but have no fixed slots.
      */
     if (clasp == FunctionClassPtr)
         nslots = 0;
 
     return nslots;
 }
 
@@ -575,71 +576,70 @@ class ArenaLists
      * For each arena kind its free list is represented as the first span with
      * free things. Initially all the spans are initialized as empty. After we
      * find a new arena with available things we move its first free span into
      * the list and set the arena as fully allocated. way we do not need to
      * update the arena header after the initial allocation. When starting the
      * GC we only move the head of the of the list of spans back to the arena
      * only for the arena that was not fully allocated.
      */
-    FreeList       freeLists[FINALIZE_LIMIT];
+    AllAllocKindArray<FreeList> freeLists;
 
-    ArenaList      arenaLists[FINALIZE_LIMIT];
+    AllAllocKindArray<ArenaList> arenaLists;
 
     enum BackgroundFinalizeStateEnum { BFS_DONE, BFS_RUN };
 
     typedef mozilla::Atomic<BackgroundFinalizeStateEnum, mozilla::ReleaseAcquire>
         BackgroundFinalizeState;
 
     /* The current background finalization state, accessed atomically. */
-    BackgroundFinalizeState backgroundFinalizeState[FINALIZE_LIMIT];
+    AllAllocKindArray<BackgroundFinalizeState> backgroundFinalizeState;
 
-  public:
     /* For each arena kind, a list of arenas remaining to be swept. */
-    ArenaHeader *arenaListsToSweep[FINALIZE_LIMIT];
+    AllAllocKindArray<ArenaHeader *> arenaListsToSweep;
 
     /* During incremental sweeping, a list of the arenas already swept. */
-    unsigned incrementalSweptArenaKind;
+    AllocKind incrementalSweptArenaKind;
     ArenaList incrementalSweptArenas;
 
     // Arena lists which have yet to be swept, but need additional foreground
     // processing before they are swept.
     ArenaHeader *gcShapeArenasToUpdate;
     ArenaHeader *gcAccessorShapeArenasToUpdate;
     ArenaHeader *gcScriptArenasToUpdate;
     ArenaHeader *gcObjectGroupArenasToUpdate;
 
     // While sweeping type information, these lists save the arenas for the
     // objects which have already been finalized in the foreground (which must
     // happen at the beginning of the GC), so that type sweeping can determine
     // which of the object pointers are marked.
-    ArenaList savedObjectArenas[FINALIZE_OBJECT_LIMIT];
+    ObjectAllocKindArray<ArenaList> savedObjectArenas;
     ArenaHeader *savedEmptyObjectArenas;
 
   public:
     explicit ArenaLists(JSRuntime *rt) : runtime_(rt) {
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
+        for (ALL_ALLOC_KINDS(i))
             freeLists[i].initAsEmpty();
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
+        for (ALL_ALLOC_KINDS(i))
             backgroundFinalizeState[i] = BFS_DONE;
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
+        for (ALL_ALLOC_KINDS(i))
             arenaListsToSweep[i] = nullptr;
-        incrementalSweptArenaKind = FINALIZE_LIMIT;
+        incrementalSweptArenaKind = AllocKind::LIMIT;
         gcShapeArenasToUpdate = nullptr;
         gcAccessorShapeArenasToUpdate = nullptr;
         gcScriptArenasToUpdate = nullptr;
         gcObjectGroupArenasToUpdate = nullptr;
         savedEmptyObjectArenas = nullptr;
     }
 
     ~ArenaLists();
 
     static uintptr_t getFreeListOffset(AllocKind thingKind) {
         uintptr_t offset = offsetof(ArenaLists, freeLists);
-        return offset + thingKind * sizeof(FreeList);
+        return offset + size_t(thingKind) * sizeof(FreeList);
     }
 
     const FreeList *getFreeList(AllocKind thingKind) const {
         return &freeLists[thingKind];
     }
 
     ArenaHeader *getFirstArena(AllocKind thingKind) const {
         return arenaLists[thingKind].head();
@@ -655,31 +655,31 @@ class ArenaLists
         return incrementalSweptArenas.head();
     }
 
     ArenaHeader *getArenaAfterCursor(AllocKind thingKind) const {
         return arenaLists[thingKind].arenaAfterCursor();
     }
 
     bool arenaListsAreEmpty() const {
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
+        for (ALL_ALLOC_KINDS(i)) {
             /*
              * The arena cannot be empty if the background finalization is not yet
              * done.
              */
             if (backgroundFinalizeState[i] != BFS_DONE)
                 return false;
             if (!arenaLists[i].isEmpty())
                 return false;
         }
         return true;
     }
 
     void unmarkAll() {
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
+        for (ALL_ALLOC_KINDS(i)) {
             /* The background finalization must have stopped at this point. */
             MOZ_ASSERT(backgroundFinalizeState[i] == BFS_DONE);
             for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next)
                 aheader->unmarkAll();
         }
     }
 
     bool doneBackgroundFinalize(AllocKind kind) const {
@@ -690,18 +690,18 @@ class ArenaLists
         return backgroundFinalizeState[kind] != BFS_DONE;
     }
 
     /*
      * Return the free list back to the arena so the GC finalization will not
      * run the finalizers over unitialized bytes from free things.
      */
     void purge() {
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
-            purge(AllocKind(i));
+        for (ALL_ALLOC_KINDS(i))
+            purge(i);
     }
 
     void purge(AllocKind i) {
         FreeList *freeList = &freeLists[i];
         if (!freeList->isEmpty()) {
             ArenaHeader *aheader = freeList->arenaHeader();
             aheader->setFirstFreeSpan(freeList->getHead());
             freeList->initAsEmpty();
@@ -711,36 +711,36 @@ class ArenaLists
     inline void prepareForIncrementalGC(JSRuntime *rt);
 
     /*
      * Temporarily copy the free list heads to the arenas so the code can see
      * the proper value in ArenaHeader::freeList when accessing the latter
      * outside the GC.
      */
     void copyFreeListsToArenas() {
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
-            copyFreeListToArena(AllocKind(i));
+        for (ALL_ALLOC_KINDS(i))
+            copyFreeListToArena(i);
     }
 
     void copyFreeListToArena(AllocKind thingKind) {
         FreeList *freeList = &freeLists[thingKind];
         if (!freeList->isEmpty()) {
             ArenaHeader *aheader = freeList->arenaHeader();
             MOZ_ASSERT(!aheader->hasFreeThings());
             aheader->setFirstFreeSpan(freeList->getHead());
         }
     }
 
     /*
      * Clear the free lists in arenas that were temporarily set there using
      * copyToArenas.
      */
     void clearFreeListsInArenas() {
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
-            clearFreeListInArena(AllocKind(i));
+        for (ALL_ALLOC_KINDS(i))
+            clearFreeListInArena(i);
     }
 
     void clearFreeListInArena(AllocKind kind) {
         FreeList *freeList = &freeLists[kind];
         if (!freeList->isEmpty()) {
             ArenaHeader *aheader = freeList->arenaHeader();
             MOZ_ASSERT(freeList->isSameNonEmptySpan(aheader->getFirstFreeSpan()));
             aheader->setAsFullyUsed();
@@ -790,18 +790,18 @@ class ArenaLists
      */
     void adoptArenas(JSRuntime *runtime, ArenaLists *fromArenaLists);
 
     /* True if the ArenaHeader in question is found in this ArenaLists */
     bool containsArena(JSRuntime *runtime, ArenaHeader *arenaHeader);
 
     void checkEmptyFreeLists() {
 #ifdef DEBUG
-        for (size_t i = 0; i < mozilla::ArrayLength(freeLists); ++i)
-            MOZ_ASSERT(freeLists[i].isEmpty());
+        for (ALL_ALLOC_KINDS(i))
+            checkEmptyFreeList(i);
 #endif
     }
 
     void checkEmptyFreeList(AllocKind kind) {
         MOZ_ASSERT(freeLists[kind].isEmpty());
     }
 
     bool relocateArenas(ArenaHeader *&relocatedListOut, JS::gcreason::Reason reason,
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -47,17 +47,17 @@ using mozilla::ArrayLength;
 using mozilla::Maybe;
 #ifdef JS_MORE_DETERMINISTIC
 using mozilla::PodCopy;
 #endif
 using mozilla::PodZero;
 
 typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
 
-static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
+static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
 
 void
 NativeIterator::mark(JSTracer *trc)
 {
     for (HeapPtrFlatString *str = begin(); str < end(); str++)
         MarkString(trc, str, "prop");
     if (obj)
         MarkObject(trc, &obj, "obj");
@@ -322,17 +322,17 @@ Snapshot(JSContext *cx, HandleObject pob
                 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
                     bool enumerable = false;
 
                     // We need to filter, if the caller just wants enumerable
                     // symbols.
                     if (!(flags & JSITER_HIDDEN)) {
                         if (!Proxy::getOwnPropertyDescriptor(cx, pobj, proxyProps[n], &desc))
                             return false;
-                        enumerable = desc.isEnumerable();
+                        enumerable = desc.enumerable();
                     }
 
                     if (!Enumerate(cx, pobj, proxyProps[n], enumerable, flags, ht, props))
                         return false;
                 }
             } else {
                 // Returns enumerable property names (no symbols).
                 if (!Proxy::getOwnEnumerablePropertyKeys(cx, pobj, proxyProps))
@@ -1170,17 +1170,17 @@ SuppressDeletedPropertyHelper(JSContext 
                         if (!ValueToId<CanGC>(cx, idv, &id))
                             return false;
 
                         Rooted<PropertyDescriptor> desc(cx);
                         if (!GetPropertyDescriptor(cx, proto, id, &desc))
                             return false;
 
                         if (desc.object()) {
-                            if (desc.isEnumerable())
+                            if (desc.enumerable())
                                 continue;
                         }
                     }
 
                     /*
                      * If GetPropertyDescriptorById above removed a property from
                      * ni, start over.
                      */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -122,126 +122,66 @@ js::InformalValueTypeName(const Value &v
     if (v.isNull())
         return "null";
     if (v.isUndefined())
         return "undefined";
     return "value";
 }
 
 bool
-js::NewPropertyDescriptorObject(JSContext *cx, Handle<PropertyDescriptor> desc,
-                                MutableHandleValue vp)
+js::FromPropertyDescriptor(JSContext *cx, Handle<PropertyDescriptor> desc,
+                           MutableHandleValue vp)
 {
     if (!desc.object()) {
         vp.setUndefined();
         return true;
     }
 
-    Rooted<PropDesc> d(cx);
-
-    d.initFromPropertyDescriptor(desc);
-    RootedObject descObj(cx);
-    if (!d.makeObject(cx, &descObj))
-        return false;
-    vp.setObject(*descObj);
-    return true;
-}
-
-void
-PropDesc::initFromPropertyDescriptor(Handle<PropertyDescriptor> desc)
-{
-    MOZ_ASSERT(isUndefined());
-
-    if (!desc.object())
-        return;
-
-    isUndefined_ = false;
-    attrs = uint8_t(desc.attributes());
-    MOZ_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-    if (desc.hasGetterOrSetterObject()) {
-        hasGet_ = desc.hasGetterObject();
-        get_ = hasGet_ && desc.getterObject()
-               ? ObjectValue(*desc.getterObject())
-               : UndefinedValue();
-        hasSet_ = desc.hasSetterObject();
-        set_ = hasSet_ && desc.setterObject()
-               ? ObjectValue(*desc.setterObject())
-               : UndefinedValue();
-        hasValue_ = false;
-        value_.setUndefined();
-        hasWritable_ = false;
-    } else {
-        hasGet_ = false;
-        get_.setUndefined();
-        hasSet_ = false;
-        set_.setUndefined();
-        hasValue_ = !(desc.attributes() & JSPROP_IGNORE_VALUE);
-        value_ = hasValue_ ? desc.value() : UndefinedValue();
-        hasWritable_ = !(desc.attributes() & JSPROP_IGNORE_READONLY);
-    }
-    hasEnumerable_ = !(desc.attributes() & JSPROP_IGNORE_ENUMERATE);
-    hasConfigurable_ = !(desc.attributes() & JSPROP_IGNORE_PERMANENT);
-}
-
-void
-PropDesc::populatePropertyDescriptor(HandleObject obj, MutableHandle<PropertyDescriptor> desc) const
-{
-    if (isUndefined()) {
-        desc.object().set(nullptr);
-        return;
-    }
-
-    desc.value().set(hasValue() ? value() : UndefinedValue());
-    desc.setGetter(getter());
-    desc.setSetter(setter());
-
-    // Make sure we honor the "has" notions in some way.
-    unsigned attrs = attributes();
-    if (!hasEnumerable())
-        attrs |= JSPROP_IGNORE_ENUMERATE;
-    if (!hasWritable())
-        attrs |= JSPROP_IGNORE_READONLY;
-    if (!hasConfigurable())
-        attrs |= JSPROP_IGNORE_PERMANENT;
-    if (!hasValue())
-        attrs |= JSPROP_IGNORE_VALUE;
-    desc.setAttributes(attrs);
-
-    desc.object().set(obj);
-}
-
-bool
-PropDesc::makeObject(JSContext *cx, MutableHandleObject obj)
-{
-    MOZ_ASSERT(!isUndefined());
-
-    obj.set(NewBuiltinClassInstance<PlainObject>(cx));
+    RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!obj)
         return false;
 
     const JSAtomState &names = cx->names();
-    RootedValue configurableVal(cx, BooleanValue((attrs & JSPROP_PERMANENT) == 0));
-    RootedValue enumerableVal(cx, BooleanValue((attrs & JSPROP_ENUMERATE) != 0));
-    RootedValue writableVal(cx, BooleanValue((attrs & JSPROP_READONLY) == 0));
-    if ((hasConfigurable() &&
-         !DefineProperty(cx, obj, names.configurable, configurableVal)) ||
-        (hasEnumerable() &&
-         !DefineProperty(cx, obj, names.enumerable, enumerableVal)) ||
-        (hasGet() &&
-         !DefineProperty(cx, obj, names.get, getterValue())) ||
-        (hasSet() &&
-         !DefineProperty(cx, obj, names.set, setterValue())) ||
-        (hasValue() &&
-         !DefineProperty(cx, obj, names.value, value())) ||
-        (hasWritable() &&
-         !DefineProperty(cx, obj, names.writable, writableVal)))
-    {
-        return false;
+    RootedValue v(cx);
+    if (desc.hasConfigurable()) {
+        v.setBoolean(desc.configurable());
+        if (!DefineProperty(cx, obj, names.configurable, v))
+            return false;
+    }
+    if (desc.hasEnumerable()) {
+        v.setBoolean(desc.enumerable());
+        if (!DefineProperty(cx, obj, names.enumerable, v))
+            return false;
+    }
+    if (desc.hasValue()) {
+        if (!DefineProperty(cx, obj, names.value, desc.value()))
+            return false;
     }
-
+    if (desc.hasWritable()) {
+        v.setBoolean(desc.writable());
+        if (!DefineProperty(cx, obj, names.writable, v))
+            return false;
+    }
+    if (desc.hasGetterObject()) {
+        if (JSObject *get = desc.getterObject())
+            v.setObject(*get);
+        else
+            v.setUndefined();
+        if (!DefineProperty(cx, obj, names.get, v))
+            return false;
+    }
+    if (desc.hasSetterObject()) {
+        if (JSObject *set = desc.setterObject())
+            v.setObject(*set);
+        else
+            v.setUndefined();
+        if (!DefineProperty(cx, obj, names.set, v))
+            return false;
+    }
+    vp.setObject(*obj);
     return true;
 }
 
 bool
 js::GetFirstArgumentAsObject(JSContext *cx, const CallArgs &args, const char *method,
                              MutableHandleObject objp)
 {
     if (args.length() == 0) {
@@ -275,149 +215,16 @@ GetPropertyIfPresent(JSContext *cx, Hand
         vp.setUndefined();
         return true;
     }
 
     return GetProperty(cx, obj, obj, id, vp);
 }
 
 bool
-PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors)
-{
-    MOZ_ASSERT(isUndefined());
-
-    RootedValue v(cx, origval);
-
-    /* 8.10.5 step 1 */
-    if (v.isPrimitive()) {
-        UniquePtr<char[], JS::FreePolicy> bytes =
-            DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
-        if (!bytes)
-            return false;
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
-        return false;
-    }
-    RootedObject desc(cx, &v.toObject());
-
-    isUndefined_ = false;
-
-    attrs = 0;
-
-    bool found = false;
-    RootedId id(cx);
-
-    /* 8.10.5 step 3 */
-    id = NameToId(cx->names().enumerable);
-    if (!GetPropertyIfPresent(cx, desc, id, &v, &found))
-        return false;
-    if (found) {
-        hasEnumerable_ = true;
-        if (ToBoolean(v))
-            attrs |= JSPROP_ENUMERATE;
-    }
-
-    /* 8.10.5 step 4 */
-    id = NameToId(cx->names().configurable);
-    if (!GetPropertyIfPresent(cx, desc, id, &v, &found))
-        return false;
-    if (found) {
-        hasConfigurable_ = true;
-        if (!ToBoolean(v))
-            attrs |= JSPROP_PERMANENT;
-    }
-
-    /* 8.10.5 step 5 */
-    id = NameToId(cx->names().value);
-    if (!GetPropertyIfPresent(cx, desc, id, &v, &found))
-        return false;
-    if (found) {
-        hasValue_ = true;
-        value_ = v;
-    }
-
-    /* 8.10.6 step 6 */
-    id = NameToId(cx->names().writable);
-    if (!GetPropertyIfPresent(cx, desc, id, &v, &found))
-        return false;
-    if (found) {
-        hasWritable_ = true;
-        if (!ToBoolean(v))
-            attrs |= JSPROP_READONLY;
-    }
-
-    /* 8.10.7 step 7 */
-    id = NameToId(cx->names().get);
-    if (!GetPropertyIfPresent(cx, desc, id, &v, &found))
-        return false;
-    if (found) {
-        hasGet_ = true;
-        get_ = v;
-        attrs |= JSPROP_GETTER | JSPROP_SHARED;
-        if (checkAccessors && !checkGetter(cx))
-            return false;
-    }
-
-    /* 8.10.7 step 8 */
-    id = NameToId(cx->names().set);
-    if (!GetPropertyIfPresent(cx, desc, id, &v, &found))
-        return false;
-    if (found) {
-        hasSet_ = true;
-        set_ = v;
-        attrs |= JSPROP_SETTER | JSPROP_SHARED;
-        if (checkAccessors && !checkSetter(cx))
-            return false;
-    }
-
-    /* 8.10.7 step 9 */
-    if ((hasGet() || hasSet()) && (hasValue() || hasWritable())) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INVALID_DESCRIPTOR);
-        return false;
-    }
-
-    MOZ_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-
-    return true;
-}
-
-void
-PropDesc::complete()
-{
-    MOZ_ASSERT(!isUndefined());
-
-    if (isGenericDescriptor() || isDataDescriptor()) {
-        if (!hasValue_) {
-            hasValue_ = true;
-            value_.setUndefined();
-        }
-        if (!hasWritable_) {
-            hasWritable_ = true;
-            attrs |= JSPROP_READONLY;
-        }
-    } else {
-        if (!hasGet_) {
-            hasGet_ = true;
-            get_.setUndefined();
-        }
-        if (!hasSet_) {
-            hasSet_ = true;
-            set_.setUndefined();
-        }
-    }
-    if (!hasEnumerable_) {
-        hasEnumerable_ = true;
-        attrs &= ~JSPROP_ENUMERATE;
-    }
-    if (!hasConfigurable_) {
-        hasConfigurable_ = true;
-        attrs |= JSPROP_PERMANENT;
-    }
-}
-
-bool
 js::Throw(JSContext *cx, jsid id, unsigned errorNumber)
 {
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1);
 
     RootedValue idVal(cx, IdToValue(id));
     JSString *idstr = ValueToSource(cx, idVal);
     if (!idstr)
        return false;
@@ -442,18 +249,18 @@ js::Throw(JSContext *cx, JSObject *obj, 
     }
     return false;
 }
 
 
 /*** Standard-compliant property definition (used by Object.defineProperty) **********************/
 
 static bool
-DefinePropertyOnObject(JSContext *cx, HandleNativeObject obj, HandleId id, const PropDesc &desc,
-                       ObjectOpResult &result)
+DefinePropertyOnObject(JSContext *cx, HandleNativeObject obj, HandleId id,
+                       Handle<PropertyDescriptor> desc, ObjectOpResult &result)
 {
     /* 8.12.9 step 1. */
     RootedShape shape(cx);
     MOZ_ASSERT(!obj->getOps()->lookupProperty);
     if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
         return false;
 
     MOZ_ASSERT(!obj->getOps()->defineProperty);
@@ -464,27 +271,29 @@ DefinePropertyOnObject(JSContext *cx, Ha
         if (!IsExtensible(cx, obj, &extensible))
             return false;
         if (!extensible)
             return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
 
         if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
             MOZ_ASSERT(!obj->getOps()->defineProperty);
             RootedValue v(cx, desc.hasValue() ? desc.value() : UndefinedValue());
-            unsigned attrs = desc.attributes();
+            unsigned attrs = desc.attributes() & (JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY);
+
             if (!desc.hasConfigurable())
                 attrs |= JSPROP_PERMANENT;
             if (!desc.hasWritable())
                 attrs |= JSPROP_READONLY;
             return NativeDefineProperty(cx, obj, id, v, nullptr, nullptr, attrs, result);
         }
 
         MOZ_ASSERT(desc.isAccessorDescriptor());
 
-        unsigned attrs = desc.attributes();
+        unsigned attrs = desc.attributes() & (JSPROP_PERMANENT | JSPROP_ENUMERATE |
+                                              JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER);
         if (!desc.hasConfigurable())
             attrs |= JSPROP_PERMANENT;
         return NativeDefineProperty(cx, obj, id, UndefinedHandleValue,
                                     desc.getter(), desc.setter(), attrs, result);
     }
 
     /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
     RootedValue v(cx);
@@ -512,31 +321,23 @@ DefinePropertyOnObject(JSContext *cx, Ha
         shapeAttributes = shape->attributes();
     }
 
     do {
         if (desc.isAccessorDescriptor()) {
             if (!shapeAccessorDescriptor)
                 break;
 
-            if (desc.hasGet()) {
-                RootedValue getter(cx, shape->getterOrUndefined());
-                bool same;
-                if (!SameValue(cx, desc.getterValue(), getter, &same))
-                    return false;
-                if (!same)
+            if (desc.hasGetterObject()) {
+                if (!shape->hasGetterValue() || desc.getterObject() != shape->getterObject())
                     break;
             }
 
-            if (desc.hasSet()) {
-                RootedValue setter(cx, shape->setterOrUndefined());
-                bool same;
-                if (!SameValue(cx, desc.setterValue(), setter, &same))
-                    return false;
-                if (!same)
+            if (desc.hasSetterObject()) {
+                if (!shape->hasSetterValue() || desc.setterObject() != shape->setterObject())
                     break;
             }
         } else {
             /*
              * Determine the current value of the property once, if the current
              * value might actually need to be used or preserved later.  NB: we
              * guard on whether the current property is a data descriptor to
              * avoid calling a getter; we won't need the value if it's not a
@@ -648,32 +449,31 @@ DefinePropertyOnObject(JSContext *cx, Ha
             }
         }
 
         callDelProperty = !shapeHasDefaultGetter || !shapeHasDefaultSetter;
     } else {
         /* 8.12.9 step 11. */
         MOZ_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
         if (!shape->configurable()) {
-            if (desc.hasSet()) {
-                RootedValue setter(cx, shape->setterOrUndefined());
-                bool same;
-                if (!SameValue(cx, desc.setterValue(), setter, &same))
-                    return false;
-                if (!same)
-                    return result.fail(JSMSG_CANT_REDEFINE_PROP);
+            // The hasSetterValue() and hasGetterValue() calls below ought to
+            // be redundant here, because accessor shapes should always have
+            // both JSPROP_GETTER and JSPROP_SETTER. But this is not the case
+            // currently; in particular Object.defineProperty(obj, key, {get: fn})
+            // creates a property without JSPROP_SETTER (bug 1133315).
+            if (desc.hasSetterObject() &&
+                desc.setterObject() != (shape->hasSetterValue() ? shape->setterObject() : nullptr))
+            {
+                return result.fail(JSMSG_CANT_REDEFINE_PROP);
             }
 
-            if (desc.hasGet()) {
-                RootedValue getter(cx, shape->getterOrUndefined());
-                bool same;
-                if (!SameValue(cx, desc.getterValue(), getter, &same))
-                    return false;
-                if (!same)
-                    return result.fail(JSMSG_CANT_REDEFINE_PROP);
+            if (desc.hasGetterObject() &&
+                desc.getterObject() != (shape->hasGetterValue() ? shape->getterObject() : nullptr))
+            {
+                return result.fail(JSMSG_CANT_REDEFINE_PROP);
             }
         }
     }
 
     /* 8.12.9 step 12. */
     unsigned attrs;
     GetterOp getter;
     SetterOp setter;
@@ -683,57 +483,59 @@ DefinePropertyOnObject(JSContext *cx, Ha
             changed |= JSPROP_PERMANENT;
         if (desc.hasEnumerable())
             changed |= JSPROP_ENUMERATE;
 
         attrs = (shapeAttributes & ~changed) | (desc.attributes() & changed);
         getter = IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->getter();
         setter = IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->setter();
     } else if (desc.isDataDescriptor()) {
-        unsigned unchanged = 0, descAttrs = desc.attributes();
-        if (!desc.hasConfigurable())
-            unchanged |= JSPROP_PERMANENT;
-        if (!desc.hasEnumerable())
-            unchanged |= JSPROP_ENUMERATE;
         /* Watch out for accessor -> data transformations here. */
-        if (!desc.hasWritable()) {
-            if (shapeDataDescriptor)
-                unchanged |= JSPROP_READONLY;
-            else
-                descAttrs |= JSPROP_READONLY;
+        unsigned changed = JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
+        unsigned descAttrs = desc.attributes();
+        if (desc.hasConfigurable())
+            changed |= JSPROP_PERMANENT;
+        if (desc.hasEnumerable())
+            changed |= JSPROP_ENUMERATE;
+
+        if (desc.hasWritable()) {
+            changed |= JSPROP_READONLY;
+        } else if (!shapeDataDescriptor) {
+            changed |= JSPROP_READONLY;
+            descAttrs |= JSPROP_READONLY;
         }
 
         if (desc.hasValue())
             v = desc.value();
-        attrs = (descAttrs & ~unchanged) | (shapeAttributes & unchanged);
+        attrs = (descAttrs & changed) | (shapeAttributes & ~changed);
         getter = nullptr;
         setter = nullptr;
     } else {
         MOZ_ASSERT(desc.isAccessorDescriptor());
 
         /* 8.12.9 step 12. */
         unsigned changed = 0;
         if (desc.hasConfigurable())
             changed |= JSPROP_PERMANENT;
         if (desc.hasEnumerable())
             changed |= JSPROP_ENUMERATE;
-        if (desc.hasGet())
+        if (desc.hasGetterObject())
             changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY;
-        if (desc.hasSet())
+        if (desc.hasSetterObject())
             changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY;
 
         attrs = (desc.attributes() & changed) | (shapeAttributes & ~changed);
-        if (desc.hasGet()) {
+        if (desc.hasGetterObject()) {
             getter = desc.getter();
         } else {
             getter = (shapeHasDefaultGetter && !shapeHasGetterValue)
                      ? nullptr
                      : shape->getter();
         }
-        if (desc.hasSet()) {
+        if (desc.hasSetterObject()) {
             setter = desc.setter();
         } else {
             setter = (shapeHasDefaultSetter && !shapeHasSetterValue)
                      ? nullptr
                      : shape->setter();
         }
     }
 
@@ -752,18 +554,18 @@ DefinePropertyOnObject(JSContext *cx, Ha
             return false;
     }
 
     return NativeDefineProperty(cx, obj, id, v, getter, setter, attrs, result);
 }
 
 /* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */
 static bool
-DefinePropertyOnArray(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, const PropDesc &desc,
-                      ObjectOpResult &result)
+DefinePropertyOnArray(JSContext *cx, Handle<ArrayObject*> arr, HandleId id,
+                      Handle<PropertyDescriptor> desc, ObjectOpResult &result)
 {
     /* Step 2. */
     if (id == NameToId(cx->names().length)) {
         // Canonicalize value, if necessary, before proceeding any further.  It
         // would be better if this were always/only done by ArraySetLength.
         // But canonicalization may throw a RangeError (or other exception, if
         // the value is an object with user-defined conversion semantics)
         // before other attributes are checked.  So as long as our internal
@@ -814,18 +616,18 @@ DefinePropertyOnArray(JSContext *cx, Han
     }
 
     /* Step 4. */
     return DefinePropertyOnObject(cx, arr, id, desc, result);
 }
 
 // ES6 draft rev31 9.4.5.3 [[DefineOwnProperty]]
 static bool
-DefinePropertyOnTypedArray(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
-                           ObjectOpResult &result)
+DefinePropertyOnTypedArray(JSContext *cx, HandleObject obj, HandleId id,
+                           Handle<PropertyDescriptor> desc, ObjectOpResult &result)
 {
     MOZ_ASSERT(IsAnyTypedArray(obj));
     // Steps 3.a-c.
     uint64_t index;
     if (IsTypedArrayIndex(id, &index)) {
         // These are all substeps of 3.c.
         // Steps i-vi.
         // We (wrongly) ignore out of range defines with a value.
@@ -864,96 +666,242 @@ DefinePropertyOnTypedArray(JSContext *cx
         return result.succeed();
     }
 
     // Step 4.
     return DefinePropertyOnObject(cx, obj.as<NativeObject>(), id, desc, result);
 }
 
 bool
-js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
-                           ObjectOpResult &result)
+js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id,
+                           Handle<PropertyDescriptor> desc, ObjectOpResult &result)
 {
     if (obj->is<ArrayObject>()) {
         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
         return DefinePropertyOnArray(cx, arr, id, desc, result);
     }
 
     if (IsAnyTypedArray(obj))
         return DefinePropertyOnTypedArray(cx, obj, id, desc, result);
 
     if (obj->is<UnboxedPlainObject>() && !UnboxedPlainObject::convertToNative(cx, obj))
         return false;
 
     if (obj->getOps()->lookupProperty) {
         if (obj->is<ProxyObject>()) {
-            Rooted<PropertyDescriptor> pd(cx);
-            desc.populatePropertyDescriptor(obj, &pd);
+            Rooted<PropertyDescriptor> pd(cx, desc);
             pd.object().set(obj);
             return Proxy::defineProperty(cx, obj, id, &pd, result);
         }
         return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
     }
 
     return DefinePropertyOnObject(cx, obj.as<NativeObject>(), id, desc, result);
 }
 
 bool
 js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id,
-                           Handle<PropertyDescriptor> descriptor, ObjectOpResult &result)
-{
-    Rooted<PropDesc> desc(cx);
-    desc.initFromPropertyDescriptor(descriptor);
-    return StandardDefineProperty(cx, obj, id, desc, result);
-}
-
-bool
-js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc)
-{
-    ObjectOpResult success;
-    return StandardDefineProperty(cx, obj, id, desc, success) &&
-           success.checkStrict(cx, obj, id);
-}
-
-bool
-js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id,
                            Handle<PropertyDescriptor> desc)
 {
     ObjectOpResult success;
     return StandardDefineProperty(cx, obj, id, desc, success) &&
            success.checkStrict(cx, obj, id);
 }
 
 bool
+CheckCallable(JSContext *cx, JSObject *obj, const char *fieldName)
+{
+    if (obj && !obj->isCallable()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
+                             fieldName);
+        return false;
+    }
+    return true;
+}
+
+bool
+js::ToPropertyDescriptor(JSContext *cx, HandleValue descval, bool checkAccessors,
+                         MutableHandle<PropertyDescriptor> desc)
+{
+    // step 2
+    if (!descval.isObject()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
+                             InformalValueTypeName(descval));
+        return false;
+    }
+    RootedObject obj(cx, &descval.toObject());
+
+    // step 3
+    desc.clear();
+
+    bool found = false;
+    RootedId id(cx);
+    RootedValue v(cx);
+    unsigned attrs = 0;
+
+    // step 4
+    id = NameToId(cx->names().enumerable);
+    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
+        return false;
+    if (found) {
+        if (ToBoolean(v))
+            attrs |= JSPROP_ENUMERATE;
+    } else {
+        attrs |= JSPROP_IGNORE_ENUMERATE;
+    }
+
+    // step 5
+    id = NameToId(cx->names().configurable);
+    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
+        return false;
+    if (found) {
+        if (!ToBoolean(v))
+            attrs |= JSPROP_PERMANENT;
+    } else {
+        attrs |= JSPROP_IGNORE_PERMANENT;
+    }
+
+    // step 6
+    id = NameToId(cx->names().value);
+    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
+        return false;
+    if (found)
+        desc.value().set(v);
+    else
+        attrs |= JSPROP_IGNORE_VALUE;
+
+    // step 7
+    id = NameToId(cx->names().writable);
+    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
+        return false;
+    if (found) {
+        if (!ToBoolean(v))
+            attrs |= JSPROP_READONLY;
+    } else {
+        attrs |= JSPROP_IGNORE_READONLY;
+    }
+
+    // step 8
+    bool hasGetOrSet;
+    id = NameToId(cx->names().get);
+    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
+        return false;
+    hasGetOrSet = found;
+    if (found) {
+        if (v.isObject()) {
+            if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_getter_str))
+                return false;
+            desc.setGetterObject(&v.toObject());
+        } else if (!v.isUndefined()) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
+                                 js_getter_str);
+            return false;
+        }
+        attrs |= JSPROP_GETTER | JSPROP_SHARED;
+    }
+
+    // step 9
+    id = NameToId(cx->names().set);
+    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
+        return false;
+    hasGetOrSet |= found;
+    if (found) {
+        if (v.isObject()) {
+            if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_setter_str))
+                return false;
+            desc.setSetterObject(&v.toObject());
+        } else if (!v.isUndefined()) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
+                                 js_setter_str);
+            return false;
+        }
+        attrs |= JSPROP_SETTER | JSPROP_SHARED;
+    }
+
+    // step 10
+    if (hasGetOrSet) {
+        if (!(attrs & JSPROP_IGNORE_READONLY) || !(attrs & JSPROP_IGNORE_VALUE)) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INVALID_DESCRIPTOR);
+            return false;
+        }
+
+        // By convention, these bits are not used on accessor descriptors.
+        attrs &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
+    }
+
+    desc.setAttributes(attrs);
+    MOZ_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+    MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
+    return true;
+}
+
+bool
+js::CheckPropertyDescriptorAccessors(JSContext *cx, Handle<PropertyDescriptor> desc)
+{
+    if (desc.hasGetterObject()) {
+        if (!CheckCallable(cx, desc.getterObject(), js_getter_str))
+            return false;
+    }
+    if (desc.hasSetterObject()) {
+        if (!CheckCallable(cx, desc.setterObject(), js_setter_str))
+            return false;
+    }
+    return true;
+}
+
+void
+js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)
+{
+    if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
+        if (!desc.hasValue())
+            desc.value().setUndefined();
+        if (!desc.hasWritable())
+            desc.attributesRef() |= JSPROP_READONLY;
+        desc.attributesRef() &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
+    } else {
+        if (!desc.hasGetterObject())
+            desc.setGetterObject(nullptr);
+        if (!desc.hasSetterObject())
+            desc.setSetterObject(nullptr);
+        desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
+    }
+    if (!desc.hasEnumerable())
+        desc.attributesRef() &= ~JSPROP_ENUMERATE;
+    if (!desc.hasConfigurable())
+        desc.attributesRef() |= JSPROP_PERMANENT;
+    desc.attributesRef() &= ~(JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_ENUMERATE);
+}
+
+bool
 js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
-                            AutoIdVector *ids, AutoPropDescVector *descs)
+                            AutoIdVector *ids, AutoPropertyDescriptorVector *descs)
 {
     if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids))
         return false;
 
     RootedId id(cx);
     for (size_t i = 0, len = ids->length(); i < len; i++) {
         id = (*ids)[i];
-        Rooted<PropDesc> desc(cx);
+        Rooted<PropertyDescriptor> desc(cx);
         RootedValue v(cx);
         if (!GetProperty(cx, props, props, id, &v) ||
-            !desc.initialize(cx, v, checkAccessors) ||
+            !ToPropertyDescriptor(cx, v, checkAccessors, &desc) ||
             !descs->append(desc))
         {
             return false;
         }
     }
     return true;
 }
 
 bool
 js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
 {
     AutoIdVector ids(cx);
-    AutoPropDescVector descs(cx);
+    AutoPropertyDescriptorVector descs(cx);
     if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
         return false;
 
     for (size_t i = 0, len = ids.length(); i < len; i++) {
         if (!StandardDefineProperty(cx, obj, ids[i], descs[i]))
             return false;
     }
 
@@ -1117,18 +1065,18 @@ js::TestIntegrityLevel(JSContext *cx, Ha
         if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
             return false;
 
         // Step 9.c.
         if (!desc.object())
             continue;
 
         // Steps 9.c.i-ii.
-        if (!desc.isPermanent() ||
-            (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.isWritable()))
+        if (desc.configurable() ||
+            (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.writable()))
         {
             *result = false;
             return true;
         }
     }
 
     // Step 10.
     *result = true;
@@ -1141,20 +1089,20 @@ js::TestIntegrityLevel(JSContext *cx, Ha
 /*
  * Get the GC kind to use for scripted 'new' on the given class.
  * FIXME bug 547327: estimate the size from the allocation site.
  */
 static inline gc::AllocKind
 NewObjectGCKind(const js::Class *clasp)
 {
     if (clasp == &ArrayObject::class_)
-        return gc::FINALIZE_OBJECT8;
+        return gc::AllocKind::OBJECT8;
     if (clasp == &JSFunction::class_)
-        return gc::FINALIZE_OBJECT2;
-    return gc::FINALIZE_OBJECT4;
+        return gc::AllocKind::OBJECT2;
+    return gc::AllocKind::OBJECT4;
 }
 
 static inline JSObject *
 NewObject(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent, gc::AllocKind kind,
           NewObjectKind newKind)
 {
     const Class *clasp = group->clasp();
 
@@ -1454,17 +1402,17 @@ NewObjectWithGroupIsCachable(ExclusiveCo
  * avoid losing creation site information for objects made by scripted 'new'.
  */
 JSObject *
 js::NewObjectWithGroupCommon(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
                              gc::AllocKind allocKind, NewObjectKind newKind)
 {
     MOZ_ASSERT(parent);
 
-    MOZ_ASSERT(allocKind <= gc::FINALIZE_OBJECT_LAST);
+    MOZ_ASSERT(allocKind <= gc::AllocKind::OBJECT_LAST);
     if (CanBeFinalizedInBackground(allocKind, group->clasp()))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     bool isCachable = NewObjectWithGroupIsCachable(cx, group, parent, newKind);
     if (isCachable) {
         NewObjectCache &cache = cx->asJSContext()->runtime()->newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         if (cache.lookupGroup(group, allocKind, &entry)) {
@@ -1529,17 +1477,17 @@ CreateThisForFunctionWithGroup(JSContext
                                NewObjectKind newKind)
 {
     if (group->maybeUnboxedLayout() && newKind != SingletonObject)
         return UnboxedPlainObject::create(cx, group, newKind);
 
     if (TypeNewScript *newScript = group->newScript()) {
         if (newScript->analyzed()) {
             // The definite properties analysis has been performed for this
-            // group, so get the shape and finalize kind to use from the
+            // group, so get the shape and alloc kind to use from the
             // TypeNewScript's template.
             RootedPlainObject templateObject(cx, newScript->templateObject());
             MOZ_ASSERT(templateObject->group() == group);
 
             RootedPlainObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
             if (!res)
                 return nullptr;
 
@@ -3186,17 +3134,17 @@ js::GetOwnPropertyDescriptor(JSContext *
         return false;
     if (!shape) {
         desc.object().set(nullptr);
         return true;
     }
 
     bool doGet = true;
     desc.setAttributes(GetShapeAttributes(obj, shape));
-    if (desc.hasGetterOrSetterObject()) {
+    if (desc.isAccessorDescriptor()) {
         MOZ_ASSERT(desc.isShared());
         doGet = false;
 
         // The result of GetOwnPropertyDescriptor() must be either undefined or
         // a complete property descriptor (per ES6 draft rev 32 (2015 Feb 2)
         // 6.1.7.3, Invariants of the Essential Internal Methods).
         //
         // It is an unfortunate fact that in SM, properties can exist that have
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -28,17 +28,17 @@
 #include "vm/Xdr.h"
 
 namespace JS {
 struct ClassInfo;
 }
 
 namespace js {
 
-class AutoPropDescVector;
+class AutoPropertyDescriptorVector;
 class GCMarker;
 struct NativeIterator;
 class Nursery;
 class ObjectElements;
 struct StackShape;
 
 namespace gc {
 class RelocationOverlay;
@@ -65,18 +65,16 @@ CastAsObjectJsval(GetterOp op)
 inline Value
 CastAsObjectJsval(SetterOp op)
 {
     return ObjectOrNullValue(CastAsObject(op));
 }
 
 /******************************************************************************/
 
-typedef Vector<PropDesc, 1> PropDescArray;
-
 extern const Class IntlClass;
 extern const Class JSONClass;
 extern const Class MathClass;
 
 class GlobalObject;
 class MapObject;
 class NewObjectCache;
 class NormalArgumentsObject;
@@ -762,31 +760,24 @@ GetOwnPropertyDescriptor(JSContext *cx, 
  * If obj is any other native object, this follows ES5 8.12.9.
  * If obj is a proxy, this calls the proxy handler's defineProperty method.
  * Otherwise, this reports an error and returns false.
  *
  * Both StandardDefineProperty functions hew close to the ES5 spec. Note that
  * the DefineProperty functions do not enforce some invariants mandated by ES6.
  */
 extern bool
-StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
-                       ObjectOpResult &result);
-
-extern bool
 StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id,
                        Handle<PropertyDescriptor> descriptor, ObjectOpResult &result);
 
 /*
- * For convenience, signatures identical to the above except without the
- * ObjectOpResult out-parameter. They throw a TypeError on failure.
+ * Same as above except without the ObjectOpResult out-parameter. Throws a
+ * TypeError on failure.
  */
 extern bool
-StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc);
-
-extern bool
 StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id,
                        Handle<PropertyDescriptor> desc);
 
 extern bool
 DefineProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
                JSGetterOp getter, JSSetterOp setter, unsigned attrs, ObjectOpResult &result);
 
 extern bool
@@ -1145,23 +1136,51 @@ extern JSObject *
 CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto);
 
 extern JSObject *
 DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind = GenericObject);
 
 extern bool
 DefineProperties(JSContext *cx, HandleObject obj, HandleObject props);
 
+inline JSGetterOp
+CastAsGetterOp(JSObject *object)
+{
+    return JS_DATA_TO_FUNC_PTR(JSGetterOp, object);
+}
+
+inline JSSetterOp
+CastAsSetterOp(JSObject *object)
+{
+    return JS_DATA_TO_FUNC_PTR(JSSetterOp, object);
+}
+
+/* ES6 draft rev 32 (2015 Feb 2) 6.2.4.5 ToPropertyDescriptor(Obj) */
+bool
+ToPropertyDescriptor(JSContext *cx, HandleValue descval, bool checkAccessors,
+                     MutableHandle<PropertyDescriptor> desc);
+
+/*
+ * Throw a TypeError if desc.getterObject() or setterObject() is not
+ * callable. This performs exactly the checks omitted by ToPropertyDescriptor
+ * when checkAccessors is false.
+ */
+bool
+CheckPropertyDescriptorAccessors(JSContext *cx, Handle<PropertyDescriptor> desc);
+
+void
+CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc);
+
 /*
  * Read property descriptors from props, as for Object.defineProperties. See
  * ES5 15.2.3.7 steps 3-5.
  */
 extern bool
 ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
-                        AutoIdVector *ids, AutoPropDescVector *descs);
+                        AutoIdVector *ids, AutoPropertyDescriptorVector *descs);
 
 /* Read the name using a dynamic lookup on the scopeChain. */
 extern bool
 LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
            MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp);
 
 extern bool
 LookupNameNoGC(JSContext *cx, PropertyName *name, JSObject *scopeChain,
@@ -1206,18 +1225,19 @@ GetPropertyPure(ExclusiveContext *cx, JS
 
 bool
 GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
                          MutableHandle<PropertyDescriptor> desc);
 
 bool
 GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp);
 
+/* ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc) */
 bool
-NewPropertyDescriptorObject(JSContext *cx, Handle<PropertyDescriptor> desc, MutableHandleValue vp);
+FromPropertyDescriptor(JSContext *cx, Handle<PropertyDescriptor> desc, MutableHandleValue vp);
 
 extern bool
 IsDelegate(JSContext *cx, HandleObject obj, const Value &v, bool *result);
 
 // obj is a JSObject*, but we root it immediately up front. We do it
 // that way because we need a Rooted temporary in this method anyway.
 extern bool
 IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -415,31 +415,16 @@ JSObject::isNewGroupUnknown() const
 inline bool
 JSObject::wasNewScriptCleared() const
 {
     return hasAllFlags(js::BaseShape::NEW_SCRIPT_CLEARED);
 }
 
 namespace js {
 
-PropDesc::PropDesc(const Value &getter, const Value &setter,
-                   Enumerability enumerable, Configurability configurable)
-  : value_(UndefinedValue()),
-    get_(getter), set_(setter),
-    attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED |
-          (enumerable ? JSPROP_ENUMERATE : 0) |
-          (configurable ? 0 : JSPROP_PERMANENT)),
-    hasGet_(true), hasSet_(true),
-    hasValue_(false), hasWritable_(false), hasEnumerable_(true), hasConfigurable_(true),
-    isUndefined_(false)
-{
-    MOZ_ASSERT(getter.isUndefined() || IsCallable(getter));
-    MOZ_ASSERT(setter.isUndefined() || IsCallable(setter));
-}
-
 static MOZ_ALWAYS_INLINE bool
 IsFunctionObject(const js::Value &v)
 {
     return v.isObject() && v.toObject().is<JSFunction>();
 }
 
 static MOZ_ALWAYS_INLINE bool
 IsFunctionObject(const js::Value &v, JSFunction **fun)
@@ -568,22 +553,22 @@ inline bool
 IsInternalFunctionObject(JSObject *funobj)
 {
     JSFunction *fun = &funobj->as<JSFunction>();
     MOZ_ASSERT_IF(fun->isLambda(),
                   fun->isInterpreted() || fun->isAsmJSNative());
     return fun->isLambda() && fun->isInterpreted() && !fun->environment();
 }
 
-class AutoPropDescVector : public AutoVectorRooter<PropDesc>
+class AutoPropertyDescriptorVector : public AutoVectorRooter<PropertyDescriptor>
 {
   public:
-    explicit AutoPropDescVector(JSContext *cx
-                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-        : AutoVectorRooter<PropDesc>(cx, DESCVECTOR)
+    explicit AutoPropertyDescriptorVector(JSContext *cx
+                                          MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+        : AutoVectorRooter(cx, DESCVECTOR)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
@@ -765,25 +750,25 @@ NewObjectWithGroup(ExclusiveContext *cx,
  * the object, zero if the final size is unknown. This should only be used for
  * objects that do not require any fixed slots.
  */
 static inline gc::AllocKind
 GuessObjectGCKind(size_t numSlots)
 {
     if (numSlots)
         return gc::GetGCObjectKind(numSlots);
-    return gc::FINALIZE_OBJECT4;
+    return gc::AllocKind::OBJECT4;
 }
 
 static inline gc::AllocKind
 GuessArrayGCKind(size_t numSlots)
 {
     if (numSlots)
         return gc::GetGCArrayKind(numSlots);
-    return gc::FINALIZE_OBJECT8;
+    return gc::AllocKind::OBJECT8;
 }
 
 inline bool
 ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
 {
     if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
         return Proxy::objectClassIs(obj, classValue, cx);
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -276,35 +276,35 @@ js::DumpPCCounts(JSContext *cx, HandleSc
         DumpIonScriptCounts(sp, ionCounts);
         ionCounts = ionCounts->previous();
     }
 }
 
 void
 js::DumpCompartmentPCCounts(JSContext *cx)
 {
-    for (ZoneCellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+    for (ZoneCellIter i(cx->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
         RootedScript script(cx, i.get<JSScript>());
         if (script->compartment() != cx->compartment())
             continue;
 
         if (script->hasScriptCounts()) {
             Sprinter sprinter(cx);
             if (!sprinter.init())
                 return;
 
             fprintf(stdout, "--- SCRIPT %s:%" PRIuSIZE " ---\n", script->filename(), script->lineno());
             DumpPCCounts(cx, script, &sprinter);
             fputs(sprinter.string(), stdout);
             fprintf(stdout, "--- END SCRIPT %s:%" PRIuSIZE " ---\n", script->filename(), script->lineno());
         }
     }
 
-    for (unsigned thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
-        for (ZoneCellIter i(cx->zone(), (AllocKind) thingKind); !i.done(); i.next()) {
+    for (OBJECT_ALLOC_KINDS(thingKind)) {
+        for (ZoneCellIter i(cx->zone(), thingKind); !i.done(); i.next()) {
             JSObject *obj = i.get<JSObject>();
             if (obj->compartment() != cx->compartment())
                 continue;
 
             if (obj->is<AsmJSModuleObject>()) {
                 AsmJSModule &module = obj->as<AsmJSModuleObject>().module();
 
                 Sprinter sprinter(cx);
@@ -2037,17 +2037,17 @@ js::StopPCCountProfiling(JSContext *cx)
 
     ReleaseAllJITCode(rt->defaultFreeOp());
 
     ScriptAndCountsVector *vec = cx->new_<ScriptAndCountsVector>(SystemAllocPolicy());
     if (!vec)
         return;
 
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-        for (ZoneCellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             if (script->hasScriptCounts() && script->types()) {
                 ScriptAndCounts sac;
                 sac.script = script;
                 sac.scriptCounts.set(script->releaseScriptCounts());
                 if (!vec->append(sac))
                     sac.scriptCounts.destroy(rt->defaultFreeOp());
             }
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -235,20 +235,20 @@ Shape::fixupDictionaryShapeAfterMovingGC
     // location of a dead object in the nursery, in which case we should never
     // touch it again.
     if (IsInsideNursery(cell)) {
         listp = nullptr;
         return;
     }
 
     AllocKind kind = TenuredCell::fromPointer(cell)->getAllocKind();
-    MOZ_ASSERT(kind == FINALIZE_SHAPE ||
-               kind == FINALIZE_ACCESSOR_SHAPE ||
-               kind <= FINALIZE_OBJECT_LAST);
-    if (kind == FINALIZE_SHAPE || kind == FINALIZE_ACCESSOR_SHAPE) {
+    MOZ_ASSERT(kind == AllocKind::SHAPE ||
+               kind == AllocKind::ACCESSOR_SHAPE ||
+               kind <= AllocKind::OBJECT_LAST);
+    if (kind == AllocKind::SHAPE || kind == AllocKind::ACCESSOR_SHAPE) {
         // listp points to the parent field of the next shape.
         Shape *next = reinterpret_cast<Shape *>(uintptr_t(listp) -
                                                 offsetof(Shape, parent));
         listp = &gc::MaybeForwarded(next)->parent;
     } else {
         // listp points to the shape_ field of an object.
         JSObject *last = reinterpret_cast<JSObject *>(uintptr_t(listp) -
                                                       JSObject::offsetOfShape());
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -244,17 +244,17 @@ class JS_PUBLIC_API(AutoGCRooter)
      */
     ptrdiff_t tag_;
 
     enum {
         VALARRAY =     -2, /* js::AutoValueArray */
         PARSER =       -3, /* js::frontend::Parser */
         SHAPEVECTOR =  -4, /* js::AutoShapeVector */
         IDARRAY =      -6, /* js::AutoIdArray */
-        DESCVECTOR =   -7, /* js::AutoPropDescVector */
+        DESCVECTOR =   -7, /* js::AutoPropertyDescriptorVector */
         VALVECTOR =   -10, /* js::AutoValueVector */
         IDVECTOR =    -11, /* js::AutoIdVector */
         IDVALVECTOR = -12, /* js::AutoIdValueVector */
         OBJVECTOR =   -14, /* js::AutoObjectVector */
         STRINGVECTOR =-15, /* js::AutoStringVector */
         SCRIPTVECTOR =-16, /* js::AutoScriptVector */
         NAMEVECTOR =  -17, /* js::AutoNameVector */
         HASHABLEVALUE=-18, /* js::HashableValue */
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -112,17 +112,17 @@ js::SetPropertyIgnoringNamedGetter(JSCon
         ownDesc.clear();
         ownDesc.setAttributes(JSPROP_ENUMERATE);
     }
 
     // Step 5.
     if (ownDesc.isDataDescriptor()) {
         // Steps 5.a-b, adapted to our nonstandard implementation of ES6
         // [[Set]] return values.
-        if (!ownDesc.isWritable())
+        if (!ownDesc.writable())
             return result.fail(JSMSG_READ_ONLY);
 
         // Nonstandard SpiderMonkey special case: setter ops.
         SetterOp setter = ownDesc.setter();
         MOZ_ASSERT(setter != JS_StrictPropertyStub);
         if (setter && setter != JS_StrictPropertyStub)
             return CallSetter(cx, receiver, id, setter, ownDesc.attributes(), vp, result);
 
@@ -178,17 +178,17 @@ BaseProxyHandler::getOwnEnumerableProper
         id = props[j];
         if (JSID_IS_SYMBOL(id))
             continue;
 
         AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET);
         Rooted<PropertyDescriptor> desc(cx);
         if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
             return false;
-        if (desc.object() && desc.isEnumerable())
+        if (desc.object() && desc.enumerable())
             props[i++].set(id);
     }
 
     MOZ_ASSERT(i <= props.length());
     props.resize(i);
 
     return true;
 }
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -3,18 +3,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/. */
 
 #include "proxy/ScriptedDirectProxyHandler.h"
 
 #include "jsapi.h"
 
-#include "vm/PropDesc.h"
-
 #include "jsobjinlines.h"
 
 using namespace js;
 using mozilla::ArrayLength;
 
 static inline bool
 IsDataDescriptor(const PropertyDescriptor &desc)
 {
@@ -26,85 +24,85 @@ IsAccessorDescriptor(const PropertyDescr
 {
     return desc.obj && desc.attrs & (JSPROP_GETTER | JSPROP_SETTER);
 }
 
 // ES6 (5 April 2014) ValidateAndApplyPropertyDescriptor(O, P, Extensible, Desc, Current)
 // Since we are actually performing 9.1.6.2 IsCompatiblePropertyDescriptor(Extensible, Desc,
 // Current), some parameters are omitted.
 static bool
-ValidatePropertyDescriptor(JSContext *cx, bool extensible, Handle<PropDesc> desc,
+ValidatePropertyDescriptor(JSContext *cx, bool extensible, Handle<PropertyDescriptor> desc,
                            Handle<PropertyDescriptor> current, bool *bp)
 {
     // step 2
     if (!current.object()) {
         // Since |O| is always undefined, substeps c and d fall away.
         *bp = extensible;
         return true;
     }
 
     // step 3
-    if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGet() && !desc.hasSet() &&
+    if (!desc.hasValue() && !desc.hasWritable() &&
+        !desc.hasGetterObject() && !desc.hasSetterObject() &&
         !desc.hasEnumerable() && !desc.hasConfigurable())
     {
         *bp = true;
         return true;
     }
 
     // step 4
-    if ((!desc.hasWritable() || desc.writable() == !current.isReadonly()) &&
-        (!desc.hasGet() || desc.getter() == current.getter()) &&
-        (!desc.hasSet() || desc.setter() == current.setter()) &&
-        (!desc.hasEnumerable() || desc.enumerable() == current.isEnumerable()) &&
-        (!desc.hasConfigurable() || desc.configurable() == !current.isPermanent()))
+    if ((!desc.hasWritable() ||
+         (current.hasWritable() && desc.writable() == current.writable())) &&
+        (!desc.hasGetterObject() || desc.getter() == current.getter()) &&
+        (!desc.hasSetterObject() || desc.setter() == current.setter()) &&
+        (!desc.hasEnumerable() || desc.enumerable() == current.enumerable()) &&
+        (!desc.hasConfigurable() || desc.configurable() == current.configurable()))
     {
         if (!desc.hasValue()) {
             *bp = true;
             return true;
         }
         bool same = false;
         if (!SameValue(cx, desc.value(), current.value(), &same))
             return false;
         if (same) {
             *bp = true;
             return true;
         }
     }
 
     // step 5
-    if (current.isPermanent()) {
+    if (!current.configurable()) {
         if (desc.hasConfigurable() && desc.configurable()) {
             *bp = false;
             return true;
         }
 
-        if (desc.hasEnumerable() &&
-            desc.enumerable() != current.isEnumerable())
-        {
+        if (desc.hasEnumerable() && desc.enumerable() != current.enumerable()) {
             *bp = false;
             return true;
         }
     }
 
     // step 6
     if (desc.isGenericDescriptor()) {
         *bp = true;
         return true;
     }
 
     // step 7a
-    if (IsDataDescriptor(current) != desc.isDataDescriptor()) {
-        *bp = !current.isPermanent();
+    if (current.isDataDescriptor() != desc.isDataDescriptor()) {
+        *bp = current.configurable();
         return true;
     }
 
     // step 8
-    if (IsDataDescriptor(current)) {
+    if (current.isDataDescriptor()) {
         MOZ_ASSERT(desc.isDataDescriptor()); // by step 7a
-        if (current.isPermanent() && current.isReadonly()) {
+        if (!current.configurable() && !current.writable()) {
             if (desc.hasWritable() && desc.writable()) {
                 *bp = false;
                 return true;
             }
 
             if (desc.hasValue()) {
                 bool same;
                 if (!SameValue(cx, desc.value(), current.value(), &same))
@@ -116,35 +114,35 @@ ValidatePropertyDescriptor(JSContext *cx
             }
         }
 
         *bp = true;
         return true;
     }
 
     // step 9
-    MOZ_ASSERT(IsAccessorDescriptor(current)); // by step 8
+    MOZ_ASSERT(current.isAccessorDescriptor()); // by step 8
     MOZ_ASSERT(desc.isAccessorDescriptor()); // by step 7a
-    *bp = (!current.isPermanent() ||
-           ((!desc.hasSet() || desc.setter() == current.setter()) &&
-            (!desc.hasGet() || desc.getter() == current.getter())));
+    *bp = (current.configurable() ||
+           ((!desc.hasSetterObject() || desc.setter() == current.setter()) &&
+            (!desc.hasGetterObject() || desc.getter() == current.getter())));
     return true;
 }
 
 // Aux.6 IsSealed(O, P)
 static bool
 IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool *bp)
 {
     // step 1
     Rooted<PropertyDescriptor> desc(cx);
     if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
         return false;
 
     // steps 2-3
-    *bp = desc.object() && desc.isPermanent();
+    *bp = desc.object() && !desc.configurable();
     return true;
 }
 
 // Get the [[ProxyHandler]] of a scripted direct proxy.
 static JSObject *
 GetDirectProxyHandlerObject(JSObject *proxy)
 {
     MOZ_ASSERT(proxy->as<ProxyObject>().handler() == &ScriptedDirectProxyHandler::singleton);
@@ -482,17 +480,17 @@ ScriptedDirectProxyHandler::getOwnProper
     if (trapResult.isUndefined()) {
         // substep a
         if (!targetDesc.object()) {
             desc.object().set(nullptr);
             return true;
         }
 
         // substep b
-        if (targetDesc.isPermanent()) {
+        if (!targetDesc.configurable()) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
             return false;
         }
 
         // substep c-e
         bool extensibleTarget;
         if (!IsExtensible(cx, target, &extensibleTarget))
             return false;
@@ -507,22 +505,22 @@ ScriptedDirectProxyHandler::getOwnProper
     }
 
     // step 14-15
     bool extensibleTarget;
     if (!IsExtensible(cx, target, &extensibleTarget))
         return false;
 
     // step 16-17
-    Rooted<PropDesc> resultDesc(cx);
-    if (!resultDesc.initialize(cx, trapResult))
+    Rooted<PropertyDescriptor> resultDesc(cx);
+    if (!ToPropertyDescriptor(cx, trapResult, true, &resultDesc))
         return false;
 
     // step 18
-    resultDesc.complete();
+    CompletePropertyDescriptor(&resultDesc);
 
     // step 19
     bool valid;
     if (!ValidatePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc, &valid))
         return false;
 
     // step 20
     if (!valid) {
@@ -532,24 +530,25 @@ ScriptedDirectProxyHandler::getOwnProper
 
     // step 21
     if (!resultDesc.configurable()) {
         if (!targetDesc.object()) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC);
             return false;
         }
 
-        if (!targetDesc.isPermanent()) {
+        if (targetDesc.configurable()) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_C_AS_NC);
             return false;
         }
     }
 
     // step 22
-    resultDesc.populatePropertyDescriptor(proxy, desc);
+    desc.set(resultDesc);
+    desc.object().set(proxy);
     return true;
 }
 
 // ES6 draft rev 31 (15 Jan 2015) 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc)
 bool
 ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                            MutableHandle<PropertyDescriptor> desc,
                                            ObjectOpResult &result) const
@@ -570,17 +569,17 @@ ScriptedDirectProxyHandler::defineProper
         return false;
 
     // step 8
     if (trap.isUndefined())
         return DirectProxyHandler::defineProperty(cx, proxy, id, desc, result);
 
     // step 9
     RootedValue descObj(cx);
-    if (!NewPropertyDescriptorObject(cx, desc, &descObj))
+    if (!FromPropertyDescriptor(cx, desc, &descObj))
         return false;
 
     // steps 10-11
     RootedValue propKey(cx);
     if (!IdToStringOrSymbol(cx, id, &propKey))
         return false;
 
     Value argv[] = {
@@ -602,38 +601,34 @@ ScriptedDirectProxyHandler::defineProper
         return false;
 
     // step 15-16
     bool extensibleTarget;
     if (!IsExtensible(cx, target, &extensibleTarget))
         return false;
 
     // step 17-18
-    // FIXME bug 1133081: settingConfigFalse should be false if we have
-    // JSPROP_IGNORE_PERMANENT.
-    bool settingConfigFalse = desc.isPermanent();
+    bool settingConfigFalse = desc.hasConfigurable() && !desc.configurable();
     if (!targetDesc.object()) {
         // step 19.a
         if (!extensibleTarget) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
             return false;
         }
         // step 19.b
         if (settingConfigFalse) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC);
             return false;
         }
     } else {
         // step 20
         bool valid;
-        Rooted<PropDesc> pd(cx);
-        pd.initFromPropertyDescriptor(desc);
-        if (!ValidatePropertyDescriptor(cx, extensibleTarget, pd, targetDesc, &valid))
+        if (!ValidatePropertyDescriptor(cx, extensibleTarget, desc, targetDesc, &valid))
             return false;
-        if (!valid || (settingConfigFalse && !targetDesc.isPermanent())) {
+        if (!valid || (settingConfigFalse && targetDesc.configurable())) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID);
             return false;
         }
     }
 
     // step 21
     return result.succeed();
 }
@@ -728,17 +723,17 @@ ScriptedDirectProxyHandler::delete_(JSCo
         return result.fail(JSMSG_PROXY_DELETE_RETURNED_FALSE);
 
     // steps 12-13
     Rooted<PropertyDescriptor> desc(cx);
     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
         return false;
 
     // step 14-15
-    if (desc.object() && desc.isPermanent()) {
+    if (desc.object() && !desc.configurable()) {
         RootedValue v(cx, IdToValue(id));
         ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, js::NullPtr());
         return false;
     }
 
     // step 16
     return result.succeed();
 }
@@ -831,17 +826,17 @@ ScriptedDirectProxyHandler::has(JSContex
 
     // step 11
     if (!success) {
         Rooted<PropertyDescriptor> desc(cx);
         if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
             return false;
 
         if (desc.object()) {
-            if (desc.isPermanent()) {
+            if (!desc.configurable()) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
                 return false;
             }
 
             bool extensible;
             if (!IsExtensible(cx, target, &extensible))
                 return false;
             if (!extensible) {
@@ -897,27 +892,27 @@ ScriptedDirectProxyHandler::get(JSContex
 
     // step 10-11
     Rooted<PropertyDescriptor> desc(cx);
     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
         return false;
 
     // step 12
     if (desc.object()) {
-        if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) {
+        if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
             bool same;
             if (!SameValue(cx, trapResult, desc.value(), &same))
                 return false;
             if (!same) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_SAME_VALUE);
                 return false;
             }
         }
 
-        if (IsAccessorDescriptor(desc) && desc.isPermanent() && desc.getterObject() == nullptr) {
+        if (desc.isAccessorDescriptor() && !desc.configurable() && desc.getterObject() == nullptr) {
             if (!trapResult.isUndefined()) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED);
                 return false;
             }
         }
     }
 
     // step 13
@@ -967,27 +962,27 @@ ScriptedDirectProxyHandler::set(JSContex
 
     // step 12-13
     Rooted<PropertyDescriptor> desc(cx);
     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
         return false;
 
     // step 14
     if (desc.object()) {
-        if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) {
+        if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
             bool same;
             if (!SameValue(cx, vp, desc.value(), &same))
                 return false;
             if (!same) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_NW_NC);
                 return false;
             }
         }
 
-        if (IsAccessorDescriptor(desc) && desc.isPermanent() && desc.setterObject() == nullptr) {
+        if (desc.isAccessorDescriptor() && !desc.configurable() && desc.setterObject() == nullptr) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER);
             return false;
         }
     }
 
     // step 15
     return result.succeed();
 }
--- a/js/src/proxy/ScriptedIndirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.cpp
@@ -197,17 +197,17 @@ ScriptedIndirectProxyHandler::getOwnProp
 bool
 ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                              MutableHandle<PropertyDescriptor> desc,
                                              ObjectOpResult &result) const
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue fval(cx), value(cx);
     return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) &&
-           NewPropertyDescriptorObject(cx, desc, &value) &&
+           FromPropertyDescriptor(cx, desc, &value) &&
            Trap2(cx, handler, fval, id, value, &value) &&
            result.succeed();
 }
 
 bool
 ScriptedIndirectProxyHandler::ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                               AutoIdVector &props) const
 {
@@ -348,17 +348,17 @@ ScriptedIndirectProxyHandler::derivedSet
     }
 
     MOZ_ASSERT_IF(descIsOwn, desc.object());
     if (desc.object()) {
         MOZ_ASSERT(desc.getter() != JS_PropertyStub);
         MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub);
 
         // Check for read-only properties.
-        if (desc.isReadonly())
+        if (desc.isDataDescriptor() && !desc.writable())
             return result.fail(descIsOwn ? JSMSG_READ_ONLY : JSMSG_CANT_REDEFINE_PROP);
 
         if (desc.hasSetterObject() || desc.setter()) {
             if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), vp, result))
                 return false;
             if (!result)
                 return true;
             if (!proxy->is<ProxyObject>() ||
--- a/js/src/tests/js1_8_5/extensions/reflect-parse.js
+++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js
@@ -88,17 +88,17 @@ function funExpr(id, args, body, gen) Pa
                                                 params: args,
                                                 body: body,
                                                 generator: false })
 function genFunExpr(id, args, body) Pattern({ type: "FunctionExpression",
                                               id: id,
                                               params: args,
                                               body: body,
                                               generator: true })
-function arrowExpr(args, body) Pattern({ type: "ArrowExpression",
+function arrowExpr(args, body) Pattern({ type: "ArrowFunctionExpression",
                                          params: args,
                                          body: body })
 
 function unExpr(op, arg) Pattern({ type: "UnaryExpression", operator: op, argument: arg })
 function binExpr(op, left, right) Pattern({ type: "BinaryExpression", operator: op, left: left, right: right })
 function aExpr(op, left, right) Pattern({ type: "AssignmentExpression", operator: op, left: left, right: right })
 function updExpr(op, arg, prefix) Pattern({ type: "UpdateExpression", operator: op, argument: arg, prefix: prefix })
 function logExpr(op, left, right) Pattern({ type: "LogicalExpression", operator: op, left: left, right: right })
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -124,17 +124,17 @@ class ArgumentsObject : public NativeObj
                                    unsigned numActuals, CopyArgs &copy);
 
     ArgumentsData *data() const {
         return reinterpret_cast<ArgumentsData *>(getFixedSlot(DATA_SLOT).toPrivate());
     }
 
   public:
     static const uint32_t RESERVED_SLOTS = 3;
-    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND;
 
     /* Create an arguments object for a frame that is expecting them. */
     static ArgumentsObject *createExpected(JSContext *cx, AbstractFramePtr frame);
 
     /*
      * Purposefully disconnect the returned arguments object from the frame
      * by always creating a new copy that does not alias formal parameters.
      * This allows function-local analysis to determine that formals are
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -86,17 +86,17 @@ ArrayObject::createArray(ExclusiveContex
 /* static */ inline ArrayObject *
 ArrayObject::createArray(ExclusiveContext *cx, gc::InitialHeap heap,
                          HandleShape shape, HandleObjectGroup group,
                          HeapSlot *elements)
 {
     // Use the smallest allocation kind for the array, as it can't have any
     // fixed slots (see the assert in createArrayInternal) and will not be using
     // its fixed elements.
-    gc::AllocKind kind = gc::FINALIZE_OBJECT0_BACKGROUND;
+    gc::AllocKind kind = gc::AllocKind::OBJECT0_BACKGROUND;
 
     ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, group);
     if (!obj)
         return nullptr;
 
     obj->elements_ = elements;
 
     return finishCreateArray(obj, shape);
@@ -108,17 +108,17 @@ ArrayObject::createCopyOnWriteArray(Excl
                                     HandleArrayObject sharedElementsOwner)
 {
     MOZ_ASSERT(sharedElementsOwner->getElementsHeader()->isCopyOnWrite());
     MOZ_ASSERT(sharedElementsOwner->getElementsHeader()->ownerObject() == sharedElementsOwner);
 
     // Use the smallest allocation kind for the array, as it can't have any
     // fixed slots (see the assert in createArrayInternal) and will not be using
     // its fixed elements.
-    gc::AllocKind kind = gc::FINALIZE_OBJECT0_BACKGROUND;
+    gc::AllocKind kind = gc::AllocKind::OBJECT0_BACKGROUND;
 
     RootedObjectGroup group(cx, sharedElementsOwner->group());
     ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, group);
     if (!obj)
         return nullptr;
 
     obj->elements_ = sharedElementsOwner->getDenseElementsAllowCopyOnWrite();
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -864,94 +864,104 @@ Debugger::wrapDebuggeeValue(JSContext *c
         vp.setUndefined();
         return false;
     }
 
     return true;
 }
 
 bool
+Debugger::unwrapDebuggeeObject(JSContext *cx, MutableHandleObject obj)
+{
+    if (obj->getClass() != &DebuggerObject_class) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
+                             "Debugger", "Debugger.Object", obj->getClass()->name);
+        return false;
+    }
+    NativeObject *ndobj = &obj->as<NativeObject>();
+
+    Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
+    if (owner.isUndefined() || &owner.toObject() != object) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             owner.isUndefined()
+                             ? JSMSG_DEBUG_OBJECT_PROTO
+                             : JSMSG_DEBUG_OBJECT_WRONG_OWNER);
+        return false;
+    }
+
+    obj.set(static_cast<JSObject*>(ndobj->getPrivate()));
+    return true;
+}
+
+bool
 Debugger::unwrapDebuggeeValue(JSContext *cx, MutableHandleValue vp)
 {
     assertSameCompartment(cx, object.get(), vp);
     if (vp.isObject()) {
-        JSObject *dobj = &vp.toObject();
-        if (dobj->getClass() != &DebuggerObject_class) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
-                                 "Debugger", "Debugger.Object", dobj->getClass()->name);
+        RootedObject dobj(cx, &vp.toObject());
+        if (!unwrapDebuggeeObject(cx, &dobj))
             return false;
-        }
-        NativeObject *ndobj = &dobj->as<NativeObject>();
-
-        Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
-        if (owner.isUndefined() || &owner.toObject() != object) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
-                                 owner.isUndefined()
-                                 ? JSMSG_DEBUG_OBJECT_PROTO
-                                 : JSMSG_DEBUG_OBJECT_WRONG_OWNER);
-            return false;
-        }
-
-        vp.setObject(*static_cast<JSObject*>(ndobj->getPrivate()));
+        vp.setObject(*dobj);
     }
     return true;
 }
 
-/*
- * Convert Debugger.Objects in desc to debuggee values.
- * Reject non-callable getters and setters.
- */
 static bool
-CheckArgCompartment(JSContext *cx, JSObject *obj, HandleValue v,
+CheckArgCompartment(JSContext *cx, JSObject *obj, JSObject *arg,
                     const char *methodname, const char *propname)
 {
-    if (v.isObject() && v.toObject().compartment() != obj->compartment()) {
+    if (arg->compartment() != obj->compartment()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_COMPARTMENT_MISMATCH,
                              methodname, propname);
         return false;
     }
     return true;
 }
 
+static bool
+CheckArgCompartment(JSContext *cx, JSObject *obj, HandleValue v,
+                    const char *methodname, const char *propname)
+{
+    if (v.isObject())
+        return CheckArgCompartment(cx, obj, &v.toObject(), methodname, propname);
+    return true;
+}
+
 bool
-Debugger::unwrapPropDescInto(JSContext *cx, HandleObject obj, Handle<PropDesc> wrapped,
-                             MutableHandle<PropDesc> unwrapped)
-{
-    MOZ_ASSERT(!wrapped.isUndefined());
-
-    unwrapped.set(wrapped);
-
-    if (unwrapped.hasValue()) {
-        RootedValue value(cx, unwrapped.value());
+Debugger::unwrapPropertyDescriptor(JSContext *cx, HandleObject obj,
+                                   MutableHandle<PropertyDescriptor> desc)
+{
+    if (desc.hasValue()) {
+        RootedValue value(cx, desc.value());
         if (!unwrapDebuggeeValue(cx, &value) ||
             !CheckArgCompartment(cx, obj, value, "defineProperty", "value"))
         {
             return false;
         }
-        unwrapped.setValue(value);
-    }
-
-    if (unwrapped.hasGet()) {
-        RootedValue get(cx, unwrapped.getterValue());
-        if (!unwrapDebuggeeValue(cx, &get) ||
+        desc.setValue(value);
+    }
+
+    if (desc.hasGetterObject()) {
+        RootedObject get(cx, desc.getterObject());
+        if (!unwrapDebuggeeObject(cx, &get) ||
             !CheckArgCompartment(cx, obj, get, "defineProperty", "get"))
         {
             return false;
         }
-        unwrapped.setGetter(get);
-    }
-
-    if (unwrapped.hasSet()) {
-        RootedValue set(cx, unwrapped.setterValue());
-        if (!unwrapDebuggeeValue(cx, &set) ||
+        desc.setGetterObject(get);
+    }
+
+    if (desc.hasSetterObject()) {
+        RootedObject set(cx, desc.setterObject());
+        if (!unwrapDebuggeeObject(cx, &set) ||
             !CheckArgCompartment(cx, obj, set, "defineProperty", "set"))
         {
             return false;
         }
-        unwrapped.setSetter(set);
+        desc.setSetterObject(set);
     }
 
     return true;
 }
 
 JSTrapStatus
 Debugger::handleUncaughtExceptionHelper(Maybe<AutoCompartment> &ac,
                                         MutableHandleValue *vp, bool callHook)
@@ -1915,17 +1925,17 @@ UpdateExecutionObservabilityOfScriptsInZ
     {
         AutoEnterAnalysis enter(fop, zone);
         if (JSScript *script = obs.singleScriptForZoneInvalidation()) {
             if (obs.shouldRecompileOrInvalidate(script)) {
                 if (!AppendAndInvalidateScript(cx, zone, script, scripts))
                     return false;
             }
         } else {
-            for (gc::ZoneCellIter iter(zone, gc::FINALIZE_SCRIPT); !iter.done(); iter.next()) {
+            for (gc::ZoneCellIter iter(zone, gc::AllocKind::SCRIPT); !iter.done(); iter.next()) {
                 JSScript *script = iter.get<JSScript>();
                 if (obs.shouldRecompileOrInvalidate(script) &&
                     !gc::IsScriptAboutToBeFinalized(&script))
                 {
                     if (!AppendAndInvalidateScript(cx, zone, script, scripts))
                         return false;
                 }
             }
@@ -6673,17 +6683,17 @@ DebuggerObject_getOwnPropertyDescriptor(
         if (desc.hasSetterObject()) {
             RootedValue set(cx, ObjectOrNullValue(desc.setterObject()));
             if (!dbg->wrapDebuggeeValue(cx, &set))
                 return false;
             desc.setSetterObject(set.toObjectOrNull());
         }
     }
 
-    return NewPropertyDescriptorObject(cx, desc, args.rval());
+    return FromPropertyDescriptor(cx, desc, args.rval());
 }
 
 static bool
 DebuggerObject_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "getOwnPropertyNames", args, obj);
 
     AutoIdVector keys(cx);
@@ -6726,23 +6736,23 @@ DebuggerObject_defineProperty(JSContext 
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperty", args, dbg, obj);
     if (!args.requireAtLeast(cx, "Debugger.Object.defineProperty", 2))
         return false;
 
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[0], &id))
         return false;
 
-    Rooted<PropDesc> desc(cx);
-    if (!desc.initialize(cx, args[1], false))
-        return false;
-
-    if (!dbg->unwrapPropDescInto(cx, obj, desc, &desc))
-        return false;
-    if (!desc.checkGetter(cx) || !desc.checkSetter(cx))
+    Rooted<PropertyDescriptor> desc(cx);
+    if (!ToPropertyDescriptor(cx, args[1], false, &desc))
+        return false;
+
+    if (!dbg->unwrapPropertyDescriptor(cx, obj, &desc))
+        return false;
+    if (!CheckPropertyDescriptorAccessors(cx, desc))
         return false;
 
     {
         Maybe<AutoCompartment> ac;
         ac.emplace(cx, obj);
         if (!cx->compartment()->wrap(cx, &desc))
             return false;
 
@@ -6763,25 +6773,25 @@ DebuggerObject_defineProperties(JSContex
         return false;
 
     RootedValue arg(cx, args[0]);
     RootedObject props(cx, ToObject(cx, arg));
     if (!props)
         return false;
 
     AutoIdVector ids(cx);
-    AutoPropDescVector descs(cx);
+    AutoPropertyDescriptorVector descs(cx);
     if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs))
         return false;
     size_t n = ids.length();
 
     for (size_t i = 0; i < n; i++) {
-        if (!dbg->unwrapPropDescInto(cx, obj, descs[i], descs[i]))
+        if (!dbg->unwrapPropertyDescriptor(cx, obj, descs[i]))
             return false;
-        if (!descs[i].checkGetter(cx) || !descs[i].checkSetter(cx))
+        if (!CheckPropertyDescriptorAccessors(cx, descs[i]))
             return false;
     }
 
     {
         Maybe<AutoCompartment> ac;
         ac.emplace(cx, obj);
         for (size_t i = 0; i < n; i++) {
             if (!cx->compartment()->wrap(cx, descs[i]))
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -694,18 +694,19 @@ class Debugger : private mozilla::Linked
      *
      * (Extreme nerd sidebar: Unwrapping happens in two steps because there are
      * two different kinds of symmetry at work: regardless of which direction
      * we're going, we want any exceptions to be created and thrown in the
      * debugger compartment--mirror symmetry. But compartment wrapping always
      * happens in the target compartment--rotational symmetry.)
      */
     bool unwrapDebuggeeValue(JSContext *cx, MutableHandleValue vp);
-    bool unwrapPropDescInto(JSContext *cx, HandleObject obj, Handle<PropDesc> wrapped,
-                            MutableHandle<PropDesc> unwrapped);
+    bool unwrapDebuggeeObject(JSContext *cx, MutableHandleObject obj);
+    bool unwrapPropertyDescriptor(JSContext *cx, HandleObject obj,
+                                  MutableHandle<PropertyDescriptor> desc);
 
     /*
      * Store the Debugger.Frame object for frame in *vp.
      *
      * Use this if you have already access to a frame pointer without having
      * to incur the cost of walking the stack.
      */
     bool getScriptFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp) {
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -905,17 +905,17 @@ GlobalHelperThreadState::finishParseTask
 
     // Point the prototypes of any objects in the script's compartment to refer
     // to the corresponding prototype in the new compartment. This will briefly
     // create cross compartment pointers, which will be fixed by the
     // MergeCompartments call below.  It's not safe for a GC to observe this
     // state, so finish any ongoing GC first and assert that we can't trigger
     // another one.
     gc::AutoFinishGC finishGC(rt);
-    for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::FINALIZE_OBJECT_GROUP);
+    for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP);
          !iter.done();
          iter.next())
     {
         JS::AutoAssertNoAlloc noAlloc(rt);
         ObjectGroup *group = iter.get<ObjectGroup>();
         TaggedProto proto(group->proto());
         if (!proto.isObject())
             continue;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -361,20 +361,21 @@ DefVarOrConstOperation(JSContext *cx, Ha
          */
         RootedId id(cx, NameToId(dn));
         Rooted<PropertyDescriptor> desc(cx);
         if (!GetOwnPropertyDescriptor(cx, obj2, id, &desc))
             return false;
 
         JSAutoByteString bytes;
         if (AtomToPrintableString(cx, dn, &bytes)) {
+            bool isConst = desc.hasWritable() && !desc.writable();
             JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
                                                          GetErrorMessage,
                                                          nullptr, JSMSG_REDECLARED_VAR,
-                                                         desc.isReadonly() ? "const" : "var",
+                                                         isConst ? "const" : "var",
                                                          bytes.ptr()));
         }
         return false;
     }
 
     return true;
 }
 
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -21,64 +21,16 @@
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::GenericNaN;
 using mozilla::DebugOnly;
 using mozilla::RoundUpPow2;
 
-PropDesc::PropDesc()
-{
-    setUndefined();
-}
-
-void
-PropDesc::setUndefined()
-{
-    value_ = UndefinedValue();
-    get_ = UndefinedValue();
-    set_ = UndefinedValue();
-    attrs = 0;
-    hasGet_ = false;
-    hasSet_ = false;
-    hasValue_ = false;
-    hasWritable_ = false;
-    hasEnumerable_ = false;
-    hasConfigurable_ = false;
-
-    isUndefined_ = true;
-}
-
-bool
-PropDesc::checkGetter(JSContext *cx)
-{
-    if (hasGet_) {
-        if (!IsCallable(get_) && !get_.isUndefined()) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
-                                 js_getter_str);
-            return false;
-        }
-    }
-    return true;
-}
-
-bool
-PropDesc::checkSetter(JSContext *cx)
-{
-    if (hasSet_) {
-        if (!IsCallable(set_) && !set_.isUndefined()) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
-                                 js_setter_str);
-            return false;
-        }
-    }
-    return true;
-}
-
 static const ObjectElements emptyElementsHeader(0, 0);
 
 /* Objects with no elements share one empty set of elements. */
 HeapSlot *const js::emptyObjectElements =
     reinterpret_cast<HeapSlot *>(uintptr_t(&emptyElementsHeader) + sizeof(ObjectElements));
 
 #ifdef DEBUG
 
@@ -283,24 +235,16 @@ js::NativeObject::dynamicSlotsCount(uint
     if (clasp != &ArrayObject::class_ && span <= SLOT_CAPACITY_MIN)
         return SLOT_CAPACITY_MIN;
 
     uint32_t slots = mozilla::RoundUpPow2(span);
     MOZ_ASSERT(slots >= span);
     return slots;
 }
 
-void
-PropDesc::trace(JSTracer *trc)
-{
-    gc::MarkValueRoot(trc, &value_, "PropDesc value");
-    gc::MarkValueRoot(trc, &get_, "PropDesc get");
-    gc::MarkValueRoot(trc, &set_, "PropDesc set");
-}
-
 inline bool
 NativeObject::updateSlotsForSpan(ExclusiveContext *cx, size_t oldSpan, size_t newSpan)
 {
     MOZ_ASSERT(oldSpan != newSpan);
 
     size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan, getClass());
     size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan, getClass());
 
--- a/js/src/vm/PropDesc.h
+++ b/js/src/vm/PropDesc.h
@@ -1,366 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 vm_PropDesc_h
-#define vm_PropDesc_h
-
-#include "jsapi.h"
-#include "NamespaceImports.h"
-
-namespace js {
-
-static inline JSGetterOp
-CastAsGetterOp(JSObject *object)
-{
-    return JS_DATA_TO_FUNC_PTR(JSGetterOp, object);
-}
-
-static inline JSSetterOp
-CastAsSetterOp(JSObject *object)
-{
-    return JS_DATA_TO_FUNC_PTR(JSSetterOp, object);
-}
-
-/*
- * A representation of ECMA-262 ed. 5's internal Property Descriptor data
- * structure.
- */
-struct PropDesc {
-  private:
-    Value value_, get_, set_;
-
-    /* Property descriptor boolean fields. */
-    uint8_t attrs;
-
-    /* Bits indicating which values are set. */
-    bool hasGet_ : 1;
-    bool hasSet_ : 1;
-    bool hasValue_ : 1;
-    bool hasWritable_ : 1;
-    bool hasEnumerable_ : 1;
-    bool hasConfigurable_ : 1;
-
-    /* Or maybe this represents a property's absence, and it's undefined. */
-    bool isUndefined_ : 1;
-
-    explicit PropDesc(const Value &v)
-      : value_(v),
-        get_(UndefinedValue()), set_(UndefinedValue()),
-        attrs(0),
-        hasGet_(false), hasSet_(false),
-        hasValue_(true), hasWritable_(false), hasEnumerable_(false), hasConfigurable_(false),
-        isUndefined_(false)
-    {
-    }
-
-  public:
-    friend struct GCMethods<PropDesc>;
-
-    void trace(JSTracer *trc);
-    static ThingRootKind rootKind() { return THING_ROOT_PROP_DESC; }
-
-    enum Enumerability { Enumerable = true, NonEnumerable = false };
-    enum Configurability { Configurable = true, NonConfigurable = false };
-    enum Writability { Writable = true, NonWritable = false };
-
-    PropDesc();
-
-    static PropDesc undefined() { return PropDesc(); }
-    static PropDesc valueOnly(const Value &v) { return PropDesc(v); }
-
-    PropDesc(const Value &v, Writability writable,
-             Enumerability enumerable, Configurability configurable)
-      : value_(v),
-        get_(UndefinedValue()), set_(UndefinedValue()),
-        attrs((writable ? 0 : JSPROP_READONLY) |
-              (enumerable ? JSPROP_ENUMERATE : 0) |
-              (configurable ? 0 : JSPROP_PERMANENT)),
-        hasGet_(false), hasSet_(false),
-        hasValue_(true), hasWritable_(true), hasEnumerable_(true), hasConfigurable_(true),
-        isUndefined_(false)
-    {}
-
-    inline PropDesc(const Value &getter, const Value &setter,
-                    Enumerability enumerable, Configurability configurable);
-
-    /*
-     * 8.10.5 ToPropertyDescriptor(Obj)
-     *
-     * If checkAccessors is false, skip steps 7.b and 8.b, which throw a
-     * TypeError if .get or .set is neither a callable object nor undefined.
-     *
-     * (DebuggerObject_defineProperty uses this: the .get and .set properties
-     * are expected to be Debugger.Object wrappers of functions, which are not
-     * themselves callable.)
-     */
-    bool initialize(JSContext *cx, const Value &v, bool checkAccessors = true);
-
-    /*
-     * If IsGenericDescriptor(desc) or IsDataDescriptor(desc) is true, then if
-     * the value of an attribute field of desc, considered as a data
-     * descriptor, is absent, set it to its default value. Else if the value of
-     * an attribute field of desc, considered as an attribute descriptor, is
-     * absent, set it to its default value.
-     */
-    void complete();
-
-    /*
-     * 8.10.4 FromPropertyDescriptor(Desc)
-     *
-     * initFromPropertyDescriptor sets pd to undefined and populates all the
-     * other fields of this PropDesc from desc.
-     *
-     * makeObject populates pd based on the other fields of *this, creating a
-     * new property descriptor JSObject and defining properties on it.
-     */
-    void initFromPropertyDescriptor(Handle<JSPropertyDescriptor> desc);
-    void populatePropertyDescriptor(HandleObject obj, MutableHandle<JSPropertyDescriptor> desc) const;
-    bool makeObject(JSContext *cx, MutableHandleObject objp);
-
-    /* Reset the descriptor entirely. */
-    void setUndefined();
-    bool isUndefined() const { return isUndefined_; }
-
-    bool hasGet() const { MOZ_ASSERT(!isUndefined()); return hasGet_; }
-    bool hasSet() const { MOZ_ASSERT(!isUndefined()); return hasSet_; }
-    bool hasValue() const { MOZ_ASSERT(!isUndefined()); return hasValue_; }
-    bool hasWritable() const { MOZ_ASSERT(!isUndefined()); return hasWritable_; }
-    bool hasEnumerable() const { MOZ_ASSERT(!isUndefined()); return hasEnumerable_; }
-    bool hasConfigurable() const { MOZ_ASSERT(!isUndefined()); return hasConfigurable_; }
-
-    uint8_t attributes() const { MOZ_ASSERT(!isUndefined()); return attrs; }
-
-    /* 8.10.1 IsAccessorDescriptor(desc) */
-    bool isAccessorDescriptor() const {
-        return !isUndefined() && (hasGet() || hasSet());
-    }
-
-    /* 8.10.2 IsDataDescriptor(desc) */
-    bool isDataDescriptor() const {
-        return !isUndefined() && (hasValue() || hasWritable());
-    }
-
-    /* 8.10.3 IsGenericDescriptor(desc) */
-    bool isGenericDescriptor() const {
-        return !isUndefined() && !isAccessorDescriptor() && !isDataDescriptor();
-    }
-
-    bool configurable() const {
-        MOZ_ASSERT(!isUndefined());
-        MOZ_ASSERT(hasConfigurable());
-        return (attrs & JSPROP_PERMANENT) == 0;
-    }
-
-    bool enumerable() const {
-        MOZ_ASSERT(!isUndefined());
-        MOZ_ASSERT(hasEnumerable());
-        return (attrs & JSPROP_ENUMERATE) != 0;
-    }
-
-    bool writable() const {
-        MOZ_ASSERT(!isUndefined());
-        MOZ_ASSERT(hasWritable());
-        return (attrs & JSPROP_READONLY) == 0;
-    }
-
-    HandleValue value() const {
-        MOZ_ASSERT(hasValue());
-        return HandleValue::fromMarkedLocation(&value_);
-    }
-    void setValue(const Value &value) {
-        MOZ_ASSERT(!isUndefined());
-        value_ = value;
-        hasValue_ = true;
-    }
-
-    JSObject * getterObject() const {
-        MOZ_ASSERT(!isUndefined());
-        MOZ_ASSERT(hasGet());
-        return get_.isUndefined() ? nullptr : &get_.toObject();
-    }
-    JSObject * setterObject() const {
-        MOZ_ASSERT(!isUndefined());
-        MOZ_ASSERT(hasSet());
-        return set_.isUndefined() ? nullptr : &set_.toObject();
-    }
-
-    HandleValue getterValue() const {
-        MOZ_ASSERT(!isUndefined());
-        MOZ_ASSERT(hasGet());
-        return HandleValue::fromMarkedLocation(&get_);
-    }
-    HandleValue setterValue() const {
-        MOZ_ASSERT(!isUndefined());
-        MOZ_ASSERT(hasSet());
-        return HandleValue::fromMarkedLocation(&set_);
-    }
-
-    void setGetter(const Value &getter) {
-        MOZ_ASSERT(!isUndefined());
-        get_ = getter;
-        hasGet_ = true;
-    }
-    void setSetter(const Value &setter) {
-        MOZ_ASSERT(!isUndefined());
-        set_ = setter;
-        hasSet_ = true;
-    }
-
-    /*
-     * Unfortunately the values produced by these methods are used such that
-     * we can't assert anything here.  :-(
-     */
-    JSGetterOp getter() const {
-        return CastAsGetterOp(get_.isUndefined() ? nullptr : &get_.toObject());
-    }
-    JSSetterOp setter() const {
-        return CastAsSetterOp(set_.isUndefined() ? nullptr : &set_.toObject());
-    }
-
-    /*
-     * Throw a TypeError if a getter/setter is present and is neither callable
-     * nor undefined. These methods do exactly the type checks that are skipped
-     * by passing false as the checkAccessors parameter of initialize.
-     */
-    bool checkGetter(JSContext *cx);
-    bool checkSetter(JSContext *cx);
-};
-
-} /* namespace js */
-
-namespace JS {
-
-template <typename Outer>
-class PropDescOperations
-{
-    const js::PropDesc * desc() const { return static_cast<const Outer*>(this)->extract(); }
-
-  public:
-    bool isUndefined() const { return desc()->isUndefined(); }
-
-    bool hasGet() const { return desc()->hasGet(); }
-    bool hasSet() const { return desc()->hasSet(); }
-    bool hasValue() const { return desc()->hasValue(); }
-    bool hasWritable() const { return desc()->hasWritable(); }
-    bool hasEnumerable() const { return desc()->hasEnumerable(); }
-    bool hasConfigurable() const { return desc()->hasConfigurable(); }
-
-    uint8_t attributes() const { return desc()->attributes(); }
-
-    bool isAccessorDescriptor() const { return desc()->isAccessorDescriptor(); }
-    bool isDataDescriptor() const { return desc()->isDataDescriptor(); }
-    bool isGenericDescriptor() const { return desc()->isGenericDescriptor(); }
-    bool configurable() const { return desc()->configurable(); }
-    bool enumerable() const { return desc()->enumerable(); }
-    bool writable() const { return desc()->writable(); }
-
-    HandleValue value() const { return desc()->value(); }
-    JSObject *getterObject() const { return desc()->getterObject(); }
-    JSObject *setterObject() const { return desc()->setterObject(); }
-    HandleValue getterValue() const { return desc()->getterValue(); }
-    HandleValue setterValue() const { return desc()->setterValue(); }
-
-    JSGetterOp getter() const { return desc()->getter(); }
-    JSSetterOp setter() const { return desc()->setter(); }
-
-    void populatePropertyDescriptor(HandleObject obj,
-                                    MutableHandle<JSPropertyDescriptor> descriptor) const {
-        desc()->populatePropertyDescriptor(obj, descriptor);
-    }
-};
-
-template <typename Outer>
-class MutablePropDescOperations : public PropDescOperations<Outer>
-{
-    js::PropDesc * desc() { return static_cast<Outer*>(this)->extractMutable(); }
-
-  public:
-
-    bool initialize(JSContext *cx, const Value &v, bool checkAccessors = true) {
-        return desc()->initialize(cx, v, checkAccessors);
-    }
-    void complete() {
-        desc()->complete();
-    }
-
-    bool checkGetter(JSContext *cx) { return desc()->checkGetter(cx); }
-    bool checkSetter(JSContext *cx) { return desc()->checkSetter(cx); }
-
-    void initFromPropertyDescriptor(Handle<JSPropertyDescriptor> descriptor) {
-        desc()->initFromPropertyDescriptor(descriptor);
-    }
-    bool makeObject(JSContext *cx, MutableHandleObject objp) {
-        return desc()->makeObject(cx, objp);
-    }
-
-    void setValue(const Value &value) {
-        desc()->setValue(value);
-    }
-    void setGetter(const Value &getter) {
-        desc()->setGetter(getter);
-    }
-    void setSetter(const Value &setter) {
-        desc()->setSetter(setter);
-    }
-
-    void setUndefined() { desc()->setUndefined(); }
-};
-
-} /* namespace JS */
-
-namespace js {
-
-template <>
-struct GCMethods<PropDesc> {
-    static PropDesc initial() { return PropDesc(); }
-    static bool poisoned(const PropDesc &desc) {
-        return JS::IsPoisonedValue(desc.value_) ||
-               JS::IsPoisonedValue(desc.get_) ||
-               JS::IsPoisonedValue(desc.set_);
-    }
-};
-
-template <>
-class RootedBase<PropDesc>
-  : public JS::MutablePropDescOperations<JS::Rooted<PropDesc> >
-{
-    friend class JS::PropDescOperations<JS::Rooted<PropDesc> >;
-    friend class JS::MutablePropDescOperations<JS::Rooted<PropDesc> >;
-    const PropDesc *extract() const {
-        return static_cast<const JS::Rooted<PropDesc>*>(this)->address();
-    }
-    PropDesc *extractMutable() {
-        return static_cast<JS::Rooted<PropDesc>*>(this)->address();
-    }
-};
-
-template <>
-class HandleBase<PropDesc>
-  : public JS::PropDescOperations<JS::Handle<PropDesc> >
-{
-    friend class JS::PropDescOperations<JS::Handle<PropDesc> >;
-    const PropDesc *extract() const {
-        return static_cast<const JS::Handle<PropDesc>*>(this)->address();
-    }
-};
-
-template <>
-class MutableHandleBase<PropDesc>
-  : public JS::MutablePropDescOperations<JS::MutableHandle<PropDesc> >
-{
-    friend class JS::PropDescOperations<JS::MutableHandle<PropDesc> >;
-    friend class JS::MutablePropDescOperations<JS::MutableHandle<PropDesc> >;
-    const PropDesc *extract() const {
-        return static_cast<const JS::MutableHandle<PropDesc>*>(this)->address();
-    }
-    PropDesc *extractMutable() {
-        return static_cast<JS::MutableHandle<PropDesc>*>(this)->address();
-    }
-};
-
-} /* namespace js */
-
-#endif /* vm_PropDesc_h */
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -227,17 +227,17 @@ class NativeIterCache
  */
 class NewObjectCache
 {
     /* Statically asserted to be equal to sizeof(JSObject_Slots16) */
     static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value);
 
     static void staticAsserts() {
         JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16));
-        JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND);
+        JS_STATIC_ASSERT(gc::AllocKind::OBJECT_LAST == gc::AllocKind::OBJECT16_BACKGROUND);
     }
 
     struct Entry
     {
         /* Class of the constructed object. */
         const Class *clasp;
 
         /*
@@ -313,17 +313,17 @@ class NewObjectCache
         return fill(entry, group->clasp(), group, kind, obj);
     }
 
     /* Invalidate any entries which might produce an object with shape/proto. */
     void invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto);
 
   private:
     EntryIndex makeIndex(const Class *clasp, gc::Cell *key, gc::AllocKind kind) {
-        uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind;
+        uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + size_t(kind);
         return hash % mozilla::ArrayLength(entries);
     }
 
     bool lookup(const Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry) {
         *pentry = makeIndex(clasp, key, kind);
         Entry *entry = &entries[*pentry];
 
         /* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -334,17 +334,17 @@ class CallObject : public ScopeObject
 
 class DeclEnvObject : public ScopeObject
 {
     // Pre-allocated slot for the named lambda.
     static const uint32_t LAMBDA_SLOT = 1;
 
   public:
     static const uint32_t RESERVED_SLOTS = 2;
-    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
 
     static const Class class_;
 
     static DeclEnvObject *
     createTemplateObject(JSContext *cx, HandleFunction fun, gc::InitialHeap heap);
 
     static DeclEnvObject *create(JSContext *cx, HandleObject enclosing, HandleFunction callee);
 
@@ -356,17 +356,17 @@ class DeclEnvObject : public ScopeObject
 // Static eval scope template objects on the static scope. Created at the
 // time of compiling the eval script, and set as its static enclosing scope.
 class StaticEvalObject : public ScopeObject
 {
     static const uint32_t STRICT_SLOT = 1;
 
   public:
     static const unsigned RESERVED_SLOTS = 2;
-    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
 
     static const Class class_;
 
     static StaticEvalObject *create(JSContext *cx, HandleObject enclosing);
 
     JSObject *enclosingScopeForStaticScopeIter() {
         return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
     }
@@ -430,33 +430,33 @@ class NestedScopeObject : public ScopeOb
     }
 };
 
 // With scope template objects on the static scope chain.
 class StaticWithObject : public NestedScopeObject
 {
   public:
     static const unsigned RESERVED_SLOTS = 1;
-    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
 
     static const Class class_;
 
     static StaticWithObject *create(ExclusiveContext *cx);
 };
 
 // With scope objects on the run-time scope chain.
 class DynamicWithObject : public NestedScopeObject
 {
     static const unsigned OBJECT_SLOT = 1;
     static const unsigned THIS_SLOT = 2;
     static const unsigned KIND_SLOT = 3;
 
   public:
     static const unsigned RESERVED_SLOTS = 4;
-    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND;
 
     static const Class class_;
 
     enum WithKind {
         SyntacticWith,
         NonSyntacticWith
     };
 
@@ -499,17 +499,17 @@ class DynamicWithObject : public NestedS
 
 class BlockObject : public NestedScopeObject
 {
   protected:
     static const unsigned DEPTH_SLOT = 1;
 
   public:
     static const unsigned RESERVED_SLOTS = 2;
-    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND;
 
     static const Class class_;
 
     /* Return the abstract stack depth right before entering this nested scope. */
     uint32_t stackDepth() const {
         return getReservedSlot(DEPTH_SLOT).toPrivateUint32();
     }
 
@@ -685,17 +685,17 @@ class ClonedBlockObject : public BlockOb
 // demands that the error be thrown after evaluating the RHS of
 // assignments. Instead, this sentinel scope object is pushed on the stack.
 // Attempting to access anything on this scope throws the appropriate
 // ReferenceError.
 class UninitializedLexicalObject : public ScopeObject
 {
   public:
     static const unsigned RESERVED_SLOTS = 1;
-    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
 
     static const Class class_;
 
     static UninitializedLexicalObject *create(JSContext *cx, HandleObject enclosing);
 };
 
 template<XDRMode mode>
 bool
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -362,36 +362,36 @@ js::intrinsic_DefineDataProperty(JSConte
 
     RootedObject obj(cx, &args[0].toObject());
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[1], &id))
         return false;
     RootedValue value(cx, args[2]);
     unsigned attributes = args[3].toInt32();
 
-    Rooted<PropDesc> desc(cx);
+    Rooted<PropertyDescriptor> desc(cx);
+    unsigned attrs = 0;
 
     MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
                "_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
-    PropDesc::Enumerability enumerable =
-        PropDesc::Enumerability(bool(attributes & ATTR_ENUMERABLE));
+    if (attributes & ATTR_ENUMERABLE)
+        attrs |= JSPROP_ENUMERATE;
 
     MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
                "_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
                "ATTR_NONCONFIGURABLE");
-    PropDesc::Configurability configurable =
-        PropDesc::Configurability(bool(attributes & ATTR_CONFIGURABLE));
+    if (attributes & ATTR_NONCONFIGURABLE)
+        attrs |= JSPROP_PERMANENT;
 
     MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
                "_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
-    PropDesc::Writability writable =
-        PropDesc::Writability(bool(attributes & ATTR_WRITABLE));
+    if (attributes & ATTR_NONWRITABLE)
+        attrs |= JSPROP_READONLY;
 
-    desc = PropDesc(value, writable, enumerable, configurable);
-
+    desc.setDataDescriptor(value, attrs);
     return StandardDefineProperty(cx, obj, id, desc);
 }
 
 bool
 js::intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -24,17 +24,16 @@
 #include "gc/Heap.h"
 #include "gc/Marking.h"
 #include "gc/Rooting.h"
 #include "js/HashTable.h"
 #include "js/MemoryMetrics.h"
 #include "js/RootingAPI.h"
 #include "js/UbiNode.h"
 #include "vm/ObjectGroup.h"
-#include "vm/PropDesc.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4800)
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
@@ -773,17 +772,17 @@ class Shape : public gc::TenuredCell
     }
 
     bool isNative() const {
         MOZ_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative());
         return !(flags & NON_NATIVE);
     }
 
     bool isAccessorShape() const {
-        MOZ_ASSERT_IF(flags & ACCESSOR_SHAPE, getAllocKind() == gc::FINALIZE_ACCESSOR_SHAPE);
+        MOZ_ASSERT_IF(flags & ACCESSOR_SHAPE, getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
         return flags & ACCESSOR_SHAPE;
     }
     AccessorShape &asAccessorShape() const {
         MOZ_ASSERT(isAccessorShape());
         return *(AccessorShape *)this;
     }
 
     const HeapPtrShape &previous() const { return parent; }
@@ -1033,26 +1032,16 @@ class Shape : public gc::TenuredCell
 
     bool isDataDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
     }
     bool isAccessorDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
     }
 
-    PropDesc::Writability writability() const {
-        return (attrs & JSPROP_READONLY) ? PropDesc::NonWritable : PropDesc::Writable;
-    }
-    PropDesc::Enumerability enumerability() const {
-        return (attrs & JSPROP_ENUMERATE) ? PropDesc::Enumerable : PropDesc::NonEnumerable;
-    }
-    PropDesc::Configurability configurability() const {
-        return (attrs & JSPROP_PERMANENT) ? PropDesc::NonConfigurable : PropDesc::Configurable;
-    }
-
     /*
      * For ES5 compatibility, we allow properties with SetterOp-flavored
      * setters to be shadowed when set. The "own" property thereby created in
      * the directly referenced object will have the same getter and setter as
      * the prototype property. See bug 552432.
      */
     bool shadowable() const {
         MOZ_ASSERT_IF(isDataDescriptor(), writable());
@@ -1378,31 +1367,31 @@ Shape::Shape(const StackShape &other, ui
     propid_(other.propid),
     slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
     attrs(other.attrs),
     flags(other.flags),
     parent(nullptr)
 {
 #ifdef DEBUG
     gc::AllocKind allocKind = getAllocKind();
-    MOZ_ASSERT_IF(other.isAccessorShape(), allocKind == gc::FINALIZE_ACCESSOR_SHAPE);
-    MOZ_ASSERT_IF(allocKind == gc::FINALIZE_SHAPE, !other.isAccessorShape());
+    MOZ_ASSERT_IF(other.isAccessorShape(), allocKind == gc::AllocKind::ACCESSOR_SHAPE);
+    MOZ_ASSERT_IF(allocKind == gc::AllocKind::SHAPE, !other.isAccessorShape());
 #endif
 
     MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
     kids.setNull();
 }
 
 inline
 AccessorShape::AccessorShape(const StackShape &other, uint32_t nfixed)
   : Shape(other, nfixed),
     rawGetter(other.rawGetter),
     rawSetter(other.rawSetter)
 {
-    MOZ_ASSERT(getAllocKind() == gc::FINALIZE_ACCESSOR_SHAPE);
+    MOZ_ASSERT(getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
 
     if ((attrs & JSPROP_GETTER) && rawGetter)
         GetterSetterWriteBarrierPost(this, &this->getterObj);
     if ((attrs & JSPROP_SETTER) && rawSetter)
         GetterSetterWriteBarrierPost(this, &this->setterObj);
 }
 
 inline
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1103,22 +1103,16 @@ FrameIter::matchCallee(JSContext *cx, Ha
     // template from which it would be cloned, we compare properties which are
     // stable across the cloning of JSFunctions.
     if (((currentCallee->flags() ^ fun->flags()) & JSFunction::STABLE_ACROSS_CLONES) != 0 ||
         currentCallee->nargs() != fun->nargs())
     {
         return false;
     }
 
-    // Only some lambdas are optimized in a way which cannot be recovered without
-    // invalidating the frame. Thus, if one of the function is not a lambda we can just
-    // compare it against the calleeTemplate.
-    if (!fun->isLambda() || !currentCallee->isLambda())
-        return currentCallee == fun;
-
     // Use the same condition as |js::CloneFunctionObject|, to know if we should
     // expect both functions to have the same JSScript. If so, and if they are
     // different, then they cannot be equal.
     bool useSameScript = CloneFunctionObjectUseSameScript(fun->compartment(), currentCallee);
     if (useSameScript &&
         (currentCallee->hasScript() != fun->hasScript() ||
          currentCallee->nonLazyScript() != fun->nonLazyScript()))
     {
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -346,37 +346,37 @@ js::StaticStrings::getLength2(char16_t c
     size_t index = (((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2];
     return length2StaticTable[index];
 }
 
 MOZ_ALWAYS_INLINE void
 JSString::finalize(js::FreeOp *fop)
 {
     /* FatInline strings are in a different arena. */
-    MOZ_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING);
+    MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
 
     if (isFlat())
         asFlat().finalize(fop);
     else
         MOZ_ASSERT(isDependent() || isRope());
 }
 
 inline void
 JSFlatString::finalize(js::FreeOp *fop)
 {
-    MOZ_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING);
+    MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
 
     if (!isInline())
         fop->free_(nonInlineCharsRaw());
 }
 
 inline void
 JSFatInlineString::finalize(js::FreeOp *fop)
 {
-    MOZ_ASSERT(getAllocKind() == js::gc::FINALIZE_FAT_INLINE_STRING);
+    MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_STRING);
 
     if (!isInline())
         fop->free_(nonInlineCharsRaw());
 }
 
 inline void
 JSAtom::finalize(js::FreeOp *fop)
 {
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -459,17 +459,17 @@ class JSString : public js::gc::TenuredC
 
     inline JSLinearString *base() const;
 
     void markBase(JSTracer *trc) {
         MOZ_ASSERT(hasBase());
         js::gc::MarkStringUnbarriered(trc, &d.s.u3.base, "base");
     }
 
-    /* Only called by the GC for strings with the FINALIZE_STRING kind. */
+    /* Only called by the GC for strings with the AllocKind::STRING kind. */
 
     inline void finalize(js::FreeOp *fop);
 
     /* Gets the number of bytes that the chars take on the heap. */
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     /* Offsets for direct field from jit code. */
@@ -871,17 +871,17 @@ class JSFatInlineString : public JSInlin
                                               -1 /* null terminator */;
 
     template <typename CharT>
     inline CharT *init(size_t length);
 
     template<typename CharT>
     static bool lengthFits(size_t length);
 
-    /* Only called by the GC for strings with the FINALIZE_FAT_INLINE_STRING kind. */
+    /* Only called by the GC for strings with the AllocKind::FAT_INLINE_STRING kind. */
 
     MOZ_ALWAYS_INLINE void finalize(js::FreeOp *fop);
 };
 
 static_assert(sizeof(JSFatInlineString) % js::gc::CellSize == 0,
               "fat inline strings shouldn't waste space up to the next cell "
               "boundary");
 
@@ -905,17 +905,17 @@ class JSExternalString : public JSFlatSt
     /*
      * External chars are never allocated inline or in the nursery, so we can
      * safely expose this without requiring an AutoCheckCannotGC argument.
      */
     const char16_t *twoByteChars() const {
         return rawTwoByteChars();
     }
 
-    /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
+    /* Only called by the GC for strings with the AllocKind::EXTERNAL_STRING kind. */
 
     inline void finalize(js::FreeOp *fop);
 };
 
 static_assert(sizeof(JSExternalString) == sizeof(JSString),
               "string subclasses must be binary-compatible with JSString");
 
 class JSUndependedString : public JSFlatString
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2471,23 +2471,23 @@ js::PrintTypes(JSContext *cx, JSCompartm
     JSAutoRequest request(cx);
 
     Zone *zone = comp->zone();
     AutoEnterAnalysis enter(nullptr, zone);
 
     if (!force && !InferSpewActive(ISpewResult))
         return;
 
-    for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+    for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
         RootedScript script(cx, i.get<JSScript>());
         if (script->types())
             script->types()->printTypes(cx, script);
     }
 
-    for (gc::ZoneCellIter i(zone, gc::FINALIZE_OBJECT_GROUP); !i.done(); i.next()) {
+    for (gc::ZoneCellIter i(zone, gc::AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
         ObjectGroup *group = i.get<ObjectGroup>();
         group->print();
     }
 #endif
 }
 
 /////////////////////////////////////////////////////////////////////
 // ObjectGroup
@@ -4248,17 +4248,17 @@ TypeZone::endSweep(JSRuntime *rt)
     sweepReleaseTypes = false;
 
     rt->gc.freeAllLifoBlocksAfterSweeping(&sweepTypeLifoAlloc);
 }
 
 void
 TypeZone::clearAllNewScriptsOnOOM()
 {
-    for (gc::ZoneCellIterUnderGC iter(zone(), gc::FINALIZE_OBJECT_GROUP);
+    for (gc::ZoneCellIterUnderGC iter(zone(), gc::AllocKind::OBJECT_GROUP);
          !iter.done(); iter.next())
     {
         ObjectGroup *group = iter.get<ObjectGroup>();
         if (!IsObjectGroupAboutToBeFinalized(&group))
             group->maybeClearNewScriptOnOOM();
     }
 }
 
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -173,17 +173,17 @@ class XDRState {
      * this function do not have to specialize the type of the enumerated field
      * as C++ will extract the parameterized from the argument list.
      */
     template <typename T>
     bool codeEnum32(T *val, typename mozilla::EnableIf<mozilla::IsEnum<T>::value, T>::Type * = NULL)
     {
         uint32_t tmp;
         if (mode == XDR_ENCODE)
-            tmp = *val;
+            tmp = uint32_t(*val);
         if (!codeUint32(&tmp))
             return false;
         if (mode == XDR_DECODE)
             *val = T(tmp);
         return true;
     }
 
     bool codeDouble(double *dp) {
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -435,17 +435,17 @@ sandbox_addProperty(JSContext *cx, Handl
     // However, in the case of |const x = 3|, we get called once for
     // JSOP_DEFCONST and once for JSOP_SETCONST. The first one creates the
     // property as readonly and configurable. The second one changes the
     // attributes to readonly and not configurable. If we use JS_SetPropertyById
     // for the second call, it will throw an exception because the property is
     // readonly. We have to use JS_CopyPropertyFrom since it ignores the
     // readonly attribute (as it calls JSObject::defineProperty). See bug
     // 1019181.
-    if (pd.object() && pd.isPermanent()) {
+    if (pd.object() && !pd.configurable()) {
         if (!JS_SetPropertyById(cx, proto, id, vp))
             return false;
     } else {
         if (!JS_CopyPropertyFrom(cx, id, unwrappedProto, obj,
                                  MakeNonConfigurableIntoConfigurable))
             return false;
     }
 
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -356,17 +356,17 @@ ExposedPropertiesOnly::check(JSContext *
         return false;
     }
 
     Access access = NO_ACCESS;
 
     if (!JS_GetPropertyDescriptorById(cx, hallpass, id, &desc)) {
         return false; // Error
     }
-    if (!desc.object() || !desc.isEnumerable())
+    if (!desc.object() || !desc.enumerable())
         return false;
 
     if (!desc.value().isString()) {
         EnterAndThrow(cx, wrapper, "property must be a string");
         return false;
     }
 
     JSFlatString *flat = JS_FlattenString(cx, desc.value().toString());
--- a/js/xpconnect/wrappers/AddonWrapper.cpp
+++ b/js/xpconnect/wrappers/AddonWrapper.cpp
@@ -127,19 +127,19 @@ AddonWrapper<Base>::set(JSContext *cx, J
     if (!Interpose(cx, wrapper, nullptr, id, &desc))
         return false;
 
     if (!desc.object())
         return Base::set(cx, wrapper, receiver, id, vp, result);
 
     if (desc.setter()) {
         MOZ_ASSERT(desc.hasSetterObject());
-        MOZ_ASSERT(!desc.isReadonly());
         JS::AutoValueVector args(cx);
-        args.append(vp);
+        if (!args.append(vp))
+            return false;
         RootedValue fval(cx, ObjectValue(*desc.setterObject()));
         if (!JS_CallFunctionValue(cx, receiver, fval, args, vp))
             return false;
         return result.succeed();
     }
 
     return result.failCantSetInterposed();
 }
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1974,28 +1974,28 @@ XrayWrapper<Base, Traits>::definePropert
         return false;
 
     // Note that the check here is intended to differentiate between own and
     // non-own properties, since the above lookup is not limited to own
     // properties. At present, this may not always do the right thing because
     // we often lie (sloppily) about where we found properties and set
     // desc.object() to |wrapper|. Once we fully fix our Xray prototype semantics,
     // this should work as intended.
-    if (existing_desc.object() == wrapper && existing_desc.isPermanent()) {
+    if (existing_desc.object() == wrapper && !existing_desc.configurable()) {
         // We have a non-configurable property. See if the caller is trying to
         // re-configure it in any way other than making it non-writable.
-        if (existing_desc.hasGetterOrSetterObject() || desc.hasGetterOrSetterObject() ||
-            existing_desc.isEnumerable() != desc.isEnumerable() ||
-            (existing_desc.isReadonly() && !desc.isReadonly()))
+        if (existing_desc.isAccessorDescriptor() || desc.isAccessorDescriptor() ||
+            (desc.hasEnumerable() && existing_desc.enumerable() != desc.enumerable()) ||
+            (desc.hasWritable() && !existing_desc.writable() && desc.writable()))
         {
             // We should technically report non-configurability in strict mode, but
             // doing that via JSAPI used to be a lot of trouble. See bug 1135997.
             return result.succeed();
         }
-        if (existing_desc.isReadonly()) {
+        if (!existing_desc.writable()) {
             // Same as the above for non-writability.
             return result.succeed();
         }
     }
 
     bool defined = false;
     if (!Traits::singleton.defineProperty(cx, wrapper, id, desc, existing_desc, result, &defined))
         return false;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2398,18 +2398,18 @@ nsLayoutUtils::TransformPoint(nsIFrame* 
   Point4D toDevPixels = downToDest.ProjectPoint(
       upToAncestor * Point(aPoint.x * devPixelsPerAppUnitFromFrame,
                            aPoint.y * devPixelsPerAppUnitFromFrame));
   if (!toDevPixels.HasPositiveWCoord()) {
     // Not strictly true, but we failed to get a valid point in this
     // coordinate space.
     return NONINVERTIBLE_TRANSFORM;
   }
-  aPoint.x = toDevPixels.x / devPixelsPerAppUnitToFrame;
-  aPoint.y = toDevPixels.y / devPixelsPerAppUnitToFrame;
+  aPoint.x = NSToCoordRound(toDevPixels.x / devPixelsPerAppUnitToFrame);
+  aPoint.y = NSToCoordRound(toDevPixels.y / devPixelsPerAppUnitToFrame);
   return TRANSFORM_SUCCEEDED;
 }
 
 nsLayoutUtils::TransformResult
 nsLayoutUtils::TransformRect(nsIFrame* aFromFrame, nsIFrame* aToFrame,
                              nsRect& aRect)
 {
   nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -7363,16 +7363,22 @@ PresShell::HandleEvent(nsIFrame* aFrame,
                  "How did we end up outside the connected prescontext/viewmanager hierarchy?");
     // If we aren't starting our event dispatch from the root frame of the root prescontext,
     // then someone must be capturing the mouse. In that case we don't want to search the popup
     // list.
     if (framePresContext == rootPresContext &&
         frame == mFrameConstructor->GetRootFrame()) {
       nsIFrame* popupFrame =
         nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent);
+      // If a remote browser is currently capturing input break out if we
+      // detect a chrome generated popup.
+      if (popupFrame && capturingContent &&
+          EventStateManager::IsRemoteTarget(capturingContent)) {
+        capturingContent = nullptr;
+      }
       // If the popupFrame is an ancestor of the 'frame', the frame should
       // handle the event, otherwise, the popup should handle it.
       if (popupFrame &&
           !nsContentUtils::ContentIsCrossDocDescendantOf(
              framePresContext->GetPresShell()->GetDocument(),
              popupFrame->GetContent())) {
         frame = popupFrame;
       }
--- a/layout/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -13,17 +13,17 @@ fuzzy-if(gtk2Widget,255,2) fuzzy-if(winW
 == 1086883-1b.html 1086883-1-ref.html
 == 1088025-1.html 1088025-1-ref.html
 == 1089388-1.html 1089388-1-ref.html
 == 1089388-2.html 1089388-2-ref.html
 == 1090159-1.html 1090159-1-ref.html
 == 1090168-1.html 1090168-1-ref.html
 != 1090168-1.html 1090168-1-notref.html
 == 1090168-2.html 1090168-2-ref.html
-fuzzy-if(B2G,244,173) == 1090168-3.html 1090168-3-ref.html
+fuzzy-if(B2G,244,173) fuzzy-if(Mulet,255,165) == 1090168-3.html 1090168-3-ref.html # bug 1142928: mark fuzzy on Mulet as on B2G
 == 1091058-1.html 1091058-1-ref.html
 random-if(gtk2Widget) == 1094434-1.html 1094434-1-ref.html # bug 1094845
 random-if(gtk2Widget) == 1094434-2.html 1094434-2-ref.html # bug 1094845
 == 1094914-1a.html 1094914-1-ref.html
 == 1094914-1b.html 1094914-1-ref.html
 == 1096224-1a.html 1096224-1-ref.html
 == 1096224-1b.html 1096224-1-ref.html
 fails == 1102175-1a.html 1102175-1-ref.html
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -481,45 +481,45 @@ nsTableCellFrame::BuildDisplayList(nsDis
   if (IsVisibleInSelection(aBuilder)) {
     nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
     int32_t emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ?
                                 StyleTableBorder()->mEmptyCells
                                 : NS_STYLE_TABLE_EMPTY_CELLS_SHOW;
     // take account of 'empty-cells'
     if (StyleVisibility()->IsVisible() &&
         (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) {
-    
-    
-      bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
-      if (!isRoot) {
-        nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
-        if (currentItem) {
-          currentItem->UpdateForFrameBackground(this);
-        }
-      }
-    
       // display outset box-shadows if we need to.
       const nsStyleBorder* borderStyle = StyleBorder();
       bool hasBoxShadow = !!borderStyle->mBoxShadow;
       if (hasBoxShadow) {
         aLists.BorderBackground()->AppendNewToTop(
           new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this));
       }
     
       // display background if we need to.
       if (aBuilder->IsForEventDelivery() ||
-          (((!tableFrame->IsBorderCollapse() || isRoot) &&
-          (!StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance)))) {
-        // The cell background was not painted by the nsTablePainter,
-        // so we need to do it. We have special background processing here
-        // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline
-        nsDisplayTableItem* item =
-          new (aBuilder) nsDisplayTableCellBackground(aBuilder, this);
-        aLists.BorderBackground()->AppendNewToTop(item);
-        item->UpdateForFrameBackground(this);
+          !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) {
+        if (!tableFrame->IsBorderCollapse() ||
+            aBuilder->IsAtRootOfPseudoStackingContext() ||
+            aBuilder->IsForEventDelivery()) {
+          // The cell background was not painted by the nsTablePainter,
+          // so we need to do it. We have special background processing here
+          // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline
+          nsDisplayTableItem* item =
+            new (aBuilder) nsDisplayTableCellBackground(aBuilder, this);
+          aLists.BorderBackground()->AppendNewToTop(item);
+          item->UpdateForFrameBackground(this);
+        } else {
+          // The nsTablePainter will paint our background. Make sure it
+          // knows if we're background-attachment:fixed.
+          nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
+          if (currentItem) {
+            currentItem->UpdateForFrameBackground(this);
+          }
+        }
       }
     
       // display inset box-shadows if we need to.
       if (hasBoxShadow) {
         aLists.BorderBackground()->AppendNewToTop(
           new (aBuilder) nsDisplayBoxShadowInner(aBuilder, this));
       }
     
--- a/media/mtransport/test/stunserver.cpp
+++ b/media/mtransport/test/stunserver.cpp
@@ -245,17 +245,17 @@ TestStunServer* TestStunServer::Create()
   }
 
   if (addr_ct < 1) {
     MOZ_MTLOG(ML_ERROR, "No local addresses");
     return nullptr;
   }
 
   NR_SOCKET fd;
-  int tries = 10;
+  int tries = 100;
   while (tries--) {
     // Bind to the first address (arbitrarily) on configured port (default 3478)
     r = server->TryOpenListenSocket(&addrs[0], instance_port);
     // We interpret R_ALREADY to mean the addr is probably in use. Try another.
     // Otherwise, it either worked or it didn't, and we check below.
     if (r != R_ALREADY) {
       break;
     }
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -1119,17 +1119,18 @@ int nr_ice_component_insert_pair(nr_ice_
       ABORT(r);
 
     /* Make sure the check timer is running, if the stream was previously
      * started. We will not start streams just because a pair was created,
      * unless it is the first pair to be created across all streams. */
     r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND-PAIR(%s): Ensure that check timer is running for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string);
 
     if(pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE ||
-       !pair->remote->stream->pctx->checks_started){
+       (pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN &&
+        !pair->remote->stream->pctx->checks_started)){
       if(nr_ice_media_stream_start_checks(pair->remote->stream->pctx, pair->remote->stream)) {
         r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Could not restart checks for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string);
         ABORT(R_INTERNAL);
       }
     }
 
     _status=0;
   abort:
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -538,20 +538,20 @@ void MediaPipeline::RtcpPacketReceived(T
   nsresult res = rtcp_.recv_srtp_->UnprotectRtcp(inner_data,
                                                  len,
                                                  len,
                                                  &out_len);
 
   if (!NS_SUCCEEDED(res))
     return;
 
-  MediaPipelineFilter::Result filter_result = MediaPipelineFilter::PASS;
-  if (filter_) {
-    filter_result = filter_->FilterRTCP(inner_data, out_len);
-    if (filter_result == MediaPipelineFilter::FAIL) {
+  // We do not filter RTCP for send pipelines, since the webrtc.org code for
+  // senders already has logic to ignore RRs that do not apply.
+  if (filter_ && direction_ == RECEIVE) {
+    if (!filter_->FilterSenderReport(inner_data, out_len)) {
       MOZ_MTLOG(ML_NOTICE, "Dropping rtcp packet");
       return;
     }
   }
 
   MOZ_MTLOG(ML_DEBUG, description_ << " received RTCP packet.");
   increment_rtcp_packets_received();
 
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipelineFilter.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipelineFilter.cpp
@@ -2,25 +2,20 @@
 /* vim: softtabstop=2:shiftwidth=2:expandtab
  * */
 /* 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/. */
 
 // Original author: bcampen@mozilla.com
 
-#include "logging.h"
-
 #include "MediaPipelineFilter.h"
 
 #include "webrtc/modules/interface/module_common_types.h"
 
-// Logging context
-MOZ_MTLOG_MODULE("mediapipeline")
-
 namespace mozilla {
 
 MediaPipelineFilter::MediaPipelineFilter() : correlator_(0) {
 }
 
 bool MediaPipelineFilter::Filter(const webrtc::RTPHeader& header,
                                  uint32_t correlator) {
   if (correlator) {
@@ -76,107 +71,32 @@ void MediaPipelineFilter::Update(const M
     remote_ssrc_set_ = filter_update.remote_ssrc_set_;
   }
 
   local_ssrc_set_ = filter_update.local_ssrc_set_;
   payload_type_set_ = filter_update.payload_type_set_;
   correlator_ = filter_update.correlator_;
 }
 
-MediaPipelineFilter::Result
-MediaPipelineFilter::FilterRTCP(const unsigned char* data,
-                                size_t len) const {
+bool
+MediaPipelineFilter::FilterSenderReport(const unsigned char* data,
+                                        size_t len) const {
   if (len < FIRST_SSRC_OFFSET) {
-    return FAIL;
+    return false;
   }
 
   uint8_t payload_type = data[PT_OFFSET];
 
-  switch (payload_type) {
-    case SENDER_REPORT_T:
-      return CheckRtcpReport(data, len, RECEIVER_REPORT_START_SR) ? PASS : FAIL;
-    case RECEIVER_REPORT_T:
-      return CheckRtcpReport(data, len, SENDER_REPORT_START_RR) ? PASS : FAIL;
-    default:
-      return UNSUPPORTED;
-  }
-
-  return UNSUPPORTED;
-}
-
-bool MediaPipelineFilter::CheckRtcpSsrc(const unsigned char* data,
-                                        size_t len,
-                                        size_t ssrc_offset,
-                                        uint8_t flags) const {
-  if (ssrc_offset + 4 > len) {
+  if (payload_type != SENDER_REPORT_T) {
     return false;
   }
 
   uint32_t ssrc = 0;
-  ssrc += (uint32_t)data[ssrc_offset++] << 24;
-  ssrc += (uint32_t)data[ssrc_offset++] << 16;
-  ssrc += (uint32_t)data[ssrc_offset++] << 8;
-  ssrc += (uint32_t)data[ssrc_offset++];
-
-  if (flags | MAYBE_LOCAL_SSRC) {
-    if (local_ssrc_set_.count(ssrc)) {
-      return true;
-    }
-  }
-
-  if (flags | MAYBE_REMOTE_SSRC) {
-    if (remote_ssrc_set_.count(ssrc)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-static uint8_t GetCount(const unsigned char* data, size_t len) {
-  // This might be a count of rr reports, or might be a count of stuff like
-  // SDES reports, or what-have-you. They all live in bits 3-7.
-  return data[0] & 0x1F;
-}
+  ssrc += (uint32_t)data[FIRST_SSRC_OFFSET] << 24;
+  ssrc += (uint32_t)data[FIRST_SSRC_OFFSET + 1] << 16;
+  ssrc += (uint32_t)data[FIRST_SSRC_OFFSET + 2] << 8;
+  ssrc += (uint32_t)data[FIRST_SSRC_OFFSET + 3];
 
-bool MediaPipelineFilter::CheckRtcpReport(const unsigned char* data,
-                                          size_t len,
-                                          size_t first_rr_offset) const {
-  bool remote_ssrc_matched = CheckRtcpSsrc(data,
-                                           len,
-                                           FIRST_SSRC_OFFSET,
-                                           MAYBE_REMOTE_SSRC);
-
-  uint8_t rr_count = GetCount(data, len);
-
-  // We need to check for consistency. If the remote ssrc matched, the local
-  // ssrcs must too. If it did not, this may just be because our filter is
-  // incomplete, so the local ssrcs could go either way.
-  bool ssrcs_must_match = remote_ssrc_matched;
-  bool ssrcs_must_not_match = false;
-
-  for (uint8_t rr_num = 0; rr_num < rr_count; ++rr_num) {
-    size_t ssrc_offset = first_rr_offset + (rr_num * RECEIVER_REPORT_SIZE);
-
-    if (!CheckRtcpSsrc(data, len, ssrc_offset, MAYBE_LOCAL_SSRC)) {
-      ssrcs_must_not_match = true;
-      if (ssrcs_must_match) {
-        break;
-      }
-    } else {
-      ssrcs_must_match = true;
-      if (ssrcs_must_not_match) {
-        break;
-      }
-    }
-  }
-
-  if (ssrcs_must_match && ssrcs_must_not_match) {
-    MOZ_MTLOG(ML_ERROR, "Received an RTCP packet with SSRCs from "
-              "multiple m-lines! This is broken.");
-  }
-
-  // This is set if any ssrc matched
-  return ssrcs_must_match;
+  return !!remote_ssrc_set_.count(ssrc);
 }
 
 } // end namespace mozilla
 
-
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipelineFilter.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipelineFilter.h
@@ -37,72 +37,48 @@ namespace mozilla {
 // 2) If the remote endpoint includes SSRC media-level attributes in its SDP,
 //    we can simply use this information to populate the filter. The only
 //    shortcoming here is when RTP packets arrive before the answer does. See
 //    above.
 //
 // 3) As a fallback, we can try to use payload type IDs to perform correlation,
 //    but only when the type id is unique to this media section.
 //    This too allows us to learn about SSRCs (mostly useful for filtering
-//    any RTCP packets that follow).
+//    sender reports later).
 class MediaPipelineFilter {
  public:
   MediaPipelineFilter();
 
   // Checks whether this packet passes the filter, possibly updating the filter
   // in the process (if the correlator or payload types are used, they can teach
   // the filter about ssrcs)
   bool Filter(const webrtc::RTPHeader& header, uint32_t correlator = 0);
 
-  typedef enum {
-    FAIL,
-    PASS,
-    UNSUPPORTED
-  } Result;
-
   // RTCP doesn't have things like the RTP correlator, and uses its own
   // payload types too.
-  Result FilterRTCP(const unsigned char* data, size_t len) const;
+  bool FilterSenderReport(const unsigned char* data, size_t len) const;
 
   void AddLocalSSRC(uint32_t ssrc);
   void AddRemoteSSRC(uint32_t ssrc);
 
   // When a payload type id is unique to our media section, add it here.
   void AddUniquePT(uint8_t payload_type);
   void SetCorrelator(uint32_t correlator);
 
   void Update(const MediaPipelineFilter& filter_update);
 
   // Some payload types
   static const uint8_t SENDER_REPORT_T = 200;
-  static const uint8_t RECEIVER_REPORT_T = 201;
 
  private:
-  static const uint8_t MAYBE_LOCAL_SSRC = 1;
-  static const uint8_t MAYBE_REMOTE_SSRC = 2;
-
   // Payload type is always in the second byte
   static const size_t PT_OFFSET = 1;
   // First SSRC always starts at the fifth byte.
   static const size_t FIRST_SSRC_OFFSET = 4;
 
-  static const size_t RECEIVER_REPORT_START_SR = 7*4;
-  static const size_t SENDER_REPORT_START_RR = 2*4;
-  static const size_t RECEIVER_REPORT_SIZE = 6*4;
-
-  bool CheckRtcpSsrc(const unsigned char* data,
-                     size_t len,
-                     size_t ssrc_offset,
-                     uint8_t flags) const;
-
-
-  bool CheckRtcpReport(const unsigned char* data,
-                        size_t len,
-                        size_t first_rr_offset) const;
-
   uint32_t correlator_;
   // The number of filters we manage here is quite small, so I am optimizing
   // for readability.
   std::set<uint32_t> remote_ssrc_set_;
   std::set<uint32_t> local_ssrc_set_;
   std::set<uint8_t> payload_type_set_;
 };
 
--- a/media/webrtc/signaling/test/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp
@@ -422,18 +422,19 @@ protected:
     return answer;
   }
 
   static const uint32_t NO_CHECKS = 0;
   static const uint32_t CHECK_SUCCESS = 1;
   static const uint32_t CHECK_TRACKS = 1 << 2;
   static const uint32_t ALL_CHECKS = CHECK_SUCCESS | CHECK_TRACKS;
 
-  void OfferAnswer(uint32_t checkFlags = ALL_CHECKS) {
-    std::string offer = CreateOffer();
+  void OfferAnswer(uint32_t checkFlags = ALL_CHECKS,
+                   const Maybe<JsepOfferOptions> options = Nothing()) {
+    std::string offer = CreateOffer(options);
     SetLocalOffer(offer, checkFlags);
     SetRemoteOffer(offer, checkFlags);
 
     std::string answer = CreateAnswer();
     SetLocalAnswer(answer, checkFlags);
     SetRemoteAnswer(answer, checkFlags);
   }
 
@@ -2008,16 +2009,66 @@ TEST_F(JsepSessionTest, OfferAnswerSendO
   ASSERT_EQ(SdpDirectionAttribute::kSendrecv,
             outputSdp->GetMediaSection(1).GetAttributeList().GetDirection());
   ASSERT_EQ(SdpMediaSection::kVideo,
             outputSdp->GetMediaSection(2).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             outputSdp->GetMediaSection(2).GetAttributeList().GetDirection());
 }
 
+TEST_F(JsepSessionTest, OfferToReceiveAudioNotUsed)
+{
+  JsepOfferOptions options;
+  options.mOfferToReceiveAudio = Some<size_t>(1);
+
+  OfferAnswer(CHECK_SUCCESS, Some(options));
+
+  SipccSdpParser parser;
+  UniquePtr<Sdp> offer(parser.Parse(mSessionOff.GetLocalDescription()));
+  ASSERT_TRUE(offer.get());
+  ASSERT_EQ(1U, offer->GetMediaSectionCount());
+  ASSERT_EQ(SdpMediaSection::kAudio,
+            offer->GetMediaSection(0).GetMediaType());
+  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
+            offer->GetMediaSection(0).GetAttributeList().GetDirection());
+
+  UniquePtr<Sdp> answer(parser.Parse(mSessionAns.GetLocalDescription()));
+  ASSERT_TRUE(answer.get());
+  ASSERT_EQ(1U, answer->GetMediaSectionCount());
+  ASSERT_EQ(SdpMediaSection::kAudio,
+            answer->GetMediaSection(0).GetMediaType());
+  ASSERT_EQ(SdpDirectionAttribute::kInactive,
+            answer->GetMediaSection(0).GetAttributeList().GetDirection());
+}
+
+TEST_F(JsepSessionTest, OfferToReceiveVideoNotUsed)
+{
+  JsepOfferOptions options;
+  options.mOfferToReceiveVideo = Some<size_t>(1);
+
+  OfferAnswer(CHECK_SUCCESS, Some(options));
+
+  SipccSdpParser parser;
+  UniquePtr<Sdp> offer(parser.Parse(mSessionOff.GetLocalDescription()));
+  ASSERT_TRUE(offer.get());
+  ASSERT_EQ(1U, offer->GetMediaSectionCount());
+  ASSERT_EQ(SdpMediaSection::kVideo,
+            offer->GetMediaSection(0).GetMediaType());
+  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
+            offer->GetMediaSection(0).GetAttributeList().GetDirection());
+
+  UniquePtr<Sdp> answer(parser.Parse(mSessionAns.GetLocalDescription()));
+  ASSERT_TRUE(answer.get());
+  ASSERT_EQ(1U, answer->GetMediaSectionCount());
+  ASSERT_EQ(SdpMediaSection::kVideo,
+            answer->GetMediaSection(0).GetMediaType());
+  ASSERT_EQ(SdpDirectionAttribute::kInactive,
+            answer->GetMediaSection(0).GetAttributeList().GetDirection());
+}
+
 TEST_F(JsepSessionTest, CreateOfferNoDatachannelDefault)
 {
   RefPtr<JsepTrack> msta(
       new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
   mSessionOff.AddTrack(msta);
 
   RefPtr<JsepTrack> mstv1(
       new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v1"));
--- a/media/webrtc/signaling/test/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/test/mediapipeline_unittest.cpp
@@ -510,291 +510,129 @@ TEST_F(MediaPipelineFilterTest, TestSSRC
   0,0,0,0, \
   0,0,0,0, \
   0,0,0,0, \
   0,0,0,0
 
 #define RTCP_TYPEINFO(num_rrs, type, size) \
   0x80 + num_rrs, type, 0, size
 
-const unsigned char rtcp_rr_s16[] = {
-  // zero rrs, size 1 words
-  RTCP_TYPEINFO(0, MediaPipelineFilter::RECEIVER_REPORT_T, 1),
-  SSRC(16)
-};
-
-const unsigned char rtcp_rr_s16_r17[] = {
-  // one rr, 7 words
-  RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 7),
-  SSRC(16),
-  REPORT_FRAGMENT(17)
-};
-
-const unsigned char rtcp_rr_s16_r17_18[] = {
-  // two rrs, size 13 words
-  RTCP_TYPEINFO(2, MediaPipelineFilter::RECEIVER_REPORT_T, 13),
-  SSRC(16),
-  REPORT_FRAGMENT(17),
-  REPORT_FRAGMENT(18)
-};
-
 const unsigned char rtcp_sr_s16[] = {
   // zero rrs, size 6 words
   RTCP_TYPEINFO(0, MediaPipelineFilter::SENDER_REPORT_T, 6),
   REPORT_FRAGMENT(16)
 };
 
 const unsigned char rtcp_sr_s16_r17[] = {
   // one rr, size 12 words
   RTCP_TYPEINFO(1, MediaPipelineFilter::SENDER_REPORT_T, 12),
   REPORT_FRAGMENT(16),
   REPORT_FRAGMENT(17)
 };
 
-const unsigned char rtcp_sr_s16_r17_18[] = {
-  // two rrs, size 18 words
-  RTCP_TYPEINFO(2, MediaPipelineFilter::SENDER_REPORT_T, 18),
-  REPORT_FRAGMENT(16),
-  REPORT_FRAGMENT(17),
-  REPORT_FRAGMENT(18)
-};
-
 const unsigned char unknown_type[] = {
   RTCP_TYPEINFO(1, 222, 0)
 };
 
 TEST_F(MediaPipelineFilterTest, TestEmptyFilterReport0) {
   MediaPipelineFilter filter;
-  ASSERT_EQ(MediaPipelineFilter::FAIL,
-            filter.FilterRTCP(rtcp_sr_s16, sizeof(rtcp_sr_s16)));
-  ASSERT_EQ(MediaPipelineFilter::FAIL,
-            filter.FilterRTCP(rtcp_rr_s16, sizeof(rtcp_rr_s16)));
+  ASSERT_FALSE(filter.FilterSenderReport(rtcp_sr_s16, sizeof(rtcp_sr_s16)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestFilterReport0) {
   MediaPipelineFilter filter;
   filter.AddRemoteSSRC(16);
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16, sizeof(rtcp_sr_s16)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16, sizeof(rtcp_rr_s16)));
-}
-
-TEST_F(MediaPipelineFilterTest, TestFilterReport0SSRCTruncated) {
-  MediaPipelineFilter filter;
-  filter.AddRemoteSSRC(16);
-  const unsigned char data[] = {
-    RTCP_TYPEINFO(0, MediaPipelineFilter::RECEIVER_REPORT_T, 1),
-    0,0,0
-  };
-  ASSERT_EQ(MediaPipelineFilter::FAIL,
-            filter.FilterRTCP(data, sizeof(data)));
+  ASSERT_TRUE(filter.FilterSenderReport(rtcp_sr_s16, sizeof(rtcp_sr_s16)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestFilterReport0PTTruncated) {
   MediaPipelineFilter filter;
   filter.AddRemoteSSRC(16);
   const unsigned char data[] = {0x80};
-  ASSERT_EQ(MediaPipelineFilter::FAIL,
-            filter.FilterRTCP(data, sizeof(data)));
+  ASSERT_FALSE(filter.FilterSenderReport(data, sizeof(data)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestFilterReport0CountTruncated) {
   MediaPipelineFilter filter;
   filter.AddRemoteSSRC(16);
   const unsigned char data[] = {};
-  ASSERT_EQ(MediaPipelineFilter::FAIL,
-            filter.FilterRTCP(data, sizeof(data)));
-}
-
-TEST_F(MediaPipelineFilterTest, TestFilterReport1BothMatch) {
-  MediaPipelineFilter filter;
-  filter.AddRemoteSSRC(16);
-  filter.AddLocalSSRC(17);
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17)));
+  ASSERT_FALSE(filter.FilterSenderReport(data, sizeof(data)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestFilterReport1SSRCTruncated) {
   MediaPipelineFilter filter;
   filter.AddRemoteSSRC(16);
   filter.AddLocalSSRC(17);
-  const unsigned char rr[] = {
-    RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 7),
-    SSRC(16),
-    0,0,0
-  };
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rr, sizeof(rr)));
   const unsigned char sr[] = {
-    RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 12),
+    RTCP_TYPEINFO(1, MediaPipelineFilter::SENDER_REPORT_T, 12),
     REPORT_FRAGMENT(16),
     0,0,0
   };
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(sr, sizeof(rr)));
+  ASSERT_TRUE(filter.FilterSenderReport(sr, sizeof(sr)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestFilterReport1BigSSRC) {
   MediaPipelineFilter filter;
   filter.AddRemoteSSRC(0x01020304);
   filter.AddLocalSSRC(0x11121314);
-  const unsigned char rr[] = {
-    RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 7),
-    SSRC(0x01020304),
-    REPORT_FRAGMENT(0x11121314)
-  };
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rr, sizeof(rr)));
   const unsigned char sr[] = {
-    RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 12),
+    RTCP_TYPEINFO(1, MediaPipelineFilter::SENDER_REPORT_T, 12),
     SSRC(0x01020304),
     REPORT_FRAGMENT(0x11121314)
   };
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(sr, sizeof(rr)));
-}
-
-TEST_F(MediaPipelineFilterTest, TestFilterReport1LocalMatch) {
-  MediaPipelineFilter filter;
-  filter.AddLocalSSRC(17);
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17)));
+  ASSERT_TRUE(filter.FilterSenderReport(sr, sizeof(sr)));
 }
 
-TEST_F(MediaPipelineFilterTest, TestFilterReport1Inconsistent) {
-  MediaPipelineFilter filter;
-  filter.AddRemoteSSRC(16);
-  // We assume that the filter is exactly correct in terms of local ssrcs.
-  // So, when RTCP shows up with a remote SSRC that matches, and a local
-  // ssrc that doesn't, we assume the other end has messed up and put ssrcs
-  // from more than one m-line in the packet.
-  // TODO: Currently, the webrtc.org code will continue putting old ssrcs
-  // in RTCP that have been negotiated away, causing the RTCP to be dropped
-  // if we leave this checking in. Not sure how we're supposed to prompt
-  // the webrtc.org code to stop doing this.
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17)));
-  //ASSERT_EQ(MediaPipelineFilter::FAIL,
-  //          filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17)));
-  //ASSERT_EQ(MediaPipelineFilter::FAIL,
-  //          filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17)));
-}
-
-TEST_F(MediaPipelineFilterTest, TestFilterReport1NeitherMatch) {
-  MediaPipelineFilter filter;
-  filter.AddRemoteSSRC(17);
-  filter.AddLocalSSRC(18);
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17)));
-}
-
-TEST_F(MediaPipelineFilterTest, TestFilterReport2AllMatch) {
+TEST_F(MediaPipelineFilterTest, TestFilterReportMatch) {
   MediaPipelineFilter filter;
   filter.AddRemoteSSRC(16);
-  filter.AddLocalSSRC(17);
-  filter.AddLocalSSRC(18);
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16_r17_18,
-                              sizeof(rtcp_sr_s16_r17_18)));
-}
-
-TEST_F(MediaPipelineFilterTest, TestFilterReport2LocalMatch) {
-  MediaPipelineFilter filter;
-  filter.AddLocalSSRC(17);
-  filter.AddLocalSSRC(18);
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16_r17_18,
-                              sizeof(rtcp_sr_s16_r17_18)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16_r17_18,
-                              sizeof(rtcp_rr_s16_r17_18)));
+  ASSERT_TRUE(filter.FilterSenderReport(rtcp_sr_s16_r17,
+                                        sizeof(rtcp_sr_s16_r17)));
 }
 
-TEST_F(MediaPipelineFilterTest, TestFilterReport2Inconsistent101) {
+TEST_F(MediaPipelineFilterTest, TestFilterReportNoMatch) {
   MediaPipelineFilter filter;
-  filter.AddRemoteSSRC(16);
-  filter.AddLocalSSRC(18);
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16_r17_18,
-                              sizeof(rtcp_sr_s16_r17_18)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16_r17_18,
-                              sizeof(rtcp_rr_s16_r17_18)));
-  //ASSERT_EQ(MediaPipelineFilter::FAIL,
-  //          filter.FilterRTCP(rtcp_sr_s16_r17_18,
-  //                            sizeof(rtcp_sr_s16_r17_18)));
-  //ASSERT_EQ(MediaPipelineFilter::FAIL,
-  //          filter.FilterRTCP(rtcp_rr_s16_r17_18,
-  //                            sizeof(rtcp_rr_s16_r17_18)));
-}
-
-TEST_F(MediaPipelineFilterTest, TestFilterReport2Inconsistent001) {
-  MediaPipelineFilter filter;
-  filter.AddLocalSSRC(18);
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16_r17_18,
-                              sizeof(rtcp_sr_s16_r17_18)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16_r17_18,
-                              sizeof(rtcp_rr_s16_r17_18)));
-  //ASSERT_EQ(MediaPipelineFilter::FAIL,
-  //          filter.FilterRTCP(rtcp_sr_s16_r17_18,
-  //                            sizeof(rtcp_sr_s16_r17_18)));
-  //ASSERT_EQ(MediaPipelineFilter::FAIL,
-  //          filter.FilterRTCP(rtcp_rr_s16_r17_18,
-  //                            sizeof(rtcp_rr_s16_r17_18)));
+  filter.AddRemoteSSRC(17);
+  ASSERT_FALSE(filter.FilterSenderReport(rtcp_sr_s16_r17,
+                                         sizeof(rtcp_sr_s16_r17)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestFilterUnknownRTCPType) {
   MediaPipelineFilter filter;
   filter.AddLocalSSRC(18);
-  ASSERT_EQ(MediaPipelineFilter::UNSUPPORTED,
-            filter.FilterRTCP(unknown_type, sizeof(unknown_type)));
+  ASSERT_FALSE(filter.FilterSenderReport(unknown_type, sizeof(unknown_type)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestCorrelatorFilter) {
   MediaPipelineFilter filter;
   filter.SetCorrelator(7777);
   ASSERT_TRUE(Filter(filter, 7777, 16, 110));
   ASSERT_FALSE(Filter(filter, 7778, 17, 110));
   // This should also have resulted in the SSRC 16 being added to the filter
   ASSERT_TRUE(Filter(filter, 0, 16, 110));
   ASSERT_FALSE(Filter(filter, 0, 17, 110));
 
   // rtcp_sr_s16 has 16 as an SSRC
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16, sizeof(rtcp_sr_s16)));
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_rr_s16, sizeof(rtcp_rr_s16)));
+  ASSERT_TRUE(filter.FilterSenderReport(rtcp_sr_s16, sizeof(rtcp_sr_s16)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestPayloadTypeFilter) {
   MediaPipelineFilter filter;
   filter.AddUniquePT(110);
   ASSERT_TRUE(Filter(filter, 0, 555, 110));
   ASSERT_FALSE(Filter(filter, 0, 556, 111));
 }
 
 TEST_F(MediaPipelineFilterTest, TestPayloadTypeFilterSSRCUpdate) {
   MediaPipelineFilter filter;
   filter.AddUniquePT(110);
   ASSERT_TRUE(Filter(filter, 0, 16, 110));
 
   // rtcp_sr_s16 has 16 as an SSRC
-  ASSERT_EQ(MediaPipelineFilter::PASS,
-            filter.FilterRTCP(rtcp_sr_s16, sizeof(rtcp_sr_s16)));
+  ASSERT_TRUE(filter.FilterSenderReport(rtcp_sr_s16, sizeof(rtcp_sr_s16)));
 }
 
 TEST_F(MediaPipelineFilterTest, TestSSRCMovedWithCorrelator) {
   MediaPipelineFilter filter;
   filter.SetCorrelator(7777);
   ASSERT_TRUE(Filter(filter, 7777, 555, 110));
   ASSERT_TRUE(Filter(filter, 0, 555, 110));
   ASSERT_FALSE(Filter(filter, 7778, 555, 110));
--- a/media/webrtc/signaling/test/signaling_unittests.cpp
+++ b/media/webrtc/signaling/test/signaling_unittests.cpp
@@ -1,17 +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/. */
 
 #include <iostream>
 #include <map>
 #include <algorithm>
 #include <string>
-#include <unistd.h>
 
 #include "base/basictypes.h"
 #include "logging.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
 
@@ -56,18 +55,17 @@ bool gTestsComplete = false;
 
 #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 = 7000;
-static bool fRtcpMux = true;
+static int kDefaultTimeout = 10000;
 
 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)
@@ -171,76 +169,33 @@ static const std::string strG711SdpOffer
     "a=ice-ufrag:cYuakxkEKH+RApYE\r\n"
     "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n"
     "a=setup:active\r\n"
     "a=sendrecv\r\n";
 
 
 enum sdpTestFlags
 {
-  SHOULD_SEND_AUDIO     = (1<<0),
-  SHOULD_RECV_AUDIO     = (1<<1),
-  SHOULD_INACTIVE_AUDIO = (1<<2),
-  SHOULD_REJECT_AUDIO   = (1<<3),
-  SHOULD_OMIT_AUDIO     = (1<<4),
-  DONT_CHECK_AUDIO      = (1<<5),
-  SHOULD_CHECK_AUDIO    = (1<<6),
-
-  SHOULD_SEND_VIDEO     = (1<<8),
-  SHOULD_RECV_VIDEO     = (1<<9),
-  SHOULD_INACTIVE_VIDEO = (1<<10),
-  SHOULD_REJECT_VIDEO   = (1<<11),
-  SHOULD_OMIT_VIDEO     = (1<<12),
-  DONT_CHECK_VIDEO      = (1<<13),
-  SHOULD_CHECK_VIDEO    = (1<<14),
-
-  SHOULD_INCLUDE_DATA   = (1 << 16),
-  DONT_CHECK_DATA       = (1 << 17),
-
-  HAS_ALL_CANDIDATES     = (1 << 18),
-
-  SHOULD_SENDRECV_AUDIO = SHOULD_SEND_AUDIO | SHOULD_RECV_AUDIO,
-  SHOULD_SENDRECV_VIDEO = SHOULD_SEND_VIDEO | SHOULD_RECV_VIDEO,
-  SHOULD_SENDRECV_AV = SHOULD_SENDRECV_AUDIO | SHOULD_SENDRECV_VIDEO,
-  SHOULD_CHECK_AV = SHOULD_CHECK_AUDIO | SHOULD_CHECK_VIDEO,
-
-  AUDIO_FLAGS = SHOULD_SEND_AUDIO | SHOULD_RECV_AUDIO
-                | SHOULD_INACTIVE_AUDIO | SHOULD_REJECT_AUDIO
-                | DONT_CHECK_AUDIO | SHOULD_OMIT_AUDIO
-                | SHOULD_CHECK_AUDIO,
-
-  VIDEO_FLAGS = SHOULD_SEND_VIDEO | SHOULD_RECV_VIDEO
-                | SHOULD_INACTIVE_VIDEO | SHOULD_REJECT_VIDEO
-                | DONT_CHECK_VIDEO | SHOULD_OMIT_VIDEO
-                | SHOULD_CHECK_VIDEO
+  HAS_ALL_CANDIDATES     = (1 << 0),
 };
 
 enum offerAnswerFlags
 {
   OFFER_NONE  = 0, // Sugar to make function calls clearer.
   OFFER_AUDIO = (1<<0),
   OFFER_VIDEO = (1<<1),
   // Leaving some room here for other media types
   ANSWER_NONE  = 0, // Sugar to make function calls clearer.
   ANSWER_AUDIO = (1<<8),
   ANSWER_VIDEO = (1<<9),
 
   OFFER_AV = OFFER_AUDIO | OFFER_VIDEO,
   ANSWER_AV = ANSWER_AUDIO | ANSWER_VIDEO
 };
 
-enum mediaPipelineFlags
-{
-  PIPELINE_RTCP_MUX = (1<<0),
-  PIPELINE_SEND = (1<<1),
-  PIPELINE_VIDEO = (1<<2),
-  PIPELINE_RTCP_NACK = (1<<3)
-};
-
-
  typedef enum {
    NO_TRICKLE = 0,
    OFFERER_TRICKLES = 1,
    ANSWERER_TRICKLES = 2,
    BOTH_TRICKLE = OFFERER_TRICKLES | ANSWERER_TRICKLES
  } TrickleType;
 
 class TestObserver : public AFakePCObserver
@@ -921,24 +876,46 @@ class PCDispatchWrapper : public nsSuppo
  private:
   nsRefPtr<PeerConnectionImpl> pc_;
   nsRefPtr<TestObserver> observer_;
 };
 
 NS_IMPL_ISUPPORTS(PCDispatchWrapper, nsISupportsWeakReference)
 
 
+struct Msid
+{
+  std::string streamId;
+  std::string trackId;
+  bool operator<(const Msid& other) const {
+    if (streamId < other.streamId) {
+      return true;
+    }
+
+    if (streamId > other.streamId) {
+      return false;
+    }
+
+    return trackId < other.trackId;
+  }
+};
+
 class SignalingAgent {
  public:
   explicit SignalingAgent(const std::string &aName,
     const std::string stun_addr = g_stun_server_address,
     uint16_t stun_port = g_stun_server_port) :
     pc(nullptr),
     name(aName),
-    mBundleEnabled(true) {
+    mBundleEnabled(true),
+    mExpectedFrameRequestType(VideoSessionConduit::FrameRequestPli),
+    mExpectNack(true),
+    mExpectRtcpMuxAudio(true),
+    mExpectRtcpMuxVideo(true),
+    mRemoteDescriptionSet(false) {
     cfg_.addStunServer(stun_addr, stun_port);
 
     PeerConnectionImpl *pcImpl =
       PeerConnectionImpl::CreatePeerConnection();
     EXPECT_TRUE(pcImpl);
     pcImpl->SetAllowIceLoopback(true);
     pc = new PCDispatchWrapper(pcImpl);
   }
@@ -963,16 +940,21 @@ class SignalingAgent {
       WrapRunnable(this, &SignalingAgent::Init_m));
   }
 
   void SetBundleEnabled(bool enabled)
   {
     mBundleEnabled = enabled;
   }
 
+  void SetExpectedFrameRequestType(VideoSessionConduit::FrameRequestType type)
+  {
+    mExpectedFrameRequestType = type;
+  }
+
   void WaitForGather() {
     ASSERT_TRUE_WAIT(ice_gathering_state() == PCImplIceGatheringState::Complete,
                      kDefaultTimeout);
 
     std::cout << name << ": Init Complete" << std::endl;
 
     // Check that the default candidate has been filled out with something
     std::string localSdp = getLocalDescription();
@@ -1072,34 +1054,193 @@ class SignalingAgent {
   }
 
   // Adds a stream to the PeerConnection.
   void AddStream(uint32_t hint =
          DOMMediaStream::HINT_CONTENTS_AUDIO |
          DOMMediaStream::HINT_CONTENTS_VIDEO,
        MediaStream *stream = nullptr) {
 
+    if (!stream && (hint & DOMMediaStream::HINT_CONTENTS_AUDIO)) {
+      // Useful default
+      // 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(),
+        WrapRunnableRet(audio_stream, &Fake_MediaStream::Start, &ret));
+
+      ASSERT_TRUE(NS_SUCCEEDED(ret));
+      stream = audio_stream;
+    }
+
     nsRefPtr<DOMMediaStream> domMediaStream = new DOMMediaStream(stream);
     domMediaStream->SetHintContents(hint);
 
     nsTArray<nsRefPtr<MediaStreamTrack>> tracks;
     domMediaStream->GetTracks(tracks);
     for (uint32_t i = 0; i < tracks.Length(); i++) {
+      Msid msid = {domMediaStream->GetId(), tracks[i]->GetId()};
+
+      ASSERT_FALSE(mAddedTracks.count(msid))
+        << msid.streamId << "/" << msid.trackId << " already added";
+
+      mAddedTracks[msid] = (tracks[i]->AsVideoStreamTrack() ?
+                            SdpMediaSection::kVideo :
+                            SdpMediaSection::kAudio);
+
       ASSERT_EQ(pc->AddTrack(tracks[i], domMediaStream), NS_OK);
     }
     domMediaStreams_.push_back(domMediaStream);
   }
 
+  // I would love to make this an overload of operator<<, but there's no way to
+  // declare it in a way that works with gtest's header files.
+  std::string DumpTracks(
+      const std::map<Msid, SdpMediaSection::MediaType>& tracks) const
+  {
+    std::ostringstream oss;
+    for (auto it = tracks.begin(); it != tracks.end(); ++it) {
+      oss << it->first.streamId << "/" << it->first.trackId
+          << " (" << it->second << ")" << std::endl;
+    }
+
+    return oss.str();
+  }
+
+  void ExpectMissingTracks(SdpMediaSection::MediaType type)
+  {
+    for (auto it = mAddedTracks.begin(); it != mAddedTracks.end();) {
+      if (it->second == type) {
+        auto temp = it;
+        ++it;
+        mAddedTracks.erase(temp);
+      } else {
+        ++it;
+      }
+    }
+  }
+
+  void CheckLocalPipeline(const std::string& streamId,
+                          const std::string& trackId,
+                          SdpMediaSection::MediaType type,
+                          int pipelineCheckFlags = 0) const
+  {
+    LocalSourceStreamInfo* info;
+    mozilla::SyncRunnable::DispatchToThread(
+      gMainThread, WrapRunnableRet(
+        pc->media(), &PeerConnectionMedia::GetLocalStreamById,
+        streamId, &info));
+
+    ASSERT_TRUE(info) << "No such local stream id: " << streamId;
+
+    RefPtr<MediaPipeline> pipeline;
+
+    mozilla::SyncRunnable::DispatchToThread(
+        gMainThread,
+        WrapRunnableRet(info,
+                        &SourceStreamInfo::GetPipelineByTrackId_m,
+                        trackId,
+                        &pipeline));
+
+    ASSERT_TRUE(pipeline) << "No such local track id: " << trackId;
+
+    if (type == SdpMediaSection::kVideo) {
+      ASSERT_TRUE(pipeline->IsVideo()) << "Local track " << trackId
+                                       << " was not video";
+      ASSERT_EQ(mExpectRtcpMuxVideo, pipeline->IsDoingRtcpMux())
+        << "Pipeline for remote track " << trackId
+        << " is" << (mExpectRtcpMuxVideo ? " not " : " ") << "using rtcp-mux";
+      // No checking for video RTP yet, since we don't have support for fake
+      // video here yet. (bug 1142320)
+    } else {
+      ASSERT_FALSE(pipeline->IsVideo()) << "Local track " << trackId
+                                        << " was not audio";
+      WAIT(pipeline->rtp_packets_sent() >= 4 &&
+           pipeline->rtcp_packets_received() >= 1,
+           kDefaultTimeout);
+      ASSERT_LE(4, pipeline->rtp_packets_sent())
+        << "Local track " << trackId << " isn't sending RTP";
+      ASSERT_LE(1, pipeline->rtcp_packets_received())
+        << "Local track " << trackId << " isn't receiving RTCP";
+      ASSERT_EQ(mExpectRtcpMuxAudio, pipeline->IsDoingRtcpMux())
+        << "Pipeline for remote track " << trackId
+        << " is" << (mExpectRtcpMuxAudio ? " not " : " ") << "using rtcp-mux";
+    }
+  }
+
+  void CheckRemotePipeline(const std::string& streamId,
+                           const std::string& trackId,
+                           SdpMediaSection::MediaType type,
+                           int pipelineCheckFlags = 0) const
+  {
+    RemoteSourceStreamInfo* info;
+    mozilla::SyncRunnable::DispatchToThread(
+      gMainThread, WrapRunnableRet(
+        pc->media(), &PeerConnectionMedia::GetRemoteStreamById,
+        streamId, &info));
+
+    ASSERT_TRUE(info) << "No such remote stream id: " << streamId;
+
+    RefPtr<MediaPipeline> pipeline;
+
+    mozilla::SyncRunnable::DispatchToThread(
+        gMainThread,
+        WrapRunnableRet(info,
+                        &SourceStreamInfo::GetPipelineByTrackId_m,
+                        trackId,
+                        &pipeline));
+
+    ASSERT_TRUE(pipeline) << "No such remote track id: " << trackId;
+
+    if (type == SdpMediaSection::kVideo) {
+      ASSERT_TRUE(pipeline->IsVideo()) << "Remote track " << trackId
+                                       << " was not video";
+      mozilla::MediaSessionConduit *conduit = pipeline->Conduit();
+      ASSERT_TRUE(conduit);
+      ASSERT_EQ(conduit->type(), mozilla::MediaSessionConduit::VIDEO);
+      mozilla::VideoSessionConduit *video_conduit =
+        static_cast<mozilla::VideoSessionConduit*>(conduit);
+      ASSERT_EQ(mExpectNack, video_conduit->UsingNackBasic());
+      ASSERT_EQ(mExpectedFrameRequestType,
+                video_conduit->FrameRequestMethod());
+      ASSERT_EQ(mExpectRtcpMuxVideo, pipeline->IsDoingRtcpMux())
+        << "Pipeline for remote track " << trackId
+        << " is" << (mExpectRtcpMuxVideo ? " not " : " ") << "using rtcp-mux";
+      // No checking for video RTP yet, since we don't have support for fake
+      // video here yet. (bug 1142320)
+    } else {
+      ASSERT_FALSE(pipeline->IsVideo()) << "Remote track " << trackId
+                                        << " was not audio";
+      WAIT(pipeline->rtp_packets_received() >= 4 &&
+           pipeline->rtcp_packets_sent() >= 1,
+           kDefaultTimeout);
+      ASSERT_LE(4, pipeline->rtp_packets_received())
+        << "Remote track " << trackId << " isn't receiving RTP";
+      ASSERT_LE(1, pipeline->rtcp_packets_sent())
+        << "Remote track " << trackId << " isn't sending RTCP";
+      ASSERT_EQ(mExpectRtcpMuxAudio, pipeline->IsDoingRtcpMux())
+        << "Pipeline for remote track " << trackId
+        << " is" << (mExpectRtcpMuxAudio ? " not " : " ") << "using rtcp-mux";
+    }
+  }
+
   void RemoveTrack(size_t streamIndex, bool videoTrack = false)
   {
     ASSERT_LT(streamIndex, domMediaStreams_.size());
     nsTArray<nsRefPtr<MediaStreamTrack>> tracks;
     domMediaStreams_[streamIndex]->GetTracks(tracks);
     for (size_t i = 0; i < tracks.Length(); ++i) {
       if (!!tracks[i]->AsVideoStreamTrack() == videoTrack) {
+        Msid msid;
+        msid.streamId = domMediaStreams_[streamIndex]->GetId();
+        msid.trackId = tracks[i]->GetId();
+        mAddedTracks.erase(msid);
         ASSERT_EQ(pc->RemoveTrack(tracks[i]), NS_OK);
       }
     }
   }
 
   void RemoveStream(size_t index) {
     nsTArray<nsRefPtr<MediaStreamTrack>> tracks;
     domMediaStreams_[index]->GetTracks(tracks);
@@ -1111,67 +1252,51 @@ class SignalingAgent {
 
   // Removes the stream that was most recently added to the PeerConnection.
   void RemoveLastStreamAdded() {
     ASSERT_FALSE(domMediaStreams_.empty());
     RemoveStream(domMediaStreams_.size() - 1);
   }
 
   void CreateOffer(OfferOptions& options,
-                   uint32_t offerFlags, uint32_t sdpCheck,
+                   uint32_t offerFlags,
                    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(),
-      WrapRunnableRet(audio_stream, &Fake_MediaStream::Start, &ret));
-
-    ASSERT_TRUE(NS_SUCCEEDED(ret));
-
     uint32_t aHintContents = 0;
     if (offerFlags & OFFER_AUDIO) {
       aHintContents |= DOMMediaStream::HINT_CONTENTS_AUDIO;
     }
     if (offerFlags & OFFER_VIDEO) {
       aHintContents |= DOMMediaStream::HINT_CONTENTS_VIDEO;
     }
-    AddStream(aHintContents, audio_stream);
+    AddStream(aHintContents);
 
     // Now call CreateOffer as JS would
     pObserver->state = TestObserver::stateNoResponse;
     ASSERT_EQ(pc->CreateOffer(options), NS_OK);
 
     ASSERT_EQ(pObserver->state, TestObserver::stateSuccess);
-    SDPSanityCheck(pObserver->lastString, sdpCheck, true);
     ASSERT_EQ(signaling_state(), endState);
     offer_ = pObserver->lastString;
     if (!mBundleEnabled) {
       offer_ = RemoveBundle(offer_);
     }
   }
 
   // sets the offer to match the local description
   // which isn't good if you are the answerer
-  void UpdateOffer(uint32_t sdpCheck) {
+  void UpdateOffer() {
     offer_ = getLocalDescription();
-    SDPSanityCheck(offer_, sdpCheck, true);
     if (!mBundleEnabled) {
       offer_ = RemoveBundle(offer_);
     }
   }
 
   void CreateAnswer(uint32_t offerAnswerFlags,
-                    uint32_t sdpCheck = DONT_CHECK_AUDIO|
-                                        DONT_CHECK_VIDEO|
-                                        DONT_CHECK_DATA,
                     PCImplSignalingState endState =
                     PCImplSignalingState::SignalingHaveRemoteOffer) {
     // Create a media stream as if it came from GUM
     Fake_AudioStreamSource *audio_stream =
       new Fake_AudioStreamSource();
 
     nsresult ret;
     mozilla::SyncRunnable::DispatchToThread(
@@ -1189,46 +1314,41 @@ class SignalingAgent {
     }
     AddStream(aHintContents, audio_stream);
 
     // Decide if streams are disabled for offer or answer
     // then perform SDP checking based on which stream disabled
     pObserver->state = TestObserver::stateNoResponse;
     ASSERT_EQ(pc->CreateAnswer(), NS_OK);
     ASSERT_EQ(pObserver->state, TestObserver::stateSuccess);
-    SDPSanityCheck(pObserver->lastString, sdpCheck, false);
     ASSERT_EQ(signaling_state(), endState);
 
     answer_ = pObserver->lastString;
     if (!mBundleEnabled) {
       answer_ = RemoveBundle(answer_);
     }
   }
 
   // sets the answer to match the local description
   // which isn't good if you are the offerer
-  void UpdateAnswer(uint32_t sdpCheck) {
+  void UpdateAnswer() {
     answer_ = getLocalDescription();
-    SDPSanityCheck(answer_, sdpCheck, false);
     if (!mBundleEnabled) {
       answer_ = RemoveBundle(answer_);
     }
   }
 
-  void CreateOfferRemoveTrack(OfferOptions& options,
-                              bool videoTrack,
-                              uint32_t sdpCheck) {
+  void CreateOfferRemoveTrack(OfferOptions& options, bool videoTrack) {
 
     RemoveTrack(0, videoTrack);
 
     // Now call CreateOffer as JS would
     pObserver->state = TestObserver::stateNoResponse;
     ASSERT_EQ(pc->CreateOffer(options), NS_OK);
     ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess);
-    SDPSanityCheck(pObserver->lastString, sdpCheck, true);
     offer_ = pObserver->lastString;
     if (!mBundleEnabled) {
       offer_ = RemoveBundle(offer_);
     }
   }
 
   void SetRemote(TestObserver::Action action, const std::string& remote,
                  bool ignoreError = false,
@@ -1243,16 +1363,17 @@ class SignalingAgent {
 
     pObserver->state = TestObserver::stateNoResponse;
     ASSERT_EQ(pc->SetRemoteDescription(action, remote.c_str()), NS_OK);
     ASSERT_EQ(signaling_state(), endState);
     if (!ignoreError) {
       ASSERT_EQ(pObserver->state, TestObserver::stateSuccess);
     }
 
+    mRemoteDescriptionSet = true;
     for (auto i = deferredCandidates_.begin();
          i != deferredCandidates_.end();
          ++i) {
       AddIceCandidate(i->candidate.c_str(),
                       i->mid.c_str(),
                       i->level,
                       i->expectSuccess);
     }
@@ -1284,17 +1405,17 @@ class SignalingAgent {
   } TrickleEncoding;
 
   bool IceCompleted() {
     return pc->IceConnectionState() == PCImplIceConnectionState::Connected;
   }
 
   void AddIceCandidateStr(const std::string& candidate, const std::string& mid,
                           unsigned short level) {
-    if (getRemoteDescription().empty()) {
+    if (!mRemoteDescriptionSet) {
       // Not time to add this, because the unit-test code hasn't set the
       // description yet.
       DeferredCandidate candidateStruct = {candidate, mid, level, true};
       deferredCandidates_.push_back(candidateStruct);
     } else {
       AddIceCandidate(candidate, mid, level, true);
     }
   }
@@ -1308,28 +1429,54 @@ class SignalingAgent {
                 expectSuccess ? TestObserver::stateSuccess :
                                 TestObserver::stateError
                );
 
     // Verify that adding ICE candidates does not change the signaling state
     ASSERT_EQ(signaling_state(), endState);
   }
 
-  int GetPacketsReceived(size_t stream) {
+  int GetPacketsReceived(const std::string& streamId) const
+  {
+    std::vector<DOMMediaStream *> streams = pObserver->GetStreams();
+
+    for (size_t i = 0; i < streams.size(); ++i) {
+      if (streams[i]->GetId() == streamId) {
+        return GetPacketsReceived(i);
+      }
+    }
+
+    EXPECT_TRUE(false);
+    return 0;
+  }
+
+  int GetPacketsReceived(size_t stream) const {
     std::vector<DOMMediaStream *> streams = pObserver->GetStreams();
 
     if (streams.size() <= stream) {
       EXPECT_TRUE(false);
       return 0;
     }
 
     return streams[stream]->GetStream()->AsSourceStream()->GetSegmentsAdded();
   }
 
-  int GetPacketsSent(size_t stream) {
+  int GetPacketsSent(const std::string& streamId) const
+  {
+    for (size_t i = 0; i < domMediaStreams_.size(); ++i) {
+      if (domMediaStreams_[i]->GetId() == streamId) {
+        return GetPacketsSent(i);
+      }
+    }
+
+    EXPECT_TRUE(false);
+    return 0;
+  }
+
+  int GetPacketsSent(size_t stream) const {
     if (stream >= domMediaStreams_.size()) {
       EXPECT_TRUE(false);
       return 0;
     }
 
     return static_cast<Fake_MediaStreamBase *>(
         domMediaStreams_[stream]->GetStream())->GetSegmentsAdded();
   }
@@ -1383,260 +1530,45 @@ class SignalingAgent {
       if (i->second->IsVideo() == video) {
         std::cout << "Got MediaPipeline " << i->second->trackid();
         return i->second;
       }
     }
     return nullptr;
   }
 
-  void CheckMediaPipeline(int stream, bool video, uint32_t flags,
-    VideoSessionConduit::FrameRequestType frameRequestMethod =
-      VideoSessionConduit::FrameRequestNone) {
-
-    std::cout << name << ": Checking media pipeline settings for "
-              << ((flags & PIPELINE_SEND) ? "sending " : "receiving ")
-              << ((flags & PIPELINE_VIDEO) ? "video" : "audio")
-              << " pipeline (stream " << stream
-              << ", track " << video << "); expect "
-              << ((flags & PIPELINE_RTCP_MUX) ? "MUX, " : "no MUX, ")
-              << ((flags & PIPELINE_RTCP_NACK) ? "NACK." : "no NACK.")
-              << std::endl;
-
-    mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
-      GetMediaPipeline((flags & PIPELINE_SEND), stream, video);
-    ASSERT_TRUE(pipeline);
-    ASSERT_EQ(pipeline->IsDoingRtcpMux(), !!(flags & PIPELINE_RTCP_MUX));
-    // We cannot yet test send/recv with video.
-    if (!(flags & PIPELINE_VIDEO)) {
-      if (flags & PIPELINE_SEND) {
-        ASSERT_TRUE_WAIT(pipeline->rtp_packets_sent() >= 40 &&
-                         pipeline->rtcp_packets_received() >= 1,
-                         kDefaultTimeout);
-        ASSERT_GE(pipeline->rtp_packets_sent(), 40);
-        ASSERT_GE(pipeline->rtcp_packets_received(), 1);
-      } else {
-        ASSERT_TRUE_WAIT(pipeline->rtp_packets_received() >= 40 &&
-                         pipeline->rtcp_packets_sent() >= 1,
-                         kDefaultTimeout);
-        ASSERT_GE(pipeline->rtp_packets_received(), 40);
-        ASSERT_GE(pipeline->rtcp_packets_sent(), 1);
-      }
-    }
-
-
-    // Check feedback method for video
-    if ((flags & PIPELINE_VIDEO) && !(flags & PIPELINE_SEND)) {
-        mozilla::MediaSessionConduit *conduit = pipeline->Conduit();
-        ASSERT_TRUE(conduit);
-        ASSERT_EQ(conduit->type(), mozilla::MediaSessionConduit::VIDEO);
-        mozilla::VideoSessionConduit *video_conduit =
-          static_cast<mozilla::VideoSessionConduit*>(conduit);
-        ASSERT_EQ(!!(flags & PIPELINE_RTCP_NACK),
-                  video_conduit->UsingNackBasic());
-        ASSERT_EQ(frameRequestMethod, video_conduit->FrameRequestMethod());
-    }
-  }
-
   void SetPeer(SignalingAgent* peer) {
     pObserver->peerAgent = peer;
   }
 
 public:
   nsRefPtr<PCDispatchWrapper> pc;
   nsRefPtr<TestObserver> pObserver;
   std::string offer_;
   std::string answer_;
   std::vector<nsRefPtr<DOMMediaStream>> domMediaStreams_;
   IceConfiguration cfg_;
   const std::string name;
   bool mBundleEnabled;
+  VideoSessionConduit::FrameRequestType mExpectedFrameRequestType;
+  bool mExpectNack;
+  bool mExpectRtcpMuxAudio;
+  bool mExpectRtcpMuxVideo;
+  bool mRemoteDescriptionSet;
+
+  std::map<Msid, SdpMediaSection::MediaType> mAddedTracks;
 
   typedef struct {
     std::string candidate;
     std::string mid;
     uint16_t level;
     bool expectSuccess;
   } DeferredCandidate;
 
   std::list<DeferredCandidate> deferredCandidates_;
-
-private:
-  void SDPSanityCheck(const std::string& sdp, uint32_t flags, bool offer)
-  {
-    ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess);
-    ASSERT_NE(sdp.find("v=0"), std::string::npos);
-    ASSERT_NE(sdp.find("c=IN IP4"), std::string::npos);
-    ASSERT_NE(sdp.find("a=fingerprint:sha-256"), std::string::npos);
-
-    std::cout << name << ": SDPSanityCheck flags for "
-              << (offer ? "offer" : "answer")
-              << " = " << std::hex << std::showbase
-              << flags << std::dec
-
-              << ((flags & SHOULD_SEND_AUDIO)?" SHOULD_SEND_AUDIO":"")
-              << ((flags & SHOULD_RECV_AUDIO)?" SHOULD_RECV_AUDIO":"")
-              << ((flags & SHOULD_INACTIVE_AUDIO)?" SHOULD_INACTIVE_AUDIO":"")
-              << ((flags & SHOULD_REJECT_AUDIO)?" SHOULD_REJECT_AUDIO":"")
-              << ((flags & SHOULD_OMIT_AUDIO)?" SHOULD_OMIT_AUDIO":"")
-              << ((flags & DONT_CHECK_AUDIO)?" DONT_CHECK_AUDIO":"")
-
-              << ((flags & SHOULD_SEND_VIDEO)?" SHOULD_SEND_VIDEO":"")
-              << ((flags & SHOULD_RECV_VIDEO)?" SHOULD_RECV_VIDEO":"")
-              << ((flags & SHOULD_INACTIVE_VIDEO)?" SHOULD_INACTIVE_VIDEO":"")