Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 30 Dec 2016 16:56:03 -0800
changeset 327690 81463aec62d02fa1915e03cda7a8ddc8d44c71fc
parent 327665 a06f92099a5d8edeb05e5971967fe8d6cd4c593c (current diff)
parent 327689 933325344e21a683e2a08f01fd7892ec835ea992 (diff)
child 327691 5507e2461786d83e213a6c531a96595e73b69b86
child 327725 37f96302e7ac1b72ce74a89ba6044a11d16d6602
child 327737 52323d42c400aae39c84fa94ad8f18d4bac0807f
push id31142
push userkwierso@gmail.com
push dateSat, 31 Dec 2016 00:56:10 +0000
treeherdermozilla-central@81463aec62d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly linux32
81463aec62d0 / 53.0a1 / 20161231030203 / files
nightly linux64
81463aec62d0 / 53.0a1 / 20161231030203 / files
nightly mac
81463aec62d0 / 53.0a1 / 20161231030203 / files
nightly win32
81463aec62d0 / 53.0a1 / 20161231030203 / files
nightly win64
81463aec62d0 / 53.0a1 / 20161231030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge MozReview-Commit-ID: KIm8XUkfyZW
tools/profiler/tests/test_saved_stacks.js
--- a/browser/base/content/test/general/browser_parsable_css.js
+++ b/browser/base/content/test/general/browser_parsable_css.js
@@ -15,16 +15,20 @@ let whitelist = [
    isFromDevTools: true},
   // The debugger uses cross-browser CSS.
   {sourceName: /devtools\/client\/debugger\/new\/styles.css/i,
    isFromDevTools: true},
   // PDFjs is futureproofing its pseudoselectors, and those rules are dropped.
   {sourceName: /web\/viewer\.css$/i,
    errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i,
    isFromDevTools: false},
+  // PDFjs rules needed for compat with other UAs.
+  {sourceName: /web\/viewer\.css$/i,
+   errorMessage: /Unknown property.*appearance/i,
+   isFromDevTools: false},
   // Tracked in bug 1004428.
   {sourceName: /aboutaccounts\/(main|normalize)\.css$/i,
    isFromDevTools: false},
   // Highlighter CSS uses a UA-only pseudo-class, see bug 985597.
   {sourceName: /highlighters\.css$/i,
    errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i,
    isFromDevTools: true},
   // Responsive Design Mode CSS uses a UA-only pseudo-class, see Bug 1241714.
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.6.401
+Current extension version is: 1.6.418
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -7,17 +7,16 @@
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* jshint esnext:true */
 /* globals Components, Services, XPCOMUtils, PdfjsChromeUtils,
            PdfjsContentUtils, PdfStreamConverter */
 
 'use strict';
 
 var EXPORTED_SYMBOLS = ['PdfJs'];
 
 const Cc = Components.classes;
--- a/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm
+++ b/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm
@@ -7,17 +7,17 @@
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* jshint esnext:true, maxlen: 100 */
+/* eslint max-len: ["error", 100] */
 /* globals Components, Services */
 
 'use strict';
 
 this.EXPORTED_SYMBOLS = ['PdfJsTelemetry'];
 
 const Cu = Components.utils;
 Cu.import('resource://gre/modules/Services.jsm');
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -7,17 +7,16 @@
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* jshint esnext:true */
 /* globals Components, Services, XPCOMUtils, NetUtil, PrivateBrowsingUtils,
            dump, NetworkManager, PdfJsTelemetry, PdfjsContentUtils */
 
 'use strict';
 
 var EXPORTED_SYMBOLS = ['PdfStreamConverter'];
 
 const Cc = Components.classes;
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -7,17 +7,16 @@
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* jshint esnext:true */
 /* globals Components, Services, XPCOMUtils */
 
 'use strict';
 
 var EXPORTED_SYMBOLS = ['PdfjsChromeUtils'];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
--- a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm
@@ -7,17 +7,16 @@
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* jshint esnext:true */
 /* globals Components, Services, XPCOMUtils */
 
 'use strict';
 
 var EXPORTED_SYMBOLS = ['PdfjsContentUtils'];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -18,18 +18,18 @@
   define('pdfjs-dist/build/pdf', ['exports'], factory);
  } else if (typeof exports !== 'undefined') {
   factory(exports);
  } else {
   factory(root['pdfjsDistBuildPdf'] = {});
  }
 }(this, function (exports) {
  'use strict';
- var pdfjsVersion = '1.6.401';
- var pdfjsBuild = 'b629be05';
+ var pdfjsVersion = '1.6.418';
+ var pdfjsBuild = '59afb4b9';
  var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
  var pdfjsLibs = {};
  (function pdfjsWrapper() {
   (function (root, factory) {
    factory(root.pdfjsSharedUtil = {});
   }(this, function (exports) {
    var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
    var FONT_IDENTITY_MATRIX = [
@@ -1803,16 +1803,25 @@
       return new LinkAnnotationElement(parameters);
      case AnnotationType.TEXT:
       return new TextAnnotationElement(parameters);
      case AnnotationType.WIDGET:
       var fieldType = parameters.data.fieldType;
       switch (fieldType) {
       case 'Tx':
        return new TextWidgetAnnotationElement(parameters);
+      case 'Btn':
+       if (parameters.data.radioButton) {
+        return new RadioButtonWidgetAnnotationElement(parameters);
+       } else if (parameters.data.checkBox) {
+        return new CheckboxWidgetAnnotationElement(parameters);
+       } else {
+        warn('Unimplemented button widget annotation: pushbutton');
+       }
+       break;
       case 'Ch':
        return new ChoiceWidgetAnnotationElement(parameters);
       }
       return new WidgetAnnotationElement(parameters);
      case AnnotationType.POPUP:
       return new PopupAnnotationElement(parameters);
      case AnnotationType.HIGHLIGHT:
       return new HighlightAnnotationElement(parameters);
@@ -2070,16 +2079,55 @@
       style.fontStyle = font.italic ? 'italic' : 'normal';
       var fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : '';
       var fallbackName = font.fallbackName || 'Helvetica, sans-serif';
       style.fontFamily = fontFamily + fallbackName;
      }
     });
     return TextWidgetAnnotationElement;
    }();
+   var CheckboxWidgetAnnotationElement = function CheckboxWidgetAnnotationElementClosure() {
+    function CheckboxWidgetAnnotationElement(parameters) {
+     WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms);
+    }
+    Util.inherit(CheckboxWidgetAnnotationElement, WidgetAnnotationElement, {
+     render: function CheckboxWidgetAnnotationElement_render() {
+      this.container.className = 'buttonWidgetAnnotation checkBox';
+      var element = document.createElement('input');
+      element.disabled = this.data.readOnly;
+      element.type = 'checkbox';
+      if (this.data.fieldValue && this.data.fieldValue !== 'Off') {
+       element.setAttribute('checked', true);
+      }
+      this.container.appendChild(element);
+      return this.container;
+     }
+    });
+    return CheckboxWidgetAnnotationElement;
+   }();
+   var RadioButtonWidgetAnnotationElement = function RadioButtonWidgetAnnotationElementClosure() {
+    function RadioButtonWidgetAnnotationElement(parameters) {
+     WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms);
+    }
+    Util.inherit(RadioButtonWidgetAnnotationElement, WidgetAnnotationElement, {
+     render: function RadioButtonWidgetAnnotationElement_render() {
+      this.container.className = 'buttonWidgetAnnotation radioButton';
+      var element = document.createElement('input');
+      element.disabled = this.data.readOnly;
+      element.type = 'radio';
+      element.name = this.data.fieldName;
+      if (this.data.fieldValue === this.data.buttonValue) {
+       element.setAttribute('checked', true);
+      }
+      this.container.appendChild(element);
+      return this.container;
+     }
+    });
+    return RadioButtonWidgetAnnotationElement;
+   }();
    var ChoiceWidgetAnnotationElement = function ChoiceWidgetAnnotationElementClosure() {
     function ChoiceWidgetAnnotationElement(parameters) {
      WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms);
     }
     Util.inherit(ChoiceWidgetAnnotationElement, WidgetAnnotationElement, {
      render: function ChoiceWidgetAnnotationElement_render() {
       this.container.className = 'choiceWidgetAnnotation';
       var selectElement = document.createElement('select');
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -18,18 +18,18 @@
   define('pdfjs-dist/build/pdf.worker', ['exports'], factory);
  } else if (typeof exports !== 'undefined') {
   factory(exports);
  } else {
   factory(root['pdfjsDistBuildPdfWorker'] = {});
  }
 }(this, function (exports) {
  'use strict';
- var pdfjsVersion = '1.6.401';
- var pdfjsBuild = 'b629be05';
+ var pdfjsVersion = '1.6.418';
+ var pdfjsBuild = '59afb4b9';
  var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
  var pdfjsLibs = {};
  (function pdfjsWrapper() {
   (function (root, factory) {
    factory(root.pdfjsCoreArithmeticDecoder = {});
   }(this, function (exports) {
    var ArithmeticDecoder = function ArithmeticDecoderClosure() {
     var QeTable = [
@@ -45240,20 +45240,20 @@
          ty = w1 * textState.fontSize + charSpacing;
          height += ty;
         }
         textState.translateTextMatrix(tx, ty);
         textChunk.str.push(glyphUnicode);
        }
        if (!font.vertical) {
         textChunk.lastAdvanceWidth = width;
-        textChunk.width += width * textChunk.textAdvanceScale;
+        textChunk.width += width;
        } else {
         textChunk.lastAdvanceHeight = height;
-        textChunk.height += Math.abs(height * textChunk.textAdvanceScale);
+        textChunk.height += Math.abs(height);
        }
        return textChunk;
       }
       function addFakeSpaces(width, strBuf) {
        if (width < textContentItem.fakeSpaceMin) {
         return;
        }
        if (width < textContentItem.fakeMultiSpaceMin) {
@@ -45264,16 +45264,18 @@
        while (fakeSpaces-- > 0) {
         strBuf.push(' ');
        }
       }
       function flushTextContentItem() {
        if (!textContentItem.initialized) {
         return;
        }
+       textContentItem.width *= textContentItem.textAdvanceScale;
+       textContentItem.height *= textContentItem.textAdvanceScale;
        textContent.items.push(runBidiTransform(textContentItem));
        textContentItem.initialized = false;
        textContentItem.str.length = 0;
       }
       var timeSlotManager = new TimeSlotManager();
       return new Promise(function promiseBody(resolve, reject) {
        var next = function (promise) {
         promise.then(function () {
@@ -45376,26 +45378,26 @@
          for (var j = 0, jj = items.length; j < jj; j++) {
           if (typeof items[j] === 'string') {
            buildTextContentItem(items[j]);
           } else if (isNum(items[j])) {
            ensureTextContentItem();
            advance = items[j] * textState.fontSize / 1000;
            var breakTextRun = false;
            if (textState.font.vertical) {
-            offset = advance * (textState.textHScale * textState.textMatrix[2] + textState.textMatrix[3]);
-            textState.translateTextMatrix(0, advance);
+            offset = advance;
+            textState.translateTextMatrix(0, offset);
             breakTextRun = textContentItem.textRunBreakAllowed && advance > textContentItem.fakeMultiSpaceMax;
             if (!breakTextRun) {
              textContentItem.height += offset;
             }
            } else {
             advance = -advance;
-            offset = advance * (textState.textHScale * textState.textMatrix[0] + textState.textMatrix[1]);
-            textState.translateTextMatrix(advance, 0);
+            offset = advance * textState.textHScale;
+            textState.translateTextMatrix(offset, 0);
             breakTextRun = textContentItem.textRunBreakAllowed && advance > textContentItem.fakeMultiSpaceMax;
             if (!breakTextRun) {
              textContentItem.width += offset;
             }
            }
            if (breakTextRun) {
             flushTextContentItem();
            } else if (advance > 0) {
@@ -45879,17 +45881,26 @@
         var keys = encoding.getKeys();
         for (var i = 0, ii = keys.length; i < ii; i++) {
          var entry = encoding.getRaw(keys[i]);
          if (isName(entry)) {
           hash.update(entry.name);
          } else if (isRef(entry)) {
           hash.update(entry.toString());
          } else if (isArray(entry)) {
-          hash.update(entry.length.toString());
+          var diffLength = entry.length, diffBuf = new Array(diffLength);
+          for (var j = 0; j < diffLength; j++) {
+           var diffEntry = entry[j];
+           if (isName(diffEntry)) {
+            diffBuf[j] = diffEntry.name;
+           } else if (isNum(diffEntry) || isRef(diffEntry)) {
+            diffBuf[j] = diffEntry.toString();
+           }
+          }
+          hash.update(diffBuf.join());
          }
         }
        }
        var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode');
        if (isStream(toUnicode)) {
         var stream = toUnicode.str || toUnicode;
         uint8array = stream.buffer ? new Uint8Array(stream.buffer.buffer, 0, stream.bufferLength) : new Uint8Array(stream.bytes.buffer, stream.start, stream.end - stream.start);
         hash.update(uint8array);
@@ -47155,16 +47166,18 @@
      case 'Text':
       return new TextAnnotation(parameters);
      case 'Widget':
       var fieldType = Util.getInheritableProperty(dict, 'FT');
       fieldType = isName(fieldType) ? fieldType.name : null;
       switch (fieldType) {
       case 'Tx':
        return new TextWidgetAnnotation(parameters);
+      case 'Btn':
+       return new ButtonWidgetAnnotation(parameters);
       case 'Ch':
        return new ChoiceWidgetAnnotation(parameters);
       }
       warn('Unimplemented widget field type "' + fieldType + '", ' + 'falling back to base field type.');
       return new WidgetAnnotation(parameters);
      case 'Popup':
       return new PopupAnnotation(parameters);
      case 'Highlight':
@@ -47582,27 +47595,82 @@
       var stream = new Stream(stringToBytes(this.data.defaultAppearance));
       return evaluator.getOperatorList(stream, task, this.fieldResources, operatorList).then(function () {
        return operatorList;
       });
      }
     });
     return TextWidgetAnnotation;
    }();
+   var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() {
+    function ButtonWidgetAnnotation(params) {
+     WidgetAnnotation.call(this, params);
+     this.data.checkBox = !this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);
+     if (this.data.checkBox) {
+      if (!isName(this.data.fieldValue)) {
+       return;
+      }
+      this.data.fieldValue = this.data.fieldValue.name;
+     }
+     this.data.radioButton = this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);
+     if (this.data.radioButton) {
+      this.data.fieldValue = this.data.buttonValue = null;
+      var fieldParent = params.dict.get('Parent');
+      if (!isDict(fieldParent) || !fieldParent.has('V')) {
+       return;
+      }
+      var fieldParentValue = fieldParent.get('V');
+      if (!isName(fieldParentValue)) {
+       return;
+      }
+      this.data.fieldValue = fieldParentValue.name;
+      var appearanceStates = params.dict.get('AP');
+      if (!isDict(appearanceStates)) {
+       return;
+      }
+      var normalAppearanceState = appearanceStates.get('N');
+      if (!isDict(normalAppearanceState)) {
+       return;
+      }
+      var keys = normalAppearanceState.getKeys();
+      for (var i = 0, ii = keys.length; i < ii; i++) {
+       if (keys[i] !== 'Off') {
+        this.data.buttonValue = keys[i];
+        break;
+       }
+      }
+     }
+    }
+    Util.inherit(ButtonWidgetAnnotation, WidgetAnnotation, {
+     getOperatorList: function ButtonWidgetAnnotation_getOperatorList(evaluator, task, renderForms) {
+      var operatorList = new OperatorList();
+      if (renderForms) {
+       return Promise.resolve(operatorList);
+      }
+      if (this.appearance) {
+       return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms);
+      }
+      return Promise.resolve(operatorList);
+     }
+    });
+    return ButtonWidgetAnnotation;
+   }();
    var ChoiceWidgetAnnotation = function ChoiceWidgetAnnotationClosure() {
     function ChoiceWidgetAnnotation(params) {
      WidgetAnnotation.call(this, params);
      this.data.options = [];
-     var options = params.dict.getArray('Opt');
+     var options = params.dict.get('Opt');
      if (isArray(options)) {
+      var xref = params.xref;
       for (var i = 0, ii = options.length; i < ii; i++) {
-       var option = options[i];
+       var option = xref.fetchIfRef(options[i]);
+       var isOptionArray = isArray(option);
        this.data.options[i] = {
-        exportValue: isArray(option) ? option[0] : option,
-        displayValue: isArray(option) ? option[1] : option
+        exportValue: isOptionArray ? xref.fetchIfRef(option[0]) : option,
+        displayValue: isOptionArray ? xref.fetchIfRef(option[1]) : option
        };
       }
      }
      if (!isArray(this.data.fieldValue)) {
       this.data.fieldValue = [this.data.fieldValue];
      }
      this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO);
      this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT);
--- a/browser/extensions/pdfjs/content/pdfjschildbootstrap.js
+++ b/browser/extensions/pdfjs/content/pdfjschildbootstrap.js
@@ -7,17 +7,16 @@
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-/* jshint esnext:true */
 /* globals Components, PdfjsContentUtils, PdfJs, Services */
 
 'use strict';
 
 /*
  * pdfjschildbootstrap.js loads into the content process to take care of
  * initializing our built-in version of pdfjs when running remote.
  */
--- a/browser/extensions/pdfjs/content/web/l10n.js
+++ b/browser/extensions/pdfjs/content/web/l10n.js
@@ -1,9 +1,8 @@
-
 'use strict';
 
 // Small subset of the webL10n API by Fabien Cazenave for pdf.js extension.
 (function(window) {
   var gLanguage = '';
   var gExternalLocalizerServices = null;
   var gReadyState = 'loading';
 
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -98,17 +98,19 @@
 
 .annotationLayer .textAnnotation img {
   position: absolute;
   cursor: pointer;
 }
 
 .annotationLayer .textWidgetAnnotation input,
 .annotationLayer .textWidgetAnnotation textarea,
-.annotationLayer .choiceWidgetAnnotation select {
+.annotationLayer .choiceWidgetAnnotation select,
+.annotationLayer .buttonWidgetAnnotation.checkBox input,
+.annotationLayer .buttonWidgetAnnotation.radioButton input {
   background-color: rgba(0, 54, 255, 0.13);
   border: 1px solid transparent;
   box-sizing: border-box;
   font-size: 9px;
   height: 100%;
   padding: 0 3px;
   vertical-align: top;
   width: 100%;
@@ -117,25 +119,29 @@
 .annotationLayer .textWidgetAnnotation textarea {
   font: message-box;
   font-size: 9px;
   resize: none;
 }
 
 .annotationLayer .textWidgetAnnotation input[disabled],
 .annotationLayer .textWidgetAnnotation textarea[disabled],
-.annotationLayer .choiceWidgetAnnotation select[disabled] {
+.annotationLayer .choiceWidgetAnnotation select[disabled],
+.annotationLayer .buttonWidgetAnnotation.checkBox input[disabled],
+.annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] {
   background: none;
   border: 1px solid transparent;
   cursor: not-allowed;
 }
 
 .annotationLayer .textWidgetAnnotation input:hover,
 .annotationLayer .textWidgetAnnotation textarea:hover,
-.annotationLayer .choiceWidgetAnnotation select:hover {
+.annotationLayer .choiceWidgetAnnotation select:hover,
+.annotationLayer .buttonWidgetAnnotation.checkBox input:hover,
+.annotationLayer .buttonWidgetAnnotation.radioButton input:hover {
   border: 1px solid #000;
 }
 
 .annotationLayer .textWidgetAnnotation input:focus,
 .annotationLayer .textWidgetAnnotation textarea:focus,
 .annotationLayer .choiceWidgetAnnotation select:focus {
   background: none;
   border: 1px solid transparent;
@@ -152,16 +158,22 @@
    * Letter spacing is placed on the right side of each character. Hence, the
    * letter spacing of the last character may be placed outside the visible
    * area, causing horizontal scrolling. We avoid this by extending the width
    * when the element has focus and revert this when it loses focus.
    */
   width: 115%;
 }
 
+.annotationLayer .buttonWidgetAnnotation.checkBox input,
+.annotationLayer .buttonWidgetAnnotation.radioButton input {
+  -moz-appearance: none;
+  appearance: none;
+}
+
 .annotationLayer .popupWrapper {
   position: absolute;
   width: 20em;
 }
 
 .annotationLayer .popup {
   position: absolute;
   z-index: 200;
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -4114,17 +4114,17 @@ var pdfjsWebLibs;
        };
       }
       var finishPaintTask = function finishPaintTask(error) {
        if (paintTask === self.paintTask) {
         self.paintTask = null;
        }
        if (error === 'cancelled') {
         self.error = null;
-        return;
+        return Promise.resolve(undefined);
        }
        self.renderingState = RenderingStates.FINISHED;
        if (self.loadingIconDiv) {
         div.removeChild(self.loadingIconDiv);
         delete self.loadingIconDiv;
        }
        if (self.zoomLayer) {
         var zoomLayerCanvas = self.zoomLayer.firstChild;
@@ -4140,31 +4140,35 @@ var pdfjsWebLibs;
        if (self.onAfterDraw) {
         self.onAfterDraw();
        }
        self.eventBus.dispatch('pagerendered', {
         source: self,
         pageNumber: self.id,
         cssTransform: false
        });
+       if (error) {
+        return Promise.reject(error);
+       }
+       return Promise.resolve(undefined);
       };
       var paintTask = this.renderer === RendererType.SVG ? this.paintOnSvg(canvasWrapper) : this.paintOnCanvas(canvasWrapper);
       paintTask.onRenderContinue = renderContinueCallback;
       this.paintTask = paintTask;
       var resultPromise = paintTask.promise.then(function () {
-       finishPaintTask(null);
-       if (textLayer) {
-        pdfPage.getTextContent({ normalizeWhitespace: true }).then(function textContentResolved(textContent) {
-         textLayer.setTextContent(textContent);
-         textLayer.render(TEXT_LAYER_RENDER_DELAY);
-        });
-       }
+       return finishPaintTask(null).then(function () {
+        if (textLayer) {
+         pdfPage.getTextContent({ normalizeWhitespace: true }).then(function textContentResolved(textContent) {
+          textLayer.setTextContent(textContent);
+          textLayer.render(TEXT_LAYER_RENDER_DELAY);
+         });
+        }
+       });
       }, function (reason) {
-       finishPaintTask(reason);
-       throw reason;
+       return finishPaintTask(reason);
       });
       if (this.annotationLayerFactory) {
        if (!this.annotationLayer) {
         this.annotationLayer = this.annotationLayerFactory.createAnnotationLayerBuilder(div, pdfPage, this.renderInteractiveForms);
        }
        this.annotationLayer.render(this.viewport, 'display');
       }
       div.setAttribute('data-loaded', true);
new file mode 100644
--- /dev/null
+++ b/build/build-clang/clang-tidy-linux64.json
@@ -0,0 +1,18 @@
+{
+    "llvm_revision": "290055",
+    "stages": "1",
+    "build_libcxx": true,
+    "build_type": "Release",
+    "assertions": false,
+    "import_clang_tidy": true,
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
+    "python_path": "/usr/bin/python2.7",
+    "gcc_dir": "/home/worker/workspace/build/src/gcc",
+    "cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
+    "cxx": "/home/worker/workspace/build/src/gcc/bin/g++"
+}
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -158,16 +158,18 @@ WebGLContext::InitWebGL2(FailureReason* 
     gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS,
                      &mGLMaxUniformBufferBindings);
 
     mIndexedUniformBufferBindings.resize(mGLMaxUniformBufferBindings);
 
     mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0);
     mBoundTransformFeedback = mDefaultTransformFeedback;
 
+    gl->fGenTransformFeedbacks(1, &mEmptyTFO);
+
     ////
 
     if (!gl->IsGLES()) {
         // Desktop OpenGL requires the following to be enabled in order to
         // support sRGB operations on framebuffers.
         gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT);
     }
 
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -127,17 +127,32 @@ WebGL2Context::GetBufferSubData(GLenum t
     const GLsizeiptr glByteLen(byteLen);
 
     ////
 
     gl->MakeCurrent();
     const ScopedLazyBind readBind(gl, target, buffer);
 
     if (byteLen) {
-        const auto mappedBytes = gl->fMapBufferRange(target, srcByteOffset, glByteLen,
+        const bool isTF = (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER);
+        GLenum mapTarget = target;
+        if (isTF) {
+            gl->fBindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, mEmptyTFO);
+            gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, buffer->mGLName);
+            mapTarget = LOCAL_GL_ARRAY_BUFFER;
+        }
+
+        const auto mappedBytes = gl->fMapBufferRange(mapTarget, srcByteOffset, glByteLen,
                                                      LOCAL_GL_MAP_READ_BIT);
-        // Warning: Possibly shared memory.  See bug 1225033.
         memcpy(bytes, mappedBytes, byteLen);
-        gl->fUnmapBuffer(target);
+        gl->fUnmapBuffer(mapTarget);
+
+        if (isTF) {
+            const GLuint vbo = (mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
+            gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, vbo);
+            const GLuint tfo = (mBoundTransformFeedback ? mBoundTransformFeedback->mGLName
+                                                        : 0);
+            gl->fBindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, tfo);
+        }
     }
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -76,20 +76,28 @@ WebGL2Context::BindTransformFeedback(GLe
         ErrorInvalidOperation("%s: Currently bound transform feedback is active and not"
                               " paused.",
                               funcName);
         return;
     }
 
     ////
 
+    if (mBoundTransformFeedback) {
+        mBoundTransformFeedback->AddBufferBindCounts(-1);
+    }
+
     mBoundTransformFeedback = (tf ? tf : mDefaultTransformFeedback);
 
     MakeContextCurrent();
     gl->fBindTransformFeedback(target, mBoundTransformFeedback->mGLName);
+
+    if (mBoundTransformFeedback) {
+        mBoundTransformFeedback->AddBufferBindCounts(+1);
+    }
 }
 
 void
 WebGL2Context::BeginTransformFeedback(GLenum primMode)
 {
     if (IsContextLost())
         return;
 
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -13,16 +13,18 @@
 namespace mozilla {
 
 WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
     : WebGLRefCountedObject(webgl)
     , mGLName(buf)
     , mContent(Kind::Undefined)
     , mUsage(LOCAL_GL_STATIC_DRAW)
     , mByteLength(0)
+    , mTFBindCount(0)
+    , mNonTFBindCount(0)
 {
     mContext->mBuffers.insertBack(this);
 }
 
 WebGLBuffer::~WebGLBuffer()
 {
     DeleteOnce();
 }
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -21,17 +21,16 @@ class WebGLElementArrayCache;
 class WebGLBuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLBuffer>
     , public LinkedListElement<WebGLBuffer>
 {
     friend class WebGLContext;
     friend class WebGL2Context;
     friend class WebGLTexture;
-    friend class WebGLTransformFeedback;
 
 public:
     enum class Kind {
         Undefined,
         ElementArray,
         OtherData
     };
 
@@ -61,25 +60,56 @@ public:
         return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     bool ValidateCanBindToTarget(const char* funcName, GLenum target);
     void BufferData(GLenum target, size_t size, const void* data, GLenum usage);
 
+    ////
+
+    static void AddBindCount(GLenum target, WebGLBuffer* buffer, int8_t addVal) {
+        if (!buffer)
+            return;
+
+        if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
+            MOZ_ASSERT_IF(addVal < 0, buffer->mTFBindCount >= size_t(-addVal));
+            buffer->mTFBindCount += addVal;
+        } else {
+            MOZ_ASSERT_IF(addVal < 0, buffer->mNonTFBindCount >= size_t(-addVal));
+            buffer->mNonTFBindCount += addVal;
+        }
+    }
+
+    static void SetSlot(GLenum target, WebGLBuffer* newBuffer,
+                        WebGLRefPtr<WebGLBuffer>* const out_slot)
+    {
+        WebGLBuffer* const oldBuffer = *out_slot;
+        AddBindCount(target, oldBuffer, -1);
+        AddBindCount(target, newBuffer, +1);
+        *out_slot = newBuffer;
+    }
+
+    bool IsBoundForTF() const { return bool(mTFBindCount); }
+    bool IsBoundForNonTF() const { return bool(mNonTFBindCount); }
+
+    ////
+
     const GLenum mGLName;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
 
 protected:
     ~WebGLBuffer();
 
     Kind mContent;
     GLenum mUsage;
     size_t mByteLength;
     UniquePtr<WebGLElementArrayCache> mCache;
+    size_t mTFBindCount;
+    size_t mNonTFBindCount;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_BUFFER_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -115,17 +115,17 @@ WebGLContextOptions::WebGLContextOptions
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
     , mBufferFetchingIsVerified(false)
     , mBufferFetchingHasPerVertex(false)
     , mMaxFetchedVertices(0)
     , mMaxFetchedInstances(0)
     , mLayerIsMirror(false)
     , mBypassShaderValidation(false)
-    , mBuffersForUB_Dirty(true)
+    , mEmptyTFO(0)
     , mContextLossHandler(this)
     , mNeedsFakeNoAlpha(false)
     , mNeedsFakeNoDepth(false)
     , mNeedsFakeNoStencil(false)
     , mNeedsEmulatedLoneDepthStencil(false)
     , mAllowFBInvalidation(gfxPrefs::WebGLFBInvalidation())
 {
     mGeneration = 0;
@@ -245,17 +245,16 @@ WebGLContext::DestroyResourcesAndContext
     mBoundTransformFeedback = nullptr;
     mDefaultTransformFeedback = nullptr;
 
     mQuerySlot_SamplesPassed = nullptr;
     mQuerySlot_TFPrimsWritten = nullptr;
     mQuerySlot_TimeElapsed = nullptr;
 
     mIndexedUniformBufferBindings.clear();
-    OnUBIndexedBindingsChanged();
 
     //////
 
     ClearLinkedList(mBuffers);
     ClearLinkedList(mFramebuffers);
     ClearLinkedList(mPrograms);
     ClearLinkedList(mQueries);
     ClearLinkedList(mRenderbuffers);
@@ -263,16 +262,23 @@ WebGLContext::DestroyResourcesAndContext
     ClearLinkedList(mShaders);
     ClearLinkedList(mSyncs);
     ClearLinkedList(mTextures);
     ClearLinkedList(mTransformFeedbacks);
     ClearLinkedList(mVertexArrays);
 
     //////
 
+    if (mEmptyTFO) {
+        gl->fDeleteTransformFeedbacks(1, &mEmptyTFO);
+        mEmptyTFO = 0;
+    }
+
+    //////
+
     mFakeBlack_2D_0000       = nullptr;
     mFakeBlack_2D_0001       = nullptr;
     mFakeBlack_CubeMap_0000  = nullptr;
     mFakeBlack_CubeMap_0001  = nullptr;
     mFakeBlack_3D_0000       = nullptr;
     mFakeBlack_3D_0001       = nullptr;
     mFakeBlack_2D_Array_0000 = nullptr;
     mFakeBlack_2D_Array_0001 = nullptr;
@@ -2380,52 +2386,16 @@ WebGLContext::ValidateArrayBufferView(co
         elemCount = elemCountOverride;
     }
 
     *out_bytes = bytes + (elemOffset * elemSize);
     *out_byteLen = elemCount * elemSize;
     return true;
 }
 
-////
-
-const decltype(WebGLContext::mBuffersForUB)&
-WebGLContext::BuffersForUB() const
-{
-    if (mBuffersForUB_Dirty) {
-        mBuffersForUB.clear();
-        for (const auto& cur : mIndexedUniformBufferBindings) {
-            if (cur.mBufferBinding) {
-                mBuffersForUB.insert(cur.mBufferBinding.get());
-            }
-        }
-        mBuffersForUB_Dirty = false;
-    }
-    return mBuffersForUB;
-}
-
-////
-
-bool
-WebGLContext::ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer)
-{
-    if (!mBoundTransformFeedback)
-        return true;
-
-    const auto& buffersForTF = mBoundTransformFeedback->BuffersForTF();
-    if (buffersForTF.count(buffer)) {
-        ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for transform"
-                              " feedback.",
-                              funcName);
-        return false;
-    }
-
-    return true;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // XPCOM goop
 
 void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const std::vector<IndexedBufferBinding>& field,
                             const char* name, uint32_t flags)
 {
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1649,18 +1649,16 @@ protected:
     bool ValidateNonNegative(const char* funcName, const char* argName, int64_t val) {
         if (MOZ_UNLIKELY(val < 0)) {
             ErrorInvalidValue("%s: `%s` must be non-negative.", funcName, argName);
             return false;
         }
         return true;
     }
 
-    bool ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer);
-
 public:
     template<typename T>
     bool ValidateNonNull(const char* funcName, const dom::Nullable<T>& maybe) {
         if (maybe.IsNull()) {
             ErrorInvalidValue("%s: `null` is invalid.", funcName);
             return false;
         }
         return true;
@@ -1877,27 +1875,19 @@ protected:
     UniquePtr<FakeBlackTexture> mFakeBlack_3D_0001;
     UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0000;
     UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0001;
 
     bool BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack);
 
     ////////////////////////////////////
 
-private:
-    mutable bool mBuffersForUB_Dirty;
-    mutable std::set<const WebGLBuffer*> mBuffersForUB;
+protected:
+    GLuint mEmptyTFO;
 
-public:
-    void OnUBIndexedBindingsChanged() const { mBuffersForUB_Dirty = true; }
-    const decltype(mBuffersForUB)& BuffersForUB() const;
-
-    ////////////////////////////////////
-
-protected:
     // Generic Vertex Attributes
     // Though CURRENT_VERTEX_ATTRIB is listed under "Vertex Shader State" in the spec
     // state tables, this isn't vertex shader /object/ state. This array is merely state
     // useful to vertex shaders, but is global state.
     UniquePtr<GLenum[]> mGenericVertexAttribTypes;
     uint8_t mGenericVertexAttrib0Data[sizeof(float) * 4];
 
     GLuint mFakeVertexAttrib0BufferObject;
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -70,18 +70,37 @@ WebGLContext::ValidateBufferSelection(co
         return nullptr;
     const auto& buffer = *slot;
 
     if (!buffer) {
         ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
         return nullptr;
     }
 
-    if (!ValidateForNonTransformFeedback(funcName, buffer.get()))
-        return nullptr;
+    if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
+        if (mBoundTransformFeedback->IsActiveAndNotPaused()) {
+            ErrorInvalidOperation("%s: Cannot select TRANSFORM_FEEDBACK_BUFFER when"
+                                  " transform feedback is active and unpaused.",
+                                  funcName);
+            return nullptr;
+        }
+        if (buffer->IsBoundForNonTF()) {
+            ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for"
+                                  " non-transform-feedback.",
+                                  funcName);
+            return nullptr;
+        }
+    } else {
+        if (buffer->IsBoundForTF()) {
+            ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for"
+                                  " transform feedback.",
+                                  funcName);
+            return nullptr;
+        }
+    }
 
     return buffer.get();
 }
 
 IndexedBufferBinding*
 WebGLContext::ValidateIndexedBufferSlot(const char* funcName, GLenum target, GLuint index)
 {
     decltype(mIndexedUniformBufferBindings)* bindings;
@@ -127,17 +146,17 @@ WebGLContext::BindBuffer(GLenum target, 
         return;
 
     if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
         return;
 
     gl->MakeCurrent();
     gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
 
-    *slot = buffer;
+    WebGLBuffer::SetSlot(target, buffer, slot);
     if (buffer) {
         buffer->SetContentAfterBind(target);
     }
 
     switch (target) {
     case LOCAL_GL_PIXEL_PACK_BUFFER:
     case LOCAL_GL_PIXEL_UNPACK_BUFFER:
         gl->fBindBuffer(target, 0);
@@ -196,33 +215,24 @@ WebGLContext::BindBufferBase(GLenum targ
 
     ////
 
     gl->MakeCurrent();
     gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
 
     ////
 
-    *genericBinding = buffer;
-    indexedBinding->mBufferBinding = buffer;
+    WebGLBuffer::SetSlot(target, buffer, genericBinding);
+    WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
     indexedBinding->mRangeStart = 0;
     indexedBinding->mRangeSize = 0;
 
     if (buffer) {
         buffer->SetContentAfterBind(target);
     }
-
-    switch (target) {
-    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
-        mBoundTransformFeedback->OnIndexedBindingsChanged();
-        break;
-    case LOCAL_GL_UNIFORM:
-        OnUBIndexedBindingsChanged();
-        break;
-    }
 }
 
 void
 WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                               WebGLintptr offset, WebGLsizeiptr size)
 {
     const char funcName[] = "bindBufferRange";
     if (IsContextLost())
@@ -291,33 +301,24 @@ WebGLContext::BindBufferRange(GLenum tar
         gl->fBindBuffer(target, buffer->mGLName);
     }
 #endif
 
     gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);
 
     ////
 
-    *genericBinding = buffer;
-    indexedBinding->mBufferBinding = buffer;
+    WebGLBuffer::SetSlot(target, buffer, genericBinding);
+    WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
     indexedBinding->mRangeStart = offset;
     indexedBinding->mRangeSize = size;
 
     if (buffer) {
         buffer->SetContentAfterBind(target);
     }
-
-    switch (target) {
-    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
-        mBoundTransformFeedback->OnIndexedBindingsChanged();
-        break;
-    case LOCAL_GL_UNIFORM:
-        OnUBIndexedBindingsChanged();
-        break;
-    }
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::BufferDataImpl(GLenum target, size_t dataLen, const uint8_t* data,
                              GLenum usage)
 {
@@ -472,49 +473,51 @@ WebGLContext::CreateBuffer()
 void
 WebGLContext::DeleteBuffer(WebGLBuffer* buffer)
 {
     if (!ValidateDeleteObject("deleteBuffer", buffer))
         return;
 
     ////
 
-    const auto fnClearIfBuffer = [&](WebGLRefPtr<WebGLBuffer>& bindPoint) {
+    const auto fnClearIfBuffer = [&](GLenum target, WebGLRefPtr<WebGLBuffer>& bindPoint) {
         if (bindPoint == buffer) {
-            bindPoint = nullptr;
+            WebGLBuffer::SetSlot(target, nullptr, &bindPoint);
         }
     };
 
-    fnClearIfBuffer(mBoundArrayBuffer);
-    fnClearIfBuffer(mBoundVertexArray->mElementArrayBuffer);
+    fnClearIfBuffer(0, mBoundArrayBuffer);
+    fnClearIfBuffer(0, mBoundVertexArray->mElementArrayBuffer);
+
+    for (auto& cur : mBoundVertexArray->mAttribs) {
+        fnClearIfBuffer(0, cur.mBuf);
+    }
 
     // WebGL binding points
     if (IsWebGL2()) {
-        fnClearIfBuffer(mBoundCopyReadBuffer);
-        fnClearIfBuffer(mBoundCopyWriteBuffer);
-        fnClearIfBuffer(mBoundPixelPackBuffer);
-        fnClearIfBuffer(mBoundPixelUnpackBuffer);
-        fnClearIfBuffer(mBoundUniformBuffer);
-        fnClearIfBuffer(mBoundTransformFeedback->mGenericBufferBinding);
+        fnClearIfBuffer(0, mBoundCopyReadBuffer);
+        fnClearIfBuffer(0, mBoundCopyWriteBuffer);
+        fnClearIfBuffer(0, mBoundPixelPackBuffer);
+        fnClearIfBuffer(0, mBoundPixelUnpackBuffer);
+        fnClearIfBuffer(0, mBoundUniformBuffer);
+        fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER,
+                        mBoundTransformFeedback->mGenericBufferBinding);
 
         if (!mBoundTransformFeedback->mIsActive) {
             for (auto& binding : mBoundTransformFeedback->mIndexedBindings) {
-                fnClearIfBuffer(binding.mBufferBinding);
+                fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER,
+                                binding.mBufferBinding);
             }
         }
 
         for (auto& binding : mIndexedUniformBufferBindings) {
-            fnClearIfBuffer(binding.mBufferBinding);
+            fnClearIfBuffer(0, binding.mBufferBinding);
         }
     }
 
-    for (auto& cur : mBoundVertexArray->mAttribs) {
-        fnClearIfBuffer(cur.mBuf);
-    }
-
     ////
 
     buffer->RequestDelete();
 }
 
 bool
 WebGLContext::IsBuffer(WebGLBuffer* buffer)
 {
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -351,16 +351,17 @@ public:
             return;
         }
         mDidFake = true;
 
         ////
         // Check UBO sizes.
 
         const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
+
         for (const auto& cur : linkInfo->uniformBlocks) {
             const auto& dataSize = cur->mDataSize;
             const auto& binding = cur->mBinding;
             if (!binding) {
                 mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is null.",
                                               funcName);
                 *out_error = true;
                 return;
@@ -369,46 +370,77 @@ public:
             const auto availByteCount = binding->ByteCount();
             if (dataSize > availByteCount) {
                 mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is smaller"
                                               " than UNIFORM_BLOCK_DATA_SIZE.",
                                               funcName);
                 *out_error = true;
                 return;
             }
-        }
 
-        ////
-
-        const auto& tfo = mWebGL->mBoundTransformFeedback;
-        if (tfo) {
-            const auto& buffersForTF = tfo->BuffersForTF();
-            const auto& buffersForUB = mWebGL->BuffersForUB();
-            if (DoSetsIntersect(buffersForTF, buffersForUB)) {
-                mWebGL->ErrorInvalidOperation("%s: At least one WebGLBuffer is bound for"
-                                              " both transform feedback and as a uniform"
-                                              " buffer.",
+            if (binding->mBufferBinding->IsBoundForTF()) {
+                mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is bound or"
+                                              " in use for transform feedback.",
                                               funcName);
                 *out_error = true;
                 return;
             }
         }
 
         ////
 
-        for (const auto& progAttrib : mWebGL->mActiveProgramLinkInfo->attribs) {
+        const auto& tfo = mWebGL->mBoundTransformFeedback;
+        if (tfo && tfo->IsActiveAndNotPaused()) {
+            uint32_t numUsed;
+            switch (linkInfo->transformFeedbackBufferMode) {
+            case LOCAL_GL_INTERLEAVED_ATTRIBS:
+                numUsed = 1;
+                break;
+
+            case LOCAL_GL_SEPARATE_ATTRIBS:
+                numUsed = linkInfo->transformFeedbackVaryings.size();
+                break;
+
+            default:
+                MOZ_CRASH();
+            }
+
+            for (uint32_t i = 0; i < numUsed; ++i) {
+                const auto& buffer = tfo->mIndexedBindings[i].mBufferBinding;
+                if (buffer->IsBoundForNonTF()) {
+                    mWebGL->ErrorInvalidOperation("%s: Transform feedback varying %u's"
+                                                  " buffer is bound for"
+                                                  " non-transform-feedback.",
+                                                  funcName, i);
+                    *out_error = true;
+                    return;
+                }
+            }
+        }
+
+        ////
+
+        for (const auto& progAttrib : linkInfo->attribs) {
             const auto& loc = progAttrib.mLoc;
             if (loc == -1)
                 continue;
 
             const auto& attribData = mWebGL->mBoundVertexArray->mAttribs[loc];
 
             GLenum attribDataBaseType;
             if (attribData.mEnabled) {
                 attribDataBaseType = attribData.BaseType();
+
+                if (attribData.mBuf->IsBoundForTF()) {
+                    mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u's buffer is bound"
+                                                  " or in use for transform feedback.",
+                                                  funcName, loc);
+                    *out_error = true;
+                    return;
+                }
             } else {
                 attribDataBaseType = mWebGL->mGenericVertexAttribTypes[loc];
             }
 
             if (attribDataBaseType != progAttrib.mBaseType) {
                 nsCString progType, dataType;
                 WebGLContext::EnumName(progAttrib.mBaseType, &progType);
                 WebGLContext::EnumName(attribDataBaseType, &dataType);
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -587,22 +587,29 @@ WebGLContext::GetAttribLocation(const We
         return -1;
 
     return prog.GetAttribLocation(name);
 }
 
 JS::Value
 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
 {
+    const char funcName[] = "getBufferParameter";
     if (IsContextLost())
         return JS::NullValue();
 
-    const auto& buffer = ValidateBufferSelection("getBufferParameter", target);
-    if (!buffer)
+    const auto& slot = ValidateBufferSlot(funcName, target);
+    if (!slot)
         return JS::NullValue();
+    const auto& buffer = *slot;
+
+    if (!buffer) {
+        ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
+        return JS::NullValue();
+    }
 
     switch (pname) {
     case LOCAL_GL_BUFFER_SIZE:
         return JS::NumberValue(buffer->ByteLength());
 
     case LOCAL_GL_BUFFER_USAGE:
         return JS::NumberValue(buffer->Usage());
 
--- a/dom/canvas/WebGLContextVertexArray.cpp
+++ b/dom/canvas/WebGLContextVertexArray.cpp
@@ -20,23 +20,30 @@ WebGLContext::BindVertexArray(WebGLVerte
 
     if (array && !ValidateObject("bindVertexArrayObject", *array))
         return;
 
     InvalidateBufferFetching();
 
     MakeContextCurrent();
 
+    if (mBoundVertexArray) {
+        mBoundVertexArray->AddBufferBindCounts(-1);
+    }
+
     if (array == nullptr) {
         array = mDefaultVertexArray;
     }
 
     array->BindVertexArray();
 
     MOZ_ASSERT(mBoundVertexArray == array);
+    if (mBoundVertexArray) {
+        mBoundVertexArray->AddBufferBindCounts(+1);
+    }
 }
 
 already_AddRefed<WebGLVertexArray>
 WebGLContext::CreateVertexArray()
 {
     if (IsContextLost())
         return nullptr;
 
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -430,16 +430,17 @@ QueryProgramInfo(WebGLProgram* prog, gl:
 
     return info.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
     : prog(prog)
+    , transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode)
 { }
 
 webgl::LinkedProgramInfo::~LinkedProgramInfo()
 {
     for (auto& cur : uniforms) {
         delete cur;
     }
     for (auto& cur : uniformBlocks) {
@@ -692,44 +693,59 @@ JS::Value
 WebGLProgram::GetProgramParameter(GLenum pname) const
 {
     gl::GLContext* gl = mContext->gl;
     gl->MakeCurrent();
 
     if (mContext->IsWebGL2()) {
         switch (pname) {
         case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
-            return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
+            if (!IsLinked())
+                return JS::NumberValue(0);
+            return JS::NumberValue(LinkInfo()->uniformBlocks.size());
 
         case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
-            return JS::Int32Value(mNextLink_TransformFeedbackVaryings.size());
+            if (!IsLinked())
+                return JS::NumberValue(0);
+            return JS::NumberValue(LinkInfo()->transformFeedbackVaryings.size());
 
         case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
-            return JS::Int32Value(mNextLink_TransformFeedbackBufferMode);
+            if (!IsLinked())
+                return JS::NumberValue(LOCAL_GL_INTERLEAVED_ATTRIBS);
+            return JS::NumberValue(LinkInfo()->transformFeedbackBufferMode);
        }
     }
 
     switch (pname) {
     case LOCAL_GL_ATTACHED_SHADERS:
+        return JS::NumberValue( int(bool(mVertShader.get())) + int(bool(mFragShader)) );
+
     case LOCAL_GL_ACTIVE_UNIFORMS:
+        if (!IsLinked())
+            return JS::NumberValue(0);
+        return JS::NumberValue(LinkInfo()->uniforms.size());
+
     case LOCAL_GL_ACTIVE_ATTRIBUTES:
-        return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
+        if (!IsLinked())
+            return JS::NumberValue(0);
+        return JS::NumberValue(LinkInfo()->attribs.size());
 
     case LOCAL_GL_DELETE_STATUS:
         return JS::BooleanValue(IsDeleteRequested());
 
     case LOCAL_GL_LINK_STATUS:
         return JS::BooleanValue(IsLinked());
 
     case LOCAL_GL_VALIDATE_STATUS:
 #ifdef XP_MACOSX
         // See comment in ValidateProgram.
         if (gl->WorkAroundDriverBugs())
             return JS::BooleanValue(true);
 #endif
+        // Todo: Implement this in our code.
         return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname)));
 
     default:
         mContext->ErrorInvalidEnumInfo("getProgramParameter: `pname`",
                                        pname);
         return JS::NullValue();
     }
 }
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -82,16 +82,17 @@ struct LinkedProgramInfo final
     friend class WebGLProgram;
 
     MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)
 
     //////
 
     WebGLProgram* const prog;
+    const GLenum transformFeedbackBufferMode;
 
     std::vector<AttribInfo> attribs;
     std::vector<UniformInfo*> uniforms; // Owns its contents.
     std::vector<UniformBlockInfo*> uniformBlocks; // Owns its contents.
     std::vector<RefPtr<WebGLActiveInfo>> transformFeedbackVaryings;
 
     // Needed for draw call validation.
     std::vector<UniformInfo*> uniformSamplers;
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -12,17 +12,16 @@
 namespace mozilla {
 
 WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl, GLuint tf)
     : WebGLRefCountedObject(webgl)
     , mGLName(tf)
     , mIndexedBindings(webgl->mGLMaxTransformFeedbackSeparateAttribs)
     , mIsPaused(false)
     , mIsActive(false)
-    , mBuffersForTF_Dirty(true)
 {
     mContext->mTransformFeedbacks.insertBack(this);
 }
 
 WebGLTransformFeedback::~WebGLTransformFeedback()
 {
     DeleteOnce();
 }
@@ -32,38 +31,16 @@ WebGLTransformFeedback::Delete()
 {
     if (mGLName) {
         mContext->MakeContextCurrent();
         mContext->gl->fDeleteTransformFeedbacks(1, &mGLName);
     }
     removeFrom(mContext->mTransformFeedbacks);
 }
 
-////
-
-const decltype(WebGLTransformFeedback::mBuffersForTF)&
-WebGLTransformFeedback::BuffersForTF() const
-{
-    // The generic bind point cannot incur undefined read/writes because otherwise it
-    // would be impossible to read back from this. The spec implies that readback from
-    // the TRANSFORM_FEEDBACK target is possible, just not simultaneously with being
-    // "bound or in use for transform feedback".
-    // Therefore, only the indexed bindings of the TFO count.
-    if (mBuffersForTF_Dirty) {
-        mBuffersForTF.clear();
-        for (const auto& cur : mIndexedBindings) {
-            if (cur.mBufferBinding) {
-                mBuffersForTF.insert(cur.mBufferBinding.get());
-            }
-        }
-        mBuffersForTF_Dirty = false;
-    }
-    return mBuffersForTF;
-}
-
 ////////////////////////////////////////
 
 void
 WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
 {
     const char funcName[] = "beginTransformFeedback";
 
     if (mIsActive)
@@ -204,16 +181,28 @@ WebGLTransformFeedback::ResumeTransformF
     ////
 
     MOZ_ASSERT(mIsActive);
     mIsPaused = false;
 }
 
 ////////////////////////////////////////
 
+void
+WebGLTransformFeedback::AddBufferBindCounts(int8_t addVal) const
+{
+    const GLenum target = LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER;
+    WebGLBuffer::AddBindCount(target, mGenericBufferBinding.get(), addVal);
+    for (const auto& binding : mIndexedBindings) {
+        WebGLBuffer::AddBindCount(target, binding.mBufferBinding.get(), addVal);
+    }
+}
+
+////////////////////////////////////////
+
 JSObject*
 WebGLTransformFeedback::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLTransformFeedbackBinding::Wrap(cx, this, givenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTransformFeedback, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTransformFeedback, Release)
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -12,16 +12,17 @@
 
 namespace mozilla {
 
 class WebGLTransformFeedback final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTransformFeedback>
     , public LinkedListElement<WebGLTransformFeedback>
 {
+    friend class ScopedDrawHelper;
     friend class ScopedDrawWithTransformFeedback;
     friend class WebGLContext;
     friend class WebGL2Context;
     friend class WebGLProgram;
 
 public:
     const GLuint mGLName;
 private:
@@ -31,36 +32,32 @@ private:
     bool mIsPaused;
     bool mIsActive;
     // Not in state tables:
     WebGLRefPtr<WebGLProgram> mActive_Program;
     MOZ_INIT_OUTSIDE_CTOR GLenum mActive_PrimMode;
     MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertPosition;
     MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertCapacity;
 
-    mutable bool mBuffersForTF_Dirty;
-    mutable std::set<const WebGLBuffer*> mBuffersForTF;
-
 public:
     WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
 private:
     ~WebGLTransformFeedback();
 
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTransformFeedback)
 
     void Delete();
     WebGLContext* GetParentObject() const { return mContext; }
     virtual JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
 
-    ////
+    bool IsActiveAndNotPaused() const { return mIsActive && !mIsPaused; }
 
-    void OnIndexedBindingsChanged() const { mBuffersForTF_Dirty = true; }
-    const decltype(mBuffersForTF)& BuffersForTF() const;
+    void AddBufferBindCounts(int8_t addVal) const;
 
     // GL Funcs
     void BeginTransformFeedback(GLenum primMode);
     void EndTransformFeedback();
     void PauseTransformFeedback();
     void ResumeTransformFeedback();
 };
 
--- a/dom/canvas/WebGLVertexArray.cpp
+++ b/dom/canvas/WebGLVertexArray.cpp
@@ -23,16 +23,31 @@ WebGLVertexArray::WrapObject(JSContext* 
 WebGLVertexArray::WebGLVertexArray(WebGLContext* webgl)
     : WebGLRefCountedObject(webgl)
     , mGLName(0)
 {
     mAttribs.SetLength(mContext->mGLMaxVertexAttribs);
     mContext->mVertexArrays.insertBack(this);
 }
 
+WebGLVertexArray::~WebGLVertexArray()
+{
+    MOZ_ASSERT(IsDeleted());
+}
+
+void
+WebGLVertexArray::AddBufferBindCounts(int8_t addVal) const
+{
+    const GLenum target = 0; // Anything non-TF is fine.
+    WebGLBuffer::AddBindCount(target, mElementArrayBuffer.get(), addVal);
+    for (const auto& attrib : mAttribs) {
+        WebGLBuffer::AddBindCount(target, attrib.mBuf.get(), addVal);
+    }
+}
+
 WebGLVertexArray*
 WebGLVertexArray::Create(WebGLContext* webgl)
 {
     WebGLVertexArray* array;
     if (webgl->gl->IsSupported(gl::GLFeature::vertex_array_object)) {
         array = new WebGLVertexArrayGL(webgl);
     } else {
         array = new WebGLVertexArrayFake(webgl);
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -5,17 +5,16 @@
 
 #ifndef WEBGL_VERTEX_ARRAY_H_
 #define WEBGL_VERTEX_ARRAY_H_
 
 #include "nsTArray.h"
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 
-#include "WebGLBuffer.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
 class WebGLVertexArrayFake;
 
@@ -43,22 +42,21 @@ public:
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLVertexArray)
 
     GLuint GLName() const { return mGLName; }
 
+    void AddBufferBindCounts(int8_t addVal) const;
+
 protected:
     explicit WebGLVertexArray(WebGLContext* webgl);
-
-    virtual ~WebGLVertexArray() {
-        MOZ_ASSERT(IsDeleted());
-    }
+    virtual ~WebGLVertexArray();
 
     virtual void GenVertexArray() = 0;
     virtual void BindVertexArrayImpl() = 0;
     virtual void DeleteImpl() = 0;
     virtual bool IsVertexArrayImpl() const = 0;
 
     GLuint mGLName;
     nsTArray<WebGLVertexAttribData> mAttribs;
--- a/dom/canvas/WebGLVertexAttribData.cpp
+++ b/dom/canvas/WebGLVertexAttribData.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLVertexAttribData.h"
 
 #include "GLContext.h"
+#include "WebGLBuffer.h"
 
 namespace mozilla {
 
 static uint8_t
 CalcBytesPerVertex(GLenum type, uint8_t size)
 {
     uint8_t bytesPerType;
     switch (type) {
@@ -66,17 +67,17 @@ AttribPointerBaseType(bool integerFunc, 
 }
 
 void
 WebGLVertexAttribData::VertexAttribPointer(bool integerFunc, WebGLBuffer* buf,
                                            uint8_t size, GLenum type, bool normalized,
                                            uint32_t stride, uint64_t byteOffset)
 {
     mIntegerFunc = integerFunc;
-    mBuf = buf;
+    WebGLBuffer::SetSlot(0, buf, &mBuf);
     mType = type;
     mBaseType = AttribPointerBaseType(integerFunc, type);
     mSize = size;
     mBytesPerVertex = CalcBytesPerVertex(mType, mSize);
     mNormalized = normalized;
     mStride = stride;
     mExplicitStride = (mStride ? mStride : mBytesPerVertex);
     mByteOffset = byteOffset;
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html
@@ -56,18 +56,16 @@ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_
 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
     "Calling bindBufferBase on a buffer where no storage is allocated should succeed.");
 shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING)", "buffer1");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer1");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "0");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "0");
 
 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4, gl.STATIC_DRAW);
-wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
-
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer1");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "0");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "0");
 
 wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 
 debug("");
 debug("bindBufferBase with UNIFORM_BUFFER target");
@@ -94,23 +92,21 @@ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK
 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
     "Calling bindBufferRange on a buffer where no storage is allocated should succeed.");
 shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING)", "buffer3");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4");
 
 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4, gl.STATIC_DRAW);
-wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4");
 
 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 12, gl.STATIC_DRAW);
-wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4");
 
 wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 
 debug("");
 debug("bindBufferRange with UNIFORM_BUFFER target");
index 5e1144db47be8c85f4545c978a5eb90d3f8293ec..50ca3ef898de2706d42e0019c923838502302225
GIT binary patch
literal 5446
zc$}?Q2{=@38y-ZmRiq?K_F|Bsl+wZ^lk8+kjAa<h42EH{D|;yh*`i-eB5T>1vQ(A{
zgJuvPg;s0Gl41Ul7QVjvbbXz<&bi)q=A8S!-{-u~GtaG~!LVT)003YHi2A!g&Y@qY
zzn}vEI5z+QYyduhH40|w4zsYal5ub|G}r`SP#^=1N8m7HG=>#Gx8W@V0Py+Zt4~1o
z?AuZ`ELHL#h=sneyy9(IR^U*3MgYS!4{^xURx%nWm_@3l_KCqHQ05X4LSpyJk%*gT
z0sT(_?G-h{9$Y6j>f9;Xwv`*q>UwEHK37TF($YIkQ#Dre3bLhqde1Gt<Ms=ENkrp*
zlu<$U@%dxZ*KaD`ynt@H`~CzbjAuZMcUstVvjt%mbc;1-NzZWD#^xFM41pxFKUP$*
zCQKk)o{;J3fAa0P&)BGYXtQrZbTys5(d-^bjaOA)jNev2mPSf&*u@rsmx>&22aicT
zK9ukbT_9zQXPSY8o-r4Hlapy|d|vecV*yz$v{|~lamEcs?AoiYCX|xa^!Pm4Ud}d>
z6%i!ZwP`r_G(SB-k;yja?(nA!xI2%fA#`XYj*pJxNiszx0Y821R)d;@a+0DW!B&wY
z@__W7fgEskHzr{LLy2iP5)OALJXu@@2iBNqRg`s$@M?%m!K_|g5WDi8C@6w;#Oo-S
zWv~m2zTzu}U>x&Z&pj#UyK>}pkf>P5@wYZE!*KYW>(+?&!Can$q3I!lrt|6)Y58#z
zf0mElCcdZ9_v~Y!5{7z*-6weTb|g>syl9pW!W(yDDH&vDTvuCHYieo0S<jILE%gPB
zSNZY7pCStHE-~oPb_ok$`Ka@AfdEz=%(PwN=I)HLw1dfh-XVS@pz(FPA9l#k5ja;N
zxWG^dOC${G{v9jfcd&N;BbGHB<&0P*FX?w6`F{mzZ)5cx_QtPZ`H(4VU{(GNR$f64
zq`3d!{zL9=Fb6joS{6Ea+FQD~ArQ_M)+iZUdncIN3ilmaK4$-zlRsaQ#vkE!a7Lq$
zmQHTp1K|A6B38-^6D<IhGc2m&wAnt|srpAm$bBaFrV;!9NN%kTY<UHPLZMFfa5xlt
zhBiXd-{<M#J)yv0B$3d=_Ie+RVU@(?IC*m>=%1xM+s^f;B!a%h=gyBCWYkCl&UJz(
zHRGLX+Zga-*bYH946)4j4qVow2+AUzAv@ZUbRY{6sj03fZ7PJ2G?wB8Z!HQAxF~#4
znK{CMtwbkEfJFeTExa|$V2|;Uw?cj~Hlx<oIYhpo#TPyEv(z^?$CJCacwA-{Du<ph
zUwT0CacS+Vr7Q+<T3y(#Sq&PJZ4nn$^4i2DeMv~}sZ$-yPmcZ=d;8;*h7v}cv{(X9
zLbE-qDLvPrL0l^d*A$v}88=t#SJ*K3)K^4EU%znanODrx?mY2=eWny&6)Y25SmySm
zTOINr6NBJ5@r9F_0oh7aZ7<ni;!V+G$!CG(R?5~a`1|%mf_hqf3X`%v3;x{ByZP~n
zD$b{Pr|;>#Ro<WXz}$lcFG9Nok~T{&K;e(uAR*)Yby)vxnv0ZnFNvM6>*sXM>+W9Q
z&L86L^%{X|8p47}?TQ-vO2OFrF1~O^?XFPFM3`p%C2t_#=8$gqk+K?<4=-3)pIoAQ
zeQI|j{;721Q4bYt%2UZkkF&tWNT4?MZueP;jI{w)^UQq&+bz7)1k+TVRyQ%Ht6flQ
z?1TYtS)|Q7VkT#J_JgeV$*V8+wTj#ivL-Zxoh5zrUnaBk8_eAnw|$qqCDZVnQ9seW
zDzS4?$-H$JM#Oob{=!s4nO9%ZVw~2KRI%`K<hz$|jVf8{2B*NG9Q;b|Z;5HD_5Q*7
z11g&~y~I(2_WCOAswsxDV>s9@SyZc3#gZ?1)^xsL@@d)@NH-xg$KHFTwe;)+XpV_O
zClJyp>@$UEF(q!v@6j3}Lw(qgrv{YZk9Y+pM2y4xjG%#gk3N)=GuIsKV-!9<a*sJ_
z;o)vYxf1LnH-@LszQ6(L<bs4<9}Z6}PO9mPPSUCFJYb3>l5-j&17)$#Y8yst%M&A?
zn+l-nUZ`VDnaj}!-+bKpocCnjj;g4P*Qb!%DM=XzLd`@{^*bi*f|EY=jGCJhYLmiy
zn?;LtgyUmyxozMvDE9flIoGk%k)1C;NZL82#vH&kkw)#&m^f5tIY(y4?J`rPW(304
zn*hOeznS<{b^2N*sb|bZiMw*RsJ#v5Iel<MEOeW$K}DsK&qG=g^B<_}Mob;7@Ubne
z4~F3@595b+tW9a@q&~i3T0;Y<{_kbi&DF`?9ri7wm(RavbZ@J6k1fwem*l8}fqKNE
zhVxPxI}G9ArvK$gkP)W4IlvkzGCXt(X$_*XxrZez3p*lHug3~_^jOX!{Hr+@SjmK?
zfb*q;MGGFrvdRUWSGg!sQA@|!rWg0n5yqz;`1QT<)pX!4T+%%uzqcEq;Ft|TqV#(k
zm}lvP`07t~dEYn;s_9E_DMY8*<vUmQZ;Wu6qLh$ag|S02)C==u+_5MA0+mT~U<~Ji
z)QskIW}BeM!+YWZ)N3>6xN3`&8!>jzog6&J+h6D1?m#)_XD1bnC>85rK@s3sJwLRe
zWOURXY~$V>*q|1f1=!@l*2t{^Zpz7t)^m+<%7cKSan1AMf8{EeqGPH~A){dS`(<Nt
z45Ex5nq^@sdtw;lMQ<6JoGFY->C7$-6SeE<i4Ss29RAQ!;@177#n)7h0OTq*!zn4B
zZXgX1{9FX>IJH1doO#5ka^vuB95I`zF`uja&Xx=5Ixeq^T0%^8!}K%zGT!v!+dX`m
zQqM`qAzMe=^cs4r9t$9m^)fnFr{(=WT)3z1sBBTelqaXxqI8Yj+%6YMyglzzJTc?y
z5FOu%!=ffB7EV{sg1dT;YB5`Z8cR>=|J9a7vc=bXH`-li)tlX(U|o4kRtmd6{xFU*
zdd5E4XAdG1LS1@SJi~dqxOCuRSz)#3^&+o0KT@`%MNt<ncM!=ha7XEInxbU(*p1DP
zt{h4ndX-ajGGu3MHgxw@oU@=KNx{_<;!S?#Rxz%SdE;Ck|FFzVy41zpGQf#z1dqSy
zJ6Uh2V<dUusUmm^J<J^>Ob8yhH=GeuYIz_@p(@$gc8TtY{{83eV`%nmz_^l`Td@{#
z_~yNOSX7R|WJJ-JMpHz>ePN&TzF8YV?7P}!^!}I5|CB75pTn)VZCIeuu4nwo23K^*
z_?ezfe3eF5c?VN{JqDN#KTi4RWw6E0cCJ1IfB&kCySpf6rd_HFe5s_N(R<{OqOMC;
zD<SWF)gj5S&itv35N{K+#q*(Zt+i$Oa^StH@5lQ?)D(A1y#nnDHrJuvKOS8Yt57(b
z;mp^H4bqyOGvzR2_vWX>eF6%HUA|1ALmM>}V`}8v3M$Iuli5B^94gO27psl7V>Hh9
z={I{XO^pc>gj7kU6<KW+-MyRb-NypQgyCu?h{VXHNL}Fej4LA%e&G(KcBeMDdR!kr
zv*G>lQ2~DAjv7*t8~J^lcy3$N=INy;m2zMc{gk^^yS22ZHeO80ESB{&NT(_fH0FDH
zwCpeNOzVCaS5!fb*bMI=@_w|ICmGh+lxpKh*>ReY?0!}SrbZtZL){zTc5qd1j9J0B
zc%fil=py}>mg-GAkM~r}L)Q_;uU|Z_DwtLi-hM6N**=0rwpwm)Mw}W#yW<_jHyxGV
zeMeU&sjZ*76gpleF(dnNDu#;s_UI1C#cQY!qNQ29`-kUmCMyjGefi;q0@|NS^}LGz
zI`2)46f+-u<ayg~-rF+T=hF=L)ytFVRK7_SrJ0W}Eyu^_O<JjQRR5LR11__-QonKt
zrc&uvhB8NwP#IQ+W<KP8s$il|ljhMB9}jZ*mz%5aZhI+WCJd^5a04y=<vW*8E3V*s
znN^n5?%)2oVFmz%e^q8>5HNR38%uY~UzJg7DEet(a(~_9>gww2H*{lfCPzG?|68vX
z%ll@On>LfAonq@^l=c9vi!^|#3o9Qn>82lT{5Q^QWNkHQIRLab&+(me9e)h09Mg?J
zh@m<`wn-DHyU!MC1+}qp(b1uGrO+U&Ucf^jI9R}1R5t7}SZ8z(4`8`@rlS{N_?bKB
z`TQQ1Ut&Pu@@TDBXI$~>KeXe^h(B|3JYPmz0HY_wY3uv$>ljy7^&4oMYtZ1U&{liA
z-^bm#2Dj)RaliS!--qX41HX~s7w{|I@82nYbbhNT%>RvIoj#Cp#RvYzQZ?4-c>YIp
ztn!4v3e3+OA<s8)MS&I@{)8a8&qBO^7MY*<zVAXZ0_hq5gb>hrBJyLmx>^W{`M(MI
zTB6pcV{JKKt>XyCFLbOb?(5@OTf|rM<gJV6b4gzx)!N#<no4*x;D<{7GfYON<!9Jw
NpIF+?xUqS8_J6=s48H&X
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -163,17 +163,20 @@ GetPluginPaths(const nsAString& aPluginP
 {
   nsCOMPtr<nsIFile> libDirectory, libFile;
   if (!GetPluginFile(aPluginPath, libDirectory, libFile)) {
     return false;
   }
 
   // Mac sandbox rules expect paths to actual files and directories -- not
   // soft links.
+  libDirectory->Normalize();
   aPluginDirectoryPath = GetNativeTarget(libDirectory);
+
+  libFile->Normalize();
   aPluginFilePath = GetNativeTarget(libFile);
 
   return true;
 }
 
 static bool
 GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath)
 {
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -3376,17 +3376,20 @@ XMLHttpRequestMainThread::OnRedirectVeri
     mErrorLoad = true;
   }
 
   mNewRedirectChannel = nullptr;
 
   mRedirectCallback->OnRedirectVerifyCallback(result);
   mRedirectCallback = nullptr;
 
-  return result;
+  // It's important that we return success here. If we return the result code
+  // that we were passed, JavaScript callers who cancel the redirect will wind
+  // up throwing an exception in the process.
+  return NS_OK;
 }
 
 /////////////////////////////////////////////////////
 // nsIProgressEventSink methods:
 //
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::OnProgress(nsIRequest *aRequest, nsISupports *aContext, int64_t aProgress, int64_t aProgressMax)
new file mode 100644
--- /dev/null
+++ b/intl/icu-patches/bug-1325858-close-key.diff
@@ -0,0 +1,26 @@
+getTZKeyName in common/wintz.cpp leaks registry handle.
+
+https://ssl.icu-project.org/trac/ticket/12908
+
+diff --git a/intl/icu/source/common/wintz.c b/intl/icu/source/common/wintz.c
+--- a/intl/icu/source/common/wintz.c
++++ b/intl/icu/source/common/wintz.c
+@@ -211,16 +211,18 @@ static LONG getTZKeyName(char* tzKeyName
+              hkey,
+              "TimeZoneKeyName",
+              NULL,
+              NULL,
+              (LPBYTE)tzKeyName,
+              &cbData);
+     }
+ 
++    RegCloseKey(hkey);
++
+     return result;
+ }
+ 
+ /*
+   This code attempts to detect the Windows time zone, as set in the
+   Windows Date and Time control panel.  It attempts to work on
+   multiple flavors of Windows (9x, Me, NT, 2000, XP) and on localized
+   installs.  It works by directly interrogating the registry and
--- a/intl/icu/source/common/wintz.c
+++ b/intl/icu/source/common/wintz.c
@@ -211,16 +211,18 @@ static LONG getTZKeyName(char* tzKeyName
              hkey,
              "TimeZoneKeyName",
              NULL,
              NULL,
              (LPBYTE)tzKeyName,
              &cbData);
     }
 
+    RegCloseKey(hkey);
+
     return result;
 }
 
 /*
   This code attempts to detect the Windows time zone, as set in the
   Windows Date and Time control panel.  It attempts to work on
   multiple flavors of Windows (9x, Me, NT, 2000, XP) and on localized
   installs.  It works by directly interrogating the registry and
--- a/intl/update-icu.sh
+++ b/intl/update-icu.sh
@@ -56,16 +56,17 @@ rm ${icu_dir}/source/data/unit/*.txt
 svn info $1 | grep -v '^Revision: [[:digit:]]\+$' > ${icu_dir}/SVN-INFO
 
 for patch in \
  bug-915735 \
  suppress-warnings.diff \
  bug-1172609-timezone-recreateDefault.diff \
  bug-1198952-workaround-make-3.82-bug.diff \
  bug-1228227-bug-1263325-libc++-gcc_hidden.diff \
+ bug-1325858-close-key.diff \
  ucol_getKeywordValuesForLocale-ulist_resetList.diff \
  unum_formatDoubleForFields.diff \
 ; do
   echo "Applying local patch $patch"
   patch -d ${icu_dir}/../../ -p1 --no-backup-if-mismatch < ${icu_dir}/../icu-patches/$patch
 done
 
 topsrcdir=`dirname $0`/../
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1188586.js
@@ -0,0 +1,7 @@
+(function(y) {
+    for (var k = 0; k < 9; ++k) {
+        try {
+            y ** y == a;
+        } catch (e) {}
+    }
+})();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1326150.js
@@ -0,0 +1,4 @@
+this.x = [];
+Function.apply(null, this.x);
+Object.defineProperty(this, "x", {get: valueOf});
+assertEq(evaluate("this.x;"), this);
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -410,19 +410,19 @@ BaselineCacheIRCompiler::emitCallScripte
 
     masm.bind(&noUnderflow);
     masm.callJit(code);
 
     stubFrame.leave(masm, true);
     return true;
 }
 
-typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
-static const VMFunction DoCallNativeGetterInfo =
-    FunctionInfo<DoCallNativeGetterFn>(DoCallNativeGetter, "DoCallNativeGetter");
+typedef bool (*CallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
+static const VMFunction CallNativeGetterInfo =
+    FunctionInfo<CallNativeGetterFn>(CallNativeGetter, "CallNativeGetter");
 
 bool
 BaselineCacheIRCompiler::emitCallNativeGetterResult()
 {
     AutoStubFrame stubFrame(*this);
 
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address getterAddr(stubAddress(reader.stubOffset()));
@@ -434,17 +434,17 @@ BaselineCacheIRCompiler::emitCallNativeG
     stubFrame.enter(masm, scratch);
 
     // Load the callee in the scratch register.
     masm.loadPtr(getterAddr, scratch);
 
     masm.Push(obj);
     masm.Push(scratch);
 
-    if (!callVM(masm, DoCallNativeGetterInfo))
+    if (!callVM(masm, CallNativeGetterInfo))
         return false;
 
     stubFrame.leave(masm);
     return true;
 }
 
 typedef bool (*ProxyGetPropertyFn)(JSContext*, HandleObject, HandleId, MutableHandleValue);
 static const VMFunction ProxyGetPropertyInfo =
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -693,17 +693,16 @@ RecompileBaselineScriptForDebugMode(JSCo
     _(CacheIR_Monitored)                        \
     _(Call_Scripted)                            \
     _(Call_AnyScripted)                         \
     _(Call_Native)                              \
     _(Call_ClassHook)                           \
     _(Call_ScriptedApplyArray)                  \
     _(Call_ScriptedApplyArguments)              \
     _(Call_ScriptedFunCall)                     \
-    _(GetProp_CallNativeGlobal)                 \
     _(GetProp_Generic)                          \
     _(SetProp_CallScripted)                     \
     _(SetProp_CallNative)
 
 static bool
 CloneOldBaselineStub(JSContext* cx, DebugModeOSREntryVector& entries, size_t entryIndex)
 {
     DebugModeOSREntry& entry = entries[entryIndex];
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -430,20 +430,16 @@ ICTypeUpdate_ObjectGroup::Compiler::gene
     masm.mov(ImmWord(1), R1.scratchReg());
     EmitReturnFromIC(masm);
 
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
-typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
-static const VMFunction DoCallNativeGetterInfo =
-    FunctionInfo<DoCallNativeGetterFn>(DoCallNativeGetter, "DoCallNativeGetter");
-
 //
 // ToBool_Fallback
 //
 
 static bool
 DoToBoolFallback(JSContext* cx, BaselineFrame* frame, ICToBool_Fallback* stub, HandleValue arg,
                  MutableHandleValue ret)
 {
@@ -958,17 +954,17 @@ DoGetElemFallback(JSContext* cx, Baselin
         stub->noteUnoptimizableAccess();
         attached = true;
     }
 
     bool isTemporarilyUnoptimizable = false;
     if (!attached && !JitOptions.disableCacheIR) {
         ICStubEngine engine = ICStubEngine::Baseline;
         GetPropIRGenerator gen(cx, pc, CacheKind::GetElem, engine, &isTemporarilyUnoptimizable,
-                               lhs, rhs);
+                               lhs, rhs, CanAttachGetter::Yes);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         engine, info.outerScript(cx), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
@@ -2352,143 +2348,16 @@ ICIn_Dense::Compiler::generateStubCode(M
     EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
-// Try to update existing SetProp setter call stubs for the given holder in
-// place with a new shape and setter.
-static bool
-UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
-                               ICStub::Kind kind,
-                               NativeObject* holder,
-                               JSObject* receiver,
-                               JSFunction* setter)
-{
-    MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
-               kind == ICStub::SetProp_CallNative);
-    MOZ_ASSERT(holder);
-    MOZ_ASSERT(receiver);
-
-    bool isOwnSetter = (holder == receiver);
-    bool foundMatchingStub = false;
-    ReceiverGuard receiverGuard(receiver);
-    for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
-        if (iter->kind() == kind) {
-            ICSetPropCallSetter* setPropStub = static_cast<ICSetPropCallSetter*>(*iter);
-            if (setPropStub->holder() == holder && setPropStub->isOwnSetter() == isOwnSetter) {
-                // If this is an own setter, update the receiver guard as well,
-                // since that's the shape we'll be guarding on. Furthermore,
-                // isOwnSetter() relies on holderShape_ and receiverGuard_ being
-                // the same shape.
-                if (isOwnSetter)
-                    setPropStub->receiverGuard().update(receiverGuard);
-
-                MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
-                           !setPropStub->receiverGuard().matches(receiverGuard),
-                           "Why didn't we end up using this stub?");
-
-                // We want to update the holder shape to match the new one no
-                // matter what, even if the receiver shape is different.
-                setPropStub->holderShape() = holder->lastProperty();
-
-                // Make sure to update the setter, since a shape change might
-                // have changed which setter we want to use.
-                setPropStub->setter() = setter;
-                if (setPropStub->receiverGuard().matches(receiverGuard))
-                    foundMatchingStub = true;
-            }
-        }
-    }
-
-    return foundMatchingStub;
-}
-
-// Attach an optimized stub for a GETGNAME/CALLGNAME getter op.
-static bool
-TryAttachGlobalNameAccessorStub(JSContext* cx, HandleScript script, jsbytecode* pc,
-                                ICGetName_Fallback* stub,
-                                Handle<LexicalEnvironmentObject*> globalLexical,
-                                HandlePropertyName name, bool* attached,
-                                bool* isTemporarilyUnoptimizable)
-{
-    MOZ_ASSERT(globalLexical->isGlobal());
-    RootedId id(cx, NameToId(name));
-
-    // There must not be a shadowing binding on the global lexical scope.
-    if (globalLexical->lookup(cx, id))
-        return true;
-
-    RootedGlobalObject global(cx, &globalLexical->global());
-
-    // The property must be found, and it must be found as a normal data property.
-    RootedShape shape(cx);
-    RootedNativeObject current(cx, global);
-    while (true) {
-        shape = current->lookup(cx, id);
-        if (shape)
-            break;
-        JSObject* proto = current->staticPrototype();
-        if (!proto || !proto->is<NativeObject>())
-            return true;
-        current = &proto->as<NativeObject>();
-    }
-
-    // Instantiate this global property, for use during Ion compilation.
-    if (IsIonEnabled(cx))
-        EnsureTrackPropertyTypes(cx, current, id);
-
-    // Try to add a getter stub. We don't handle scripted getters yet; if this
-    // changes we need to make sure IonBuilder::getPropTryCommonGetter (which
-    // requires a Baseline stub) handles non-outerized this objects correctly.
-    bool isScripted;
-    if (IsCacheableGetPropCall(cx, global, current, shape, &isScripted, isTemporarilyUnoptimizable) &&
-        !isScripted)
-    {
-        ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-        RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
-
-        // The CallNativeGlobal stub needs to generate 3 shape checks:
-        //
-        // 1. The global lexical scope shape check.
-        // 2. The global object shape check.
-        // 3. The holder shape check.
-        //
-        // 1 is done as the receiver check, as for GETNAME the global lexical scope is in the
-        // receiver position. 2 is done as a manual check that other GetProp stubs don't do. 3 is
-        // done as the holder check per normal.
-        //
-        // In the case the holder is the global object, check 2 is redundant but is not yet
-        // optimized away.
-        JitSpew(JitSpew_BaselineIC, "  Generating GetName(GlobalName/NativeGetter) stub");
-        if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNativeGlobal, current,
-                                           globalLexical, getter))
-        {
-            *attached = true;
-            return true;
-        }
-        ICGetPropCallNativeCompiler compiler(cx, ICStub::GetProp_CallNativeGlobal,
-                                             ICStubCompiler::Engine::Baseline,
-                                             monitorStub, globalLexical, current,
-                                             getter, script->pcToOffset(pc),
-                                             /* inputDefinitelyObject = */ true);
-
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!newStub)
-            return false;
-
-        stub->addNewStub(newStub);
-        *attached = true;
-    }
-    return true;
-}
-
 static bool
 DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_,
                   HandleObject envChain, MutableHandleValue res)
 {
     SharedStubInfo info(cx, frame, stub_->icEntry());
 
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICGetName_Fallback*> stub(frame, stub_);
@@ -2497,30 +2366,33 @@ DoGetNameFallback(JSContext* cx, Baselin
     jsbytecode* pc = stub->icEntry()->pc(script);
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetName(%s)", CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_GETNAME || op == JSOP_GETGNAME);
 
     RootedPropertyName name(cx, script->getName(pc));
     bool attached = false;
-    bool isTemporarilyUnoptimizable = false;
 
     // Attach new stub.
     if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with generic stub.
         attached = true;
     }
 
-    if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) {
-        if (!TryAttachGlobalNameAccessorStub(cx, script, pc, stub,
-                                             envChain.as<LexicalEnvironmentObject>(),
-                                             name, &attached, &isTemporarilyUnoptimizable))
-        {
-            return false;
+    if (!attached && !JitOptions.disableCacheIR) {
+        ICStubEngine engine = ICStubEngine::Baseline;
+        GetNameIRGenerator gen(cx, pc, script, envChain, name);
+        if (gen.tryAttachStub()) {
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        engine, info.outerScript(cx), stub);
+            if (newStub) {
+                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                attached = true;
+            }
         }
     }
 
     static_assert(JSOP_GETGNAME_LENGTH == JSOP_GETNAME_LENGTH,
                   "Otherwise our check for JSOP_TYPEOF isn't ok");
     if (JSOp(pc[JSOP_GETGNAME_LENGTH]) == JSOP_TYPEOF) {
         if (!GetEnvironmentNameForTypeOf(cx, envChain, name, res))
             return false;
@@ -2533,33 +2405,18 @@ DoGetNameFallback(JSContext* cx, Baselin
 
     // Check if debug mode toggling made the stub invalid.
     if (stub.invalid())
         return true;
 
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, &info, res))
         return false;
-    if (attached)
-        return true;
-
-    if (!JitOptions.disableCacheIR) {
-        ICStubEngine engine = ICStubEngine::Baseline;
-        GetNameIRGenerator gen(cx, pc, script, envChain, name);
-        if (gen.tryAttachStub()) {
-            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                        engine, info.outerScript(cx), stub);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
-                attached = true;
-            }
-        }
-    }
-
-    if (!attached && !isTemporarilyUnoptimizable)
+
+    if (!attached)
         stub->noteUnoptimizableAccess();
     return true;
 }
 
 typedef bool (*DoGetNameFallbackFn)(JSContext*, BaselineFrame*, ICGetName_Fallback*,
                                     HandleObject, MutableHandleValue);
 static const VMFunction DoGetNameFallbackInfo =
     FunctionInfo<DoGetNameFallbackFn>(DoGetNameFallback, "DoGetNameFallback", TailCall);
@@ -2798,16 +2655,64 @@ TryAttachSetValuePropStub(JSContext* cx,
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     return true;
 }
 
+// Try to update existing SetProp setter call stubs for the given holder in
+// place with a new shape and setter.
+static bool
+UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
+                               ICStub::Kind kind,
+                               NativeObject* holder,
+                               JSObject* receiver,
+                               JSFunction* setter)
+{
+    MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
+               kind == ICStub::SetProp_CallNative);
+    MOZ_ASSERT(holder);
+    MOZ_ASSERT(receiver);
+
+    bool isOwnSetter = (holder == receiver);
+    bool foundMatchingStub = false;
+    ReceiverGuard receiverGuard(receiver);
+    for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
+        if (iter->kind() == kind) {
+            ICSetPropCallSetter* setPropStub = static_cast<ICSetPropCallSetter*>(*iter);
+            if (setPropStub->holder() == holder && setPropStub->isOwnSetter() == isOwnSetter) {
+                // If this is an own setter, update the receiver guard as well,
+                // since that's the shape we'll be guarding on. Furthermore,
+                // isOwnSetter() relies on holderShape_ and receiverGuard_ being
+                // the same shape.
+                if (isOwnSetter)
+                    setPropStub->receiverGuard().update(receiverGuard);
+
+                MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
+                           !setPropStub->receiverGuard().matches(receiverGuard),
+                           "Why didn't we end up using this stub?");
+
+                // We want to update the holder shape to match the new one no
+                // matter what, even if the receiver shape is different.
+                setPropStub->holderShape() = holder->lastProperty();
+
+                // Make sure to update the setter, since a shape change might
+                // have changed which setter we want to use.
+                setPropStub->setter() = setter;
+                if (setPropStub->receiverGuard().matches(receiverGuard))
+                    foundMatchingStub = true;
+            }
+        }
+    }
+
+    return foundMatchingStub;
+}
+
 // Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on
 // an accessor property.
 static bool
 TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
                              ICSetProp_Fallback* stub,
                              HandleObject obj, const RootedReceiverGuard& receiverGuard,
                              HandlePropertyName name,
                              HandleId id, HandleValue rhs, bool* attached,
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -6,16 +6,18 @@
 
 #include "jit/BaselineInspector.h"
 
 #include "mozilla/DebugOnly.h"
 
 #include "jit/BaselineIC.h"
 #include "jit/CacheIRCompiler.h"
 
+#include "jsscriptinlines.h"
+
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/ObjectGroup-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
@@ -665,32 +667,16 @@ BaselineInspector::templateCallObject()
         return nullptr;
 
     JSObject* res = baselineScript()->templateEnvironment();
     MOZ_ASSERT(res);
 
     return &res->as<CallObject>();
 }
 
-static Shape*
-GlobalShapeForGetPropFunction(ICStub* stub)
-{
-    if (stub->isGetProp_CallNativeGlobal()) {
-        ICGetProp_CallNativeGlobal* nstub = stub->toGetProp_CallNativeGlobal();
-        if (nstub->isOwnGetter())
-            return nullptr;
-
-        Shape* shape = nstub->globalShape();
-        MOZ_ASSERT(shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL);
-        return shape;
-    }
-
-    return nullptr;
-}
-
 static bool
 MatchCacheIRReceiverGuard(CacheIRReader& reader, ICCacheIR_Monitored* stub, ObjOperandId objId,
                           ReceiverGuard* receiver)
 {
     // This matches the CacheIR emitted in TestMatchingReceiver.
     //
     // Either:
     //
@@ -730,21 +716,98 @@ MatchCacheIRReceiverGuard(CacheIRReader&
     if (!reader.matchOp(CacheOp::GuardShape, expandoId))
         return false;
 
     receiver->shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
     return true;
 }
 
 static bool
+AddCacheIRGlobalGetter(ICCacheIR_Monitored* stub, bool innerized,
+                       JSObject** holder_, Shape** holderShape_,
+                       JSFunction** commonGetter, Shape** globalShape_, bool* isOwnProperty,
+                       BaselineInspector::ReceiverVector& receivers,
+                       BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
+                       JSScript* script)
+{
+    // We are matching on the IR generated by tryAttachGlobalNameGetter:
+    //
+    //   GuardShape objId
+    //   globalId = LoadEnclosingEnvironment objId
+    //   GuardShape globalId
+    //   <holderId = LoadObject <holder>>
+    //   <GuardShape holderId>
+    //   CallNativeGetterResult globalId
+
+    CacheIRReader reader(stub->stubInfo());
+
+    ObjOperandId objId = ObjOperandId(0);
+    if (!reader.matchOp(CacheOp::GuardShape, objId))
+        return false;
+    Shape* globalLexicalShape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
+
+    if (!reader.matchOp(CacheOp::LoadEnclosingEnvironment, objId))
+        return false;
+    ObjOperandId globalId = reader.objOperandId();
+
+    if (!reader.matchOp(CacheOp::GuardShape, globalId))
+        return false;
+    Shape* globalShape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
+    MOZ_ASSERT(globalShape->getObjectClass()->flags & JSCLASS_IS_GLOBAL);
+
+    JSObject* holder = &script->global();
+    Shape* holderShape = globalShape;
+    if (reader.matchOp(CacheOp::LoadObject)) {
+        ObjOperandId holderId = reader.objOperandId();
+        holder = stub->stubInfo()->getStubField<JSObject*>(stub, reader.stubOffset()).get();
+
+        if (!reader.matchOp(CacheOp::GuardShape, holderId))
+            return false;
+        holderShape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
+    }
+
+    // This guard will always fail, try the next stub.
+    if (holder->as<NativeObject>().lastProperty() != holderShape)
+        return true;
+
+    if (!reader.matchOp(CacheOp::CallNativeGetterResult, globalId))
+        return false;
+    size_t offset = reader.stubOffset();
+    JSFunction* getter =
+        &stub->stubInfo()->getStubField<JSObject*>(stub, offset)->as<JSFunction>();
+
+    ReceiverGuard receiver;
+    receiver.shape = globalLexicalShape;
+    if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
+        return false;
+
+    if (!*commonGetter) {
+        *holder_ = holder;
+        *holderShape_ = holderShape;
+        *commonGetter = getter;
+        *globalShape_ = globalShape;
+
+        // This is always false, because the getters never live on the globalLexical.
+        *isOwnProperty = false;
+    } else if (*isOwnProperty || holderShape != *holderShape_ || globalShape != *globalShape_) {
+        return false;
+    } else {
+        MOZ_ASSERT(*commonGetter == getter);
+    }
+
+    return true;
+}
+
+static bool
 AddCacheIRGetPropFunction(ICCacheIR_Monitored* stub, bool innerized,
                           JSObject** holder, Shape** holderShape,
                           JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty,
                           BaselineInspector::ReceiverVector& receivers,
-                          BaselineInspector::ObjectGroupVector& convertUnboxedGroups)
+                          BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
+                          JSScript* script)
 {
     // We match either an own getter:
     //
     //   GuardIsObject objId
     //   [..WindowProxy innerization..]
     //   <GuardReceiver objId>
     //   Call(Scripted|Native)GetterResult objId
     //
@@ -763,18 +826,21 @@ AddCacheIRGetPropFunction(ICCacheIR_Moni
     // [..WindowProxy innerization..] above:
     //
     //   GuardClass objId WindowProxy
     //   objId = LoadObject <global>
 
     CacheIRReader reader(stub->stubInfo());
 
     ObjOperandId objId = ObjOperandId(0);
-    if (!reader.matchOp(CacheOp::GuardIsObject, objId))
-        return false;
+    if (!reader.matchOp(CacheOp::GuardIsObject, objId)) {
+        return AddCacheIRGlobalGetter(stub, innerized, holder, holderShape, commonGetter,
+                                      globalShape, isOwnProperty, receivers, convertUnboxedGroups,
+                                      script);
+    }
 
     if (innerized) {
         if (!reader.matchOp(CacheOp::GuardClass, objId) ||
             reader.guardClassKind() != GuardClassKind::WindowProxy)
         {
             return false;
         }
         if (!reader.matchOp(CacheOp::LoadObject))
@@ -875,41 +941,21 @@ BaselineInspector::commonGetPropFunction
     MOZ_ASSERT(receivers.empty());
     MOZ_ASSERT(convertUnboxedGroups.empty());
 
     *globalShape = nullptr;
     *commonGetter = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
     for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
-        if (stub->isGetProp_CallNativeGlobal()) {
-            ICGetPropCallGetter* nstub = static_cast<ICGetPropCallGetter*>(stub);
-            bool isOwn = nstub->isOwnGetter();
-            if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
-                return false;
-
-            if (!*commonGetter) {
-                *holder = isOwn ? nullptr : nstub->holder().get();
-                *holderShape = nstub->holderShape();
-                *commonGetter = nstub->getter();
-                *globalShape = GlobalShapeForGetPropFunction(nstub);
-                *isOwnProperty = isOwn;
-            } else if (nstub->holderShape() != *holderShape ||
-                       GlobalShapeForGetPropFunction(nstub) != *globalShape ||
-                       isOwn != *isOwnProperty)
-            {
-                return false;
-            } else {
-                MOZ_ASSERT(*commonGetter == nstub->getter());
-            }
-        } else if (stub->isCacheIR_Monitored()) {
+        if (stub->isCacheIR_Monitored()) {
             if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), innerized,
                                            holder, holderShape,
                                            commonGetter, globalShape, isOwnProperty, receivers,
-                                           convertUnboxedGroups))
+                                           convertUnboxedGroups, script))
             {
                 return false;
             }
         } else if (stub->isGetProp_Fallback()) {
             // If we have an unoptimizable access, don't try to optimize.
             if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
                 return false;
         } else if (stub->isGetName_Fallback()) {
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -25,22 +25,24 @@ IRGenerator::IRGenerator(JSContext* cx, 
     cx_(cx),
     pc_(pc),
     cacheKind_(cacheKind)
 {}
 
 GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind,
                                        ICStubEngine engine,
                                        bool* isTemporarilyUnoptimizable,
-                                       HandleValue val, HandleValue idVal)
+                                       HandleValue val, HandleValue idVal,
+                                       CanAttachGetter canAttachGetter)
   : IRGenerator(cx, pc, cacheKind),
     val_(val),
     idVal_(idVal),
     engine_(engine),
     isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
+    canAttachGetter_(canAttachGetter),
     preliminaryObjectAction_(PreliminaryObjectAction::None)
 {}
 
 static void
 EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder,
                    Shape* shape)
 {
     if (holder->isFixedSlot(shape->slot())) {
@@ -218,17 +220,18 @@ enum NativeGetPropCacheability {
     CanAttachNone,
     CanAttachReadSlot,
     CanAttachCallGetter,
 };
 
 static NativeGetPropCacheability
 CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id,
                        MutableHandleNativeObject holder, MutableHandleShape shape,
-                       jsbytecode* pc, ICStubEngine engine, bool* isTemporarilyUnoptimizable)
+                       jsbytecode* pc, ICStubEngine engine, CanAttachGetter canAttachGetter,
+                       bool* isTemporarilyUnoptimizable)
 {
     MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
 
     // The lookup needs to be universally pure, otherwise we risk calling hooks out
     // of turn. We don't mind doing this even when purity isn't required, because we
     // only miss out on shape hashification, which is only a temporary perf cost.
     // The limits were arbitrarily set, anyways.
     JSObject* baseHolder = nullptr;
@@ -248,16 +251,19 @@ CanAttachNativeGetProp(JSContext* cx, Ha
     // Idempotent ICs only support plain data properties, see
     // tryAttachIdempotentStub.
     if (!pc)
         return CanAttachNone;
 
     if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc))
         return CanAttachReadSlot;
 
+    if (canAttachGetter == CanAttachGetter::No)
+        return CanAttachNone;
+
     if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable)) {
         // See bug 1226816.
         if (engine != ICStubEngine::IonSharedIC)
             return CanAttachCallGetter;
     }
 
     if (IsCacheableGetPropCallNative(obj, holder, shape))
         return CanAttachCallGetter;
@@ -420,17 +426,18 @@ EmitCallGetterResult(CacheIRWriter& writ
 
 bool
 GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id)
 {
     RootedShape shape(cx_);
     RootedNativeObject holder(cx_);
 
     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_,
-                                                            engine_, isTemporarilyUnoptimizable_);
+                                                            engine_, canAttachGetter_,
+                                                            isTemporarilyUnoptimizable_);
     MOZ_ASSERT_IF(idempotent(),
                   type == CanAttachNone || (type == CanAttachReadSlot && holder));
     switch (type) {
       case CanAttachNone:
         return false;
       case CanAttachReadSlot:
         maybeEmitIdGuard(id);
         if (holder) {
@@ -471,17 +478,18 @@ GetPropIRGenerator::tryAttachWindowProxy
     MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
 
     // Now try to do the lookup on the Window (the current global) and see if
     // it's a native getter.
     HandleObject windowObj = cx_->global();
     RootedShape shape(cx_);
     RootedNativeObject holder(cx_);
     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_,
-                                                            engine_, isTemporarilyUnoptimizable_);
+                                                            engine_, canAttachGetter_,
+                                                            isTemporarilyUnoptimizable_);
     if (type != CanAttachCallGetter ||
         !IsCacheableGetPropCallNative(windowObj, holder, shape))
     {
         return false;
     }
 
     // Make sure the native getter is okay with the IC passing the Window
     // instead of the WindowProxy as |this| value.
@@ -579,17 +587,17 @@ GetPropIRGenerator::tryAttachDOMProxyUns
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
     RootedObject checkObj(cx_, obj->staticPrototype());
     RootedNativeObject holder(cx_);
     RootedShape shape(cx_);
 
     NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape,
-                                                                pc_, engine_,
+                                                                pc_, engine_, canAttachGetter_,
                                                                 isTemporarilyUnoptimizable_);
     MOZ_ASSERT_IF(idempotent(),
                   canCache == CanAttachNone || (canCache == CanAttachReadSlot && holder));
     if (canCache == CanAttachNone)
         return false;
 
     maybeEmitIdGuard(id);
     writer.guardShape(objId, obj->maybeShape());
@@ -1148,93 +1156,152 @@ GetNameIRGenerator::tryAttachStub()
 
     AutoAssertNoPendingException aanpe(cx_);
 
     ObjOperandId envId(writer.setInputOperandId(0));
     RootedId id(cx_, NameToId(name_));
 
     if (tryAttachGlobalNameValue(envId, id))
         return true;
+    if (tryAttachGlobalNameGetter(envId, id))
+        return true;
     if (tryAttachEnvironmentName(envId, id))
         return true;
 
     return false;
 }
 
 bool
+CanAttachGlobalName(JSContext* cx, Handle<LexicalEnvironmentObject*> globalLexical, HandleId id,
+                    MutableHandleNativeObject holder, MutableHandleShape shape)
+{
+    // The property must be found, and it must be found as a normal data property.
+    RootedNativeObject current(cx, globalLexical);
+    while (true) {
+        shape.set(current->lookup(cx, id));
+        if (shape)
+            break;
+
+        if (current == globalLexical) {
+            current = &globalLexical->global();
+        } else {
+            // In the browser the global prototype chain should be immutable.
+            if (!current->staticPrototypeIsImmutable())
+                return false;
+
+            JSObject* proto = current->staticPrototype();
+            if (!proto || !proto->is<NativeObject>())
+                return false;
+
+            current = &proto->as<NativeObject>();
+        }
+    }
+
+    holder.set(current);
+    return true;
+}
+
+bool
 GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
 {
     if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
         return false;
 
     Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
     MOZ_ASSERT(globalLexical->isGlobal());
 
-    // The property must be found, and it must be found as a normal data property.
+
+    RootedNativeObject holder(cx_);
     RootedShape shape(cx_);
-    RootedNativeObject current(cx_, globalLexical);
-    while (true) {
-        shape = current->lookup(cx_, id);
-        if (shape)
-            break;
-        if (current == globalLexical) {
-            current = &globalLexical->global();
-        } else {
-            // In the browser the global prototype chain should be immutable.
-            if (!current->staticPrototypeIsImmutable())
-                return false;
-            JSObject* proto = current->staticPrototype();
-            if (!proto || !proto->is<NativeObject>())
-                return false;
-            current = &proto->as<NativeObject>();
-        }
-    }
+    if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
+        return false;
 
+    // The property must be found, and it must be found as a normal data property.
     if (!shape->hasDefaultGetter() || !shape->hasSlot())
         return false;
 
     // Instantiate this global property, for use during Ion compilation.
     if (IsIonEnabled(cx_))
-        EnsureTrackPropertyTypes(cx_, current, id);
+        EnsureTrackPropertyTypes(cx_, holder, id);
 
-    if (current == globalLexical) {
+    if (holder == globalLexical) {
         // There is no need to guard on the shape. Lexical bindings are
         // non-configurable, and this stub cannot be shared across globals.
-        size_t dynamicSlotOffset = current->dynamicSlotIndex(shape->slot()) * sizeof(Value);
+        size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
         writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
     } else {
-        // Check the prototype chain from the global to the current
+        // Check the prototype chain from the global to the holder
         // prototype. Ignore the global lexical scope as it doesn't figure
         // into the prototype chain. We guard on the global lexical
         // scope's shape independently.
-        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), current, shape))
+        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), holder, shape))
             return false;
 
         // Shape guard for global lexical.
         writer.guardShape(objId, globalLexical->lastProperty());
 
         // Guard on the shape of the GlobalObject.
         ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
         writer.guardShape(globalId, globalLexical->global().lastProperty());
 
         ObjOperandId holderId = globalId;
-        if (current != &globalLexical->global()) {
+        if (holder != &globalLexical->global()) {
             // Shape guard holder.
-            holderId = writer.loadObject(current);
-            writer.guardShape(holderId, current->lastProperty());
+            holderId = writer.loadObject(holder);
+            writer.guardShape(holderId, holder->lastProperty());
         }
 
-        EmitLoadSlotResult(writer, holderId, current, shape);
+        EmitLoadSlotResult(writer, holderId, holder, shape);
     }
 
     writer.typeMonitorResult();
     return true;
 }
 
 bool
+GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id)
+{
+    if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
+        return false;
+
+    Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
+    MOZ_ASSERT(globalLexical->isGlobal());
+
+    RootedNativeObject holder(cx_);
+    RootedShape shape(cx_);
+    if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
+        return false;
+
+    if (holder == globalLexical)
+        return false;
+
+    if (!IsCacheableGetPropCallNative(&globalLexical->global(), holder, shape))
+        return false;
+
+    if (IsIonEnabled(cx_))
+        EnsureTrackPropertyTypes(cx_, holder, id);
+
+    // Shape guard for global lexical.
+    writer.guardShape(objId, globalLexical->lastProperty());
+
+    // Guard on the shape of the GlobalObject.
+    ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
+    writer.guardShape(globalId, globalLexical->global().lastProperty());
+
+    if (holder != &globalLexical->global()) {
+        // Shape guard holder.
+        ObjOperandId holderId = writer.loadObject(holder);
+        writer.guardShape(holderId, holder->lastProperty());
+    }
+
+    EmitCallGetterResultNoGuards(writer, &globalLexical->global(), holder, shape, globalId);
+    return true;
+}
+
+bool
 GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
 {
     if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
         return false;
 
     RootedObject env(cx_, env_);
     RootedShape shape(cx_);
     RootedNativeObject holder(cx_);
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -715,23 +715,26 @@ class MOZ_RAII IRGenerator
 
   public:
     explicit IRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind);
 
     const CacheIRWriter& writerRef() const { return writer; }
     CacheKind cacheKind() const { return cacheKind_; }
 };
 
+enum class CanAttachGetter { Yes, No };
+
 // GetPropIRGenerator generates CacheIR for a GetProp IC.
 class MOZ_RAII GetPropIRGenerator : public IRGenerator
 {
     HandleValue val_;
     HandleValue idVal_;
     ICStubEngine engine_;
     bool* isTemporarilyUnoptimizable_;
+    CanAttachGetter canAttachGetter_;
 
     enum class PreliminaryObjectAction { None, Unlink, NotePreliminary };
     PreliminaryObjectAction preliminaryObjectAction_;
 
     bool tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id);
@@ -772,17 +775,18 @@ class MOZ_RAII GetPropIRGenerator : publ
     bool idempotent() const { return pc_ == nullptr; }
 
     // If this is a GetElem cache, emit instructions to guard the incoming Value
     // matches |id|.
     void maybeEmitIdGuard(jsid id);
 
   public:
     GetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind, ICStubEngine engine,
-                       bool* isTemporarilyUnoptimizable, HandleValue val, HandleValue idVal);
+                       bool* isTemporarilyUnoptimizable, HandleValue val, HandleValue idVal,
+                       CanAttachGetter canAttachGetter);
 
     bool tryAttachStub();
     bool tryAttachIdempotentStub();
 
     bool shouldUnlinkPreliminaryObjectStubs() const {
         return preliminaryObjectAction_ == PreliminaryObjectAction::Unlink;
     }
     bool shouldNotePreliminaryObjectStub() const {
@@ -793,16 +797,17 @@ class MOZ_RAII GetPropIRGenerator : publ
 // GetPropIRGenerator generates CacheIR for a GetName IC.
 class MOZ_RAII GetNameIRGenerator : public IRGenerator
 {
     HandleScript script_;
     HandleObject env_;
     HandlePropertyName name_;
 
     bool tryAttachGlobalNameValue(ObjOperandId objId, HandleId id);
+    bool tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id);
     bool tryAttachEnvironmentName(ObjOperandId objId, HandleId id);
 
   public:
     GetNameIRGenerator(JSContext* cx, jsbytecode* pc, HandleScript script,
                        HandleObject env, HandlePropertyName name);
 
     bool tryAttachStub();
 };
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -122,22 +122,28 @@ IonGetPropertyIC::update(JSContext* cx, 
 
     // If the IC is idempotent, we will redo the op in the interpreter.
     if (ic->idempotent())
         adi.disable();
 
     bool attached = false;
     if (!JitOptions.disableCacheIR && !ic->disabled()) {
         if (ic->canAttachStub()) {
+            // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it
+            // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier
+            // does not account for getters, so we should only attach a getter
+            // stub if we inserted a type barrier.
+            CanAttachGetter canAttachGetter =
+                ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No;
             jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc();
             RootedValue objVal(cx, ObjectValue(*obj));
             bool isTemporarilyUnoptimizable;
             GetPropIRGenerator gen(cx, pc, ic->kind(), ICStubEngine::IonIC,
                                    &isTemporarilyUnoptimizable,
-                                   objVal, idVal);
+                                   objVal, idVal, canAttachGetter);
             if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub()) {
                 attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                  outerScript);
             }
         }
         ic->maybeDisable(cx->zone(), attached);
     }
 
--- a/js/src/jit/JitAllocPolicy.h
+++ b/js/src/jit/JitAllocPolicy.h
@@ -168,17 +168,17 @@ struct TempObject
         return pos;
     }
     template <class T>
     inline void* operator new(size_t nbytes, mozilla::NotNullTag, T* pos) {
         static_assert(mozilla::IsConvertible<T*, TempObject*>::value,
                       "Placement new argument type must inherit from TempObject");
         MOZ_ASSERT(pos);
         return pos;
-    }        
+    }
 };
 
 template <typename T>
 class TempObjectPool
 {
     TempAllocator* alloc_;
     InlineForwardList<T> freed_;
 
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -232,16 +232,26 @@ DefaultJitOptions::DefaultJitOptions()
 
     // Test whether wasm bounds check should always be generated.
     SET_DEFAULT(wasmAlwaysCheckBounds, false);
 
     // Toggles the optimization whereby offsets are folded into loads and not
     // included in the bounds check.
     SET_DEFAULT(wasmFoldOffsets, true);
 
+    // Until which wasm bytecode size should we accumulate functions, in order
+    // to compile efficiently on helper threads (see also bug 1320374).
+    SET_DEFAULT(wasmBatchThreshold, 10000);
+
+    // In order to have different batching thresholds for Ion and the wasm
+    // baseline, and since a same batch can contain both Ion and baseline
+    // compiled functions, we make Ion functions weight more by using a scaling
+    // factor.
+    SET_DEFAULT(wasmBatchIonScaleFactor, 9);
+
     // Determines whether we suppress using signal handlers
     // for interrupting jit-ed code. This is used only for testing.
     SET_DEFAULT(ionInterruptWithoutSignals, false);
 }
 
 bool
 DefaultJitOptions::isSmallFunction(JSScript* script) const
 {
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -82,16 +82,18 @@ struct DefaultJitOptions
     uint32_t osrPcMismatchesBeforeRecompile;
     uint32_t smallFunctionMaxBytecodeLength_;
     uint32_t jumpThreshold;
     uint32_t branchPruningHitCountFactor;
     uint32_t branchPruningInstFactor;
     uint32_t branchPruningBlockSpanFactor;
     uint32_t branchPruningEffectfulInstFactor;
     uint32_t branchPruningThreshold;
+    uint32_t wasmBatchThreshold;
+    uint32_t wasmBatchIonScaleFactor;
     mozilla::Maybe<uint32_t> forcedDefaultIonWarmUpThreshold;
     mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold;
     mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
 
     // The options below affect the rest of the VM, and not just the JIT.
     bool disableUnboxedObjects;
 
     DefaultJitOptions();
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -170,17 +170,16 @@ ICStub::NonCacheIRStubMakesGCCalls(Kind 
       case Call_AnyScripted:
       case Call_Native:
       case Call_ClassHook:
       case Call_ScriptedApplyArray:
       case Call_ScriptedApplyArguments:
       case Call_ScriptedFunCall:
       case Call_StringSplit:
       case WarmUpCounter_Fallback:
-      case GetProp_CallNativeGlobal:
       case GetProp_Generic:
       case SetProp_CallScripted:
       case SetProp_CallNative:
       case RetSub_Fallback:
       // These two fallback stubs don't actually make non-tail calls,
       // but the fallback code for the bailout path needs to pop the stub frame
       // pushed during the bailout.
       case GetProp_Fallback:
@@ -347,25 +346,16 @@ ICStub::trace(JSTracer* trc)
         TraceEdge(trc, &inStub->shape(), "baseline-in-dense-shape");
         break;
       }
       case ICStub::GetIntrinsic_Constant: {
         ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant();
         TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value");
         break;
       }
-      case ICStub::GetProp_CallNativeGlobal: {
-        ICGetProp_CallNativeGlobal* callStub = toGetProp_CallNativeGlobal();
-        callStub->receiverGuard().trace(trc);
-        TraceEdge(trc, &callStub->holder(), "baseline-getpropcallnativeglobal-stub-holder");
-        TraceEdge(trc, &callStub->holderShape(), "baseline-getpropcallnativeglobal-stub-holdershape");
-        TraceEdge(trc, &callStub->globalShape(), "baseline-getpropcallnativeglobal-stub-globalshape");
-        TraceEdge(trc, &callStub->getter(), "baseline-getpropcallnativeglobal-stub-getter");
-        break;
-      }
       case ICStub::SetProp_Native: {
         ICSetProp_Native* propStub = toSetProp_Native();
         TraceEdge(trc, &propStub->shape(), "baseline-setpropnative-stub-shape");
         TraceEdge(trc, &propStub->group(), "baseline-setpropnative-stub-group");
         break;
       }
       case ICStub::SetProp_NativeAdd: {
         ICSetProp_NativeAdd* propStub = toSetProp_NativeAdd();
@@ -2103,78 +2093,16 @@ IsCacheableGetPropCall(JSContext* cx, JS
         *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     *isScripted = true;
     return true;
 }
 
-// Try to update all existing GetProp/GetName getter call stubs that match the
-// given holder in place with a new shape and getter.  fallbackStub can be
-// either an ICGetProp_Fallback or an ICGetName_Fallback.
-//
-// If 'getter' is an own property, holder == receiver must be true.
-bool
-UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub,
-                               ICStub::Kind kind,
-                               HandleNativeObject holder,
-                               HandleObject receiver,
-                               HandleFunction getter)
-{
-    MOZ_ASSERT(kind == ICStub::GetProp_CallNativeGlobal);
-    MOZ_ASSERT(fallbackStub->isGetName_Fallback() ||
-               fallbackStub->isGetProp_Fallback());
-    MOZ_ASSERT(holder);
-    MOZ_ASSERT(receiver);
-
-    bool isOwnGetter = (holder == receiver);
-    bool foundMatchingStub = false;
-    ReceiverGuard receiverGuard(receiver);
-    for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
-        if (iter->kind() == kind) {
-            ICGetPropCallGetter* getPropStub = static_cast<ICGetPropCallGetter*>(*iter);
-            if (getPropStub->holder() == holder && getPropStub->isOwnGetter() == isOwnGetter) {
-                // If this is an own getter, update the receiver guard as well,
-                // since that's the shape we'll be guarding on. Furthermore,
-                // isOwnGetter() relies on holderShape_ and receiverGuard_ being
-                // the same shape.
-                if (isOwnGetter)
-                    getPropStub->receiverGuard().update(receiverGuard);
-
-                MOZ_ASSERT(getPropStub->holderShape() != holder->lastProperty() ||
-                           !getPropStub->receiverGuard().matches(receiverGuard) ||
-                           getPropStub->toGetProp_CallNativeGlobal()->globalShape() !=
-                           receiver->as<LexicalEnvironmentObject>().global().lastProperty(),
-                           "Why didn't we end up using this stub?");
-
-                // We want to update the holder shape to match the new one no
-                // matter what, even if the receiver shape is different.
-                getPropStub->holderShape() = holder->lastProperty();
-
-                // Make sure to update the getter, since a shape change might
-                // have changed which getter we want to use.
-                getPropStub->getter() = getter;
-
-                if (getPropStub->isGetProp_CallNativeGlobal()) {
-                    ICGetProp_CallNativeGlobal* globalStub =
-                        getPropStub->toGetProp_CallNativeGlobal();
-                    globalStub->globalShape() =
-                        receiver->as<LexicalEnvironmentObject>().global().lastProperty();
-                }
-
-                if (getPropStub->receiverGuard().matches(receiverGuard))
-                    foundMatchingStub = true;
-            }
-        }
-    }
-
-    return foundMatchingStub;
-}
-
 bool
 CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id,
                        JSObject** lastProto, size_t* protoChainDepthOut)
 {
     size_t depth = 0;
     JSObject* curObj = obj;
     while (curObj) {
         if (curObj->isNative()) {
@@ -2291,17 +2219,17 @@ DoGetPropFallback(JSContext* cx, void* p
             return false;
         stub->addNewStub(newStub);
         attached = true;
     }
 
     if (!attached && !JitOptions.disableCacheIR) {
         RootedValue idVal(cx, StringValue(name));
         GetPropIRGenerator gen(cx, pc, CacheKind::GetProp, engine, &isTemporarilyUnoptimizable,
-                               val, idVal);
+                               val, idVal, CanAttachGetter::Yes);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         engine, info.outerScript(cx), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
@@ -2425,172 +2353,31 @@ GuardReceiverObject(MacroAssembler& masm
             masm.bind(&done);
             masm.pop(object);
         } else {
             masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
         }
     }
 }
 
-static void
-GuardGlobalObject(MacroAssembler& masm, HandleObject holder, Register globalLexicalReg,
-                  Register holderReg, Register scratch, size_t globalShapeOffset, Label* failure)
-{
-    if (holder->is<GlobalObject>())
-        return;
-    masm.extractObject(Address(globalLexicalReg, EnvironmentObject::offsetOfEnclosingEnvironment()),
-                       holderReg);
-    masm.loadPtr(Address(ICStubReg, globalShapeOffset), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, failure);
-}
-
 bool
 GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle<ShapeVector> shapes)
 {
     JSObject* curProto = obj->staticPrototype();
     for (size_t i = 0; i < protoChainDepth; i++) {
         if (!shapes.append(curProto->as<NativeObject>().lastProperty()))
             return false;
         curProto = curProto->staticPrototype();
     }
 
     MOZ_ASSERT(!curProto,
                "longer prototype chain encountered than this stub permits!");
     return true;
 }
 
-//
-// VM function to help call native getters.
-//
-
-bool
-DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
-                   MutableHandleValue result)
-{
-    MOZ_ASSERT(callee->isNative());
-    JSNative natfun = callee->native();
-
-    JS::AutoValueArray<2> vp(cx);
-    vp[0].setObject(*callee.get());
-    vp[1].setObject(*obj.get());
-
-    if (!natfun(cx, 0, vp.begin()))
-        return false;
-
-    result.set(vp[0]);
-    return true;
-}
-
-typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
-static const VMFunction DoCallNativeGetterInfo =
-    FunctionInfo<DoCallNativeGetterFn>(DoCallNativeGetter, "DoCallNativeGetter");
-
-bool
-ICGetPropCallNativeCompiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
-    Register objReg = InvalidReg;
-
-    if (inputDefinitelyObject_) {
-        objReg = R0.scratchReg();
-    } else {
-        // Guard input is an object and unbox.
-        masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-        objReg = masm.extractObject(R0, ExtractTemp0);
-    }
-
-    Register scratch = regs.takeAnyExcluding(ICTailCallReg);
-
-    // Shape guard.
-    GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
-                        ICGetPropCallGetter::offsetOfReceiverGuard(), &failure);
-
-    if (receiver_ != holder_) {
-        Register holderReg = regs.takeAny();
-
-        // If we are generating a non-lexical GETGNAME stub, we must also
-        // guard on the shape of the GlobalObject.
-        if (kind == ICStub::GetProp_CallNativeGlobal) {
-            MOZ_ASSERT(receiver_->is<LexicalEnvironmentObject>() &&
-                       receiver_->as<LexicalEnvironmentObject>().isGlobal());
-            GuardGlobalObject(masm, holder_, objReg, holderReg, scratch,
-                              ICGetProp_CallNativeGlobal::offsetOfGlobalShape(), &failure);
-        }
-
-        masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfHolder()), holderReg);
-        masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfHolderShape()), scratch);
-        masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure);
-        regs.add(holderReg);
-    }
-
-    // Box and push obj onto baseline frame stack for decompiler
-    if (engine_ == Engine::Baseline) {
-        if (inputDefinitelyObject_)
-            masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0);
-        EmitStowICValues(masm, 1);
-        if (inputDefinitelyObject_)
-            objReg = masm.extractObject(R0, ExtractTemp0);
-    }
-
-    // Push a stub frame so that we can perform a non-tail call.
-    enterStubFrame(masm, scratch);
-
-    // Load callee function.
-    Register callee = regs.takeAny();
-    masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfGetter()), callee);
-
-    // If we're calling a getter on the global, inline the logic for the
-    // 'this' hook on the global lexical scope and manually push the global.
-    if (kind == ICStub::GetProp_CallNativeGlobal)
-        masm.extractObject(Address(objReg, EnvironmentObject::offsetOfEnclosingEnvironment()),
-                           objReg);
-
-    // Push args for vm call.
-    masm.Push(objReg);
-    masm.Push(callee);
-
-    regs.add(R0);
-
-    if (!callVM(DoCallNativeGetterInfo, masm))
-        return false;
-    leaveStubFrame(masm);
-
-    if (engine_ == Engine::Baseline)
-        EmitUnstowICValues(masm, 1, /* discard = */true);
-
-    // Enter type monitor IC to type-check result.
-    EmitEnterTypeMonitorIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-ICStub*
-ICGetPropCallNativeCompiler::getStub(ICStubSpace* space)
-{
-    ReceiverGuard guard(receiver_);
-    Shape* holderShape = holder_->as<NativeObject>().lastProperty();
-
-    switch (kind) {
-      case ICStub::GetProp_CallNativeGlobal: {
-        Shape* globalShape = receiver_->as<LexicalEnvironmentObject>().global().lastProperty();
-        return newStub<ICGetProp_CallNativeGlobal>(space, getStubCode(), firstMonitorStub_,
-                                                   guard, holder_, holderShape, globalShape,
-                                                   getter_, pcOffset_);
-      }
-
-      default:
-        MOZ_CRASH("Bad stub kind");
-    }
-}
-
 /* static */ ICGetProp_Generic*
 ICGetProp_Generic::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
                          ICGetProp_Generic& other)
 {
     return New<ICGetProp_Generic>(cx, space, other.jitCode(), firstMonitorStub);
 }
 
 static bool
@@ -2673,41 +2460,16 @@ BaselineScript::noteAccessedGetter(uint3
 {
     ICEntry& entry = icEntryFromPCOffset(pcOffset);
     ICFallbackStub* stub = entry.fallbackStub();
 
     if (stub->isGetProp_Fallback())
         stub->toGetProp_Fallback()->noteAccessedGetter();
 }
 
-ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                                         ReceiverGuard receiverGuard, JSObject* holder,
-                                         Shape* holderShape, JSFunction* getter,
-                                         uint32_t pcOffset)
-  : ICMonitoredStub(kind, stubCode, firstMonitorStub),
-    receiverGuard_(receiverGuard),
-    holder_(holder),
-    holderShape_(holderShape),
-    getter_(getter),
-    pcOffset_(pcOffset)
-{
-    MOZ_ASSERT(kind == ICStub::GetProp_CallNativeGlobal);
-}
-
-/* static */ ICGetProp_CallNativeGlobal*
-ICGetProp_CallNativeGlobal::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
-                            ICGetProp_CallNativeGlobal& other)
-{
-    return New<ICGetProp_CallNativeGlobal>(cx, space, other.jitCode(), firstMonitorStub,
-                                           other.receiverGuard(), other.holder_,
-                                           other.holderShape_, other.globalShape_,
-                                           other.getter_, other.pcOffset_);
-}
-
-//
 // TypeMonitor_Fallback
 //
 
 bool
 ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, SharedStubInfo* info, HandleValue val)
 {
     bool wasDetachedMonitorChain = lastMonitorStubPtrAddr_ == nullptr;
     MOZ_ASSERT_IF(wasDetachedMonitorChain, numOptimizedMonitorStubs_ == 0);
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -2251,20 +2251,16 @@ GuardReceiverObject(MacroAssembler& masm
                     size_t receiverGuardOffset, Label* failure);
 
 MOZ_MUST_USE bool
 GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle<ShapeVector> shapes);
 
 void
 CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Label* failure);
 
-MOZ_MUST_USE bool
-DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
-                   MutableHandleValue result);
-
 void
 LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result);
 
 bool
 IsPrimitiveArrayTypedObject(JSObject* obj);
 
 Scalar::Type
 TypedThingElementType(JSObject* obj);
@@ -2379,168 +2375,16 @@ ScalarTypeFromSimpleTypeDescrKey(uint32_
 
 inline ReferenceTypeDescr::Type
 ReferenceTypeFromSimpleTypeDescrKey(uint32_t key)
 {
     MOZ_ASSERT(!SimpleTypeDescrKeyIsScalar(key));
     return ReferenceTypeDescr::Type(key >> 1);
 }
 
-class ICGetPropCallGetter : public ICMonitoredStub
-{
-    friend class ICStubSpace;
-
-  protected:
-    // Shape/group of receiver object. Used for both own and proto getters.
-    // In the GetPropCallDOMProxyNative case, the receiver guard enforces
-    // the proxy handler, because Shape implies Class.
-    HeapReceiverGuard receiverGuard_;
-
-    // Holder and holder shape. For own getters, guarding on receiverGuard_ is
-    // sufficient, although Ion may use holder_ and holderShape_ even for own
-    // getters. In this case holderShape_ == receiverGuard_.shape_ (isOwnGetter
-    // below relies on this).
-    GCPtrObject holder_;
-
-    GCPtrShape holderShape_;
-
-    // Function to call.
-    GCPtrFunction getter_;
-
-    // PC offset of call
-    uint32_t pcOffset_;
-
-    ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                        ReceiverGuard receiverGuard, JSObject* holder,
-                        Shape* holderShape, JSFunction* getter, uint32_t pcOffset);
-
-  public:
-    GCPtrObject& holder() {
-        return holder_;
-    }
-    GCPtrShape& holderShape() {
-        return holderShape_;
-    }
-    GCPtrFunction& getter() {
-        return getter_;
-    }
-    HeapReceiverGuard& receiverGuard() {
-        return receiverGuard_;
-    }
-
-    bool isOwnGetter() const {
-        MOZ_ASSERT(holder_->isNative());
-        MOZ_ASSERT(holderShape_);
-        return receiverGuard_.shape() == holderShape_;
-    }
-
-    static size_t offsetOfHolder() {
-        return offsetof(ICGetPropCallGetter, holder_);
-    }
-    static size_t offsetOfHolderShape() {
-        return offsetof(ICGetPropCallGetter, holderShape_);
-    }
-    static size_t offsetOfGetter() {
-        return offsetof(ICGetPropCallGetter, getter_);
-    }
-    static size_t offsetOfPCOffset() {
-        return offsetof(ICGetPropCallGetter, pcOffset_);
-    }
-    static size_t offsetOfReceiverGuard() {
-        return offsetof(ICGetPropCallGetter, receiverGuard_);
-    }
-
-    class Compiler : public ICStubCompiler {
-      protected:
-        ICStub* firstMonitorStub_;
-        RootedObject receiver_;
-        RootedObject holder_;
-        RootedFunction getter_;
-        uint32_t pcOffset_;
-
-        virtual int32_t getKey() const {
-            // ICGetPropCallNativeCompiler::getKey adds more bits to our
-            // return value, so be careful when making changes here.
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (HeapReceiverGuard::keyBits(receiver_) << 17) |
-                  (static_cast<int32_t>(receiver_ != holder_) << 19);
-        }
-
-      public:
-        Compiler(JSContext* cx, ICStub::Kind kind, Engine engine, ICStub* firstMonitorStub,
-                 HandleObject receiver, HandleObject holder, HandleFunction getter,
-                 uint32_t pcOffset)
-          : ICStubCompiler(cx, kind, engine),
-            firstMonitorStub_(firstMonitorStub),
-            receiver_(cx, receiver),
-            holder_(cx, holder),
-            getter_(cx, getter),
-            pcOffset_(pcOffset)
-        {
-            MOZ_ASSERT(kind == ICStub::GetProp_CallNativeGlobal);
-        }
-    };
-};
-
-// Stub for calling a native getter on the GlobalObject.
-class ICGetProp_CallNativeGlobal : public ICGetPropCallGetter
-{
-    friend class ICStubSpace;
-
-  protected:
-    GCPtrShape globalShape_;
-
-    ICGetProp_CallNativeGlobal(JitCode* stubCode, ICStub* firstMonitorStub,
-                               ReceiverGuard receiverGuard,
-                               JSObject* holder, Shape* holderShape, Shape* globalShape,
-                               JSFunction* getter, uint32_t pcOffset)
-      : ICGetPropCallGetter(GetProp_CallNativeGlobal, stubCode, firstMonitorStub,
-                            receiverGuard, holder, holderShape, getter, pcOffset),
-        globalShape_(globalShape)
-    { }
-
-  public:
-    static ICGetProp_CallNativeGlobal* Clone(JSContext* cx, ICStubSpace* space,
-                                             ICStub* firstMonitorStub,
-                                             ICGetProp_CallNativeGlobal& other);
-
-    GCPtrShape& globalShape() {
-        return globalShape_;
-    }
-    static size_t offsetOfGlobalShape() {
-        return offsetof(ICGetProp_CallNativeGlobal, globalShape_);
-    }
-};
-
-class ICGetPropCallNativeCompiler : public ICGetPropCallGetter::Compiler
-{
-    bool inputDefinitelyObject_;
-  protected:
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-    virtual int32_t getKey() const {
-        int32_t baseKey = ICGetPropCallGetter::Compiler::getKey();
-        MOZ_ASSERT((baseKey >> 21) == 0);
-        return baseKey | (static_cast<int32_t>(inputDefinitelyObject_) << 21);
-    }
-
-  public:
-    ICGetPropCallNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStubCompiler::Engine engine,
-                                ICStub* firstMonitorStub, HandleObject receiver,
-                                HandleObject holder, HandleFunction getter, uint32_t pcOffset,
-                                bool inputDefinitelyObject = false)
-      : ICGetPropCallGetter::Compiler(cx, kind, engine, firstMonitorStub, receiver, holder,
-                                      getter, pcOffset),
-        inputDefinitelyObject_(inputDefinitelyObject)
-    {}
-
-    ICStub* getStub(ICStubSpace* space);
-};
-
 // JSOP_NEWARRAY
 // JSOP_NEWINIT
 
 class ICNewArray_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
     GCPtrObject templateObject_;
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -30,17 +30,16 @@ namespace jit {
     _(Compare_NumberWithUndefined)               \
     _(Compare_String)                            \
     _(Compare_Boolean)                           \
     _(Compare_Object)                            \
     _(Compare_ObjectWithUndefined)               \
     _(Compare_Int32WithBoolean)                  \
                                                  \
     _(GetProp_Fallback)                          \
-    _(GetProp_CallNativeGlobal)                  \
     _(GetProp_Generic)                           \
                                                  \
     _(CacheIR_Monitored)                         \
                                                  \
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1368,16 +1368,34 @@ ProxyGetPropertyByValue(JSContext* cx, H
     if (!ValueToId<CanGC>(cx, idVal, &id))
         return false;
 
     RootedValue receiver(cx, ObjectValue(*proxy));
     return Proxy::get(cx, proxy, receiver, id, vp);
 }
 
 bool
+CallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
+                 MutableHandleValue result)
+{
+    MOZ_ASSERT(callee->isNative());
+    JSNative natfun = callee->native();
+
+    JS::AutoValueArray<2> vp(cx);
+    vp[0].setObject(*callee.get());
+    vp[1].setObject(*obj.get());
+
+    if (!natfun(cx, 0, vp.begin()))
+        return false;
+
+    result.set(vp[0]);
+    return true;
+}
+
+bool
 EqualStringsHelper(JSString* str1, JSString* str2)
 {
     MOZ_ASSERT(str1->isAtom());
     MOZ_ASSERT(!str2->isAtom());
     MOZ_ASSERT(str1->length() == str2->length());
 
     JSLinearString* str2Linear = str2->ensureLinear(nullptr);
     if (!str2Linear)
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -814,14 +814,19 @@ BaselineGetFunctionThis(JSContext* cx, B
 MOZ_MUST_USE bool
 ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp);
 
 MOZ_MUST_USE bool
 ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal,
                         MutableHandleValue vp);
 
 MOZ_MUST_USE bool
+CallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
+                 MutableHandleValue result);
+
+
+MOZ_MUST_USE bool
 EqualStringsHelper(JSString* str1, JSString* str2);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -8431,19 +8431,21 @@ static JS::AsmJSCacheResult
 StoreAsmJSModuleInCache(AsmJSParser& parser, Module& module, ExclusiveContext* cx)
 {
     ModuleCharsForStore moduleChars;
     if (!moduleChars.init(parser))
         return JS::AsmJSCache_InternalError;
 
     size_t bytecodeSize, compiledSize;
     module.serializedSize(&bytecodeSize, &compiledSize);
-
-    size_t serializedSize = 2 * sizeof(uint32_t) +
-                            bytecodeSize + compiledSize +
+    MOZ_RELEASE_ASSERT(bytecodeSize == 0);
+    MOZ_RELEASE_ASSERT(compiledSize <= UINT32_MAX);
+
+    size_t serializedSize = sizeof(uint32_t) +
+                            compiledSize +
                             moduleChars.serializedSize();
 
     JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
     if (!open)
         return JS::AsmJSCache_Disabled_Internal;
 
     const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
     const char16_t* end = parser.tokenStream.rawCharPtrAt(ModuleChars::endOffset(parser));
@@ -8456,26 +8458,20 @@ StoreAsmJSModuleInCache(AsmJSParser& par
         return openResult;
 
     uint8_t* cursor = entry.memory;
 
     // Everything serialized before the Module must not change incompatibly
     // between any two builds (regardless of platform, architecture, ...).
     // (The Module::assumptionsMatch() guard everything in the Module and
     // afterwards.)
-    MOZ_RELEASE_ASSERT(bytecodeSize <= UINT32_MAX);
-    MOZ_RELEASE_ASSERT(compiledSize <= UINT32_MAX);
-    cursor = WriteScalar<uint32_t>(cursor, bytecodeSize);
     cursor = WriteScalar<uint32_t>(cursor, compiledSize);
 
-    uint8_t* compiledBegin = cursor;
-    uint8_t* bytecodeBegin = compiledBegin + compiledSize;;
-
-    module.serialize(bytecodeBegin, bytecodeSize, compiledBegin, compiledSize);
-    cursor = bytecodeBegin + bytecodeSize;
+    module.serialize(/* bytecodeBegin = */ nullptr, /* bytecodeSize = */ 0, cursor, compiledSize);
+    cursor += compiledSize;
 
     cursor = moduleChars.serialize(cursor);
 
     MOZ_RELEASE_ASSERT(cursor == entry.memory + serializedSize);
 
     return JS::AsmJSCache_Success;
 }
 
@@ -8496,43 +8492,39 @@ LookupAsmJSModuleInCache(ExclusiveContex
 
     ScopedCacheEntryOpenedForRead entry(cx);
     if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
         return true;
 
     size_t remain = entry.serializedSize;
     const uint8_t* cursor = entry.memory;
 
-    uint32_t bytecodeSize, compiledSize;
-    (cursor = ReadScalarChecked<uint32_t>(cursor, &remain, &bytecodeSize)) &&
-    (cursor = ReadScalarChecked<uint32_t>(cursor, &remain, &compiledSize));
+    uint32_t compiledSize;
+    cursor = ReadScalarChecked<uint32_t>(cursor, &remain, &compiledSize);
     if (!cursor)
         return true;
 
-    const uint8_t* compiledBegin = cursor;
-    const uint8_t* bytecodeBegin = compiledBegin + compiledSize;
-
     Assumptions assumptions;
     if (!assumptions.initBuildIdFromContext(cx))
         return false;
 
-    if (!Module::assumptionsMatch(assumptions, compiledBegin, remain))
+    if (!Module::assumptionsMatch(assumptions, cursor, remain))
         return true;
 
     MutableAsmJSMetadata asmJSMetadata = cx->new_<AsmJSMetadata>();
     if (!asmJSMetadata)
         return false;
 
-    *module = Module::deserialize(bytecodeBegin, bytecodeSize, compiledBegin, compiledSize,
-                                  asmJSMetadata.get());
+    *module = Module::deserialize(/* bytecodeBegin = */ nullptr, /* bytecodeSize = */ 0,
+                                  cursor, compiledSize, asmJSMetadata.get());
     if (!*module) {
         ReportOutOfMemory(cx);
         return false;
     }
-    cursor = bytecodeBegin + bytecodeSize;
+    cursor += compiledSize;
 
     // Due to the hash comparison made by openEntryForRead, this should succeed
     // with high probability.
     ModuleCharsForLookup moduleChars;
     cursor = moduleChars.deserialize(cursor);
     if (!moduleChars.match(parser))
         return true;
 
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -507,17 +507,17 @@ class BaseCompiler
     TrapOffset                  prologueTrapOffset_;
     CodeOffset                  stackAddOffset_;
 
     LatentOp                    latentOp_;       // Latent operation for branch (seen next)
     ValType                     latentType_;     // Operand type, if latentOp_ is true
     Assembler::Condition        latentIntCmp_;   // Comparison operator, if latentOp_ == Compare, int types
     Assembler::DoubleCondition  latentDoubleCmp_;// Comparison operator, if latentOp_ == Compare, float types
 
-    FuncCompileResults&         compileResults_;
+    FuncOffsets                 offsets_;
     MacroAssembler&             masm;            // No '_' suffix - too tedious...
 
     AllocatableGeneralRegisterSet availGPR_;
     AllocatableFloatRegisterSet availFPU_;
 #ifdef DEBUG
     bool                        scratchRegisterTaken_;
 #endif
 
@@ -562,21 +562,22 @@ class BaseCompiler
 
     // More members: see the stk_ and ctl_ vectors, defined below.
 
   public:
     BaseCompiler(const ModuleEnvironment& env,
                  Decoder& decoder,
                  const FuncBytes& func,
                  const ValTypeVector& locals,
-                 FuncCompileResults& compileResults);
+                 TempAllocator* alloc,
+                 MacroAssembler* masm);
 
     MOZ_MUST_USE bool init();
 
-    void finish();
+    FuncOffsets finish();
 
     MOZ_MUST_USE bool emitFunction();
 
     // Used by some of the ScratchRegister implementations.
     operator MacroAssembler&() const { return masm; }
 
 #ifdef DEBUG
     bool scratchRegisterTaken() const {
@@ -2048,17 +2049,17 @@ class BaseCompiler
     //////////////////////////////////////////////////////////////////////
     //
     // Function prologue and epilogue.
 
     void beginFunction() {
         JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code");
 
         SigIdDesc sigId = env_.funcSigs[func_.index()]->id;
-        GenerateFunctionPrologue(masm, localSize_, sigId, &compileResults_.offsets());
+        GenerateFunctionPrologue(masm, localSize_, sigId, &offsets_);
 
         MOZ_ASSERT(masm.framePushed() == uint32_t(localSize_));
 
         maxFramePushed_ = localSize_;
 
         // We won't know until after we've generated code how big the frame will
         // be (we may need arbitrary spill slots and outgoing param slots) so
         // emit a patchable add that is patched in endFunction().
@@ -2143,31 +2144,31 @@ class BaseCompiler
             masm.addToStackPtr(Imm32(localSize_));
         masm.jump(TrapDesc(prologueTrapOffset_, Trap::StackOverflow, /* framePushed = */ 0));
 
         masm.bind(&returnLabel_);
 
         // Restore the TLS register in case it was overwritten by the function.
         loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
 
-        GenerateFunctionEpilogue(masm, localSize_, &compileResults_.offsets());
+        GenerateFunctionEpilogue(masm, localSize_, &offsets_);
 
 #if defined(JS_ION_PERF)
         // FIXME - profiling code missing.  Bug 1286948.
 
         // Note the end of the inline code and start of the OOL code.
         //gen->perfSpewer().noteEndInlineCode(masm);
 #endif
 
         if (!generateOutOfLineCode())
             return false;
 
         masm.wasmEmitTrapOutOfLineCode();
 
-        compileResults_.offsets().end = masm.currentOffset();
+        offsets_.end = masm.currentOffset();
 
         // A frame greater than 256KB is implausible, probably an attack,
         // so fail the compilation.
 
         if (maxFramePushed_ > 256 * 1024)
             return false;
 
         return true;
@@ -7535,36 +7536,36 @@ BaseCompiler::emitFunction()
 
     return true;
 }
 
 BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
                            Decoder& decoder,
                            const FuncBytes& func,
                            const ValTypeVector& locals,
-                           FuncCompileResults& compileResults)
+                           TempAllocator* alloc,
+                           MacroAssembler* masm)
     : env_(env),
       iter_(decoder, func.lineOrBytecode()),
       func_(func),
       lastReadCallSite_(0),
-      alloc_(compileResults.alloc()),
+      alloc_(*alloc),
       locals_(locals),
       localSize_(0),
       varLow_(0),
       varHigh_(0),
       maxFramePushed_(0),
       deadCode_(false),
       prologueTrapOffset_(trapOffset()),
       stackAddOffset_(0),
       latentOp_(LatentOp::None),
       latentType_(ValType::I32),
       latentIntCmp_(Assembler::Equal),
       latentDoubleCmp_(Assembler::DoubleEqual),
-      compileResults_(compileResults),
-      masm(compileResults_.masm()),
+      masm(*masm),
       availGPR_(GeneralRegisterSet::All()),
       availFPU_(FloatRegisterSet::All()),
 #ifdef DEBUG
       scratchRegisterTaken_(false),
 #endif
       tlsSlot_(0),
 #ifdef JS_CODEGEN_X64
       specific_rax(RegI64(Register64(rax))),
@@ -7697,23 +7698,25 @@ BaseCompiler::init()
 
     localSize_ = AlignBytes(localSize_, 16u);
 
     addInterruptCheck();
 
     return true;
 }
 
-void
+FuncOffsets
 BaseCompiler::finish()
 {
     MOZ_ASSERT(done(), "all bytes must be consumed");
     MOZ_ASSERT(func_.callSiteLineNums().length() == lastReadCallSite_);
 
     masm.flushBuffer();
+
+    return offsets_;
 }
 
 static LiveRegisterSet
 volatileReturnGPR()
 {
     GeneralRegisterSet rtn;
     rtn.addAllocatable(ReturnReg);
     return LiveRegisterSet(RegisterSet::VolatileNot(RegisterSet(rtn, FloatRegisterSet())));
@@ -7751,46 +7754,44 @@ js::wasm::BaselineCanCompile(const Funct
 
     return true;
 #else
     return false;
 #endif
 }
 
 bool
-js::wasm::BaselineCompileFunction(CompileTask* task)
-{
-    MOZ_ASSERT(task->mode() == CompileTask::CompileMode::Baseline);
-
-    const FuncBytes& func = task->func();
-    FuncCompileResults& results = task->results();
+js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit)
+{
+    MOZ_ASSERT(unit->mode() == CompileMode::Baseline);
+
+    const FuncBytes& func = unit->func();
 
     Decoder d(func.bytes());
 
     // Build the local types vector.
 
     ValTypeVector locals;
     if (!locals.appendAll(func.sig().args()))
         return false;
     if (!DecodeLocalEntries(d, task->env().kind, &locals))
         return false;
 
     // The MacroAssembler will sometimes access the jitContext.
 
-    JitContext jitContext(&results.alloc());
+    JitContext jitContext(&task->alloc());
 
     // One-pass baseline compilation.
 
-    BaseCompiler f(task->env(), d, func, locals, results);
+    BaseCompiler f(task->env(), d, func, locals, &task->alloc(), &task->masm());
     if (!f.init())
         return false;
 
     if (!f.emitFunction())
         return false;
 
-    f.finish();
-
+    unit->finish(f.finish());
     return true;
 }
 
 #undef INT_DIV_I64_CALLOUT
 #undef I64_TO_FLOAT_CALLOUT
 #undef FLOAT_TO_I64_CALLOUT
--- a/js/src/wasm/WasmBaselineCompile.h
+++ b/js/src/wasm/WasmBaselineCompile.h
@@ -19,29 +19,30 @@
 #ifndef asmjs_wasm_baseline_compile_h
 #define asmjs_wasm_baseline_compile_h
 
 namespace js {
 namespace wasm {
 
 class FunctionGenerator;
 class CompileTask;
+class FuncCompileUnit;
 
 // Return true if BaselineCompileFunction can generate code for the
 // function held in the FunctionGenerator.  If false is returned a
 // different compilation strategy must be chosen.
 //
 // This allows the baseline compiler to have different capabilities on
 // different platforms and defer to the full Ion compiler if
 // capabilities are missing.  The FunctionGenerator and other data
 // structures contain information about the capabilities that are
 // required to compile the function.
 bool
 BaselineCanCompile(const FunctionGenerator* fg);
 
 // Generate adequate code quickly.
 bool
-BaselineCompileFunction(CompileTask* task);
+BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit);
 
 } // namespace wasm
 } // namespace js
 
 #endif // asmjs_wasm_baseline_compile_h
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -14,17 +14,16 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_compile_h
 #define wasm_compile_h
 
-#include "wasm/WasmJS.h"
 #include "wasm/WasmModule.h"
 
 namespace js {
 namespace wasm {
 
 // Describes the JS scripted caller of a request to compile a wasm module.
 
 struct ScriptedCaller
@@ -35,17 +34,17 @@ struct ScriptedCaller
 };
 
 // Describes all the parameters that control wasm compilation.
 
 struct CompileArgs
 {
     Assumptions assumptions;
     ScriptedCaller scriptedCaller;
-    MOZ_INIT_OUTSIDE_CTOR bool alwaysBaseline;
+    bool alwaysBaseline;
 
     CompileArgs(Assumptions&& assumptions, ScriptedCaller&& scriptedCaller)
       : assumptions(Move(assumptions)),
         scriptedCaller(Move(scriptedCaller)),
         alwaysBaseline(false)
     {}
 
     // If CompileArgs is constructed without arguments, initFromContext() must
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -50,16 +50,18 @@ ModuleGenerator::ModuleGenerator()
     numTables_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     masmAlloc_(&lifo_),
     masm_(MacroAssembler::WasmToken(), masmAlloc_),
     lastPatchedCallsite_(0),
     startOfUnpatchedCallsites_(0),
     parallel_(false),
     outstanding_(0),
+    currentTask_(nullptr),
+    batchedBytecode_(0),
     activeFuncDef_(nullptr),
     startedFuncDefs_(false),
     finishedFuncDefs_(false),
     numFinishedFuncDefs_(0)
 {
     MOZ_ASSERT(IsCompilingWasm());
 }
 
@@ -91,16 +93,18 @@ ModuleGenerator::~ModuleGenerator()
             }
         }
 
         MOZ_ASSERT(HelperThreadState().wasmCompilationInProgress);
         HelperThreadState().wasmCompilationInProgress = false;
     } else {
         MOZ_ASSERT(!outstanding_);
     }
+    MOZ_ASSERT_IF(finishedFuncDefs_, !batchedBytecode_);
+    MOZ_ASSERT_IF(finishedFuncDefs_, !currentTask_);
 }
 
 bool
 ModuleGenerator::initAsmJS(Metadata* asmJSMetadata)
 {
     MOZ_ASSERT(env_->isAsmJS());
 
     metadata_ = asmJSMetadata;
@@ -386,52 +390,55 @@ ModuleGenerator::patchFarJumps(const Tra
         masm_.patchFarJump(farJump.jump, trapExits[farJump.trap].begin);
 
     return true;
 }
 
 bool
 ModuleGenerator::finishTask(CompileTask* task)
 {
-    const FuncBytes& func = task->func();
-    FuncCompileResults& results = task->results();
-
     masm_.haltingAlign(CodeAlignment);
 
     // Before merging in the new function's code, if calls in a prior function
     // body might go out of range, insert far jumps to extend the range.
-    if ((masm_.size() - startOfUnpatchedCallsites_) + results.masm().size() > JumpRange()) {
+    if ((masm_.size() - startOfUnpatchedCallsites_) + task->masm().size() > JumpRange()) {
         startOfUnpatchedCallsites_ = masm_.size();
         if (!patchCallSites())
             return false;
     }
 
-    // Offset the recorded FuncOffsets by the offset of the function in the
-    // whole module's code segment.
     uint32_t offsetInWhole = masm_.size();
-    results.offsets().offsetBy(offsetInWhole);
+    for (const FuncCompileUnit& unit : task->units()) {
+        const FuncBytes& func = unit.func();
+
+        // Offset the recorded FuncOffsets by the offset of the function in the
+        // whole module's code segment.
+        FuncOffsets offsets = unit.offsets();
+        offsets.offsetBy(offsetInWhole);
 
-    // Add the CodeRange for this function.
-    uint32_t funcCodeRangeIndex = metadata_->codeRanges.length();
-    if (!metadata_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), results.offsets()))
-        return false;
+        // Add the CodeRange for this function.
+        uint32_t funcCodeRangeIndex = metadata_->codeRanges.length();
+        if (!metadata_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), offsets))
+            return false;
 
-    MOZ_ASSERT(!funcIsCompiled(func.index()));
-    funcToCodeRange_[func.index()] = funcCodeRangeIndex;
+        MOZ_ASSERT(!funcIsCompiled(func.index()));
+        funcToCodeRange_[func.index()] = funcCodeRangeIndex;
+    }
 
     // Merge the compiled results into the whole-module masm.
     mozilla::DebugOnly<size_t> sizeBefore = masm_.size();
-    if (!masm_.asmMergeWith(results.masm()))
+    if (!masm_.asmMergeWith(task->masm()))
         return false;
-    MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size());
+    MOZ_ASSERT(masm_.size() == offsetInWhole + task->masm().size());
 
-    UniqueBytes recycled;
-    task->reset(&recycled);
+    if (!task->reset(&freeFuncBytes_))
+        return false;
+
     freeTasks_.infallibleAppend(task);
-    return freeBytes_.emplaceBack(Move(recycled));
+    return true;
 }
 
 bool
 ModuleGenerator::finishFuncExports()
 {
     // In addition to all the functions that were explicitly exported, any
     // element of an exported table is also exported.
 
@@ -859,99 +866,116 @@ ModuleGenerator::startFuncDefs()
     for (size_t i = 0; i < numTasks; i++)
         tasks_.infallibleEmplaceBack(*env_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
 
     if (!freeTasks_.reserve(numTasks))
         return false;
     for (size_t i = 0; i < numTasks; i++)
         freeTasks_.infallibleAppend(&tasks_[i]);
 
-    if (!freeBytes_.reserve(numTasks))
-        return false;
-    for (size_t i = 0; i < numTasks; i++) {
-        auto bytes = js::MakeUnique<Bytes>();
-        if (!bytes)
-            return false;
-        freeBytes_.infallibleAppend(Move(bytes));
-    }
-
     startedFuncDefs_ = true;
     MOZ_ASSERT(!finishedFuncDefs_);
     return true;
 }
 
 bool
 ModuleGenerator::startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg)
 {
     MOZ_ASSERT(startedFuncDefs_);
     MOZ_ASSERT(!activeFuncDef_);
     MOZ_ASSERT(!finishedFuncDefs_);
 
-    if (!freeBytes_.empty()) {
-        fg->bytes_ = Move(freeBytes_.back());
-        freeBytes_.popBack();
+    if (!freeFuncBytes_.empty()) {
+        fg->funcBytes_ = Move(freeFuncBytes_.back());
+        freeFuncBytes_.popBack();
     } else {
-        fg->bytes_ = js::MakeUnique<Bytes>();
-        if (!fg->bytes_)
+        fg->funcBytes_ = js::MakeUnique<FuncBytes>();
+        if (!fg->funcBytes_)
             return false;
     }
 
-    fg->lineOrBytecode_ = lineOrBytecode;
+    if (!currentTask_) {
+        if (freeTasks_.empty() && !finishOutstandingTask())
+            return false;
+        currentTask_ = freeTasks_.popCopy();
+    }
+
+    fg->funcBytes_->setLineOrBytecode(lineOrBytecode);
     fg->m_ = this;
     activeFuncDef_ = fg;
     return true;
 }
 
 bool
+ModuleGenerator::launchBatchCompile()
+{
+    MOZ_ASSERT(currentTask_);
+
+    size_t numBatchedFuncs = currentTask_->units().length();
+    MOZ_ASSERT(numBatchedFuncs);
+
+    if (parallel_) {
+        if (!StartOffThreadWasmCompile(currentTask_))
+            return false;
+        outstanding_++;
+    } else {
+        if (!CompileFunction(currentTask_))
+            return false;
+        if (!finishTask(currentTask_))
+            return false;
+    }
+
+    currentTask_ = nullptr;
+    batchedBytecode_ = 0;
+
+    numFinishedFuncDefs_ += numBatchedFuncs;
+    return true;
+}
+
+bool
 ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg)
 {
     MOZ_ASSERT(activeFuncDef_ == fg);
 
-    auto func = js::MakeUnique<FuncBytes>(Move(fg->bytes_),
-                                          funcIndex,
-                                          funcSig(funcIndex),
-                                          fg->lineOrBytecode_,
-                                          Move(fg->callSiteLineNums_));
-    if (!func)
-        return false;
+    UniqueFuncBytes func = Move(fg->funcBytes_);
+
+    func->setFunc(funcIndex, &funcSig(funcIndex));
 
     auto mode = alwaysBaseline_ && BaselineCanCompile(fg)
-                ? CompileTask::CompileMode::Baseline
-                : CompileTask::CompileMode::Ion;
+                ? CompileMode::Baseline
+                : CompileMode::Ion;
 
-    if (freeTasks_.empty() && !finishOutstandingTask())
+    CheckedInt<uint32_t> newBatched = func->bytes().length();
+    if (mode == CompileMode::Ion)
+        newBatched *= JitOptions.wasmBatchIonScaleFactor;
+    newBatched += batchedBytecode_;
+
+    if (!currentTask_->units().emplaceBack(Move(func), mode))
         return false;
 
-    CompileTask* task = freeTasks_.popCopy();
-    task->init(Move(func), mode);
-
-    if (parallel_) {
-        if (!StartOffThreadWasmCompile(task))
-            return false;
-        outstanding_++;
-    } else {
-        if (!CompileFunction(task))
-            return false;
-        if (!finishTask(task))
-            return false;
-    }
+    if (newBatched.isValid() && newBatched.value() < JitOptions.wasmBatchThreshold)
+        batchedBytecode_ = newBatched.value();
+    else if (!launchBatchCompile())
+        return false;
 
     fg->m_ = nullptr;
     activeFuncDef_ = nullptr;
-    numFinishedFuncDefs_++;
     return true;
 }
 
 bool
 ModuleGenerator::finishFuncDefs()
 {
     MOZ_ASSERT(startedFuncDefs_);
     MOZ_ASSERT(!activeFuncDef_);
     MOZ_ASSERT(!finishedFuncDefs_);
 
+    if (currentTask_ && !launchBatchCompile())
+        return false;
+
     while (outstanding_ > 0) {
         if (!finishOutstandingTask())
             return false;
     }
 
     linkData_.functionCodeLength = masm_.size();
     finishedFuncDefs_ = true;
 
@@ -1141,19 +1165,23 @@ ModuleGenerator::finish(const ShareableB
 }
 
 bool
 wasm::CompileFunction(CompileTask* task)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
 
-    switch (task->mode()) {
-      case wasm::CompileTask::CompileMode::Ion:
-        return wasm::IonCompileFunction(task);
-      case wasm::CompileTask::CompileMode::Baseline:
-        return wasm::BaselineCompileFunction(task);
-      case wasm::CompileTask::CompileMode::None:
-        break;
+    for (FuncCompileUnit& unit : task->units()) {
+        switch (unit.mode()) {
+          case CompileMode::Ion:
+            if (!IonCompileFunction(task, &unit))
+                return false;
+            break;
+          case CompileMode::Baseline:
+            if (!BaselineCompileFunction(task, &unit))
+                return false;
+            break;
+        }
     }
 
-    MOZ_CRASH("Uninitialized task");
+    return true;
 }
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -31,134 +31,168 @@ struct ModuleEnvironment;
 typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
 typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
 typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
 
 struct CompileArgs;
 
 class FunctionGenerator;
 
-typedef Vector<UniqueBytes, 0, SystemAllocPolicy> UniqueBytesVector;
-
 // The FuncBytes class represents a single, concurrently-compilable function.
 // A FuncBytes object is composed of the wasm function body bytes along with the
 // ambient metadata describing the function necessary to compile it.
 
 class FuncBytes
 {
-    UniqueBytes      bytes_;
+    Bytes            bytes_;
     uint32_t         index_;
-    const SigWithId& sig_;
+    const SigWithId* sig_;
     uint32_t         lineOrBytecode_;
     Uint32Vector     callSiteLineNums_;
 
   public:
-    FuncBytes(UniqueBytes bytes,
-              uint32_t index,
-              const SigWithId& sig,
-              uint32_t lineOrBytecode,
-              Uint32Vector&& callSiteLineNums)
-      : bytes_(Move(bytes)),
-        index_(index),
-        sig_(sig),
-        lineOrBytecode_(lineOrBytecode),
-        callSiteLineNums_(Move(callSiteLineNums))
+    FuncBytes()
+      : index_(UINT32_MAX),
+        sig_(nullptr),
+        lineOrBytecode_(UINT32_MAX)
     {}
 
-    Bytes& bytes() { return *bytes_; }
-    const Bytes& bytes() const { return *bytes_; }
-    UniqueBytes recycle() { return Move(bytes_); }
+    Bytes& bytes() {
+        return bytes_;
+    }
+    MOZ_MUST_USE bool addCallSiteLineNum(uint32_t lineno) {
+        return callSiteLineNums_.append(lineno);
+    }
+    void setLineOrBytecode(uint32_t lineOrBytecode) {
+        MOZ_ASSERT(lineOrBytecode_ == UINT32_MAX);
+        lineOrBytecode_ = lineOrBytecode;
+    }
+    void setFunc(uint32_t index, const SigWithId* sig) {
+        MOZ_ASSERT(index_ == UINT32_MAX);
+        MOZ_ASSERT(sig_ == nullptr);
+        index_ = index;
+        sig_ = sig;
+    }
+    void reset() {
+        bytes_.clear();
+        index_ = UINT32_MAX;
+        sig_ = nullptr;
+        lineOrBytecode_ = UINT32_MAX;
+        callSiteLineNums_.clear();
+    }
+
+    const Bytes& bytes() const { return bytes_; }
     uint32_t index() const { return index_; }
-    const SigWithId& sig() const { return sig_; }
+    const SigWithId& sig() const { return *sig_; }
     uint32_t lineOrBytecode() const { return lineOrBytecode_; }
     const Uint32Vector& callSiteLineNums() const { return callSiteLineNums_; }
 };
 
 typedef UniquePtr<FuncBytes> UniqueFuncBytes;
+typedef Vector<UniqueFuncBytes, 8, SystemAllocPolicy> UniqueFuncBytesVector;
 
-// The FuncCompileResults class contains the results of compiling a single
-// function body, ready to be merged into the whole-module MacroAssembler.
-
-class FuncCompileResults
+enum class CompileMode
 {
-    jit::TempAllocator alloc_;
-    jit::MacroAssembler masm_;
+    Baseline,
+    Ion
+};
+
+// FuncCompileUnit contains all the data necessary to produce and store the
+// results of a single function's compilation.
+
+class FuncCompileUnit
+{
+    UniqueFuncBytes func_;
+    CompileMode mode_;
     FuncOffsets offsets_;
-
-    FuncCompileResults(const FuncCompileResults&) = delete;
-    FuncCompileResults& operator=(const FuncCompileResults&) = delete;
+    DebugOnly<bool> finished_;
 
   public:
-    explicit FuncCompileResults(LifoAlloc& lifo)
-      : alloc_(&lifo),
-        masm_(jit::MacroAssembler::WasmToken(), alloc_)
+    FuncCompileUnit(UniqueFuncBytes func, CompileMode mode)
+      : func_(Move(func)),
+        mode_(mode),
+        finished_(false)
     {}
 
-    jit::TempAllocator& alloc() { return alloc_; }
-    jit::MacroAssembler& masm() { return masm_; }
-    FuncOffsets& offsets() { return offsets_; }
+    const FuncBytes& func() const { return *func_; }
+    CompileMode mode() const { return mode_; }
+    FuncOffsets offsets() const { MOZ_ASSERT(finished_); return offsets_; }
+
+    void finish(FuncOffsets offsets) {
+        MOZ_ASSERT(!finished_);
+        offsets_ = offsets;
+        finished_ = true;
+    }
+
+    UniqueFuncBytes recycle() {
+        MOZ_ASSERT(finished_);
+        func_->reset();
+        return Move(func_);
+    }
 };
 
-// A CompileTask represents the task of compiling a single function body. An
-// CompileTask is filled with the wasm code to be compiled on the main
-// validation thread, sent off to a compilation helper thread which creates
-// the FuncCompileResults, and finally sent back to the validation thread. To
-// save time allocating and freeing memory, CompileTasks are reset() and
-// reused.
+typedef Vector<FuncCompileUnit, 8, SystemAllocPolicy> FuncCompileUnitVector;
+
+// A CompileTask represents the task of compiling a batch of functions. It is
+// filled with a certain number of function's bodies that are sent off to a
+// compilation helper thread, which fills in the resulting code offsets, and
+// finally sent back to the validation thread. To save time allocating and
+// freeing memory, CompileTasks are reset() and reused.
 
 class CompileTask
 {
-  public:
-    enum class CompileMode { None, Baseline, Ion };
-
-  private:
-    const ModuleEnvironment&  env_;
-    LifoAlloc                 lifo_;
-    UniqueFuncBytes           func_;
-    CompileMode               mode_;
-    Maybe<FuncCompileResults> results_;
+    const ModuleEnvironment&   env_;
+    LifoAlloc                  lifo_;
+    Maybe<jit::TempAllocator>  alloc_;
+    Maybe<jit::MacroAssembler> masm_;
+    FuncCompileUnitVector      units_;
 
     CompileTask(const CompileTask&) = delete;
     CompileTask& operator=(const CompileTask&) = delete;
 
+    void init() {
+        alloc_.emplace(&lifo_);
+        masm_.emplace(jit::MacroAssembler::WasmToken(), *alloc_);
+    }
+
   public:
     CompileTask(const ModuleEnvironment& env, size_t defaultChunkSize)
-      : env_(env), lifo_(defaultChunkSize), func_(nullptr), mode_(CompileMode::None)
-    {}
+      : env_(env),
+        lifo_(defaultChunkSize)
+    {
+        init();
+    }
     LifoAlloc& lifo() {
         return lifo_;
     }
+    jit::TempAllocator& alloc() {
+        return *alloc_;
+    }
     const ModuleEnvironment& env() const {
         return env_;
     }
-    void init(UniqueFuncBytes func, CompileMode mode) {
-        MOZ_ASSERT(!func_);
-        func_ = Move(func);
-        results_.emplace(lifo_);
-        mode_ = mode;
+    jit::MacroAssembler& masm() {
+        return *masm_;
     }
-    CompileMode mode() const {
-        return mode_;
-    }
-    const FuncBytes& func() const {
-        MOZ_ASSERT(func_);
-        return *func_;
+    FuncCompileUnitVector& units() {
+        return units_;
     }
-    FuncCompileResults& results() {
-        return *results_;
-    }
-    void reset(UniqueBytes* recycled) {
-        if (func_) {
-            *recycled = Move(func_->recycle());
-            (*recycled)->clear();
+    bool reset(UniqueFuncBytesVector* freeFuncBytes) {
+        for (FuncCompileUnit& unit : units_) {
+            if (!freeFuncBytes->emplaceBack(Move(unit.recycle())))
+                return false;
         }
-        func_.reset(nullptr);
-        results_.reset();
+
+        units_.clear();
+        masm_.reset();
+        alloc_.reset();
         lifo_.releaseAll();
-        mode_ = CompileMode::None;
+
+        init();
+        return true;
     }
 };
 
 // A ModuleGenerator encapsulates the creation of a wasm module. During the
 // lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created
 // and destroyed to compile the individual function bodies. After generating all
 // functions, ModuleGenerator::finish() must be called to complete the
 // compilation and extract the resulting wasm module.
@@ -191,43 +225,45 @@ class MOZ_STACK_CLASS ModuleGenerator
     uint32_t                        lastPatchedCallsite_;
     uint32_t                        startOfUnpatchedCallsites_;
 
     // Parallel compilation
     bool                            parallel_;
     uint32_t                        outstanding_;
     CompileTaskVector               tasks_;
     CompileTaskPtrVector            freeTasks_;
-    UniqueBytesVector               freeBytes_;
+    UniqueFuncBytesVector           freeFuncBytes_;
+    CompileTask*                    currentTask_;
+    uint32_t                        batchedBytecode_;
 
     // Assertions
     DebugOnly<FunctionGenerator*>   activeFuncDef_;
     DebugOnly<bool>                 startedFuncDefs_;
     DebugOnly<bool>                 finishedFuncDefs_;
     DebugOnly<uint32_t>             numFinishedFuncDefs_;
 
     bool funcIsCompiled(uint32_t funcIndex) const;
     const CodeRange& funcCodeRange(uint32_t funcIndex) const;
+    uint32_t numFuncImports() const;
     MOZ_MUST_USE bool patchCallSites(TrapExitOffsetArray* maybeTrapExits = nullptr);
     MOZ_MUST_USE bool patchFarJumps(const TrapExitOffsetArray& trapExits);
     MOZ_MUST_USE bool finishTask(CompileTask* task);
     MOZ_MUST_USE bool finishOutstandingTask();
     MOZ_MUST_USE bool finishFuncExports();
     MOZ_MUST_USE bool finishCodegen();
     MOZ_MUST_USE bool finishLinkData(Bytes& code);
     MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
     MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
     MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
 
+    MOZ_MUST_USE bool launchBatchCompile();
+
     MOZ_MUST_USE bool initAsmJS(Metadata* asmJSMetadata);
     MOZ_MUST_USE bool initWasm();
 
-    // Functions declarations:
-    uint32_t numFuncImports() const;
-
   public:
     explicit ModuleGenerator();
     ~ModuleGenerator();
 
     MOZ_MUST_USE bool init(UniqueModuleEnvironment env, const CompileArgs& args,
                            Metadata* maybeAsmJSMetadata = nullptr);
 
     const ModuleEnvironment& env() const { return *env_; }
@@ -270,39 +306,35 @@ class MOZ_STACK_CLASS ModuleGenerator
     MOZ_MUST_USE bool addGlobal(ValType type, bool isConst, uint32_t* index);
     MOZ_MUST_USE bool addExport(CacheableChars&& fieldChars, uint32_t funcIndex);
 
     // Finish compilation of the given bytecode.
     SharedModule finish(const ShareableBytes& bytecode);
 };
 
 // A FunctionGenerator encapsulates the generation of a single function body.
-// ModuleGenerator::startFunc must be called after construction and before doing
-// anything else. After the body is complete, ModuleGenerator::finishFunc must
-// be called before the FunctionGenerator is destroyed and the next function is
-// started.
+// ModuleGenerator::startFuncDef must be called after construction and before
+// doing anything else.
+//
+// After the body is complete, ModuleGenerator::finishFuncDef must be called
+// before the FunctionGenerator is destroyed and the next function is started.
 
 class MOZ_STACK_CLASS FunctionGenerator
 {
     friend class ModuleGenerator;
 
     ModuleGenerator* m_;
     bool             usesSimd_;
     bool             usesAtomics_;
 
-    // Data created during function generation, then handed over to the
-    // FuncBytes in ModuleGenerator::finishFunc().
-    UniqueBytes      bytes_;
-    Uint32Vector     callSiteLineNums_;
-
-    uint32_t lineOrBytecode_;
+    UniqueFuncBytes  funcBytes_;
 
   public:
     FunctionGenerator()
-      : m_(nullptr), usesSimd_(false), usesAtomics_(false), bytes_(nullptr), lineOrBytecode_(0)
+      : m_(nullptr), usesSimd_(false), usesAtomics_(false), funcBytes_(nullptr)
     {}
 
     bool usesSimd() const {
         return usesSimd_;
     }
     void setUsesSimd() {
         usesSimd_ = true;
     }
@@ -310,19 +342,19 @@ class MOZ_STACK_CLASS FunctionGenerator
     bool usesAtomics() const {
         return usesAtomics_;
     }
     void setUsesAtomics() {
         usesAtomics_ = true;
     }
 
     Bytes& bytes() {
-        return *bytes_;
+        return funcBytes_->bytes();
     }
     MOZ_MUST_USE bool addCallSiteLineNum(uint32_t lineno) {
-        return callSiteLineNums_.append(lineno);
+        return funcBytes_->addCallSiteLineNum(lineno);
     }
 };
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_generator_h
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -163,50 +163,45 @@ class FunctionCompiler
     MBasicBlock*               curBlock_;
     CallCompileStateVector     callStack_;
     uint32_t                   maxStackArgBytes_;
 
     uint32_t                   loopDepth_;
     uint32_t                   blockDepth_;
     ControlFlowPatchsVector    blockPatches_;
 
-    FuncCompileResults&        compileResults_;
-
     // TLS pointer argument to the current function.
     MWasmParameter*            tlsPointer_;
 
   public:
     FunctionCompiler(const ModuleEnvironment& env,
                      Decoder& decoder,
                      const FuncBytes& func,
                      const ValTypeVector& locals,
-                     MIRGenerator& mirGen,
-                     FuncCompileResults& compileResults)
+                     MIRGenerator& mirGen)
       : env_(env),
         iter_(decoder, func.lineOrBytecode()),
         func_(func),
         locals_(locals),
         lastReadCallSite_(0),
         alloc_(mirGen.alloc()),
         graph_(mirGen.graph()),
         info_(mirGen.info()),
         mirGen_(mirGen),
         dummyIns_(nullptr),
         curBlock_(nullptr),
         maxStackArgBytes_(0),
         loopDepth_(0),
         blockDepth_(0),
-        compileResults_(compileResults),
         tlsPointer_(nullptr)
     {}
 
     const ModuleEnvironment&   env() const   { return env_; }
     IonOpIter&                 iter()        { return iter_; }
     TempAllocator&             alloc() const { return alloc_; }
-    MacroAssembler&            masm() const  { return compileResults_.masm(); }
     const Sig&                 sig() const   { return func_.sig(); }
 
     TrapOffset trapOffset() const {
         return iter_.trapOffset();
     }
     Maybe<TrapOffset> trapIfNotAsmJS() const {
         return env_.isAsmJS() ? Nothing() : Some(iter_.trapOffset());
     }
@@ -3692,50 +3687,50 @@ EmitExpr(FunctionCompiler& f)
         return EmitCurrentMemory(f);
       case Op::Limit:;
     }
 
     MOZ_CRASH("unexpected wasm opcode");
 }
 
 bool
-wasm::IonCompileFunction(CompileTask* task)
+wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit)
 {
-    MOZ_ASSERT(task->mode() == CompileTask::CompileMode::Ion);
-
-    const FuncBytes& func = task->func();
-    FuncCompileResults& results = task->results();
+    MOZ_ASSERT(unit->mode() == CompileMode::Ion);
+
+    const FuncBytes& func = unit->func();
+    const ModuleEnvironment& env = task->env();
 
     Decoder d(func.bytes());
 
     // Build the local types vector.
 
     ValTypeVector locals;
     if (!locals.appendAll(func.sig().args()))
         return false;
-    if (!DecodeLocalEntries(d, task->env().kind, &locals))
+    if (!DecodeLocalEntries(d, env.kind, &locals))
         return false;
 
     // Set up for Ion compilation.
 
-    JitContext jitContext(&results.alloc());
+    JitContext jitContext(&task->alloc());
     const JitCompileOptions options;
-    MIRGraph graph(&results.alloc());
+    MIRGraph graph(&task->alloc());
     CompileInfo compileInfo(locals.length());
-    MIRGenerator mir(nullptr, options, &results.alloc(), &graph, &compileInfo,
+    MIRGenerator mir(nullptr, options, &task->alloc(), &graph, &compileInfo,
                      IonOptimizations.get(OptimizationLevel::Wasm));
-    mir.initMinWasmHeapLength(task->env().minMemoryLength);
+    mir.initMinWasmHeapLength(env.minMemoryLength);
 
     // Capture the prologue's trap site before decoding the function.
 
     TrapOffset prologueTrapOffset;
 
     // Build MIR graph
     {
-        FunctionCompiler f(task->env(), d, func, locals, mir, results);
+        FunctionCompiler f(env, d, func, locals, mir);
         if (!f.init())
             return false;
 
         prologueTrapOffset = f.iter().trapOffset();
 
         if (!f.startBlock())
             return false;
 
@@ -3765,17 +3760,21 @@ wasm::IonCompileFunction(CompileTask* ta
 
         if (!OptimizeMIR(&mir))
             return false;
 
         LIRGraph* lir = GenerateLIR(&mir);
         if (!lir)
             return false;
 
-        SigIdDesc sigId = task->env().funcSigs[func.index()]->id;
-
-        CodeGenerator codegen(&mir, lir, &results.masm());
-        if (!codegen.generateWasm(sigId, prologueTrapOffset, &results.offsets()))
+        SigIdDesc sigId = env.funcSigs[func.index()]->id;
+
+        CodeGenerator codegen(&mir, lir, &task->masm());
+
+        FuncOffsets offsets;
+        if (!codegen.generateWasm(sigId, prologueTrapOffset, &offsets))
             return false;
+
+        unit->finish(offsets);
     }
 
     return true;
 }
--- a/js/src/wasm/WasmIonCompile.h
+++ b/js/src/wasm/WasmIonCompile.h
@@ -20,17 +20,18 @@
 #define wasm_ion_compile_h
 
 #include "mozilla/Attributes.h"
 
 namespace js {
 namespace wasm {
 
 class CompileTask;
+class FuncCompileUnit;
 
 // Generates very fast code at the expense of compilation time.
 MOZ_MUST_USE bool
-IonCompileFunction(CompileTask* task);
+IonCompileFunction(CompileTask* task, FuncCompileUnit* unit);
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_ion_compile_h
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -136,17 +136,17 @@ LinkData::sizeOfExcludingThis(MallocSize
     return internalLinks.sizeOfExcludingThis(mallocSizeOf) +
            symbolicLinks.sizeOfExcludingThis(mallocSizeOf);
 }
 
 /* virtual */ void
 Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const
 {
     if (maybeBytecodeSize)
-        *maybeBytecodeSize = SerializedPodVectorSize(bytecode_->bytes);
+        *maybeBytecodeSize = bytecode_->bytes.length();
 
     if (maybeCompiledSize) {
         *maybeCompiledSize = assumptions_.serializedSize() +
                              SerializedPodVectorSize(code_) +
                              linkData_.serializedSize() +
                              SerializedVectorSize(imports_) +
                              SerializedVectorSize(exports_) +
                              SerializedPodVectorSize(dataSegments_) +
@@ -159,19 +159,23 @@ Module::serializedSize(size_t* maybeByte
 Module::serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
                   uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const
 {
     MOZ_ASSERT(!!maybeBytecodeBegin == !!maybeBytecodeSize);
     MOZ_ASSERT(!!maybeCompiledBegin == !!maybeCompiledSize);
 
     if (maybeBytecodeBegin) {
         // Bytecode deserialization is not guarded by Assumptions and thus must not
-        // change incompatibly between builds.
+        // change incompatibly between builds. Thus, for simplicity, the format
+        // of the bytecode file is simply a .wasm file (thus, backwards
+        // compatibility is ensured by backwards compatibility of the wasm
+        // binary format).
 
-        uint8_t* bytecodeEnd = SerializePodVector(maybeBytecodeBegin, bytecode_->bytes);
+        const Bytes& bytes = bytecode_->bytes;
+        uint8_t* bytecodeEnd = WriteBytes(maybeBytecodeBegin, bytes.begin(), bytes.length());
         MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize);
     }
 
     if (maybeCompiledBegin) {
         // Assumption must be serialized at the beginning of the compiled bytes so
         // that compiledAssumptionsMatch can detect a build-id mismatch before any
         // other decoding occurs.
 
@@ -199,24 +203,20 @@ Module::assumptionsMatch(const Assumptio
 }
 
 /* static */ SharedModule
 Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
                     const uint8_t* compiledBegin, size_t compiledSize,
                     Metadata* maybeMetadata)
 {
     MutableBytes bytecode = js_new<ShareableBytes>();
-    if (!bytecode)
+    if (!bytecode || !bytecode->bytes.initLengthUninitialized(bytecodeSize))
         return nullptr;
 
-    const uint8_t* bytecodeEnd = DeserializePodVector(bytecodeBegin, &bytecode->bytes);
-    if (!bytecodeEnd)
-        return nullptr;
-
-    MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeBegin + bytecodeSize);
+    memcpy(bytecode->bytes.begin(), bytecodeBegin, bytecodeSize);
 
     Assumptions assumptions;
     const uint8_t* cursor = assumptions.deserialize(compiledBegin, compiledSize);
     if (!cursor)
         return nullptr;
 
     Bytes code;
     cursor = DeserializePodVector(cursor, &code);
@@ -339,25 +339,25 @@ wasm::DeserializeModule(PRFileDesc* byte
         UniqueMapping compiledMapping = MapFile(compiledFile, &compiledInfo);
         if (!compiledMapping)
             return nullptr;
 
         return Module::deserialize(bytecodeMapping.get(), bytecodeInfo.size,
                                    compiledMapping.get(), compiledInfo.size);
     }
 
+    // Since the compiled file's assumptions don't match, we must recompile from
+    // bytecode. The bytecode file format is simply that of a .wasm (see
+    // Module::serialize).
+
     MutableBytes bytecode = js_new<ShareableBytes>();
-    if (!bytecode)
+    if (!bytecode || !bytecode->bytes.initLengthUninitialized(bytecodeInfo.size))
         return nullptr;
 
-    const uint8_t* bytecodeEnd = DeserializePodVector(bytecodeMapping.get(), &bytecode->bytes);
-    if (!bytecodeEnd)
-        return nullptr;
-
-    MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeMapping.get() + bytecodeInfo.size);
+    memcpy(bytecode->bytes.begin(), bytecodeMapping.get(), bytecodeInfo.size);
 
     ScriptedCaller scriptedCaller;
     scriptedCaller.filename = Move(filename);
     scriptedCaller.line = line;
     scriptedCaller.column = column;
 
     CompileArgs args(Assumptions(Move(buildId)), Move(scriptedCaller));
 
--- a/js/src/wasm/WasmSerialize.h
+++ b/js/src/wasm/WasmSerialize.h
@@ -132,17 +132,17 @@ SerializedPodVectorSize(const mozilla::V
            vec.length() * sizeof(T);
 }
 
 template <class T, size_t N>
 static inline uint8_t*
 SerializePodVector(uint8_t* cursor, const mozilla::Vector<T, N, SystemAllocPolicy>& vec)
 {
     // This binary format must not change without taking into consideration the
-    // constraints in Assumptions::serialize and Module::serialize.
+    // constraints in Assumptions::serialize.
 
     cursor = WriteScalar<uint32_t>(cursor, vec.length());
     cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T));
     return cursor;
 }
 
 template <class T, size_t N>
 static inline const uint8_t*
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -48,16 +48,17 @@
 #include "mozilla/css/ImportRule.h"
 #include "nsThreadUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIThreadInternal.h"
 #include "nsINetworkPredictor.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/ConsoleReportCollector.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
 
@@ -1400,39 +1401,37 @@ Loader::InsertSheetInDoc(StyleSheet* aSh
  * to put it anyway.  So just append for now.  (In the future if we
  * want to insert the sheet at the correct position, we'll need to
  * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
  * bug 1220506.)
  */
 nsresult
 Loader::InsertChildSheet(StyleSheet* aSheet,
                          StyleSheet* aParentSheet,
-                         ImportRule* aParentRule)
+                         ImportRule* aGeckoParentRule,
+                         const RawServoImportRule* aServoParentRule)
 {
   LOG(("css::Loader::InsertChildSheet"));
-  NS_PRECONDITION(aSheet, "Nothing to insert");
-  NS_PRECONDITION(aParentSheet, "Need a parent to insert into");
-  NS_PRECONDITION(aParentSheet, "How did we get imported?");
-
-  // XXXheycam The InsertChildSheet API doesn't work with ServoStyleSheets,
-  // since they won't have Gecko ImportRules in them.
-  if (aSheet->IsServo()) {
-    return NS_ERROR_FAILURE;
+  MOZ_ASSERT(aSheet, "Nothing to insert");
+  MOZ_ASSERT(aParentSheet, "Need a parent to insert into");
+  MOZ_ASSERT_IF(aSheet->IsGecko(), aGeckoParentRule && !aServoParentRule);
+  MOZ_ASSERT_IF(aSheet->IsServo(), aServoParentRule && !aGeckoParentRule);
+  if (aSheet->IsGecko()) {
+    // child sheets should always start out enabled, even if they got
+    // cloned off of top-level sheets which were disabled
+    aSheet->AsGecko()->SetEnabled(true);
+    aGeckoParentRule->SetSheet(aSheet->AsGecko()); // This sets the ownerRule on the sheet
+  } else {
+    RefPtr<RawServoStyleSheet> sheet =
+      Servo_ImportRule_GetSheet(aServoParentRule).Consume();
+    aSheet->AsServo()->SetSheetForImport(sheet);
   }
-
-  // child sheets should always start out enabled, even if they got
-  // cloned off of top-level sheets which were disabled
-  aSheet->AsGecko()->SetEnabled(true);
-
   aParentSheet->AppendStyleSheet(aSheet);
-  aParentRule->SetSheet(aSheet->AsGecko()); // This sets the ownerRule on the sheet
 
   LOG(("  Inserting into parent sheet"));
-  //  LOG(("  Inserting into parent sheet at position %d", insertionPoint));
-
   return NS_OK;
 }
 
 /**
  * LoadSheet handles the actual load of a sheet.  If the load is
  * supposed to be synchronous it just opens a channel synchronously
  * using the given uri, wraps the resulting stream in a converter
  * stream and calls ParseSheet.  Otherwise it tries to look for an
@@ -1770,17 +1769,18 @@ Loader::ParseSheet(const nsAString& aInp
 
   if (aLoadData->mSheet->IsGecko()) {
     nsCSSParser parser(this, aLoadData->mSheet->AsGecko());
     rv = parser.ParseSheet(aInput, sheetURI, baseURI,
                            aLoadData->mSheet->Principal(),
                            aLoadData->mLineNumber);
   } else {
     rv =
-      aLoadData->mSheet->AsServo()->ParseSheet(aInput, sheetURI, baseURI,
+      aLoadData->mSheet->AsServo()->ParseSheet(this,
+                                               aInput, sheetURI, baseURI,
                                                aLoadData->mSheet->Principal(),
                                                aLoadData->mLineNumber);
   }
 
   mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
 
   if (NS_FAILED(rv)) {
     LOG_ERROR(("  Low-level error in parser!"));
@@ -1986,17 +1986,17 @@ Loader::LoadInlineStyle(nsIContent* aEle
                         const nsAString& aTitle,
                         const nsAString& aMedia,
                         Element* aScopeElement,
                         nsICSSLoaderObserver* aObserver,
                         bool* aCompleted,
                         bool* aIsAlternate)
 {
   LOG(("css::Loader::LoadInlineStyle"));
-  NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
+  MOZ_ASSERT(mParsingDatas.IsEmpty(), "We're in the middle of a parse?");
 
   *aCompleted = true;
 
   if (!mEnabled) {
     LOG_WARN(("  Not enabled"));
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -2192,35 +2192,45 @@ HaveAncestorDataWithURI(SheetLoadData *a
 
   return false;
 }
 
 nsresult
 Loader::LoadChildSheet(StyleSheet* aParentSheet,
                        nsIURI* aURL,
                        nsMediaList* aMedia,
-                       ImportRule* aParentRule,
+                       ImportRule* aGeckoParentRule,
+                       const RawServoImportRule* aServoParentRule,
                        LoaderReusableStyleSheets* aReusableSheets)
 {
   LOG(("css::Loader::LoadChildSheet"));
   NS_PRECONDITION(aURL, "Must have a URI to load");
   NS_PRECONDITION(aParentSheet, "Must have a parent sheet");
 
+  // Servo doesn't support reusable sheets.
+  MOZ_ASSERT_IF(aReusableSheets, aParentSheet->IsGecko());
+  MOZ_ASSERT_IF(aParentSheet->IsGecko(), aGeckoParentRule && !aServoParentRule);
+  MOZ_ASSERT_IF(aParentSheet->IsServo(), aServoParentRule && !aGeckoParentRule);
+
   if (!mEnabled) {
     LOG_WARN(("  Not enabled"));
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   LOG_URI("  Child uri: '%s'", aURL);
 
   nsCOMPtr<nsINode> owningNode;
 
   // check for an owning document: if none, don't bother walking up the parent
   // sheets
-  if (aParentSheet->GetOwningDocument()) {
+  //
+  // FIXME(emilio): Figure out whether this walk up is necessary (try seems
+  // green without it), and fix the parenting of stylesheets in the servo case
+  // if that's the case.
+  if (aParentSheet->GetOwningDocument() && aParentSheet->IsGecko()) {
     StyleSheet* topSheet = aParentSheet;
     while (StyleSheet* parent = topSheet->GetParentSheet()) {
       topSheet = parent;
     }
     owningNode = topSheet->GetOwnerNode();
   }
 
   nsISupports* context = owningNode;
@@ -2261,34 +2271,35 @@ Loader::LoadChildSheet(StyleSheet* aPare
 
   // Now that we know it's safe to load this (passes security check and not a
   // loop) do so.
   RefPtr<StyleSheet> sheet;
   RefPtr<CSSStyleSheet> reusableSheet;
   StyleSheetState state;
   if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, reusableSheet)) {
     sheet = reusableSheet;
-    aParentRule->SetSheet(reusableSheet);
+    aGeckoParentRule->SetSheet(reusableSheet);
     state = eSheetComplete;
   } else {
     bool isAlternate;
     const nsSubstring& empty = EmptyString();
     // For now, use CORS_NONE for child sheets
     rv = CreateSheet(aURL, nullptr, principal,
                      aParentSheet->ParsingMode(),
                      CORS_NONE, aParentSheet->GetReferrerPolicy(),
                      EmptyString(), // integrity is only checked on main sheet
                      parentData ? parentData->mSyncLoad : false,
                      false, empty, state, &isAlternate, &sheet);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate);
   }
 
-  rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
+  rv = InsertChildSheet(sheet, aParentSheet, aGeckoParentRule,
+                        aServoParentRule);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (state == eSheetComplete) {
     LOG(("  Sheet already complete"));
     // We're completely done.  No need to notify, even, since the
     // @import rule addition/modification will trigger the right style
     // changes automatically.
     return NS_OK;
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -280,25 +280,29 @@ public:
    * complete aParentSheet will be QIed to nsICSSLoaderObserver and
    * asynchronously notified, just like for LoadStyleLink.  Note that if the
    * child sheet is already complete when this method returns, no
    * nsICSSLoaderObserver notification will be sent.
    *
    * @param aParentSheet the parent of this child sheet
    * @param aURL the URL of the child sheet
    * @param aMedia the already-parsed media list for the child sheet
-   * @param aRule the @import rule importing this child.  This is used to
-   *              properly order the child sheet list of aParentSheet.
+   * @param aGeckoParentRule the @import rule importing this child, when using
+   *                         Gecko's style system. This is used to properly
+   *                         order the child sheet list of aParentSheet.
+   * @param aServoParentRule the @import rule importing this child, when using
+   *                         Servo's style system.
    * @param aSavedSheets any saved style sheets which could be reused
    *              for this load
    */
   nsresult LoadChildSheet(StyleSheet* aParentSheet,
                           nsIURI* aURL,
                           nsMediaList* aMedia,
-                          ImportRule* aRule,
+                          ImportRule* aGeckoParentRule,
+                          const RawServoImportRule* aServoParentRule,
                           LoaderReusableStyleSheets* aSavedSheets);
 
   /**
    * Synchronously load and return the stylesheet at aURL.  Any child sheets
    * will also be loaded synchronously.  Note that synchronous loads over some
    * protocols may involve spinning up a new event loop, so use of this method
    * does NOT guarantee not receiving any events before the sheet loads.  This
    * method can be used to load sheets not associated with a document.
@@ -479,17 +483,18 @@ private:
                     bool isAlternate);
 
   nsresult InsertSheetInDoc(StyleSheet* aSheet,
                             nsIContent* aLinkingContent,
                             nsIDocument* aDocument);
 
   nsresult InsertChildSheet(StyleSheet* aSheet,
                             StyleSheet* aParentSheet,
-                            ImportRule* aParentRule);
+                            ImportRule* aGeckoParentRule,
+                            const RawServoImportRule* aServoParentRule);
 
   nsresult InternalLoadNonDocumentSheet(nsIURI* aURL,
                                         bool aIsPreload,
                                         SheetParsingMode aParsingMode,
                                         bool aUseSystemPrincipal,
                                         nsIPrincipal* aOriginPrincipal,
                                         const nsCString& aCharset,
                                         RefPtr<StyleSheet>* aSheet,
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -6,8 +6,9 @@
 
 /* a list of all Servo Arc types used in stylo bindings for preprocessing */
 
 SERVO_ARC_TYPE(CssRules, ServoCssRules)
 SERVO_ARC_TYPE(StyleSheet, RawServoStyleSheet)
 SERVO_ARC_TYPE(ComputedValues, ServoComputedValues)
 SERVO_ARC_TYPE(DeclarationBlock, RawServoDeclarationBlock)
 SERVO_ARC_TYPE(StyleRule, RawServoStyleRule)
+SERVO_ARC_TYPE(ImportRule, RawServoImportRule)
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -21,22 +21,36 @@
 // Element data
 SERVO_BINDING_FUNC(Servo_Element_ClearData, void, RawGeckoElementBorrowed node)
 SERVO_BINDING_FUNC(Servo_Element_ShouldTraverse, bool, RawGeckoElementBorrowed node)
 
 // Styleset and Stylesheet management
 SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetStrong,
                    mozilla::css::SheetParsingMode parsing_mode)
 SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetStrong,
+                   mozilla::css::Loader* loader,
+                   mozilla::ServoStyleSheet* gecko_stylesheet,
                    const nsACString* data,
                    mozilla::css::SheetParsingMode parsing_mode,
                    const nsACString* base_url,
                    ThreadSafeURIHolder* base,
                    ThreadSafeURIHolder* referrer,
                    ThreadSafePrincipalHolder* principal)
+SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet,
+                   RawServoStyleSheetStrong,
+                   const RawServoImportRuleBorrowed import_rule)
+SERVO_BINDING_FUNC(Servo_StyleSheet_ClearAndUpdate,
+                   void,
+                   RawServoStyleSheetBorrowed stylesheet,
+                   mozilla::css::Loader* loader,
+                   mozilla::ServoStyleSheet* gecko_stylesheet,
+                   const nsACString* data,
+                   ThreadSafeURIHolder* base,
+                   ThreadSafeURIHolder* referrer,
+                   ThreadSafePrincipalHolder* principal)
 SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_GetRules, ServoCssRulesStrong,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_Init, RawServoStyleSetOwned)
 SERVO_BINDING_FUNC(Servo_StyleSet_Drop, void, RawServoStyleSetOwned set)
 SERVO_BINDING_FUNC(Servo_StyleSet_AppendStyleSheet, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet, bool flush)
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -5,26 +5,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoBindings.h"
 
 #include "ChildIterator.h"
 #include "StyleStructContext.h"
 #include "gfxFontFamilyList.h"
 #include "nsAttrValueInlines.h"
+#include "nsCSSParser.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsIContentInlines.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsINode.h"
 #include "nsIPrincipal.h"
 #include "nsNameSpaceManager.h"
+#include "nsNetUtil.h"
 #include "nsRuleNode.h"
 #include "nsString.h"
 #include "nsStyleStruct.h"
 #include "nsStyleUtil.h"
 #include "nsTArray.h"
 
 #include "mozilla/EventStates.h"
 #include "mozilla/ServoElementSnapshot.h"
@@ -1022,16 +1024,50 @@ Gecko_CSSValue_SetFunction(nsCSSValueBor
 }
 
 nsCSSValueBorrowedMut
 Gecko_CSSValue_GetArrayItem(nsCSSValueBorrowedMut aCSSValue, int32_t aIndex)
 {
   return &aCSSValue->GetArrayValue()->Item(aIndex);
 }
 
+void
+Gecko_LoadStyleSheet(css::Loader* aLoader,
+                     ServoStyleSheet* aParent,
+                     RawServoImportRuleBorrowed aImportRule,
+                     const uint8_t* aURLString,
+                     uint32_t aURLStringLength,
+                     const uint8_t* aMediaString,
+                     uint32_t aMediaStringLength)
+{
+  MOZ_ASSERT(aLoader, "Should've catched this before");
+  MOZ_ASSERT(aParent, "Only used for @import, so parent should exist!");
+  MOZ_ASSERT(aURLString, "Invalid URLs shouldn't be loaded!");
+  RefPtr<nsMediaList> media = new nsMediaList();
+  if (aMediaStringLength) {
+    MOZ_ASSERT(aMediaString);
+    // TODO(emilio, bug 1325878): This is not great, though this is going away
+    // soon anyway, when we can have a Servo-backed nsMediaList.
+    nsDependentCSubstring medium(reinterpret_cast<const char*>(aMediaString),
+                                 aMediaStringLength);
+    nsCSSParser mediumParser(aLoader);
+    mediumParser.ParseMediaList(
+        NS_ConvertUTF8toUTF16(medium), nullptr, 0, media);
+  }
+
+  nsDependentCSubstring urlSpec(reinterpret_cast<const char*>(aURLString),
+                                aURLStringLength);
+
+  // Servo's loader guarantees that the URL is valid.
+  nsCOMPtr<nsIURI> uri;
+  MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), urlSpec));
+
+  aLoader->LoadChildSheet(aParent, uri, media, nullptr, aImportRule, nullptr);
+}
+
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
 
 #define STYLE_STRUCT(name, checkdata_cb)                                      \
                                                                               \
 void                                                                          \
 Gecko_Construct_nsStyle##name(nsStyle##name* ptr)                             \
 {                                                                             \
   new (ptr) nsStyle##name(StyleStructContext::ServoContext());                \
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -24,16 +24,17 @@
  * Functions beginning with Servo_ are implemented in Servo and invoked from Gecko.
  */
 
 class nsIAtom;
 class nsIPrincipal;
 class nsIURI;
 struct nsFont;
 namespace mozilla {
+  class ServoStyleSheet;
   class FontFamilyList;
   enum FontFamilyType : uint32_t;
 }
 using mozilla::FontFamilyList;
 using mozilla::FontFamilyType;
 using mozilla::ServoElementSnapshot;
 struct nsStyleList;
 struct nsStyleImage;
@@ -90,16 +91,23 @@ RawGeckoNodeBorrowedOrNull Gecko_GetLast
 RawGeckoNodeBorrowedOrNull Gecko_GetPrevSibling(RawGeckoNodeBorrowed node);
 RawGeckoNodeBorrowedOrNull Gecko_GetNextSibling(RawGeckoNodeBorrowed node);
 RawGeckoElementBorrowedOrNull Gecko_GetParentElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetFirstChildElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetLastChildElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetPrevSiblingElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetNextSiblingElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetDocumentElement(RawGeckoDocumentBorrowed document);
+void Gecko_LoadStyleSheet(mozilla::css::Loader* loader,
+                          mozilla::ServoStyleSheet* parent,
+                          RawServoImportRuleBorrowed import_rule,
+                          const uint8_t* url_bytes,
+                          uint32_t url_length,
+                          const uint8_t* media_bytes,
+                          uint32_t media_length);
 
 // By default, Servo walks the DOM by traversing the siblings of the DOM-view
 // first child. This generally works, but misses anonymous children, which we
 // want to traverse during styling. To support these cases, we create an
 // optional heap-allocated iterator for nodes that need it. If the creation
 // method returns null, Servo falls back to the aforementioned simpler (and
 // faster) sibling traversal.
 StyleChildrenIteratorOwnedOrNull Gecko_MaybeCreateStyleChildrenIterator(RawGeckoNodeBorrowed node);
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -57,43 +57,46 @@ ServoStyleSheet::GetParentSheet() const
   // to fix SetOwningDocument to propagate the owning document down
   // to the children.
   MOZ_CRASH("stylo: not implemented");
 }
 
 void
 ServoStyleSheet::AppendStyleSheet(ServoStyleSheet* aSheet)
 {
-  // XXXheycam: When we implement support for child sheets, we'll have
-  // to fix SetOwningDocument to propagate the owning document down
-  // to the children.
-  MOZ_CRASH("stylo: not implemented");
+  aSheet->mDocument = mDocument;
 }
 
 nsresult
-ServoStyleSheet::ParseSheet(const nsAString& aInput,
+ServoStyleSheet::ParseSheet(css::Loader* aLoader,
+                            const nsAString& aInput,
                             nsIURI* aSheetURI,
                             nsIURI* aBaseURI,
                             nsIPrincipal* aSheetPrincipal,
                             uint32_t aLineNumber)
 {
-  DropSheet();
-
   RefPtr<ThreadSafeURIHolder> base = new ThreadSafeURIHolder(aBaseURI);
   RefPtr<ThreadSafeURIHolder> referrer = new ThreadSafeURIHolder(aSheetURI);
   RefPtr<ThreadSafePrincipalHolder> principal =
     new ThreadSafePrincipalHolder(aSheetPrincipal);
 
   nsCString baseString;
   nsresult rv = aBaseURI->GetSpec(baseString);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ConvertUTF16toUTF8 input(aInput);
-  mSheet = Servo_StyleSheet_FromUTF8Bytes(&input, mParsingMode, &baseString,
-                                          base, referrer, principal).Consume();
+  if (!mSheet) {
+    mSheet =
+      Servo_StyleSheet_FromUTF8Bytes(aLoader, this, &input, mParsingMode,
+                                     &baseString, base, referrer,
+                                     principal).Consume();
+  } else {
+    Servo_StyleSheet_ClearAndUpdate(mSheet, aLoader, this, &input, base,
+                                    referrer, principal);
+  }
 
   return NS_OK;
 }
 
 void
 ServoStyleSheet::LoadFailed()
 {
   mSheet = Servo_StyleSheet_Empty(mParsingMode).Consume();
--- a/layout/style/ServoStyleSheet.h
+++ b/layout/style/ServoStyleSheet.h
@@ -13,16 +13,20 @@
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInfo.h"
 #include "nsStringFwd.h"
 
 namespace mozilla {
 
 class ServoCSSRuleList;
 
+namespace css {
+class Loader;
+}
+
 /**
  * CSS style sheet object that is a wrapper for a Servo Stylesheet.
  */
 class ServoStyleSheet : public StyleSheet
 {
 public:
   ServoStyleSheet(css::SheetParsingMode aParsingMode,
                   CORSMode aCORSMode,
@@ -31,17 +35,18 @@ public:
 
   bool HasRules() const;
 
   void SetOwningDocument(nsIDocument* aDocument);
 
   ServoStyleSheet* GetParentSheet() const;
   void AppendStyleSheet(ServoStyleSheet* aSheet);
 
-  MOZ_MUST_USE nsresult ParseSheet(const nsAString& aInput,
+  MOZ_MUST_USE nsresult ParseSheet(css::Loader* aLoader,
+                                   const nsAString& aInput,
                                    nsIURI* aSheetURI,
                                    nsIURI* aBaseURI,
                                    nsIPrincipal* aSheetPrincipal,
                                    uint32_t aLineNumber);
 
   /**
    * Called instead of ParseSheet to initialize the Servo stylesheet object
    * for a failed load. Either ParseSheet or LoadFailed must be called before
@@ -51,16 +56,20 @@ public:
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
 #ifdef DEBUG
   void List(FILE* aOut = stdout, int32_t aIndex = 0) const;
 #endif
 
   RawServoStyleSheet* RawSheet() const { return mSheet; }
+  void SetSheetForImport(RawServoStyleSheet* aSheet) {
+    MOZ_ASSERT(!mSheet);
+    mSheet = aSheet;
+  }
 
   // WebIDL StyleSheet API
   nsMediaList* Media() final;
 
   // WebIDL CSSStyleSheet API
   // Can't be inline because we can't include ImportRule here.  And can't be
   // called GetOwnerRule because that would be ambiguous with the ImportRule
   // version.
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -3791,18 +3791,18 @@ void
 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
                              nsMediaList* aMedia,
                              RuleAppendFunc aAppendFunc,
                              void* aData,
                              uint32_t aLineNumber,
                              uint32_t aColumnNumber)
 {
   RefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec,
-                                                       aLineNumber,
-                                                       aColumnNumber);
+                                                     aLineNumber,
+                                                     aColumnNumber);
   (*aAppendFunc)(rule, aData);
 
   // Diagnose bad URIs even if we don't have a child loader.
   nsCOMPtr<nsIURI> url;
   // Charset will be deduced from mBaseURI, which is more or less correct.
   nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
 
   if (NS_FAILED(rv)) {
@@ -3810,17 +3810,19 @@ CSSParserImpl::ProcessImport(const nsStr
       // import url is bad
       REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
       OUTPUT_ERROR();
     }
     return;
   }
 
   if (mChildLoader) {
-    mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets);
+    mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule,
+                                 /* aServoParentRule = */ nullptr,
+                                 mReusableSheets);
   }
 }
 
 // Parse the {} part of an @media or @-moz-document rule.
 bool
 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
                               RuleAppendFunc aAppendFunc,
                               void* aData)
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -966,17 +966,20 @@ nsLayoutStylesheetCache::BuildPreference
 
   NS_ASSERTION(sheetText.Length() <= kPreallocSize,
                "kPreallocSize should be big enough to build preference style "
                "sheet without reallocation");
 
   if (sheet->IsGecko()) {
     sheet->AsGecko()->ReparseSheet(sheetText);
   } else {
-    nsresult rv = sheet->AsServo()->ParseSheet(sheetText, uri, uri, nullptr, 0);
+    ServoStyleSheet* servoSheet = sheet->AsServo();
+    // NB: The pref sheet never has @import rules.
+    nsresult rv =
+      servoSheet->ParseSheet(nullptr, sheetText, uri, uri, nullptr, 0);
     // Parsing the about:PreferenceStyleSheet URI can only fail on OOM. If we
     // are OOM before we parsed any documents we might as well abort.
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   }
 
 #undef NS_GET_R_G_B
 }
 
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1022,17 +1022,30 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
   if (gfxPrefs::DrawMaskLayer()) {
     gfxContextAutoSaveRestore saver(&context);
 
     context.NewPath();
     gfxRect drawingRect =
       nsLayoutUtils::RectToGfxRect(aParams.borderArea,
                                    frame->PresContext()->AppUnitsPerDevPixel());
     context.Rectangle(drawingRect, true);
-    context.SetColor(Color(0.0, 1.0, 0.0, 1.0));
+    Color overlayColor(0.0f, 0.0f, 0.0f, 0.8f);
+    if (maskUsage.shouldGenerateMaskLayer) {
+      overlayColor.r = 1.0f; // red represents css positioned mask.
+    }
+    if (maskUsage.shouldApplyClipPath ||
+        maskUsage.shouldGenerateClipMaskLayer) {
+      overlayColor.g = 1.0f; // green represents clip-path:<clip-source>.
+    }
+    if (maskUsage.shouldApplyBasicShape) {
+      overlayColor.b = 1.0f; // blue represents
+                             // clip-path:<basic-shape>||<geometry-box>.
+    }
+
+    context.SetColor(overlayColor);
     context.Fill();
   }
 
   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     context.PopClip();
   }
 
   if (shouldPushMask) {
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -130,17 +130,16 @@ static const char pluginSandboxRules[] =
   "    (global-name \"com.apple.cfprefsd.agent\")\n"
   "    (global-name \"com.apple.cfprefsd.daemon\")\n"
   "    (global-name \"com.apple.system.opendirectoryd.libinfo\")\n"
   "    (global-name \"com.apple.system.logger\")\n"
   "    (global-name \"com.apple.ls.boxd\"))\n"
   "(allow file-read*\n"
   "    (regex #\"^/etc$\")\n"
   "    (regex #\"^/dev/u?random$\")\n"
-  "    (regex #\"^/(private/)?var($|/)\")\n"
   "    (literal \"/usr/share/icu/icudt51l.dat\")\n"
   "    (regex #\"^/System/Library/Displays/Overrides/*\")\n"
   "    (regex #\"^/System/Library/CoreServices/CoreTypes.bundle/*\")\n"
   "    (regex #\"^/System/Library/PrivateFrameworks/*\")\n"
   "    (regex #\"^/usr/lib/libstdc\\+\\+\\..*dylib$\")\n"
   "    (literal \"%s\")\n"
   "    (literal \"%s\")\n"
   "    (literal \"%s\"))\n";
--- a/taskcluster/ci/toolchain/linux.yml
+++ b/taskcluster/ci/toolchain/linux.yml
@@ -18,16 +18,38 @@ linux64-clang/opt:
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     when:
         files-changed:
             - 'build/build-clang/**'
             - 'taskcluster/scripts/misc/build-clang-linux.sh'
             - 'taskcluster/taskgraph/transforms/job/toolchain.py'
 
+linux64-clang-tidy/opt:
+    description: "Clang-tidy build"
+    treeherder:
+        kind: build
+        platform: linux64/opt
+        symbol: Cc(Clang-Tidy)
+        tier: 1
+    run:
+        using: toolchain-script
+        script: build-clang-tidy-linux.sh
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+    worker:
+        implementation: docker-worker
+        docker-image: {in-tree: desktop-build}
+        max-run-time: 36000
+    when:
+        files-changed:
+            - 'build/clang-plugin/**'
+            - 'build/build-clang/**'
+            - 'taskcluster/scripts/misc/build-clang-tidy-linux.sh'
+            - 'taskcluster/taskgraph/transforms/job/toolchain.py'
+
 linux64-gcc/opt:
     description: "GCC toolchain build"
     treeherder:
         kind: build
         platform: linux64/opt
         symbol: Cc(GCC)
         tier: 1
     run:
new file mode 100755
--- /dev/null
+++ b/taskcluster/scripts/misc/build-clang-tidy-linux.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -x -e -v
+
+# This script is for building clang for Linux.
+
+WORKSPACE=$HOME/workspace
+HOME_DIR=$WORKSPACE/build
+UPLOAD_DIR=$WORKSPACE/artifacts
+
+# Fetch our toolchain from tooltool
+cd $HOME_DIR
+wget -O tooltool.py https://raw.githubusercontent.com/mozilla/build-tooltool/master/tooltool.py
+chmod +x tooltool.py
+: TOOLTOOL_CACHE                ${TOOLTOOL_CACHE:=/home/worker/tooltool-cache}
+export TOOLTOOL_CACHE
+cd src
+$HOME_DIR/tooltool.py -m browser/config/tooltool-manifests/linux64/releng.manifest fetch
+
+# gets a bit too verbose here
+set +x
+
+cd build/build-clang
+# |mach python| sets up a virtualenv for us!
+../../mach python ./build-clang.py -c clang-tidy-linux64.json
+
+set -x
+
+# Put a tarball in the artifacts dir
+mkdir -p $UPLOAD_DIR
+cp clang.tar.* $UPLOAD_DIR
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -539,27 +539,23 @@ SpecialPowersObserverAPI.prototype = {
 
         extension.on("test-message", messageListener);
 
         this._extensions.set(id, extension);
         return undefined;
       }
 
       case "SPStartupExtension": {
-        let {ExtensionData, Management} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+        let {ExtensionData} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
 
         let id = aMessage.data.id;
         let extension = this._extensions.get(id);
-        let startupListener = (msg, ext) => {
-          if (ext == extension) {
-            this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionSetId", args: [extension.id]});
-            Management.off("startup", startupListener);
-          }
-        };
-        Management.on("startup", startupListener);
+        extension.on("startup", () => {
+          this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionSetId", args: [extension.id]});
+        });
 
         // Make sure the extension passes the packaging checks when
         // they're run on a bare archive rather than a running instance,
         // as the add-on manager runs them.
         let extensionData = new ExtensionData(extension.rootURI);
         extensionData.readManifest().then(
           () => {
             return extensionData.initAllLocales().then(() => {
@@ -574,17 +570,16 @@ SpecialPowersObserverAPI.prototype = {
             // case.
           }
         ).then(() => {
           return extension.startup();
         }).then(() => {
           this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionStarted", args: []});
         }).catch(e => {
           dump(`Extension startup failed: ${e}\n${e.stack}`);
-          Management.off("startup", startupListener);
           this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionFailed", args: []});
         });
         return undefined;
       }
 
       case "SPExtensionMessage": {
         let id = aMessage.data.id;
         let extension = this._extensions.get(id);
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -808,24 +808,24 @@ HttpObserverManager = {
 
   examine(channel, topic, data) {
     let loadContext = this.getLoadContext(channel);
 
     if (this.needTracing) {
       // Check whether we've already added a listener to this channel,
       // so we don't wind up chaining multiple listeners.
       let channelData = getData(channel);
-      if (!channelData.listener && channel instanceof Ci.nsITraceableChannel) {
+      if (!channelData.hasListener && channel instanceof Ci.nsITraceableChannel) {
         let responseStatus = channel.responseStatus;
         // skip redirections, https://bugzilla.mozilla.org/show_bug.cgi?id=728901#c8
         if (responseStatus < 300 || responseStatus >= 400) {
           let listener = new StartStopListener(this, loadContext);
           let orig = channel.setNewListener(listener);
           listener.orig = orig;
-          channelData.listener = listener;
+          channelData.hasListener = true;
         }
       }
     }
 
     this.runChannelListener(channel, loadContext, "headersReceived");
   },
 
   onChannelReplaced(oldChannel, newChannel) {
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -145,8 +145,11 @@ if CONFIG['MOZ_TASK_TRACER']:
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-error=shadow',
         '-Wno-ignored-qualifiers', # due to use of breakpad headers
     ]
+
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'Gecko Profiler')
deleted file mode 100644
--- a/tools/profiler/tests/test_saved_stacks.js
+++ /dev/null
@@ -1,69 +0,0 @@
-// Test that we get `js::SavedStacks::saveCurrentStack` frames.
-
-function run_test() {
-  let p = Cc["@mozilla.org/tools/profiler;1"];
-  // Just skip the test if the profiler component isn't present.
-  if (!p)
-    return;
-  p = p.getService(Ci.nsIProfiler);
-  if (!p)
-    return;
-
-  const { saveStack } = Cu.getJSTestingFunctions();
-
-  const ms = 5;
-  p.StartProfiler(100, ms, ["js"], 1);
-
-  let then = Date.now();
-  while (Date.now() - then < 30000) {
-    function a() {
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-      saveStack();
-    }
-
-    a();
-    a();
-    a();
-    a();
-    a();
-
-    function b() {
-      a();
-    }
-
-    b();
-    b();
-    b();
-    b();
-    b();
-  }
-
-  var profile = p.getProfileData().threads[0];
-
-  do_check_neq(profile.samples.data.length, 0);
-
-  let found = false;
-  for (let sample of profile.samples.data) {
-    const stack = getInflatedStackLocations(profile, sample);
-    for (let frame of stack) {
-      if (frame.indexOf("js::SavedStacks::saveCurrentStack") >= 0) {
-        found = true;
-        break;
-      }
-    }
-  }
-  do_check_true(found);
-
-  p.StopProfiler();
-}
--- a/tools/profiler/tests/xpcshell.ini
+++ b/tools/profiler/tests/xpcshell.ini
@@ -10,10 +10,9 @@ skip-if = true
 [test_run.js]
 skip-if = true
 [test_pause.js]
 [test_enterjit_osr.js]
 [test_enterjit_osr_disabling.js]
 skip-if = !debug
 [test_enterjit_osr_enabling.js]
 skip-if = !debug
-[test_asm.js]
-[test_saved_stacks.js]
+[test_asm.js]
\ No newline at end of file